modify: worker 管理端点

This commit is contained in:
2026-08-27 09:49:47 +08:00
parent 2270f2f6e8
commit 378128fc16
10 changed files with 967 additions and 143 deletions
@@ -0,0 +1,42 @@
# 修复 Worker 单副本重启 500:优雅处理「副本未响应管理指令」
## 摘要
单副本重启端点 `POST /api/v1/admin/workers/restart` 在目标 worker 不在线/无 `__admin__` 管理能力时,`_send_admin_cmd` 抛 `RuntimeError`,端点未捕获,被全局异常处理器记录后 re-raise,最终 ASGI 返回 500 并伴随 `net::ERR_FAILED`。需让该端点对「未响应/不在线/运行旧版」返回明确的非 500 结构化错误,同时改进错误文案以便运维定位。
## 现状分析
- [worker_admin.py](file:///home/chakcy/chongming-zenoh/src/chongming/app/routes/worker_admin.py#L60-L80):`restart_worker` 直接 `await manager.restart_worker(...)`,无 try/except。对比 `restart_all`(L107 已捕获单副本失败不中断)、`update_worker_config`(L169-178 已捕获映射 4xx),唯独单副本端点无兜底。
- [worker_manager.py](file:///home/chakcy/chongming-zenoh/src/chongming/app/core/worker_manager.py#L507-L553):`_send_admin_cmd` 在 Zenoh QUERY 超时/无回复时 `raise RuntimeError("副本 <type>/<id> 未响应管理指令(可能不在线)")`(L544-547)。
- [__init__.py](file:///home/chakcy/chongming-zenoh/src/chongming/app/__init__.py#L244-L255):全局处理器仅记录 `logger.error(f"全局异常: {exc}")` 后 re-raise → 500。
- 底层:报错实例 `worker_id=worker-0-52047` 是**旧版命名**(`worker-<index>-<pid>`,无 epoch 段)。新版 runtime 才生成 `worker-<index>-<epoch>-<pid>` 并声明 `__admin__`。故该副本极可能仍运行**旧版 chongming_worker**(未加载管理能力),管理层才会未响应。
## 变更方案
### 1. 单副本重启端点兜底 [worker_admin.py](file:///home/chakcy/chongming-zenoh/src/chongming/app/routes/worker_admin.py)
- 在 `restart_worker` 中 `try: result = await manager.restart_worker(...)`。
- `except Exception as e:`(noqa BLE001)→ `logger.warning(...)` 记录;若错误含「未响应/不在线」,抛 `HTTPException(status_code=502, detail=f"重启失败: {e}(该副本可能不在线或运行旧版 Worker,未加载管理能力,请先以新版重启其进程)")`;其余错误保持映射 502/500 语义(统一用 502 + detail=str(e) 即可)。
- 效果:不再触发全局异常 handler / ASGI 500;前端 `restartOne` 已在其 catch 中 `ElMessage.error` 展示 `detail`,用户得到明确提示。
### 2. 优化下发错误文案 [worker_manager.py](file:///home/chakcy/chongming-zenoh/src/chongming/app/core/worker_manager.py)
- 将 L545-547 的超时错误文案改为:
`"副本 {worker_type}/{worker_id} 未响应管理指令(可能不在线,或该 Worker 运行旧版运行时未声明 __admin__ 管理能力)"`
- 便于网关日志与前端 detail 直接反映根因。
### 3. 前端(可选,本次不强制改)
- `restartOne` 已能在非 2xx 时展示后端 `detail`,无需改动。仅确认错误提示文案来自后端 detail 即可。
## 假设与决策
- 「未响应」属**预期可解释的失败**,应返回 5xx(502)而非 200+ok:false:前端只对非 2xx 弹错,保持「消息清晰」。
- 不修改全局异常处理器:它是通用兜底,无需为此特化。
- 不修改 alive 判定/列表逻辑:`alive=true` 仅代表 liveliness token 存在,无法区分旧版进程;根因修复依赖「用新版 runtime 重启 worker 进程」,属运维动作,不写代码。
## 验证步骤
1. `.venv/bin/python -m py_compile src/chongming/app/routes/worker_admin.py src/chongming/app/core/worker_manager.py`。
2. 重启网关(让其加载 CORS 与最新代码)。
3. 用仍为旧版命名/未响应的 worker(如 `aibox_sophon_adapter/worker-0-52047`)调用 `POST /api/v1/admin/workers/restart?worker_type=aibox_sophon_adapter&worker_id=...`:
- 期望返回 JSON `{"detail":"重启失败: 副本 ... 未响应管理指令 ... 请先以新版重启其进程"}` 且 HTTP 502,不再打印「全局异常」,前端弹明确错误。
4. 对在线且加载新版 runtime 的 worker 重启,验证仍返回 `{"success":true}`(未破坏正常路径)。
5. `cd src/chongming-web && npx vue-tsc --noEmit`(如需改前端才跑;本次预计无前端改动)。
6. (可选)确认 Worker 管理重启按钮不再触发浏览器 `net::ERR_FAILED`,而是展示后端错误消息。
## 前置/运维提示(不写代码)
- 要让 `aibox_sophon_adapter` 等 external worker 能被单副本精细化管理(重启/停止/config_set),需用本项目最新 `chongming-worker`(`uv pip install -e utils/worker` 已确保)**重启该 worker 进程**,使其加载新版 runtime 并声明 `worker/<type>/<id>/__admin__`。
+110 -2
View File
@@ -28,6 +28,7 @@
| **流媒体代理** | ffmpeg + MediaMTX RTSP 流转发 |
| **构建打包** | PyInstaller / Nuitka 一键打包为独立可执行文件 |
| **Vue 3 前端** | Element Plus UI、Pinia 状态管理、类型安全的 API 客户端 |
| **Worker 运维管理** | 前端经 Zenoh 向单个副本下发 restart/stop/改配置指令(超管),前端可视化列表 + 分页管理 |
---
@@ -207,18 +208,107 @@ uv run serve → FastAPI lifespan:
2. setup_zenoh() — Zenoh peer 会话
3. setup_mqtt_broker() — 内嵌 amqtt Broker(子进程 + 文件锁单例;
`use_embedded_broker=false` 时跳过,改用外部 Broker)
4. setup_worker_manager() — WorkerManager + 定期 liveliness 刷新
4. setup_worker_manager() — WorkerManager + 定期 liveliness 刷新 + 网关登记(register_gateway,参与全局分摊)+ 该插件副本数经 resolved_count 按 launch_worker_count 从 cache/toml 解析
5. load_workers() — 按插件 [workers].load_mode 分派:
inline → 进程内 import 插件 → 启动 Worker 协程
external → 跳过(独立进程运行,盘扫条目仅观测)
5.1 setup_route_registry() — 常驻订阅 chongming/routes/**,external 插件路由动态挂载/摘除
6. validate_contracts() — inline 插件三方契约校验,失败终止启动
7. register_routes() — inline 插件盘扫生成 HTTP 路由
8. setup_mqtt() — MQTT Bridge(inline 主题静态收集 + external 主题动态推送)
8. setup_mqtt() — MQTT Bridge(inline 主题静态收集 + external 主题动态推送;基于共享缓存的分布式锁保证全局单实例)
```
---
## Worker 管理系统
通过 Zenoh 向 `worker/<type>/<worker_id>/__admin__` 管理端点下发指令,**精细化控制单个 Worker 副本**(inline 与 external 进程插件共用同一管理协议)。提供 REST 管理端点 + Vue 前端页面,操作需超级管理员鉴权。
### 管理协议
每个用 Worker 框架(`chongming_worker.runtime.create_workers`)创建的副本都会注册一个管理 Queryable:
```text
worker/<type>/<worker_id>/__admin__
```
| 指令 | 说明 |
| ----------- | ---------------------------------------------------------------------- |
| `status` | 查询副本状态 |
| `restart` | 重启该副本,副本槽位(index)由管理循环驱动,重新生成新 `worker_id`(`worker-<index>-<epoch>-<pid>`)并经 liveliness 更新 |
| `stop` | 停止该副本 |
| `drain` | **优雅排空**:摘除 liveliness/业务 Queryable 隔绝新请求,等待正在处理的在途请求完成后再退出,管理循环不重建(数量-1) |
| `rescale` | 按 `data.scale` 把**当前进程内**该类型副本数调整为指定值(`data:{"scale":N}`),并把 `[workers].count` 写回本进程侧配置 |
| `config_set`| 更新 `[workers.config]` 运行时配置(路由配置除外);成功则写回配置并重启该类型全部对应副本,任一失败返回失败信息 |
### REST 管理端点(仅超管)
| 方法 | 路径 | 说明 |
| ------ | --------------------------------------- | -------------------------------------- |
| `GET` | `/api/v1/admin/workers` | 盘点全部副本(inline 本地注册 + external liveliness 发现) |
| `POST` | `/api/v1/admin/workers/restart` | 重启单个副本(`worker_type` / `worker_id` 查询参数) |
| `POST` | `/api/v1/admin/workers/<type>/stop` | 停止 single 副本 / 某插件全部副本 |
| `GET` | `/api/v1/admin/workers/<type>/config` | 读取该插件的现有 `[workers.config]`(展示当前值) |
| `POST` | `/api/v1/admin/workers/<type>/config` | 更新配置(body `{updates:{k:v}}`),下发并重启 |
| `POST` | `/api/v1/admin/workers/<type>/scale` | 调整该类型副本数(body `{count:N}`),扩缩容 + 持久化 |
> 旧版运行时未声明 `__admin__` 管理能力时,管理指令会超时(HTTP 502,见日志提示)。
### 配置热更新(路由配置除外)
- worker 回写插件 `config.toml` 的 `[workers]` 段(保留其他段),**拒绝写保留键**(路由/加载控制键如 `type` / `module` / `count` 等)。
- `Plugin.reload(new_config)` 在互斥锁下先 `on_cleanup()` 释放旧资源、再 `on_init()` 用新配置重建客户端(如 AI/DB client),使配置**立即生效、无需重启网关**。
- 仅 index0 副本落盘避免并发写冲突,其余副本同样应用新配置;任一失败则返回失败明细且不触发重启方式的成功确认。
### 副本数量管理(扩缩容)
同类 worker 的副本数可随时调整,并**持久化到配置**(重启网关后保持):
- **扩容**:新增副本槽位(`WorkerPool` 新增 `_managed_worker` 管理循环,新副本注册 liveliness 后可被负载均衡)。
- **缩容**:随机挑选多余副本触发 **`drain`(优雅排空)**——先摘除 liveliness/业务 Queryable **隔绝新请求**,等待正在处理的**在途请求完成**后再关闭该副本(`in-flight` 计数实现),避免中断业务;管理循环不重建。
- **目标数量持久化**:`rescale` 把 `[workers].count` 写回 `config.toml`,网关/进程重启后按新数量加载。
- **统一协议**:inline 与 external 共用。网关对 inline 直接调整本进程 `WorkerPool`;对 external 经 Zenoh 向任一存活副本下发 `rescale`,由其**所在进程内**的池扩缩并写回自身配置(无需重启整个进程/容器)。
### 全局副本数协调(多网关)
多个网关共享同一份 `chongming-cache`(根 `config.toml` 的 `[cache]` 段)时,副本数「count」语义为**该类 worker 的全局目标总量**,由 chongming-cache 作为**单一事实来源**,各网关按在线数量自动分摊。这样只在任意一台网关执行扩缩容,即可全局生效。
- **单一事实来源**:`count` 存于 cache 键 `worker:cm:counts:<type>`,在线网关 id 列表存 `worker:cm:gateways`,各网关分摊配额存 `worker:cm:quota:<type>:<gw>`(可观测)。
- **网关登记/注销 + 心跳租约**:网关启动时 `register_gateway()` 加入在线列表,并每 15s `heartbeat_gateway()` 刷新自身时间戳;超过租约(60s)未心跳的死网关会被自动清理(crash/kill -9 也不残留,避免配额失真)。当前由 `CHONGMING_GATEWAY_ID` 环境变量指定(多实例需唯一,尤其**同宿主多网关必须显式指定**;缺省回退 `hostname`,稳定且重启不变,不残留);关闭时 `unregister_gateway()` 移除,其余网关实时重算分摊。
- **分摊算法**:`own_quota()` 将全局总量按在线网关 sorted(id) 均分,余量按序补前若干。`/admin/workers/<type>/scale`(body `{count:<全局总量>}`)写全局 count 后,各网关把自己的 session 副本调到自己的 quota。
- **共享方式与后端**:单实例本机 `diskcache` 即可;**多实例必须共享同一 cache 后端**(同一共享卷的 diskcache 或 redis),否则各写各的 cache 无法收敛成全局量。
- **启动优先级开关**:根 `config.toml` 的 `[cache].launch_worker_count` 决定网关启动时副本数的来源:
```toml
[cache]
backend = "diskcache" # "diskcache"(默认) | "redis"
cache_store_path = "./cache_data"
launch_worker_count = "toml" # "toml"(默认)=启动用 toml 的 count 覆盖并写回 cache;"cache"=cache 为单一事实来源,toml 仅在缺失时引导
```
- **进程插件(external)不受该开关影响**:网关只对 **inline** 插件用 cache 解析启动副本数(见「启动流程」第 5 步 `load_workers`);external 插件由外部编排按其自身配置 `config/workers/<type>/config.toml` 的 `count` 启动,不经网关、也不读 cache。运行时 `scale` 对 external 仍走「向存活副本下发 rescale」路径,同样与该开关无关。
### 前端管理页
`Worker 管理`(`/workers`,深色主题):
- **登录**:经 auth 插件 `POST /auth/login` 领取管理 Token(无需手填 JWT),从 JWT 解析当前用户与超管标识。
- **鉴权**:仅 `is_superuser=true` 可管理;权限不足时前端/后端弹出对应提示(403)。
- **列表分页**:展示副本(插件类型 / 副本 ID / 模式 inline|external / 状态),内置分页。
- **操作**:单副本重启、批量重启(选中)、插件级重启全部副本、更新插件配置(弹窗展示现有配置值,点行带入编辑)、**调整副本数量**(当前数 + `el-input-number` 设目标 + 应用,可扩缩容)。
- **auth 核心插件**:配置不可更改,不在列表中展示(`Worker` 列表自动过滤 `worker_type == "auth"`)。
- 调小副本数会**触发优雅排空**并在前端确认提示中说明。
### external(进程)插件同样可管理
external 副本走同一 `__admin__` Queryable,路由发现、指令下发与扩缩容(`rescale`)与 inline 一致。**前提**:
1. external 容器运行的是含管理能力(`__admin__` + `reload` + `rescale`)的新版 `chongming-worker` 框架;
2. `config_set` / `rescale` 由副本在**其进程 cwd** 下写 `config/workers/<type>/config.toml`,external 容器需挂载与网关同款配置目录才能写回网关侧配置(含 `[workers].count` 持久化);
3. 纯自定义语言(如 Rust `rsdemo`)进程插件需自行实现 `__admin__` Queryable + liveliness + 数量管理,否则只能发现、无法改写配置或扩缩容。
---
## 端点类型与路由注册
### 端点类型
@@ -482,6 +572,23 @@ response_topic = "/board_ping/response" # 可选,响应主题;默认 "<
- 多条自定义主题按配置顺序匹配,先匹配者优先;内置 `chongming/...` 接口最后兜底。
- 全局 `[<env>.mqtt].custom_topics` 仍可作为通用兜底(不依赖具体插件时使用,可选)。
### MQTT Bridge 单例(多实例免重复响应)
MQTT Bridge 是**网关进程内**的 MQTT 客户端组件,负责订阅主题并把请求经 `WorkerManager` 转发给 Worker、回发 `/response`。为了让「同一条请求只被消费一次、只回一份响应」,Bridge 的启动采用**基于共享缓存的分布式锁**(`chongming-cache` 提供,`DistributedLock`),而不再使用本机文件锁。
- **抢锁机制**:启动时 `set_nx`(SET NX + TTL 租约)原子抢锁,key 为 `mqtt_bridge`;抢到锁的网关才启动 Bridge,其余网关跳过。
- **租约续约**:持锁网关后台定期 `touch` 续约;若持锁网关宕机/失联,租约到期后锁自动释放,其他网关可重新抢到(重启后生效)。
- **受保护释放**:关闭时仅当 token 仍匹配(仍是自己持有)才删除,绝不误删其他网关抢到的锁。
```toml
[cache]
backend = "redis" # 跨主机收敛必须用 redis(或共享卷 diskcache)
```
- **关键条件**:跨机器收敛依赖**共享缓存后端**(与「全局副本数协调」同一 `[cache]`,单一事实来源)。仅 `redis`(或共享卷的 `diskcache`)后端时,不同物理机上的网关才会看到同一把锁,真正全局单 Bridge。
- **`use_embedded_broker = true`(各机内嵌独立 Broker)**:各机 Broker 互不相连、本身不产生重复响应;此时全局单 Bridge 只会连到持锁网关那台机器的 Broker。
- **`use_embedded_broker = false`(共享外部 Broker)**:原先 N 台网关的 N 个 Bridge 会共同订阅同一批主题,导致一条请求被各消费一次、各回一份响应;改用分布式锁后全局只有一个 Bridge 消费,彻底规避重复响应。
### 请求负载格式(信封协议)
MQTT 请求负载为信封结构,控制字段与业务数据分离:
@@ -655,6 +762,7 @@ uv pip install -e utils/<name> # 安装/更新工具包子包
| 字段 | 位置 | 说明 |
| -------------------- | --------------------- | --------------------------------------- |
| `count` | `<插件>/config.toml` | Worker 副本数(`[workers]` 节) |
| `launch_worker_count`| `根 config.toml` | 启动时副本数来源:`"toml"`=用 toml count 覆盖 cache(默认);`"cache"`=以 cache 为单一事实来源(`[cache]` 节) |
| `router_prefix` | `<插件>/config.toml` | API 路由前缀 |
| `defaults.timeout` | `<插件>/config.toml` | 默认超时(秒) |
| `defaults.cacheable` | `<插件>/config.toml` | 是否缓存 RPC 结果 |
+8 -1
View File
@@ -17,6 +17,11 @@ job_store_path = "scheduler_jobs.db"
backend = "diskcache" # "diskcache"(默认) | "redis"
cache_store_path = "./cache_data"
# 启动时该类 worker 的全局副本数优先来源(指 config/workers/<type>/config.toml 的 count):
# "toml" (默认) = 经典模式:启动时用 toml 的 count 覆盖 cache 并写回为新的全局总量
# "cache" = 全局模式:chongming-cache 为单一事实来源,toml 仅在 cache 缺失时引导
launch_worker_count = "toml"
# redis 后端专用(backend = "redis" 时生效,均有默认值)
# redis_host = "127.0.0.1"
# redis_port = 6379
@@ -50,7 +55,9 @@ access_sample_rate = 0.1
allow_credentials = true
allow_headers = ["*"]
allow_methods = ["*"]
allow_origins = ["*"]
# 注意:allow_credentials=true 时 allow_origins 不能为 ["*"](starlette 会丢弃该 header),
# 需显式列出来源;本地前端 dev 为 http://localhost:5173。
allow_origins = ["http://localhost:5173", "http://127.0.0.1:5173"]
[development.mqtt]
enabled = true
-2
View File
@@ -56,8 +56,6 @@ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/simple/"
[[tool.uv.index]]
url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
[tool.uv.sources]
[project.scripts]
gunicorn = "chongming:gunicorn_serve"
serve = "chongming:serve"
+8 -1
View File
@@ -22,7 +22,7 @@ from chongming_config import get_config
from .core.logger import get_logger
from .core.static_files import SVFSStaticFiles
from .core import lifespan_setup, call_stats
from .routes import storage_router, stats_router
from .routes import storage_router, stats_router, worker_admin_router
config = get_config()
@@ -49,6 +49,9 @@ async def lifespan(app: FastAPI):
# 4. WorkerManager
manager = lifespan_setup.setup_worker_manager(app, session)
# 4.1 全局副本数协调:把当前网关登记进在线列表,供按在线网关分摊 quota
manager.register_gateway()
# 5. 插件加载(混合模式:inline 进程内加载;external 由独立进程运行,
# 其路由经 Zenoh 动态注册——进程编排交给 Docker/K8s,网关不拉起进程)
worker_tasks, custom_routers, inline_types, external_types = (
@@ -117,6 +120,9 @@ async def lifespan(app: FastAPI):
# 4.5 取消后台统计/审计任务
await call_stats.shutdown()
# 4.6 全局副本数协调:关闭时把当前网关从在线列表移除(其余网关将重新分摊)
manager.unregister_gateway()
# 5. 关闭 Zenoh 会话
session.close()
logger.info("Zenoh 会话已关闭")
@@ -270,3 +276,4 @@ if vfs_db_path := config[env].get("file_system", {}).get("path", None):
app.include_router(storage_router)
app.include_router(stats_router)
app.include_router(worker_admin_router)
+24 -10
View File
@@ -8,7 +8,7 @@ import asyncio
import socket
import sys
from chongming_cache import Cache
from chongming_cache import Cache, DistributedLock
from fastapi import FastAPI
import zenoh
@@ -259,7 +259,10 @@ def setup_zenoh(app: FastAPI) -> zenoh.Session:
def setup_worker_manager(app: FastAPI, session: zenoh.Session) -> WorkerManager:
"""创建 WorkerManager,启动定期刷新任务,并存入 app.state。"""
cache = Cache.from_config(config["cache"])
manager = WorkerManager(session, cache)
launch_worker_count = str(
config["cache"].get("launch_worker_count", "toml")
).lower()
manager = WorkerManager(session, cache, launch_worker_count=launch_worker_count)
app.state.worker_manager = manager
refresh_task = asyncio.create_task(manager.refresh_workers())
@@ -429,7 +432,9 @@ async def load_workers(app: FastAPI, session: zenoh.Session, manager: WorkerMana
for wc in inline_plugins:
module_name = wc["module"]
factory_name = wc["factory"]
count = wc["count"]
# count 以 chongming-cache 为单一事实来源:cache 有值时用 cache 的全局总量,
# 否则用配置值引导并写回 cache(多网关共享同一 cache 时自动对齐)。
count = manager.resolved_count(wc["type"], wc["count"])
worker_type = wc["type"]
worker_config = wc["config"]
@@ -608,12 +613,17 @@ async def setup_mqtt(app: FastAPI, worker_manager: WorkerManager) -> MQTTBridge
# Broker 来源:use_embedded_broker=true 时内嵌 amqtt Broker 子进程已在
# lifespan 第 3 步启动(setup_mqtt_broker + 文件锁单例);false 时连接外部 Broker。
# 原子锁确保只有一个 worker 启动 MQTT Bridge
if not _acquire_singleton_lock("mqtt_bridge"):
logger.info("MQTT Bridge 已由其他 worker 启动,跳过")
# 排他锁:基于共享缓存(redis / 共享卷 diskcache),保证全局只有一个 Bridge 消费。
# 相比原文件锁,可跨多台主机收敛——多实例共享同一个外部 Broker 时,
# 避免一条 MQTT 请求被 N 台网关上各自消费、各回一份响应。
bridge_lock = DistributedLock(worker_manager.cache, "mqtt_bridge")
if not bridge_lock.acquire():
logger.info(
"MQTT Bridge 已由其他网关实例持有(分布式锁),本网关跳过,"
"以规避共享 Broker 下单请求多响应"
)
return None
# 记录本进程持有锁,关闭时由持有者释放(避免误删其他 worker 的锁)
app.state.mqtt_bridge_lock_acquired = True
app.state.mqtt_bridge_lock = bridge_lock
bridge = MQTTBridge(
worker_manager=worker_manager,
@@ -625,6 +635,7 @@ async def setup_mqtt(app: FastAPI, worker_manager: WorkerManager) -> MQTTBridge
)
app.state.mqtt_bridge = bridge
await bridge.start()
bridge_lock.start() # 后台续约租约,防止持有方宕机/长暂停导致锁被错误抢走
# 把 Bridge 挂接给常驻路由注册表(动态主题 + 补推已到达的元信息)
registry = getattr(app.state, "route_registry", None)
if registry is not None:
@@ -640,8 +651,11 @@ async def shutdown_mqtt(app: FastAPI):
bridge: MQTTBridge | None = getattr(app.state, "mqtt_bridge", None)
if bridge and bridge.is_running:
await bridge.stop()
if getattr(app.state, "mqtt_bridge_lock_acquired", False):
_release_singleton_lock("mqtt_bridge")
# 停止续约并受保护释放分布式锁(仅本网关仍持有时删除)
bridge_lock = getattr(app.state, "mqtt_bridge_lock", None)
if bridge_lock is not None:
await bridge_lock.stop()
bridge_lock.release()
await shutdown_mqtt_broker(app)
+420 -1
View File
@@ -1,7 +1,10 @@
import asyncio
import concurrent.futures
import hashlib
import os
import random
import socket
import time
import orjson
from typing import Any, AsyncGenerator, Dict, List, Optional
import zenoh
@@ -10,6 +13,17 @@ from .logger import get_logger
logger = get_logger("worker_manager")
# ── 全局副本数(chongming-cache 单一事实来源)──
# 多网关共用同一 cache(单实例=本地 diskcache,多实例=共享卷/redis),
# 「count」语义 = 该类 worker 的**全局目标总量**,各网关按在线数量分摊。
_SCALE_DESIRED_KEY = "worker:cm:counts:{type}" # 全局目标总量(int)
_SCALE_GATEWAYS_KEY = "worker:cm:gateways" # 在线网关列表:[[id, 最近心跳 ts], ...](JSON)
_SCALE_QUOTA_KEY = "worker:cm:quota:{type}:{gw}" # 各网关分摊配额(int)
# 在线网关租约(秒):网关心跳每 refresh_interval(15s) 更新一次自身 ts,超过 LEASE 未刷新
# 视为离线的死网关,在下一次读写时被清理。容忍多次漏跳,且 crash/kill -9 不残留。
_GATEWAY_LEASE = 60
_SENTINEL = object() # 哨兵值,用于标记"No reply received"
# Zenoh RPC 专用线程池:隔离于 asyncio.to_thread 的全局默认池
@@ -50,13 +64,133 @@ class WorkerManager:
2. 本地:通过 register_worker() 直接注册(用于同进程 worker)
"""
def __init__(self, session: zenoh.Session, cache: Optional[Cache] = None):
def __init__(self, session: zenoh.Session, cache: Optional[Cache] = None,
launch_worker_count: str = "toml"):
self.session = session
self.cache = cache
# 启动时全局副本数的优先来源:"toml"=用 toml 的 count 覆盖 cache(经典模式);
# "cache"=以 cache 为单一事实来源。对应 config.toml [cache].launch_worker_count。
self._launch_worker_count = launch_worker_count
self._workers_cache: Dict[str, List[str]] = {} # type -> list of worker key_exprs
self._local_workers: Dict[str, List[str]] = {} # type -> list of locally registered workers
self._lock = asyncio.Lock()
self._refresh_interval = 15 # seconds
# 当前网关心跳标识:优先环境变量(多实例需唯一,尤其同宿主多网关须显式指定),
# 否则回退 hostname(稳定,重启不变——避免用 PID 导致每次重启标识变化、旧条目
# 残留撑大在线列表,进而让配额计算失真把副本全排空)。用于全局副本数按在线网关分摊。
self._gateway_id: str = (
os.environ.get("CHONGMING_GATEWAY_ID") or socket.gethostname()
)
# ── 全局副本数协调(chongming-cache 单一事实来源)────────────
def _scale_desired_key(self, worker_type: str) -> str:
return _SCALE_DESIRED_KEY.format(type=worker_type)
def _scale_quota_key(self, worker_type: str) -> str:
return _SCALE_QUOTA_KEY.format(type=worker_type, gw=self._gateway_id)
def _read_gateways_raw(self) -> List[List]:
"""读取在线网关原始列表 [[id, ts], ...]。"""
if self.cache is None:
return []
return list(self.cache.get(_SCALE_GATEWAYS_KEY) or [])
def _write_gateways(self, gws: List[List]) -> None:
"""写回在线网关列表(去重,按 id 稳定)。"""
if self.cache is None:
return
dedup = {}
for gw_id, ts in gws:
dedup.setdefault(gw_id, ts) # 保序去重
self.cache[_SCALE_GATEWAYS_KEY] = [[gw_id, ts] for gw_id, ts in dedup.items()]
def _prune_gateways(self, gws: List[List]) -> List[List]:
"""剔除超过租约未心跳的死网关,写回清理结果。
兼容旧版残留:旧格式为纯字符串 id 列表(无心跳 ts),视为失效,仅保留
[[id, ts], ...] 两元组且 ts 在租约内的在线网关。
"""
deadline = time.time() - _GATEWAY_LEASE
alive: List[List] = []
for entry in gws:
if not isinstance(entry, (list, tuple)) or len(entry) != 2:
continue # 旧格式/畸形条目视为失效,一并清理
gw_id, ts = entry
if isinstance(ts, (int, float)) and isinstance(gw_id, str) and ts >= deadline:
alive.append([gw_id, ts])
if len(alive) != len(gws):
self._write_gateways(alive)
return alive
def _gateways(self) -> List[str]:
"""读取在线网关 id 列表(先按心跳租约清理僵尸条目,杜绝死网关残留)。"""
gws = self._prune_gateways(self._read_gateways_raw())
return [gw_id for gw_id, _ in gws]
def register_gateway(self) -> None:
"""把当前网关登记进在线列表(带当前心跳时间戳),参与全局分摊。启动时调用一次。"""
if self.cache is None:
return
gws = self._prune_gateways(self._read_gateways_raw())
gws = [[gw_id, ts] for gw_id, ts in gws if gw_id != self._gateway_id]
gws.append([self._gateway_id, time.time()])
self._write_gateways(gws)
def heartbeat_gateway(self) -> None:
"""周期性地刷新自身心跳 ts,保持在线;同时清理死网关。由 refresh_workers 循环调用。"""
if self.cache is None:
return
gws = self._prune_gateways(self._read_gateways_raw())
gws = [[gw_id, ts] for gw_id, ts in gws if gw_id != self._gateway_id]
gws.append([self._gateway_id, time.time()])
self._write_gateways(gws)
def unregister_gateway(self) -> None:
"""关闭时从在线列表移除当前网关(并清理死网关)。"""
if self.cache is None:
return
gws = self._prune_gateways(self._read_gateways_raw())
gws = [[gw_id, ts] for gw_id, ts in gws if gw_id != self._gateway_id]
self._write_gateways(gws)
def resolved_count(self, worker_type: str, default: int) -> int:
"""获取该类 worker 的全局目标总量。
依 [cache].launch_worker_count 决定启动优先级:
- "toml"(经典):始终用 toml 的 count(default)覆盖 cache 并写回为新的全局总量;
- "cache"(全局):cache 为单一事实来源,缺失时才用 toml 引导并写回。
"""
if self.cache is None:
return int(default)
key = self._scale_desired_key(worker_type)
total = max(0, int(default))
if self._launch_worker_count == "toml":
# 经典模式:toml count 覆盖并写回 cache
self.cache[key] = total
return total
if key in self.cache:
return int(self.cache[key])
self.cache[key] = total
return total
def own_quota(self, worker_type: str, total: int) -> int:
"""在「count=全局总量」语义下,计算当前网关应承担的副本数(分摊)。
单网关(本场景)即总量本身;多网关按在线 id 排序均分,余量按序补前若干。
结果写回 cache(observability),并在全局下发给各网关后各自按其 quota 调整。
"""
total = max(0, int(total))
gws = sorted(self._gateways())
if not gws:
quota = total
else:
base, rem = divmod(total, len(gws))
idx = gws.index(self._gateway_id) if self._gateway_id in gws else 0
quota = base + (1 if idx < rem else 0)
if self.cache is not None:
self.cache[self._scale_quota_key(worker_type)] = quota
return quota
def register_worker(self, worker_type: str, worker_id: str):
"""
@@ -79,6 +213,11 @@ class WorkerManager:
self._local_workers[worker_type].remove(key_expr)
except ValueError:
pass
# 同步从存活缓存剔除:已关闭/被 drain 的 inline 副本若仍留在 _workers_cache,
# 会在下次 liveliness 刷新(15s)前被 list_workers 误判为 external 并残留显示。
cache = self._workers_cache.get(worker_type)
if cache and key_expr in cache:
self._workers_cache[worker_type] = [k for k in cache if k != key_expr]
async def refresh_workers(self):
"""定期刷新存活 worker 列表(合并 Zenoh liveliness + 本地注册)"""
@@ -105,6 +244,11 @@ class WorkerManager:
async with self._lock:
self._workers_cache = new_cache
logger.debug(f"Worker 存活列表更新: {self._workers_cache}")
# 3. 网关心跳:刷新自身在线时间戳并清理死网关,保持在线列表准确
try:
self.heartbeat_gateway()
except Exception as e: # noqa: BLE001
logger.error(f"网关心跳失败: {e}")
except Exception as e:
logger.error(f"刷新 worker 列表失败: {e}")
await asyncio.sleep(self._refresh_interval)
@@ -448,3 +592,278 @@ class WorkerManager:
f"Worker '{worker_type}' 调用失败 (已尝试 {len(tried_workers)} 个 Worker, "
f"共 {retries + 1} 次, timeout={timeout}s): {last_error}"
)
# ------------------------------------------------------------------
# 管理能力:盘点 worker 实例 + 精细化控制单个副本(admin Queryable)
#
# 单副本协议(worker 框架声明):worker/<type>/<worker_id>/__admin__
# POST {"data": {"cmd": "restart"}} 平滑退出并以新 worker_id 重建
# POST {"data": {"cmd": "stop"}} 平滑退出且不自动重建
# (无 cmd / GET) 返回副本实时状态
# ------------------------------------------------------------------
def list_workers(self) -> List[Dict[str, Any]]:
"""
盘点当前全部 worker 实例。
合并 Zenoh liveliness 与本地注册表:
- 仅出现在本地注册表 / 也出现在 liveliness 的 = inline(网关进程内加载)
- 仅出现在 liveliness 的 = external(独立进程)
每个实例含 worker_type / worker_id / mode / alive。
Returns:
List[{"worker_type", "worker_id", "mode"}]
"""
result: List[Dict[str, Any]] = []
# 本地注册(inline):type -> [key],key 形如 "worker/<type>/<id>"
local_keys: Dict[str, List[str]] = {
t: list(v) for t, v in self._local_workers.items()
}
for wtype, keys in local_keys.items():
for key in keys:
parts = key.split("/")
wid = parts[2] if len(parts) >= 3 else key
result.append({
"worker_type": wtype,
"worker_id": wid,
"mode": "inline",
"alive": True,
})
# liveliness(inline 亦会出现,去重;其余为 external)
known = {(r["worker_type"], r["worker_id"]) for r in result}
for wtype, keys in self._workers_cache.items():
for key in keys:
parts = key.split("/")
if len(parts) < 3:
continue
wid = parts[2]
if (wtype, wid) in known:
continue
result.append({
"worker_type": wtype,
"worker_id": wid,
"mode": "external",
"alive": True,
})
result.sort(key=lambda w: (w["worker_type"], w["worker_id"]))
return result
async def _send_admin_cmd(
self,
worker_type: str,
worker_id: str,
cmd: str,
timeout: float = 5.0,
data_extra: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
"""向单个副本的 admin Queryable 下发指令并等待回复。
Args:
worker_type: Worker 类型
worker_id: 副本 id(如 worker-0-1-12345)
cmd: "restart" / "stop" / "config_set"
timeout: 单次下发超时(秒)
data_extra: 追加到 data 字段的附加载荷(如 config_set 的 config)
Returns:
worker 侧回复(dict)
Raises:
RuntimeError: 无可用副本 / 下发失败 / 超时
"""
admin_key = f"worker/{worker_type}/{worker_id}/__admin__"
data = {"cmd": cmd, **(data_extra or {})}
payload = orjson.dumps({"meta": {"method": "POST"}, "data": data})
reply_iter = self.session.get(
admin_key,
payload=payload,
consolidation=zenoh.ConsolidationMode.NONE,
timeout=timeout,
)
loop = asyncio.get_running_loop()
reply = await asyncio.wait_for(
loop.run_in_executor(_ZENOH_EXECUTOR, _next_reply, reply_iter),
timeout=timeout,
)
if reply is _SENTINEL:
raise RuntimeError(
f"副本 {worker_type}/{worker_id} 未响应管理指令(可能不在线,"
"或该 Worker 运行旧版运行时未声明 __admin__ 管理能力)"
)
if reply.ok: # type: ignore
data = bytes(reply.ok.payload) if reply.ok.payload else b"{}" # type: ignore
return orjson.loads(data)
error_payload = reply.err.payload # type: ignore
error_msg = str(error_payload) if error_payload else "unknown error"
raise RuntimeError(f"副本 {worker_type}/{worker_id} 管理指令失败: {error_msg}")
async def restart_worker(
self,
worker_type: str,
worker_id: str,
timeout: float = 5.0,
) -> Dict[str, Any]:
"""精细化重启单个 worker 副本(经 Zenoh 下发 restart,副本以新 id 重建)。"""
return await self._send_admin_cmd(worker_type, worker_id, "restart", timeout)
async def stop_worker(
self,
worker_type: str,
worker_id: str,
timeout: float = 5.0,
) -> Dict[str, Any]:
"""精细化停止单个 worker 副本(经 Zenoh 下发 stop,副本退出且不自动重建)。"""
return await self._send_admin_cmd(worker_type, worker_id, "stop", timeout)
async def scale_workers(
self,
worker_type: str,
count: int,
timeout: float = 5.0,
) -> Dict[str, Any]:
"""把某类型的副本数调整为 count(扩缩容)并持久化到配置。
下发逻辑(与 inline/external 的统一管理协议一致):
- inline:类型在本进程内有 WorkerPool,可直接调用其 set_target(进程内扩缩,
无需经过 Zenoh 往返);
- external:类型由独立进程/容器运行,向该类型任一存活副本经 Zenoh 下发
rescale 指令,由其**所在进程内**的 WorkerPool 扩缩并写回自身配置。
缩容走优雅排空:摘除 liveliness/业务 Queryable 隔绝新请求,等正在处理的
在途请求完成后再关闭副本(避免中断业务)。
Args:
worker_type: 插件类型
count: 目标副本数(>= 0)
timeout: 下发超时(秒)
Returns:
{"worker_type", "target"(全局总量), "quota", "mode"..., "persist"|...}
Raises:
RuntimeError: count 非法 / 类型无可用副本或本进程无其池
"""
count = max(0, int(count))
# 「count=全局总量」:写全局 cache 单一来源;本网关按在线网关分摊承担 quota。
if self.cache is not None:
self.cache[self._scale_desired_key(worker_type)] = count
quota = self.own_quota(worker_type, count)
logger.info(
"[scale] %s 全局目标=%d,本网关分摊 quota=%d", worker_type, count, quota
)
# inline:优先走本进程内池(按分摊配额),直接且即时应答
try:
from chongming_worker.runtime import get_process_pool
pool = get_process_pool(worker_type)
except Exception: # noqa: BLE001 - 运行时缺依赖时退回 Zenoh 下发
pool = None
if pool is not None:
await pool.set_target(quota)
persist = "未持久化"
if self.cache is not None:
persist = f"count={count}(全局)已写 cache,本网关 quota={quota}"
else:
persist = f"cache 未启用,quota={quota}(重启后将按配置还原)"
logger.info(
"[scale] %s inline 池已调整至 %d 副本(全局目标 %d)",
worker_type, quota, count,
)
return {
"worker_type": worker_type,
"target": count,
"quota": quota,
"mode": "inline",
"reply": {"status": "accepted", "cmd": "rescale", "persist": persist},
}
# external(或本进程无池):下发 rescale 给任一存活副本,由其进程内池扩缩+持久化
candidate = next(
(
w for w in self.list_workers()
if w["worker_type"] == worker_type and w.get("alive")
),
None,
)
if candidate is None:
raise RuntimeError(f"类型 '{worker_type}' 没有可用副本,无法调整数量")
reply = await self._send_admin_cmd(
worker_type, candidate["worker_id"], "rescale",
timeout=timeout, data_extra={"scale": count},
)
return {
"worker_type": worker_type,
"target": count,
"mode": "external",
"sent_to": candidate["worker_id"],
"reply": reply,
}
async def update_worker_config(
self,
worker_type: str,
updates: Dict[str, Any],
timeout: float = 5.0,
) -> Dict[str, Any]:
"""经 Zenoh 下发 config_set 指令,让目标类型的全部副本修改配置并自我重启。
worker 侧行为(见 chongming_worker.runtime):
- 仅 index0 副本把 updates 写入插件 config.toml 的 [workers.config](原子写回,
路由等保留键会被拒绝);
- 所有副本合并 updates 后以新配置重启(新 worker_id)。
本方法只负责"下发 + 汇总",写操作由 worker 触发执行,故进程插件位于
不同路径/宿主机时也可正确写回各自的配置文件。
Args:
worker_type: 插件类型
updates: 待写入 [workers.config] 的键值({"key": value})
timeout: 单副本下发超时(秒)
Returns:
{
"total": 下发副本数,
"failed": 失败数, "ok": bool,
"results": [{worker_id, ok, reply|error}],
"errors": [错误信息列表]
}
Raises:
RuntimeError: 该类型无任何存活副本
"""
candidates = [
w for w in self.list_workers()
if w["worker_type"] == worker_type
]
if not candidates:
raise RuntimeError(f"类型 '{worker_type}' 没有可用 Worker,无法更新其配置")
results: List[Dict[str, Any]] = []
errors: List[str] = []
for w in candidates:
try:
reply = await self._send_admin_cmd(
worker_type, w["worker_id"], "config_set",
timeout=timeout, data_extra={"config": updates},
)
ok = reply.get("status") == "accepted"
if not ok:
errors.append(
f"{w['worker_id']}: {reply.get('error', '未知错误')}"
)
results.append({
"worker_id": w["worker_id"], "ok": ok, "reply": reply,
})
except Exception as e: # noqa: BLE001 - 单副本失败不中断其他副本
errors.append(f"{w['worker_id']}: {e}")
results.append({
"worker_id": w["worker_id"], "ok": False, "error": str(e),
})
return {
"total": len(results),
"failed": len(errors),
"ok": not errors,
"results": results,
"errors": errors,
}
+2 -1
View File
@@ -1,4 +1,5 @@
from .storage import router as storage_router
from .stats import router as stats_router
from .worker_admin import router as worker_admin_router
__all__ = ["storage_router", "stats_router"]
__all__ = ["storage_router", "stats_router", "worker_admin_router"]
+238
View File
@@ -0,0 +1,238 @@
"""
Worker 管理端点(插件 / 单副本重启)
提供插件实例盘点与"精细化重启单个 worker 副本"的能力:
- 经 Zenoh 下发管理指令到目标的 admin Queryable(worker/<type>/<id>/__admin__)
- 仅超级管理员(JWT payload.is_superuser)可访问
协议说明(见 chongming_worker.runtime 单副本管理协议):
- 每个 worker 副本在 worker/<type>/<worker_id>/__admin__ 注册内部 Queryable
- {"data": {"cmd": "restart"}} → 副本平滑退出并以新 worker_id 重建
- {"data": {"cmd": "stop"}} → 副本平滑退出且不自动重建
"""
from fastapi import APIRouter, Depends, HTTPException, Request
from pydantic import BaseModel, Field
import pathlib
import tomllib
from chongming_config import get_config
from ..core.auth import jwt_auth_dependency
from ..core.logger import get_logger
config = get_config()
logger = get_logger("worker_admin")
router = APIRouter(prefix=config["default"]["prefix"], tags=["worker_admin"])
def require_admin(
payload: dict = Depends(jwt_auth_dependency),
) -> dict:
"""仅超级管理员可操作(重启属高风险操作,普通登录用户无权限)。"""
if not payload.get("is_superuser"):
raise HTTPException(
status_code=403,
detail="需要超级管理员权限 (is_superuser)",
)
return payload
def _get_manager(request: Request):
"""从应用状态获取 WorkerManager;未初始化时(lifespan 外)报 503。"""
manager = getattr(request.app.state, "worker_manager", None)
if manager is None:
raise HTTPException(status_code=503, detail="WorkerManager 尚未就绪")
return manager
@router.get("/admin/workers", summary="盘点 Worker 实例")
async def list_workers(
request: Request,
_admin: dict = Depends(require_admin),
):
"""返回全部插件实例(worker_type / worker_id / mode / alive),供前端渲染与选择。"""
manager = _get_manager(request)
return {"workers": manager.list_workers(), "success": True}
@router.post("/admin/workers/restart", summary="重启单个 Worker 副本")
async def restart_worker(
request: Request,
worker_type: str,
worker_id: str,
_admin: dict = Depends(require_admin),
):
"""精细化重启单个 worker 副本:经 Zenoh 下发 restart,副本以新 worker_id 重建。
- worker_type: 插件类型(如 example)
- worker_id: 副本 id(如 worker-0-1-12345),由 GET /admin/workers 提供
"""
manager = _get_manager(request)
try:
result = await manager.restart_worker(worker_type, worker_id)
except RuntimeError as e:
logger.warning(
"重启 Worker 副本失败: %s/%s 用户=%s 错误=%s",
worker_type, worker_id,
_admin.get("username", _admin.get("sub", "-")),
e,
)
raise HTTPException(
status_code=502,
detail=f"重启失败: {e}",
)
logger.info(
"重启 Worker 副本: %s/%s 用户=%s 结果=%s",
worker_type, worker_id,
_admin.get("username", _admin.get("sub", "-")),
result,
)
return {"success": True, "result": result}
@router.post("/admin/workers/{worker_type}/restart", summary="重启整个插件(全部副本)")
async def restart_all(
request: Request,
worker_type: str,
_admin: dict = Depends(require_admin),
):
"""重启某插件的全部副本(逐个下发 restart,各自以新 id 重建)。
注意:网关进程内加载的 inline 插件副本经本接口重启,仅重建 worker 协程,
不会卸载插件;external 插件同理逐个副本重启。
"""
manager = _get_manager(request)
candidates = [
w for w in manager.list_workers()
if w["worker_type"] == worker_type
]
if not candidates:
raise HTTPException(status_code=404, detail=f"无类型为 {worker_type} 的 worker")
results = []
failed = []
for w in candidates:
try:
res = await manager.restart_worker(worker_type, w["worker_id"])
results.append({"worker_id": w["worker_id"], "ok": True, "result": res})
except Exception as e: # noqa: BLE001 - 批量重启不因单个失败中断
failed.append({"worker_id": w["worker_id"], "ok": False, "error": str(e)})
results.append({"worker_id": w["worker_id"], "ok": False, "error": str(e)})
logger.info(
"重启插件全部副本: %s (成功 %d/%d) 用户=%s",
worker_type, len(results) - len(failed), len(results),
_admin.get("username", _admin.get("sub", "-")),
)
return {"success": not failed, "total": len(results), "failed": len(failed), "results": results}
class ScaleBody(BaseModel):
"""副本数量调整请求体:目标副本数。"""
count: int = Field(..., ge=0, description="目标副本数(>=0)")
@router.post("/admin/workers/{worker_type}/scale", summary="调整同类 worker 副本数量(扩缩容)")
async def scale_workers(
request: Request,
worker_type: str,
body: ScaleBody,
_admin: dict = Depends(require_admin),
):
"""把某类型的副本数调整为 body.count 并持久化到配置。
- 扩容新增副本;缩容随机关闭副本,并**优雅排空**(摘除 liveliness/业务
Queryable 隔绝新请求,等待正在处理的在途请求完成后再关闭)。
- inline 直接调整本进程 Worker 池;external 经 Zenoh 下发 rescale 到存活副本,
由其进程内池扩缩并写回自身配置。
"""
manager = _get_manager(request)
try:
result = await manager.scale_workers(worker_type, body.count)
except Exception as e: # noqa: BLE001 - 无副本/失败映射为 4xx
logger.warning(
"调整副本数量失败: %s -> %d 用户=%s 错误=%s",
worker_type, body.count,
_admin.get("username", _admin.get("sub", "-")),
e,
)
raise HTTPException(status_code=404, detail=str(e))
logger.info(
"调整副本数量: %s -> %d 用户=%s 结果=%s",
worker_type, body.count,
_admin.get("username", _admin.get("sub", "-")),
result,
)
return {"success": True, "result": result}
class ConfigUpdateBody(BaseModel):
"""配置更新请求体:写目标插件 config.toml 的 [workers.config] 段。"""
updates: dict = Field(..., description="待写入 [workers.config] 的键值(路由/加载控制键除外)")
@router.get("/admin/workers/{worker_type}/config", summary="读取插件现有运行时配置")
async def read_worker_config(
request: Request,
worker_type: str,
_admin: dict = Depends(require_admin),
):
"""读取某插件 config.toml 的 [workers.config] 现有值(供前端展示)。
直接从插件配置文件读取,反映最近一次落盘结果(含 worker 写回后的值)。
路由配置(add.toml 等)不在此范围。
- worker_type: 插件类型(如 example)
"""
path = pathlib.Path("config/workers") / worker_type / "config.toml"
if not path.exists():
raise HTTPException(status_code=404, detail=f"插件 {worker_type} 的配置文件不存在: {path}")
with open(path, "rb") as f:
data = tomllib.load(f)
workers = data.get("workers") or {}
cfg = workers.get("config")
return {
"worker_type": worker_type,
"config": cfg if isinstance(cfg, dict) else {},
"success": True,
}
@router.post("/admin/workers/{worker_type}/config", summary="更新插件运行时配置(下发至 worker)")
async def update_worker_config(
request: Request,
worker_type: str,
body: ConfigUpdateBody,
_admin: dict = Depends(require_admin),
):
"""更新某插件的运行时配置 `[workers.config]`(路由配置 add.toml 等除外)。
流程:网关经 Zenoh 下发 config_set 给该类型全部副本 → 由 worker 触发写回各自
配置文件(仅 index0 落盘避免同文件并发写)→ 写成功则以新配置重启所有对应副本;
任一失败则返回失败明细(error 信息)。
- worker_type: 插件类型(如 example)
- body.updates: 待更新的键值,如 {"data_dir": "./data2"}
"""
manager = _get_manager(request)
if not body.updates:
raise HTTPException(status_code=422, detail="updates 不能为空")
try:
result = await manager.update_worker_config(worker_type, body.updates)
except Exception as e: # noqa: BLE001 - 无副本等运行时错误映射为 4xx
logger.warning(
"更新插件配置失败: %s 用户=%s 错误=%s",
worker_type,
_admin.get("username", _admin.get("sub", "-")),
e,
)
raise HTTPException(status_code=404, detail=str(e))
logger.info(
"更新插件配置: %s\n updates=%s\n 结果=成功 %d/%d (失败: %s) 用户=%s",
worker_type,
body.updates,
result["total"] - result["failed"], result["total"],
result["errors"],
_admin.get("username", _admin.get("sub", "-")),
)
return result
Generated
+115 -125
View File
@@ -431,20 +431,20 @@ wheels = [
[[package]]
name = "chongming-cache"
version = "0.1.0"
version = "0.2.0"
source = { registry = "http://124.71.68.6:3000/api/packages/chongming/pypi/simple/" }
dependencies = [
{ name = "diskcache" },
{ name = "redis" },
]
sdist = { url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-cache/0.1.0/chongming_cache-0.1.0.tar.gz", hash = "sha256:e81e046b3b9cf05699a579aac697c29a647c0ecbb310a5d5ffa17a14b2a6eb5c" }
sdist = { url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-cache/0.2.0/chongming_cache-0.2.0.tar.gz", hash = "sha256:7b092c6b96d67f50aac1754c364e4a647efa089bf9dbbc989d0f5993925dec66" }
wheels = [
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-cache/0.1.0/chongming_cache-0.1.0-py3-none-any.whl", hash = "sha256:7c04f3ad5cbfd9d6a99541e6d7ab14423c2851c2cde50fca5b70f5c2f4ad031e" },
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-cache/0.2.0/chongming_cache-0.2.0-py3-none-any.whl", hash = "sha256:4e95eddbb691479b23559020375c8d041a03b4a4c6163df1a7f6b5150b9e469e" },
]
[[package]]
name = "chongming-cli"
version = "0.1.0"
version = "0.1.2"
source = { registry = "http://124.71.68.6:3000/api/packages/chongming/pypi/simple/" }
dependencies = [
{ name = "chongming-config" },
@@ -452,9 +452,9 @@ dependencies = [
{ name = "sqlite-vfs" },
{ name = "tomli" },
]
sdist = { url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-cli/0.1.0/chongming_cli-0.1.0.tar.gz", hash = "sha256:bef08a46b4ecbb6d3a92ccb7afc3d4d7f77a6f09b9a09ab056dd6c9bf04e8361" }
sdist = { url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-cli/0.1.2/chongming_cli-0.1.2.tar.gz", hash = "sha256:e169b81cb8c5dc6198c685b963b862f7cd27a2ac273da789fc23cbe027323265" }
wheels = [
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-cli/0.1.0/chongming_cli-0.1.0-py3-none-any.whl", hash = "sha256:94bcd6dd0967d6403b56bdc0e23c9861e3d4797edf66c949889f998a34fc55ce" },
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-cli/0.1.2/chongming_cli-0.1.2-py3-none-any.whl", hash = "sha256:f72edb901ba1b355e2937f7fb1e9a9da4144c328bba59ea97421b5041b565e67" },
]
[[package]]
@@ -489,7 +489,9 @@ name = "chongming-jwt"
version = "0.1.0"
source = { registry = "http://124.71.68.6:3000/api/packages/chongming/pypi/simple/" }
wheels = [
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-jwt/0.1.0/chongming_jwt-0.1.0-cp312-cp312-linux_aarch64.whl", hash = "sha256:0512956acaa7a17aaf9708da8d7b3a7dc366ef0226f03550d7b1b06940ba733b" },
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-jwt/0.1.0/chongming_jwt-0.1.0-cp312-cp312-linux_x86_64.whl", hash = "sha256:527676e710d038f270093d0964c92fc5e976f1f5db23e846e3819b1c8a566721" },
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-jwt/0.1.0/chongming_jwt-0.1.0-cp312-cp312-manylinux_2_34_aarch64.whl", hash = "sha256:d2329de197af17141048da32b52dcda87ff578353b4fae78c3bbd0043244cdfc" },
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-jwt/0.1.0/chongming_jwt-0.1.0-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:d63cc24550b7084e55d4dd0cb9f99cbd0a9d77da39ad0685698fd484e4376e72" },
]
@@ -510,7 +512,9 @@ name = "chongming-password"
version = "0.1.0"
source = { registry = "http://124.71.68.6:3000/api/packages/chongming/pypi/simple/" }
wheels = [
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-password/0.1.0/chongming_password-0.1.0-cp312-cp312-linux_aarch64.whl", hash = "sha256:752651cce38a999466e9b342f18f0ceb78b17907646440d2b1c6306ebc9c7648" },
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-password/0.1.0/chongming_password-0.1.0-cp312-cp312-linux_x86_64.whl", hash = "sha256:97414ddb764a8efa0183eb6a4519530a8522729ff633a06285a88b7b22fa77b4" },
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-password/0.1.0/chongming_password-0.1.0-cp312-cp312-manylinux_2_34_aarch64.whl", hash = "sha256:65fbe21f0d9cd152e0142b129e23b1697234f5eeba6a9d0ded1a14949849cca3" },
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-password/0.1.0/chongming_password-0.1.0-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:9620458d04e1cc9c93842f7b960b2b7af8b9bc5f88643e785d12686301071b3c" },
]
@@ -537,17 +541,18 @@ wheels = [
[[package]]
name = "chongming-worker"
version = "0.3.0"
version = "0.4.0"
source = { registry = "http://124.71.68.6:3000/api/packages/chongming/pypi/simple/" }
dependencies = [
{ name = "eclipse-zenoh" },
{ name = "minio" },
{ name = "pydantic" },
{ name = "tomli" },
{ name = "tomli-w" },
]
sdist = { url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-worker/0.3.0/chongming_worker-0.3.0.tar.gz", hash = "sha256:5c9c9c92fca65ba4836460e81d31f31fdaf3f01407ecfa342d950262d02b6195" }
sdist = { url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-worker/0.4.0/chongming_worker-0.4.0.tar.gz", hash = "sha256:c2b13fc1c2eb262f0ca5a1b64035bff1881a912c5a95446a9ce77b1ba968b03b" }
wheels = [
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-worker/0.3.0/chongming_worker-0.3.0-py3-none-any.whl", hash = "sha256:2886b2ed3e44897489e99af2a3ce6ffebcea7210dce131760cc5e802c472be82" },
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/chongming-worker/0.4.0/chongming_worker-0.4.0-py3-none-any.whl", hash = "sha256:96e1dd6b8c22a294b83b54d8ac3723f4555865625ed210a6e1974bc6ac3c4a9d" },
]
[[package]]
@@ -635,14 +640,11 @@ dev = [
[[package]]
name = "click"
version = "8.4.2"
version = "8.5.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" },
]
[[package]]
@@ -656,52 +658,52 @@ wheels = [
[[package]]
name = "cryptography"
version = "50.0.0"
version = "50.0.1"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bb/ad/5d6702db60b1e40b41ef513b6967ff5848f307d50f8449baf1634f5908f1/cryptography-50.0.1.tar.gz", hash = "sha256:5dd9bda1c12b4162f6ff568eeb5e0ff956c28d14406e875cfe8a63a2d414ff20", size = 880381, upload-time = "2026-08-25T19:45:45.499Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c5/5c/59086b4aac5e879d38ddbcf74e4be7ade89cebc3eb199a55da998c3bb46a/cryptography-50.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:031e2d5dd4bb9caa3ca9c82e5a197fd8ae680232cee62603d1a813f3f07e3d03", size = 4001252, upload-time = "2026-07-31T14:23:33.331Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/57/ef/8f2df13c7216bcad3e1c74e07f6e193d93e998e114f524a53877c9af27ad/cryptography-50.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645", size = 4719554, upload-time = "2026-07-31T14:23:35.611Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d9/41/029086c34d91052fc3b88bcc8056f709a7c915c7a23b235a54eb800b1c97/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7", size = 4702130, upload-time = "2026-07-31T14:23:37.635Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7d/ff/b6ce0954962e7f7b969f850a883744197bb3910bdfd7b6da162eab7d9f68/cryptography-50.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3", size = 4725244, upload-time = "2026-07-31T14:23:39.471Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/06/1e/63a1027cb7fec360a182208e1b7767d5aa1fe57be3d6aa856e69a321edc0/cryptography-50.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f", size = 5342265, upload-time = "2026-07-31T14:23:41.286Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6b/72/a1116d683a6d7ece94590013882515de087edf9ef0e6292aae615a44df73/cryptography-50.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae", size = 4734609, upload-time = "2026-07-31T14:23:43.139Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/15/37/36a9c479bbe49acea2636c7fd3360d20f7b7e079c300352011c44850b181/cryptography-50.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a", size = 4356517, upload-time = "2026-07-31T14:23:44.939Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/32/98/8a151d64367204cbc63ec65d37502f1d9c53cf4bfc6ec3c532614dbec60d/cryptography-50.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987", size = 4724529, upload-time = "2026-07-31T14:23:46.93Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/22/f6/ec13b470172126464a86bf54d2294a46d29837fc51ba3e45d4047946fb5e/cryptography-50.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169", size = 5299852, upload-time = "2026-07-31T14:23:48.851Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/da/3a/f05e32c99d440c9bb891ea0e36c9091891e36be5a9a87ab2ee6ea20729f6/cryptography-50.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f", size = 4734462, upload-time = "2026-07-31T14:23:50.861Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ca/dc/bd72b26be8953f80625f63151efd38eee71c76ca6cf591c08ff34615a79e/cryptography-50.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105", size = 4852708, upload-time = "2026-07-31T14:23:52.715Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/27/20/c930314a2ab476d15dec966ec87e2e9637bb02b06106b12c0396c57bb603/cryptography-50.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef", size = 5004179, upload-time = "2026-07-31T14:23:54.887Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/32/2e/c9db68a0c4bfa28e310707527c0ee3a2bd254104d2e02e68f368e197aa4c/cryptography-50.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30", size = 3840395, upload-time = "2026-07-31T14:23:56.677Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c3/fb/951032a3bf22a5697c83183fb6294a4843772947a70e616c57b3ff5f522e/cryptography-50.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:49e7d93abdbd2990caced757e5fade25302f719c3c8fb6e6fff2dde98999fc41", size = 3989258, upload-time = "2026-07-31T14:23:58.881Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d4/67/91eb047e69c5e845f2f14b8a2e4a1aab0f283cb885531e9e22c8adb176bc/cryptography-50.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc", size = 4700648, upload-time = "2026-07-31T14:24:00.702Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/30/82/85f0f7425c856b9f96459411eb12e74ef72df9caf6f8f15bf23a33ff131f/cryptography-50.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f", size = 4682442, upload-time = "2026-07-31T14:24:02.538Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1a/28/b555a365adff1cca2fbe7b9e487d68a40de6bc67ff2cb587473eb43de0e7/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3", size = 4707596, upload-time = "2026-07-31T14:24:04.394Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/72/d8/f52538140cc719df62a01cf87d1c7142318d235817109d6f4054d7c352d6/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533", size = 5314552, upload-time = "2026-07-31T14:24:06.31Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/38/14/6120e5bd7c5aa022ad15424ba4d5c5269d0d9448ed4d55e492ea91e3c1c4/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037", size = 4717113, upload-time = "2026-07-31T14:24:08.349Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fa/71/190bf38c3ee2e0f8efc9860ae100c9df4169742eef274b91e7aa1cb133b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f", size = 4338580, upload-time = "2026-07-31T14:24:10.227Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3a/63/504ccfbbe61fd8aa983f7f146399cdf034c72c2fc55f5b2dfdcdcdb20c99/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11", size = 4707038, upload-time = "2026-07-31T14:24:12.169Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/01/77/2cf79bbfc4d12ca106437a6e170d6aaa01a373e93093118aaaef0e801bd4/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d", size = 5273110, upload-time = "2026-07-31T14:24:14.38Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e5/45/8aae2972c520145377ea3559a605a899bebe227bf070b33cdb445929a9b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c", size = 4716439, upload-time = "2026-07-31T14:24:16.415Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7b/20/4fe50b619a48c2525cc46e2dbc1ac490708d704be5d467bdaac6dc955682/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c", size = 4837383, upload-time = "2026-07-31T14:24:18.553Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/92/91/3a31366e183343d3703f8995c095f5734676bd6938118047e50fcf279eb4/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95", size = 4985772, upload-time = "2026-07-31T14:24:20.385Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/74/9a/02ffe35b2853d121689871eb5dce862092562b3a1ed5cc98f1aaed441506/cryptography-50.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:12b9c6996425c76ea6c457ace4f3073e715b8c545add07cd1a8f3a4f90691269", size = 3816291, upload-time = "2026-07-31T14:24:22.125Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/03/37/73d005be173aff344af30e9fd2a576575cb2391a7101d9cd3842e1fa8cce/cryptography-50.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ccdc4a71a4dabae05de219404f9f4abc38e3b58422177ff93d0da05967dafa07", size = 4036009, upload-time = "2026-07-31T14:24:24.122Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ff/c6/7a6202a534e32103a285b7834a120869557fe198d51d7cfe59754c8bda9c/cryptography-50.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3", size = 4745252, upload-time = "2026-07-31T14:24:26.118Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/85/4f/0fa8c2f4428198f15d9ff8d63400e27afbf94ce833f6108da1eb3753f945/cryptography-50.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f", size = 4728939, upload-time = "2026-07-31T14:24:27.994Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d1/63/54dd723490ba2dc09b299682c10b38db38f159728bcaae8c591b8af2f22d/cryptography-50.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5", size = 4748483, upload-time = "2026-07-31T14:24:30.254Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1d/dd/7c77d26285cc7f6991efce64a0f5b4f9383bfa5dd8c5033003eaf7db4cdb/cryptography-50.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f", size = 5367599, upload-time = "2026-07-31T14:24:32.457Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/46/c9/f60aed34c013f317f92817b6c171c2d22a78270fa41109bd4b08af26b194/cryptography-50.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025", size = 4762647, upload-time = "2026-07-31T14:24:34.599Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/be/f3/f9a0173b139372c3a48ed98154b45cc6b9de17c789d5ab552e621c293609/cryptography-50.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a", size = 4385197, upload-time = "2026-07-31T14:24:36.647Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d8/36/83bb81f6e569bc38e1e4a7bc80f29b46bb9601920bc455fc8e888f5d5742/cryptography-50.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b", size = 4748095, upload-time = "2026-07-31T14:24:39.493Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6b/16/d3008eff98c764979865834c3d386d4fd041b5f52e7f34fc29ac1a5eb515/cryptography-50.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708", size = 5325948, upload-time = "2026-07-31T14:24:41.556Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9c/f8/d97f9603efda3888187bfdb893f26c41be4735c10631d05d284ee6b047c4/cryptography-50.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47", size = 4762400, upload-time = "2026-07-31T14:24:43.636Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/64/a2/4615c8f7d81a00b1d6e6afe19f694e1543582349fb5f4076f6cb5dc36485/cryptography-50.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9", size = 4878208, upload-time = "2026-07-31T14:24:45.522Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d2/1a/efcfb02f91407149a0dacffffab791f7e19bf6385f63b3666dc8b5e5c9c8/cryptography-50.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7", size = 5037050, upload-time = "2026-07-31T14:24:47.697Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/57/30/4a22984d4f1bdfb8c054f07a92bc176b97a3134cc1d6c4b3bffb1f3688b4/cryptography-50.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:d24fead1d4d076e1bfb006dcec392074a3cd8d7b4fc8a595aa64073b2b7a96ba", size = 3874135, upload-time = "2026-07-31T14:24:50.085Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ba/19/797e2aaac9df6a66f1550f49979dc1b1e39ecd2077501c30efa81e8d5d67/cryptography-50.0.1-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:b8f852c65863251b9e3a1b8c150ce21e59b522dbb6a7d4bc80e680d38388e986", size = 4010153, upload-time = "2026-08-25T19:44:03.155Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/90/34/9ce9a62ed9dc82ca9fd6a34445b6904af56e5f38b3eae2ed32e49c36053d/cryptography-50.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:53e279950892dc102c6b4e52af03ae5ea92fac572a1ddab78ca73a997f62b69f", size = 4723133, upload-time = "2026-08-25T19:44:05.461Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/57/26/e6d4fc8512a51a5f9ee7bfdbfb853bce1197087df40c9ad993ad370b846f/cryptography-50.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ff838d62ec1bfce4f9ba7fa16f4a7b554cd8d0c299e6be37502161a660c84eef", size = 4712478, upload-time = "2026-08-25T19:44:07.375Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e6/de/d3cdc2815697aae84126cbd6a030ca7b6b452e28a88b501b836bd3aa7a86/cryptography-50.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e74591e283fe6eb956416c929eb58262a719fe0311fd9054c62c3350ed8760d8", size = 4730726, upload-time = "2026-08-25T19:44:09.294Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/55/32/38c0d344b98c06d34b5df8946565a9c0d6dbf32c8e0730a7f05f0a3c6cab/cryptography-50.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5fe002589592ed749ce77fe0695fcbd3500dd61d7d6db5858a7544c612fa8e45", size = 5353524, upload-time = "2026-08-25T19:44:11.96Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e1/1b/82f0f0d8858d4432be1af790477edf62aef90324041aa07c57e57bef1af7/cryptography-50.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:51593d180cf6d179bde5c5d065bed81386b1f381656ae7d042b7ffc87a9895ad", size = 4746720, upload-time = "2026-08-25T19:44:14.051Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/29/ba/042ca458b8c64348c768284b5d23e69b92ed53d057ab779fee628564676d/cryptography-50.0.1-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:359e62deae718bce96170e223fdcb6357e4fbd3bb7a3a75f4430763532560e49", size = 4361866, upload-time = "2026-08-25T19:44:16.167Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/39/3b/e96c1ef71edef71057c7e3c3d982ce8fda554e0c52d0cc19c18845cde3eb/cryptography-50.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e2ca8fd1b6b4b82a1c4cb02841d0837e3c12336c2e24b520ab8ab3b969733d8f", size = 4730028, upload-time = "2026-08-25T19:44:18.085Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e3/38/45abd72ef63f2e7d0754a6cacf97bd8b69512ace7f6130d24c39ece65da2/cryptography-50.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:76de83fbd91ac49c0feaaa983d0748fd7a53176afac5fb3bf7478d244f0eb527", size = 5308405, upload-time = "2026-08-25T19:44:20.197Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/85/66/6ccca4722987ddedaa7fc9c3f4708af7431f5535666c174350830888c6b7/cryptography-50.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:51afcfceb15597cf2635068e4ac9a56b2abde622edde17f37d85fd7b5306497a", size = 4746230, upload-time = "2026-08-25T19:44:22.376Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/13/0e/b1f92e013228111413f2e6743948b80bc24dfd3c1b87ba98ceea16f5df89/cryptography-50.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:be224a65493ec5b74a158ff22a5522ce4a5ca1e543c647a3a4730d4a09e5f959", size = 4862596, upload-time = "2026-08-25T19:44:24.472Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7e/22/c3654cccc856e9d682817b04ac3ee79731cb09ca6f95996a95c904de2883/cryptography-50.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9ebcdd5519be9b652a46f507817a74591774fc3d6923ac364e4dfa64e36b291b", size = 5014082, upload-time = "2026-08-25T19:44:26.709Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/42/8b/cb12b1b60c91b074ca6bf0fdd59aa8f10d8bc5f73af8faece86ef0421b37/cryptography-50.0.1-cp311-abi3-win_amd64.whl", hash = "sha256:aed8db4f6d71c51efb89530e12d9464e7bf2923d46c3205dc794a2a93f8c0648", size = 3842826, upload-time = "2026-08-25T19:44:28.784Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5b/f0/424cb557d99aa86ac55da5e2add02e2882e44047b6264f93ade1b975a993/cryptography-50.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:30a125032e5642a21ff816e021152bd4e7e94f03eff3f4b7fca41cd22bc3110f", size = 3973525, upload-time = "2026-08-25T19:44:30.7Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4d/72/3a2711d967977ab5fc80b782837c7e8d1ac7445e764c20c381a265c57ef3/cryptography-50.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a0b1a59e3a089064a0ec309e9428c8e3ae4e161419d20ac33600767e83fc658a", size = 4708817, upload-time = "2026-08-25T19:44:32.773Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b4/f2/bb1f56e10815b789df0b409a69fa4992ff3d3fef9c72747f4a6b26fed38e/cryptography-50.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8921d58f426793c5f1b47f0b59575780de9a095214958d0eb37d909593db8367", size = 4697300, upload-time = "2026-08-25T19:44:35.144Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/08/bd/ed5396be499ffcf8807a585bfe38b71a1fbdd1c342b4f9b6d0ef5162a946/cryptography-50.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a8f40ea47330e71b594a7e246898f93177c259490c63183dbaf9e571d71ed9a5", size = 4716039, upload-time = "2026-08-25T19:44:37.192Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f6/6e/1cf405c5c8e8df7545378048e954792f00b7f2367af8863ce8b8f3e10607/cryptography-50.0.1-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:a255449073358275b64b67d3f595f268bbef70e72b6edb65e0c70c735bf739c9", size = 5332388, upload-time = "2026-08-25T19:44:39.16Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/47/92/b4317e8c32c4f47b062f5398bd79106b220a124546f42be83bf32b761e2a/cryptography-50.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:8df2de9102026855887e4587084f6eabd80ed0f345b8ad8a7ac27ab9bf4723e0", size = 4730293, upload-time = "2026-08-25T19:44:41.298Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/39/0d/a1e7633e2c744d0f2983320a27e924ef2264c79c56e1a58d5fb0a1cfd413/cryptography-50.0.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:ac02b07824d4d1001bd4367599f839c19cb171924c796e52c23508ac14c2c0cc", size = 4346031, upload-time = "2026-08-25T19:44:43.245Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/88/dd/b215616f9bab3fc18510c78a4e5c9f362d77838503c363dc747c7d4f5c6f/cryptography-50.0.1-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:cbf74a81765ee67413503ca6e26dcc4f6f5a519822436cc0a1b97aab6c1b8a17", size = 4715344, upload-time = "2026-08-25T19:44:45.291Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b1/1b/ec3ebd31741d0e963612c4fe43caa39341b9b1e031e469820e42e4c83918/cryptography-50.0.1-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:16c5ecd954b3330ebfb6605eca4fd952da8bef376551d5cc264534e3770a9ee6", size = 5287201, upload-time = "2026-08-25T19:44:47.297Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1a/01/0127d11a762b31a9ee0221894f540318761783f3fdc4bc5d057698caebd5/cryptography-50.0.1-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:79bf008d1f9af6071c797ad133e39915dfee7614f18f18f4db9072eb715064a3", size = 4730023, upload-time = "2026-08-25T19:44:49.435Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9e/b9/e7425ebfb599241a0c1d7000f1b466c3062da66c19d9525031315dff7213/cryptography-50.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:330fbb252391c596f1ae42c5754449dc924e6ad012dca8efe0d703f9f2d12ec6", size = 4847362, upload-time = "2026-08-25T19:44:51.94Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2d/fd/60d0ddf4defa12e482c9d5e0f554384d6e8ab25341fd15f060028fd92e6a/cryptography-50.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:42be3bb70596b3abe4ac097b75be223e8b3ab614a0e5de068e3dcc54d71d6149", size = 4999247, upload-time = "2026-08-25T19:44:53.876Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4d/56/bc4f2b209e766c93372cfcd59b781a0b2b59700f62a969580415b699c2b2/cryptography-50.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f74455bb086a85d5e81246412602aaa97ed095e504cd40dd261ef50be42205bf", size = 3825806, upload-time = "2026-08-25T19:44:56.209Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/84/a9/ee16a903f13755e914d1eecc482fe64d1f10761c3960e5d8fa6837377aff/cryptography-50.0.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ca83d00d9e69cd5eb63f2e69c3a5a59e0cecae5ae14c6ae0b35830fe3b37bad0", size = 4035307, upload-time = "2026-08-25T19:44:58.305Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5e/a5/9ec7e81e8526c0d7a387d73386b2daed3f39e10d81a85930bd1b6bfba65c/cryptography-50.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:05ba322c4da95b262a212c345af888ef2c37c88c0509756ea00a0e6d68850f23", size = 4751900, upload-time = "2026-08-25T19:45:00.401Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7e/3c/0e77bd5ffcf078e9dd27d3074aad6c030d9b10d0bf69329d573c927a188c/cryptography-50.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e22dfed744bd4002e909464cb23d2f0b05c6f3113a79ef2e9864a53db737c733", size = 4738357, upload-time = "2026-08-25T19:45:02.786Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/27/3a/3c5f80daa4dcd47323c7af8a2fcb90de27a33564d4fcac69846c0972691a/cryptography-50.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4c4188f7c0cf655be5c06342b817ed0f9595b69ffa2b12026e5353eed29dea88", size = 4758474, upload-time = "2026-08-25T19:45:04.889Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6e/2b/214cf0cf93db9628c3c20c896b229f327f6fb1b20e4b3743d8ad3f00af8b/cryptography-50.0.1-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2ebbfb0f1fed745e91796e3e1080a1440423fdae8ece1b995a1d80883a409054", size = 5375862, upload-time = "2026-08-25T19:45:07.163Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d6/51/3f9701867a46b6c1740c9b52fc4d3bed6cbdcfedcc9b6e64305c07f39cff/cryptography-50.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:407fe2b6db00939c05c0e945e9914238f2f0a430974839429dafc82b1ee6bee5", size = 4772942, upload-time = "2026-08-25T19:45:09.396Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0d/5c/13ea642e08e2544d0f5396122055f4820cfacb3203562197b5967125ea97/cryptography-50.0.1-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:2b34d76a652ea2b6faf777c35df230c5637842cd904e04f16230c3f9f03e4361", size = 4383347, upload-time = "2026-08-25T19:45:11.659Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/84/d5/7d1fe1cb93f91c428093ff234e128c89ba8ea61a6f26aab406081f9b996e/cryptography-50.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:01f41478cf33fc605a6a089cd56d28b45c6c0b45a1928b61797f2621a04bac71", size = 4758050, upload-time = "2026-08-25T19:45:13.745Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dd/04/557fc5ead96a829e0bc812a3b9dc4a52a2f27e4f7f5950da7ff27653a805/cryptography-50.0.1-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:fc3ed7ebd2a8c96f5b166de0ab9b624996bef3b07bbeb19364dfb78222c22c80", size = 5332955, upload-time = "2026-08-25T19:45:16.193Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8c/eb/5d7124083e8d8cda8f5b348f544b71ad6f707ad63193758ef4d8e569da02/cryptography-50.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9dde0a357190eb3b1da1bb9ab750e9c85cba82ca5977aa0836cbb94e92611239", size = 4772694, upload-time = "2026-08-25T19:45:18.315Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/63/8e/f1f955e0921dd2b6d22eae7e8d24a4c4b638d10735ffbf6a71f99eb0fcb8/cryptography-50.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd3718b960d0b5dd213cdf03f3bcb7000e69dda0de8b956061947ff6bcff5558", size = 4888413, upload-time = "2026-08-25T19:45:20.4Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1f/ab/89e2b798d2c3925f82e2bb72d5979f3d2f6da2dd22ef4a8cd8b70d920039/cryptography-50.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2a93d05e34d5f67fba6f891fe85d929999baa7195e853923ea6d7576c9e68c5e", size = 5044355, upload-time = "2026-08-25T19:45:22.353Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/99/89/87ef49ffe383ef4e147d27b7bf2088fb0b54ea409dd87b5a89442e5828a5/cryptography-50.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:55d16b1ef3ee0958d893a977b19777887e546c9954ea81b200c3301a864013f2", size = 3875429, upload-time = "2026-08-25T19:45:24.418Z" },
]
[[package]]
@@ -1009,15 +1011,15 @@ wheels = [
[[package]]
name = "jwcrypto"
version = "1.5.8"
version = "1.5.9"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
dependencies = [
{ name = "cryptography" },
{ name = "typing-extensions" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/de/7e/53c14813521693e931e420d9db00efe317419ff194495903bce03402275e/jwcrypto-1.5.8.tar.gz", hash = "sha256:c3d7114b6f6e65b52f6b7da817eb8cb8423e1da31e1ef13508447c81ecbdcc34", size = 90772, upload-time = "2026-06-24T19:36:50.782Z" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/09/c7/00b87b49ffec758a5a957e448c863f6c4bb6223b3fab22c8c5161362ddcf/jwcrypto-1.5.9.tar.gz", hash = "sha256:dbbbfcdad7a6fc40cf3d5a635b11115fc42591b7ab869a36ad347a516a6e1c5d", size = 115793, upload-time = "2026-08-26T12:54:09.689Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6e/7c/f87c5db042c4f7b4476346fc0d22f1025a777fee08eebde3720d3b9c4eb5/jwcrypto-1.5.8-py3-none-any.whl", hash = "sha256:85aeb475f808d56bbc2f2ed1f6f73e6a317c4011a4321505f02f0aed695a3742", size = 96133, upload-time = "2026-06-24T19:36:49.519Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9e/54/b819eb2f8caedb1bccef0bf6188c051f0d898a177b271e1ed3a5cc0a8b66/jwcrypto-1.5.9-py3-none-any.whl", hash = "sha256:30b1c9afc898eefd112c31d836eb2d7a4c6febcac95d4aa727e963cd42911d8e", size = 120772, upload-time = "2026-08-26T12:54:08.637Z" },
]
[[package]]
@@ -1102,7 +1104,7 @@ sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3f/d8/bd
[[package]]
name = "openai"
version = "3.3.1"
version = "3.5.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
dependencies = [
{ name = "anyio" },
@@ -1112,9 +1114,9 @@ dependencies = [
{ name = "sniffio" },
{ name = "typing-extensions" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7d/9c/ba0c292b4032ede74c249ca314ad64eb1bb5a03a843f6e01facb02f80cd8/openai-3.3.1.tar.gz", hash = "sha256:6f22807de1a976c932cecda620e8172a8c3fdbaeed29c7f21564e0c2410edf56", size = 1282113, upload-time = "2026-08-19T16:31:35.006Z" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3e/d3/50ffb9a7bce5097ffeb476905c0661f4468a3ca7bb489b152542f14fdd8e/openai-3.5.0.tar.gz", hash = "sha256:743738bb458a586d0d02d173bf398d29d7d7a80d182d167aa74f1c08814ecc78", size = 1355486, upload-time = "2026-08-27T01:00:49.113Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6a/db/2b7a1b3de659bb82aef979116c74e809982b13e42c057759767552b5155f/openai-3.3.1-py3-none-any.whl", hash = "sha256:9652df7fdf8ee6f5bd58e0a12f2b1d414a18e0f06bb7a9a57c8643a5f5469bd3", size = 1690337, upload-time = "2026-08-19T16:31:32.812Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9d/9f/03bba05ade050f306c9b9e8b99e3681791dbb30148cd76a793c277fed494/openai-3.5.0-py3-none-any.whl", hash = "sha256:7c0873d379655f65fa515c5692c8620e73bf6a6dce3e63f37589bc99bda3fdfd", size = 1697968, upload-time = "2026-08-27T01:00:47.131Z" },
]
[[package]]
@@ -1246,71 +1248,50 @@ wheels = [
[[package]]
name = "pyarmor-cli-core"
version = "8.1.1"
version = "8.1.2"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a5/b4/c0b6f09ea1dc87c00839908d31fd902075496feb319ffd1b03b665a65477/pyarmor_cli_core-8.1.1-2-cp312-cp312-win32.whl", hash = "sha256:790f0957124d44d1527edf7a677ffc006cddc827998e4c7981f3b20fd3db14a9", size = 3743797, upload-time = "2026-05-20T09:27:01.65Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0b/0d/0bdccf84af431405b1e52d5a8cd6e4bf37a84e04520276da372d0ae2b577/pyarmor_cli_core-8.1.1-2-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:f1fe8ec72cd1a1aeffcb39b6323f2ae3e5a2ba45d02bc67ef938e46bc7e207bc", size = 522710, upload-time = "2026-05-20T15:25:57.965Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5d/6c/6f6bc236208a4e3577ccf8b4849b520234294f804a33a77f0b91ead0027e/pyarmor_cli_core-8.1.1-2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6d22b879c1a2fa5a81de4de504e244c808e49e7911f95aed6d3329e38f8326ec", size = 414409, upload-time = "2026-05-20T15:26:04.24Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/47/e9/e9f8a52202ee74b9eec6a318f0fb8ed9849c7dbf47573a05d891f39f2476/pyarmor_cli_core-8.1.1-2-cp314-cp314-manylinux1_i686.whl", hash = "sha256:8b481c807197f3cd2a15857adf2937cec5ae5a178c6008225b9ff94b83ff5ccb", size = 495375, upload-time = "2026-05-20T15:26:13.74Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fb/97/9e70a68220e190a886b74dabd080f6b8995266e2130ec79d28f6f6e31d20/pyarmor_cli_core-8.1.1-2-cp314-cp314-manylinux1_x86_64.whl", hash = "sha256:5b5fd2301e628873be9f3ec32788d52e32625c414bc8fb5f4e93136e64a3f58d", size = 462070, upload-time = "2026-05-20T15:26:18.961Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7e/1a/07859e23cfa07d249da1bff11ee9beda8431f5a978d1a16256c325fe592c/pyarmor_cli_core-8.1.1-2-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:6187ca1d4b2260332f74e5e60a4c9b0fc178dee9e69155c5cbb4befe3babccec", size = 609497, upload-time = "2026-05-20T15:26:24.233Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6a/0f/1a074362b19dd18a3f4ea27910cb1d08589609e4ec1ef9c181999bec015a/pyarmor_cli_core-8.1.1-2-cp314-cp314-manylinux2014_armv7l.whl", hash = "sha256:a2c0d67975d669a7045c809299a6dcdab305541948c24955fad737f5349bf26f", size = 1004965, upload-time = "2026-05-20T15:26:31.519Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cf/98/70b210c8bb2cf27c12eed161c6441f753aa68b0739a177f5bbf5a002be20/pyarmor_cli_core-8.1.1-2-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3c6c748f98231dcdb75bcce9cf6b8fe86d61674c4a44ba49c12c4a8c478348cf", size = 655390, upload-time = "2026-05-20T15:26:38.625Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e2/e0/10b373cbb4fe1b140adb804b156fa7c7483f3c24e76a283cfb4047361b1f/pyarmor_cli_core-8.1.1-2-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:20162349d62ec09d22299c9bedfe0b13e22c2d27757cb53c523eb950adcd7140", size = 450993, upload-time = "2026-05-20T15:26:44.049Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f2/97/f1a395a177ab833d560fcf56d3f97625a708669cdc567b46abe286c64ce8/pyarmor_cli_core-8.1.1-2-cp314-cp314-win32.whl", hash = "sha256:999a25c17a09af3e9481a9b2bc3f011526ef3333077cfa703a8014f899d528eb", size = 465426, upload-time = "2026-05-20T15:26:52.662Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/40/58/44112ea2938c023f8f94335deaae40c53a925cd5cb37386cbb904e4a97a5/pyarmor_cli_core-8.1.1-2-cp314-cp314-win_amd64.whl", hash = "sha256:add3f37e0fe78ca4678788c4c4042761aa9db7948a4395ccf9db9091fc7e07c6", size = 424587, upload-time = "2026-05-20T15:26:57.955Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b8/b4/48b1b05625ed48f2d786ea0c50a9b446b0d105c554a85e9eccb81bd98ec0/pyarmor_cli_core-8.1.1-2-cp315-cp315-macosx_10_14_x86_64.whl", hash = "sha256:119a0be4d75fc6c4f1c36a7beeee74a55819a86f4cedf3f0cab7afaf7f509d3a", size = 522845, upload-time = "2026-05-20T15:27:04.646Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cb/04/a09dd4b72dca9e43e02597aabf6213f27c2f12fafb477992fe8276a82d7b/pyarmor_cli_core-8.1.1-2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:e2b656a703146507d5d800c4d952a656c9f4d68a4dd96a0c39eca28b63642914", size = 414405, upload-time = "2026-05-20T15:27:16.212Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ad/91/7e3a773c6e1756682db9c11783dd0e4a0894b9642771118ae2a4d1c436db/pyarmor_cli_core-8.1.1-2-cp315-cp315-manylinux1_i686.whl", hash = "sha256:e0210f89acae597c574a5a58290b20feb94952dc6c002f8e280cc1b372db0189", size = 495327, upload-time = "2026-05-20T15:27:21.122Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/38/4d/fd2e36ecf479c9fe57b52c19c3f6bca0913c85fa980447af29a2ba9339ba/pyarmor_cli_core-8.1.1-2-cp315-cp315-manylinux1_x86_64.whl", hash = "sha256:1675d2c7e157dbeb096fab72d8c06c5fda0ff68e468a28d3d9624aaaf90ff237", size = 461865, upload-time = "2026-05-20T15:27:27.146Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/05/8c/967d1ac0908d8c2c9ea73fbb2cb8ba31e4ec4fc290b7e3532ca0400d9115/pyarmor_cli_core-8.1.1-2-cp315-cp315-manylinux2014_aarch64.whl", hash = "sha256:bbe9429f19c4d6e70cec183cc78abb2f7ed483789fc17af6059871149d722e62", size = 609349, upload-time = "2026-05-20T15:27:33.218Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/66/50/2b2f5db7f6c28c22a2a67e1483dfda65e2285a94456dcbc6122fe9402849/pyarmor_cli_core-8.1.1-2-cp315-cp315-manylinux2014_armv7l.whl", hash = "sha256:5396c158a32cc6e2dc9ae317d819bb7c28ac83d832014e2db3e062679380ce35", size = 1004843, upload-time = "2026-05-20T15:27:40.199Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5b/c6/ff8d539dddc7e28a8f987a16fa85b9c27b142b64e54b644461f1e1bd8ed1/pyarmor_cli_core-8.1.1-2-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:5625035d8026ad905701f8c9c56140357776112cdd533ac614d3427c3084fbe8", size = 655278, upload-time = "2026-05-20T15:27:46.939Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4b/52/a8586c25e8ba3ec9c69556d6bc153097b8e065f87753e40bec9e1bea7f09/pyarmor_cli_core-8.1.1-2-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:a6ebbcbde0d650ef5f11e8c614dabde12636c7277c22ea9176f03aa8733c2cdb", size = 450928, upload-time = "2026-05-20T15:27:53.671Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/34/fb/d9df3bdce9c7480df3d91139539fd27332db3f5a984551201db0189dfdd3/pyarmor_cli_core-8.1.1-2-cp315-cp315-win32.whl", hash = "sha256:4fdd33615b75302fd48a0851a3f7ace1f4a4f515cb09edce5fd0d5d4e3919bf1", size = 465363, upload-time = "2026-05-20T15:27:58.894Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e4/e7/39a4c74668fdb7ad536264aa73d0439a2d98f816452e1955c2cce36b65bf/pyarmor_cli_core-8.1.1-2-cp315-cp315-win_amd64.whl", hash = "sha256:3c430c33fde5f36305c6e90356d7136da532166b20b9ad69bb5aefdbaea2e9cc", size = 424487, upload-time = "2026-05-20T15:28:06.382Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/58/b4/33fd5192cedc4600e9de8c9c7519205f421840da1b0df6e9491afca0a072/pyarmor_cli_core-8.1.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:4a6e09545fe74dc304992a0b002cc8d426c16ac3670d6f602d51838f080a2f31", size = 515480, upload-time = "2026-04-09T01:27:47.282Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a6/e5/3310cb2e29bab80bd923d7e0749c2163550effe1d1a6c6d5d95f5c58d183/pyarmor_cli_core-8.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:df035854a6603d4861a50156484027a03c01a7b9385b5365cabea4871ac72e3e", size = 407703, upload-time = "2026-04-09T01:27:51.869Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/26/66/1aa494d360a05fd90677db8c4b8385dedd2083fe96fe6607ea555f800c87/pyarmor_cli_core-8.1.1-cp312-cp312-manylinux1_i686.whl", hash = "sha256:ec304457ff31b4939c8527640d7f525e78e9c91cc8307a11f068ef1291710b40", size = 489280, upload-time = "2026-04-09T01:27:56.492Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ef/ad/5f246234ce778dd3b7bd4faa2bded71690bbcddbf8f29506cba131ae4f85/pyarmor_cli_core-8.1.1-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:9b65a62a71a88fb7534002067b972aac2f363e871ad34563579550fec1a260cb", size = 457685, upload-time = "2026-04-09T01:28:01.313Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/05/5a/cbaa2088483837ded4bb6301993de16743c633f230bf29947cdc10f7c307/pyarmor_cli_core-8.1.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:d1c4e623dbb8bd93fc4273a7ca79d192618ca36cf74aceaeca9d36c83608ed77", size = 604340, upload-time = "2026-04-09T01:28:06.227Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/87/39/2686a96a3d80c5ef61fea10066677ddf3678bdd48afcec04a0d268203fa0/pyarmor_cli_core-8.1.1-cp312-cp312-manylinux2014_armv7l.whl", hash = "sha256:0c10ad18ca2cb285b4b6c2bfdba6bc0234e84272f52bf692dce25ae7c6a21d56", size = 1001029, upload-time = "2026-04-09T01:28:11.426Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/77/e9/da903ea517387ac3d560b324fd03a7f50267cc2f68e99f84934fd9ee3ef9/pyarmor_cli_core-8.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:812201887aa2fb90f8b865a5dd443aa841cb951bd25d14dad843ce11b3ceef57", size = 649535, upload-time = "2026-04-09T01:28:16.373Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/91/eb/fcd742456b3a475e16eb5c3468132a9950095169cf1e6a64e4fb4bac208d/pyarmor_cli_core-8.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88e129603512326ce1440e3aa105506304424ee40366cc19f0a72a9aeeabecf7", size = 446443, upload-time = "2026-04-09T01:28:21.054Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b1/87/b58aed090a6a9debf65045f37e93b779e330c22106aa4cbb9aaaf7bb12b8/pyarmor_cli_core-8.1.1-cp312-cp312-win32.whl", hash = "sha256:b645d69c88f55a08d34fdd547759079ac1722e246d8e994ce3eb07937bdf318b", size = 460155, upload-time = "2026-04-09T01:28:25.665Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ac/b8/1dc8b15bc1607a27ed78025d5f50c97c78542930cab15a6d6d457b664bb3/pyarmor_cli_core-8.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:09df045973b5de85ecb950f3fffc23686caa5b43749d4d966ccd150719639e2b", size = 420620, upload-time = "2026-04-09T01:28:30.36Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2b/35/4b2969ebaea72869a5ba13847d47682ec77a1ff894fc83450e23e7257845/pyarmor_cli_core-8.1.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:86a576252f26752cfed5a1f0d6a12352f7ca8f38aadfd7e1df44453d933bf70c", size = 517354, upload-time = "2026-04-09T01:28:35.086Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cc/0f/c688f09d158d5d4bf3c83289b9d16d454d5ac001ae962a7501d4209dbcad/pyarmor_cli_core-8.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef4cd7ecf1aeca5748d221e548a6203f58ede46e3b1f31cc1fbba5170f1dc5c5", size = 409747, upload-time = "2026-04-09T01:28:40.159Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f5/f1/c97c393fa0f6431ac6537acd21f117025488b1254c255ae42da1809d2ae3/pyarmor_cli_core-8.1.1-cp313-cp313-manylinux1_i686.whl", hash = "sha256:e95cfbaafee2eec3a3ef918c80f6ff6d6e26535c0b9f5ea615a97cf419a45603", size = 489761, upload-time = "2026-04-09T01:28:45.023Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9c/78/5874a8cecc9123475bcf5884037a6abd5859e4b93e89bc5eacc06dfd493f/pyarmor_cli_core-8.1.1-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:2f42447c95f0de7df586a5899b395dcbf3193b5288a290f77dde90d4459700a6", size = 458105, upload-time = "2026-04-09T01:28:49.719Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ff/a0/73eb8557f0dd6f9910dfb1ff70ad31550d5b037dff78f80a006732efb085/pyarmor_cli_core-8.1.1-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:635144791ddcf3a7bbd31ac10181828d40636e4b6a477ffefa0c6f4073524604", size = 604874, upload-time = "2026-04-09T01:28:54.871Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ce/6c/3eeab3e37748ae5354eb629ed5e03ee09c85c6abac8d77926d4d27507f52/pyarmor_cli_core-8.1.1-cp313-cp313-manylinux2014_armv7l.whl", hash = "sha256:248bfb04d86f38851bdc13652d5e447e0415128c0cdfbb04498802e624a88475", size = 1001331, upload-time = "2026-04-09T01:29:00.195Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8f/f3/9736f5049e0fff5a333a8e14058e5b6b89ef68f469bcc23f9001fd47ab42/pyarmor_cli_core-8.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a71b3b4ab8d07259b82c9fdad18e099d86b0880bce352949b834db825cbd697", size = 651229, upload-time = "2026-04-09T01:29:05.072Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/13/e5/988ebde164710de231a93127a1df25a278307bb8cd31e75b207266a3ae8b/pyarmor_cli_core-8.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:bc51089e37602a4e4c75b990448c44e25d7b1c7048805ad789863c889e80793f", size = 447196, upload-time = "2026-04-09T01:29:09.99Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3a/5c/4be2fe6a77477c6a82b776c147487030978fb3c6c825dfcecafe4cdaf1d1/pyarmor_cli_core-8.1.1-cp313-cp313-win32.whl", hash = "sha256:689356f95fec04a53f91a624b0d1f4bfc8547e2ddede6799fb1c728377a51e19", size = 460848, upload-time = "2026-04-09T01:29:15.702Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1b/d1/80a1085e94a8bcc3f1d8c6d1ef703c1fc35afe9a8f2b5f0676d74fc1a80d/pyarmor_cli_core-8.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:2e7b259c4dda700360030cf212b2cca625aaf9e7498dc9bfcbee9ef85acfec9b", size = 421343, upload-time = "2026-04-09T01:29:20.354Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b4/57/12936c3ea69e88481e71d4f61a878aa49b26181c0fed9282b590f46b9132/pyarmor_cli_core-8.1.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:4bfbfe564881bd467b2103b47b4fdfe883746e39651545280f4c815d409e45f7", size = 522708, upload-time = "2026-04-09T01:29:25.471Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fc/4c/ba326b377d0d698fa80864a478822e56acd0b1dd74db7b2645ca66e3c732/pyarmor_cli_core-8.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0ff3494acdacbec1565a1d98065ab2a2b47683778ad02b89b643008a1ffe90bd", size = 414399, upload-time = "2026-04-09T01:29:30.291Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f5/15/f6121674cca8487665bc4803da745a1f4e7f98d233bf33e659ca3dbfec29/pyarmor_cli_core-8.1.1-cp314-cp314-manylinux1_i686.whl", hash = "sha256:2d905427cd23226f86998a28f6c79c849136dfb85e569e32b208ad7d75b24853", size = 495367, upload-time = "2026-04-09T01:29:35.223Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/02/6a/67121dc6f489120322bf4742774a3ebd05eb4b6d1ebcf971b34ea91ec2b4/pyarmor_cli_core-8.1.1-cp314-cp314-manylinux1_x86_64.whl", hash = "sha256:4de138aacd465e0a0d4601b290d0555d47657f59b86d5eeec1bc660fd01e149a", size = 462060, upload-time = "2026-04-09T01:29:39.799Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bf/87/09d5c023474495dd3dbb789ecf538af1bcbe53c49cd07a703cfa5da03bde/pyarmor_cli_core-8.1.1-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:1777c7c2c7130a34b7c7125ddd828569e3f938fc91efc0db99fb0ee1eba88aa4", size = 609493, upload-time = "2026-04-09T01:29:44.322Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5c/55/c6f403c7a33900d1a194839e55677814a35ab33d10c4234394f7ad12c311/pyarmor_cli_core-8.1.1-cp314-cp314-manylinux2014_armv7l.whl", hash = "sha256:5172681e95bfbf4eb250d53e1ede75b270e7ec7c29a4accfb0acf766bc7b6351", size = 1004962, upload-time = "2026-04-09T01:29:49.616Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bb/bc/ce7146830a08306884929b53f785bb708df7c360a73a2a3687ff73533918/pyarmor_cli_core-8.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:06de0a47cb8ae86fe9270f3930d8e245fff73f87bd3c49cdc2af47c8ce1a5b06", size = 655381, upload-time = "2026-04-09T01:29:54.994Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/aa/05/0fac5b9e9d37e1ea46ceae2733ab07d348b199257d99f69d290eae363ac0/pyarmor_cli_core-8.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:7ae1c265c6560e869d355373fd86c2880a951db6bbf2bbdffec698d47c269be9", size = 450987, upload-time = "2026-04-09T01:30:00.065Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/82/84/173c89753b13037ad45fd6c220aeb12e055de261c8453025df9dc0c82b2c/pyarmor_cli_core-8.1.1-cp314-cp314-win32.whl", hash = "sha256:18a7f8aa4054395be9fe5b051727f2c32bdce74d609ef75cb869a9fd0396da90", size = 465415, upload-time = "2026-04-09T01:30:04.793Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/38/ce/32fb60e0971791c7e102ab87140d8c5da444e8b22b05614d7b63b7a90113/pyarmor_cli_core-8.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:9e50864097734b039060074b4913ffca73069162ff3c32556f3b594888129661", size = 424573, upload-time = "2026-04-09T01:30:09.704Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d2/02/7632897343d0b1ac3faa6e882f62880c4910d1331a46f1796816b99e9f8d/pyarmor_cli_core-8.1.1-cp315-cp315-macosx_10_14_x86_64.whl", hash = "sha256:eb3a63691a67aca10e2af7937baf0c88effc459c78c3c4ef1c48cf562376b830", size = 522819, upload-time = "2026-04-09T01:30:14.265Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0b/aa/09cce8ed725419d8a3c1a4a4b588b5f369094ef0b9768bc7e2d7fe855897/pyarmor_cli_core-8.1.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:75328d808fb790546272ba92440c8ae9e80f3c84cce3f423fefb00a9af230af0", size = 414398, upload-time = "2026-04-09T01:30:18.603Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/14/a9/844a2fcb09dc30994e03b5469ed80d6066e794828cd48af437da33029f8d/pyarmor_cli_core-8.1.1-cp315-cp315-manylinux1_i686.whl", hash = "sha256:e0e0965e8a1d779485f747b93d6f4c7a20395553851921b0fd22ea89fb09287e", size = 495322, upload-time = "2026-04-09T01:30:23.59Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4e/1b/b169c06628dc9ac375c5d3f270cd742e344ddfa6dab5e00216a984ccfa01/pyarmor_cli_core-8.1.1-cp315-cp315-manylinux1_x86_64.whl", hash = "sha256:3562142724c8cd48a6b161b35a7b4ebfc30820f08ca0adc28f8729077531c3f4", size = 461861, upload-time = "2026-04-09T01:30:28.655Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bb/98/8ab28d635e03d269234243c4ac2fd82d722435640d9be89e78cb6f4ba15f/pyarmor_cli_core-8.1.1-cp315-cp315-manylinux2014_aarch64.whl", hash = "sha256:27c6e269131da709bcf1aac04ed8285eaa8a328f7682e98c4e93a81f97e2e4db", size = 609340, upload-time = "2026-04-09T01:30:33.652Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f3/68/f6e381d81e988077c6fdddcfa659ed30953640e863928575886815e1eefa/pyarmor_cli_core-8.1.1-cp315-cp315-manylinux2014_armv7l.whl", hash = "sha256:0cc8d7c337663eb9d108649ba296973284ff8b6fc0f83ae0ce347697533a983d", size = 1004837, upload-time = "2026-04-09T01:30:38.944Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cb/83/4958e7eb4a4dd27b024a034443636e7db418d06ee743eb09eecab2360e18/pyarmor_cli_core-8.1.1-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:00cccda40da819c45550625c910f836b9c768699ea40b04c058c3221270b514b", size = 655276, upload-time = "2026-04-09T01:30:44.053Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/68/d8/c8992fc6f66fcd8b29f8497ca352165bd9e0972a1f5371eb8c01aaca7a3e/pyarmor_cli_core-8.1.1-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:488be5b88a0c434fb17457972f6b7f681b38650ce078c3f19dced5b15091146d", size = 450926, upload-time = "2026-04-09T01:30:49.022Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/85/61/6cb081493c75662dd979a6cf8f6092d81605a9a59c2439a2ab77f3abfc1b/pyarmor_cli_core-8.1.1-cp315-cp315-win32.whl", hash = "sha256:a0f087fe8c388d178859dbc2a14e11e12bf8efc694f035e41d1dfc5b4a630052", size = 465352, upload-time = "2026-04-09T01:30:53.861Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6e/87/9daf968e6e6add10d089eef86b46a26c9908bfae74820bc97aacbf37d081/pyarmor_cli_core-8.1.1-cp315-cp315-win_amd64.whl", hash = "sha256:c1b96471bb1d9fc575ba526841829c6bf03cd9db5caabf5688518a29ed389870", size = 424475, upload-time = "2026-04-09T01:30:58.457Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bd/90/89c95f00699600a2c476b1c80e97df5ea1f09b995a8abfc0a8f118f0bf38/pyarmor_cli_core-8.1.1-py3-none-any.whl", hash = "sha256:06eb9771c7d1f0ad7c00884f13ea42edfbffab8b28ef718ce197c0b4c69d97d5", size = 5177, upload-time = "2026-04-09T01:33:22.702Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1e/f2/3dbe618b9a2a21e7eb731b238c297cfd4da8301d2d8cf79ab4ed89537c73/pyarmor_cli_core-8.1.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:0bfabe4d8db7510a09419a8cbf38d1952759ae48044854666b2a9e7d1e0bfc16", size = 515507, upload-time = "2026-08-26T04:42:40.407Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/45/47/de7e83d98d15882810bd865ca782101cf7c29b69ea0db42ed5e3b3f2a0a5/pyarmor_cli_core-8.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ce0275112a7e502b18db4a67d292ee7a764dc48cd422334c02377b797a2ce91", size = 407727, upload-time = "2026-08-26T04:42:45.129Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8a/ce/326cb3f7ed988c6bfd8c7365837d2bc99f1105d8a5f029ca591201a962ac/pyarmor_cli_core-8.1.2-cp312-cp312-manylinux1_i686.whl", hash = "sha256:d68e86e01b47f91c1f7b97ed7ff4079159d813624873e4e5d5c660a089f32432", size = 489298, upload-time = "2026-08-26T04:42:51.84Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8c/71/ce177e470033c27d58d519d5f2547edd09b3415ffaa3ac9fa60fa5c7b846/pyarmor_cli_core-8.1.2-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:05212ececa5195d1c72113528b9379f691d5f7671ef3c0b5e1c72908c7a46ae8", size = 457692, upload-time = "2026-08-26T04:42:56.947Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/97/8b/3f68f677ddadf7e3730a91c98de10f048146a359e76542207eac7744b621/pyarmor_cli_core-8.1.2-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:2cf87045cc73fe096721d0e7fd972849bca84da8dde3ba478de75b4abc85e3fe", size = 604351, upload-time = "2026-08-26T04:43:07.276Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a0/f3/1f26f562840586fdcccde66c602f44f3695bcda9b23efaa39227f39e6614/pyarmor_cli_core-8.1.2-cp312-cp312-manylinux2014_armv7l.whl", hash = "sha256:18adeee02d6d829741d28a2ac7a4229020583c491eea475d4164b1d311cc540e", size = 1001047, upload-time = "2026-08-26T04:43:12.975Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/73/4b/50a847ef3a0b8fa12d1891cae2383b6efe04cfafef5bd2137ab4468cb29a/pyarmor_cli_core-8.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f5f4c573a435619273442f1b9f8938d31c7d0f26100a80453e6d0ae054d1e0a2", size = 649548, upload-time = "2026-08-26T04:43:20.541Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/64/2d/0ca6b9036195971200975c69c0a65a2049304458ebfa73caeeb860a94414/pyarmor_cli_core-8.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:35ac4f6ba07b20391ab9624c70bbc133537560e227579de6db51fe45535ab93c", size = 446449, upload-time = "2026-08-26T04:43:25.667Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6e/de/6969ae804814dc1842f363847484dfb05664c4ae2ecb60ac9a2ef69a1e3c/pyarmor_cli_core-8.1.2-cp312-cp312-win32.whl", hash = "sha256:609f5a63d0135d3723203f859e89f7a28c45e09474250705e995d59845d8c307", size = 459858, upload-time = "2026-08-26T04:43:30.669Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cb/b7/2f3de91992fd22f7d62d7f8c59cc47898e6a260e195c1131da1e7b6a9c25/pyarmor_cli_core-8.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:952558497cbe06bc12beaf014e2c53b20203e4ddef3c8b6028744abf72e14505", size = 420611, upload-time = "2026-08-26T04:43:35.533Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0d/7e/3d79d27c130736326503abc0ef358e4fb1b102c4a21f2e9affbfbdbd25f2/pyarmor_cli_core-8.1.2-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5dc5cbf1089a503544cadd63baf4a65953983459718d4e1aeb2954a80e8f73dd", size = 517370, upload-time = "2026-08-26T04:43:40.387Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e7/64/bd3419284798569f60ad9db0766cb7c0e3a9ea882489bad0fa8c5a754daa/pyarmor_cli_core-8.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b068b77395b3f74d4384e3c18d4684c56a4360c8fde848692444d5e6dac8d1e", size = 409769, upload-time = "2026-08-26T04:43:45.45Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b7/af/77fa373c09063b91b13579c7321c6e1a5f6f6be1851b3c72ec3c2a4cad32/pyarmor_cli_core-8.1.2-cp313-cp313-manylinux1_i686.whl", hash = "sha256:405405e44855307ee26264ea253358009dc73ad08440ba89935efa4fde87bae4", size = 489760, upload-time = "2026-08-26T04:43:51.707Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/45/8e/b411da54be4bc2143dd35dd6b3423376c052906eb7d0cb63c58dc01a31f6/pyarmor_cli_core-8.1.2-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:66d6d9c15aabb52f87ff98d4819a5ecdc32b13100348be2872c373354470d8d5", size = 458108, upload-time = "2026-08-26T04:43:56.449Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bb/69/82cd83355e1470eb5f7fb505bc8ff85a8f8caf67c0da02de86beb31505be/pyarmor_cli_core-8.1.2-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e77c780dce6c0a50824927a03e0f9c39c642e1ca8fa1671c70c03b01c2e90cef", size = 604916, upload-time = "2026-08-26T04:44:01.603Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5b/ff/8be81c5d943cc14b8bae44780249b657565690bea65ff52ee692548e8bc0/pyarmor_cli_core-8.1.2-cp313-cp313-manylinux2014_armv7l.whl", hash = "sha256:49ecb1d3f76bedee50be3047d18ce50609db16bff8702a7f096531503d737785", size = 1001331, upload-time = "2026-08-26T04:44:06.741Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/12/6e/cc65256fa20d49dc704231530c693122e31c8c18783b2f7985624d3c2c1b/pyarmor_cli_core-8.1.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9b1f648743a32f417bf908acc5800711175edf411fa24bf729a6d2aeb24a2617", size = 651236, upload-time = "2026-08-26T04:44:12.025Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c6/69/5c6bf32fed77aed5b50aac0b97b7df0f1f72b3343e9bef4a014392e85cdd/pyarmor_cli_core-8.1.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d890021c640fb01dda0b7a4c8580b12e103bd349817bc86f01c04f3c9358cae1", size = 447212, upload-time = "2026-08-26T04:44:17.021Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0f/b6/58410290dae04ebcf85aa3084d31ba07930121a44ccc7714c738fdfe6810/pyarmor_cli_core-8.1.2-cp313-cp313-win32.whl", hash = "sha256:b3f2d55db32e6453a3e070fadeef827c147f21106e801a7f0688158f665c0726", size = 460848, upload-time = "2026-08-26T04:44:25.4Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/29/e8/d748641756c7a3353ce161b3316eae46fa5f7355a4208b7a3488e18764b1/pyarmor_cli_core-8.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:c61a86ab3f3a85550e148dd874e1ad2f2295870ad3204b1d92287c3fded3d087", size = 421322, upload-time = "2026-08-26T04:44:30.229Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dc/ce/bbfe583c3e7ee7b76b51173c0ba9a073b222cd3a3e7d28b13e6a5373d5d9/pyarmor_cli_core-8.1.2-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:3416df1e56f10a12f42c124ed4c29a849fca1fe33bbc70e725fd0bb395d2301b", size = 522725, upload-time = "2026-08-26T04:44:43.116Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8f/16/9bbc4f0591bf28b481e079c1c38deae5fa78d23b4eade36f4fa6bcdf1956/pyarmor_cli_core-8.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c5c06fee0a63cc8c3326f6f7e44a3c39d88dfdcc64465c50e584e4fdda6c4da6", size = 414438, upload-time = "2026-08-26T04:44:47.886Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3b/26/e24b62ad18e4c8f18abdae1b12915dbdd9a7e08118082a7f427a7922a93f/pyarmor_cli_core-8.1.2-cp314-cp314-manylinux1_i686.whl", hash = "sha256:0b2a1d47555d750f6c9cc9a7883c12c4f6b81966a85815ba309ae887a0866209", size = 495370, upload-time = "2026-08-26T04:44:52.993Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5a/43/5d9dd141e4bd463092fa2c1b2da524f23e7f78d1a5180664ea9810ac7a85/pyarmor_cli_core-8.1.2-cp314-cp314-manylinux1_x86_64.whl", hash = "sha256:27b5246ec4ef12cf9190da8d0caf4c69ea09c3082ca3b9317b8b27eadb665044", size = 462068, upload-time = "2026-08-26T04:44:58.693Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ba/91/84110437e6cef14eccc4555ddeed0964e36a4d33cde2c6827dbd059d743a/pyarmor_cli_core-8.1.2-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:f83c6a2062aaba0a905c9274e53eb89a3054cf5c6d53ec25873dc8e8991408a5", size = 609512, upload-time = "2026-08-26T04:45:04.128Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/47/bf/bef14c1746e0a1ada92e066c899cbb8fc20878d8eeeff9c2893eade22333/pyarmor_cli_core-8.1.2-cp314-cp314-manylinux2014_armv7l.whl", hash = "sha256:ed08a46cc52e9c1d22f1472452f7d0b02eb1cc3d8bd3494f20ecbb20179c8696", size = 1004951, upload-time = "2026-08-26T04:45:11.196Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ce/14/677a5246b700a30b541c4d3c83b654641946a5dc11ba8bb9efc3f647e973/pyarmor_cli_core-8.1.2-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:6ee50a3307ad6c69cdd4873a32591fea6791cbcc040f9271678b6597d9214dc2", size = 655375, upload-time = "2026-08-26T04:45:17.309Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f8/26/8b33b3f3808f792fd35847346814709a722bb981096a785aa0c8dac35deb/pyarmor_cli_core-8.1.2-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:b18735b1f3d26a6b8f3776d82a4457596972f7a2f986d729740b6443812b0f62", size = 451004, upload-time = "2026-08-26T04:45:23.568Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/30/ee/2b8260c1fafd042282eac62dad02285b2088049e6e1a8a1771e87891f707/pyarmor_cli_core-8.1.2-cp314-cp314-win32.whl", hash = "sha256:1baf50f8efde7817899313092de743129da063db2265c66c7ac06003a7b7e8bb", size = 465410, upload-time = "2026-08-26T04:45:28.365Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/73/d5/357b7850aa061eddf14b5fff3b0230eff65d62e3005e008b28f4807aeeed/pyarmor_cli_core-8.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:84a398bc0873890010f812b3190d4518bb393d3c0c17d7b2cdf2f547e65ce119", size = 424558, upload-time = "2026-08-26T04:45:32.967Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8d/8a/873ffc711607601460fffbf91895c04b754ab934bd29e8cb4de6c8cfb785/pyarmor_cli_core-8.1.2-cp315-cp315-macosx_10_14_x86_64.whl", hash = "sha256:794bf98430e6ac28ff2ab61a9771a9c30736182c098eb930d2b31d8cec8782c0", size = 522825, upload-time = "2026-08-26T04:45:39.577Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a0/3c/8f5b61f254324d308ba6164ee11c3970974356d0928bfaaed76d0c2a6cab/pyarmor_cli_core-8.1.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:d1bf8f4d9e66af72dcbd14dba919e64e3f415227d159b97813efd728728e8ca8", size = 414438, upload-time = "2026-08-26T04:45:44.23Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d8/33/f8a746bf9dfb634ee7b189668acd58eb730c09305be86fb70149c1c2c442/pyarmor_cli_core-8.1.2-cp315-cp315-manylinux1_i686.whl", hash = "sha256:ed12d37d5be2d8f03248541285bfed0a5545495d7e6d8352652e40f3ff320725", size = 495326, upload-time = "2026-08-26T04:45:50.413Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8c/92/5a1e5fa2fecd0819844678bfa1b3ee3a1358922d47d4925b4f3d4d1f3f5d/pyarmor_cli_core-8.1.2-cp315-cp315-manylinux1_x86_64.whl", hash = "sha256:3ba57f26a78a5ef82fc8b1a79176de9c0c32082e2874b3e71c32178cc3028d75", size = 461872, upload-time = "2026-08-26T04:45:55.631Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f7/45/770ab7dbf55e6af6b38511f14c691a26bfe60940a2e1138ab66f986fe44e/pyarmor_cli_core-8.1.2-cp315-cp315-manylinux2014_aarch64.whl", hash = "sha256:2f0b11a99e348567dc2f7d7ff32fa4c4aec5656db5369eaa8417422c654b9d00", size = 609345, upload-time = "2026-08-26T04:46:01.045Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/71/c7/e4ef2d2c43dff6ead90fb212605725646234b3d440fc38e49cf8d349b1d3/pyarmor_cli_core-8.1.2-cp315-cp315-manylinux2014_armv7l.whl", hash = "sha256:135388b7b20054be4111f78c21fb171f7718bc763edd2395bbf00760d3b059ad", size = 1004824, upload-time = "2026-08-26T04:46:07.942Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e7/11/77da23b337acb01a6a2dcb372eea995bb8bcba4cdd7772cefccb5daf4eb9/pyarmor_cli_core-8.1.2-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:748c2906e29ef4de3bc83e7d39014d4416fa6a76ab1d07770067982fefcc1246", size = 655255, upload-time = "2026-08-26T04:46:13.011Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7a/45/c24cd03929ef50bb34407e58393121179b628c2561fecd7acda062408df7/pyarmor_cli_core-8.1.2-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:9ae31de935d673420cf3b5592a00ff2546d040c65923431f799966ff6e2976e5", size = 450939, upload-time = "2026-08-26T04:46:17.739Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/51/7a/31ea2e2983da14c6d41f4b350d2e127942c504e082b2c46aee0a836ba254/pyarmor_cli_core-8.1.2-cp315-cp315-win32.whl", hash = "sha256:816b34c1565c26d1a302ceae71e1791b4447606bfe8f6548ed4b0ed49ab4b372", size = 465364, upload-time = "2026-08-26T04:46:22.582Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5e/88/18309ad305f0ae6f462d62128d7cb882222ff0a29e8aa3461c60cad14c25/pyarmor_cli_core-8.1.2-cp315-cp315-win_amd64.whl", hash = "sha256:1a288d24fb3ca77f5060976eff3de70fc0a916074fdbe2485649a3cf9c383864", size = 424465, upload-time = "2026-08-26T04:46:27.282Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7e/58/7758001a43902bf162a41d6950cae17bf46432250c37800092b0aea7d815/pyarmor_cli_core-8.1.2-py3-none-any.whl", hash = "sha256:1f07c1cabe45511a4a91a615ded15adabeb8b5b93c4732dd633c4913299185bd", size = 5178, upload-time = "2026-08-26T04:49:14.149Z" },
]
[[package]]
@@ -1802,6 +1783,15 @@ wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
]
[[package]]
name = "tomli-w"
version = "1.2.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
]
[[package]]
name = "transitions"
version = "0.9.3"