diff --git a/frontend/composables/useApi.js b/frontend/composables/useApi.js index ffe5601..9ae1de6 100644 --- a/frontend/composables/useApi.js +++ b/frontend/composables/useApi.js @@ -530,8 +530,8 @@ export const useApi = () => { } // 获取数据库密钥 - const getDbKey = async () => { - return await request('/get_db_key') + const getKeys = async () => { + return await request('/get_keys') } // 获取图片密钥 @@ -589,7 +589,7 @@ export const useApi = () => { getWrappedAnnual, getWrappedAnnualMeta, getWrappedAnnualCard, - getDbKey, + getKeys, getImageKey, getWxStatus, } diff --git a/frontend/pages/decrypt.vue b/frontend/pages/decrypt.vue index 148ec45..4d60b64 100644 --- a/frontend/pages/decrypt.vue +++ b/frontend/pages/decrypt.vue @@ -58,7 +58,7 @@ - {{ isGettingDbKey ? '获取中...' : '自动获取' }} + {{ isGettingDbKey ? '获取中...' : '一键获取全部密钥' }}

@@ -71,7 +71,7 @@ - 尝试自动获取,或者使用 wx_key 等工具获取的64位十六进制字符串 + 点击按钮将自动获取【数据库】与【图片】双重密钥。您也可以手动输入已知的64位密钥(使用wx_key等工具获取)。

@@ -183,53 +183,37 @@
- 手动输入或通过微信获取 - + 此步骤将为您解密微信聊天中的图片
+

+ + + + 如果您在第一步使用了“一键获取”或触发了云端解析,下方输入框已被自动填充。您也可可以使用wx_key等工具手动获取。 +

{{ manualKeyErrors.xor_key }}

{{ manualKeyErrors.aes_key }}

- -

- - - - 尝试自动获取,或使用 wx_key 获取图片密钥;AES 可选(V4-V2 需要) -

@@ -450,7 +434,7 @@ import { ref, reactive, computed, onMounted, onBeforeUnmount } from 'vue' import { useApi } from '~/composables/useApi' -const { decryptDatabase, saveMediaKeys, getSavedKeys, getDbKey, getImageKey, getWxStatus } = useApi() +const { decryptDatabase, saveMediaKeys, getSavedKeys, getKeys, getImageKey, getWxStatus } = useApi() const loading = ref(false) const error = ref('') @@ -458,7 +442,6 @@ const warning = ref('') // 警告,用于密钥提示 const currentStep = ref(0) const mediaAccount = ref('') const isGettingDbKey = ref(false) -const isGettingImageKey = ref(false) // 步骤定义 const steps = [ @@ -548,74 +531,45 @@ const handleGetDbKey = async () => { formErrors.key = '' try { - const statusRes = await getWxStatus() // pid不是主进程,但是没关系 + const statusRes = await getWxStatus() const wxStatus = statusRes?.wx_status if (wxStatus?.is_running) { - warning.value = '检测到微信正在运行,5秒后将终止进程并重启以获取密钥!!' + warning.value = '检测到微信正在运行,5秒后将终止进程并重启以获取全套密钥!' await new Promise(resolve => setTimeout(resolve, 5000)) - } else { - // 没有逻辑 } - warning.value = '正在启动微信以获取密钥,请确保微信未开启“自动登录”,并在启动后 1 分钟内完成登录操作。' + warning.value = '正在启动微信,请确保微信未开启“自动登录”,并在弹窗中正常登录。' - const res = await getDbKey() + const res = await getKeys() if (res && res.status === 0) { if (res.data?.db_key) { formData.key = res.data.db_key - warning.value = '' } - - if (res.errmsg && res.errmsg !== 'ok') { - warning.value = res.errmsg + // 直接把图片密钥也存好 + if (res.data?.xor_key) { + manualKeys.xor_key = res.data.xor_key } + if (res.data?.aes_key) { + manualKeys.aes_key = res.data.aes_key + } + warning.value = '🎉 数据库与图片密钥均已获取成功!' + // 3秒后清除成功提示,保持 UI 干净 + setTimeout(() => { if(warning.value.includes('获取成功')) warning.value = '' }, 3000) } else { error.value = '获取失败: ' + (res?.errmsg || '未知错误') + warning.value = '' } } catch (e) { console.error(e) error.value = '系统错误: ' + e.message + warning.value = '' } finally { isGettingDbKey.value = false } } -const handleGetImageKey = async () => { - if (isGettingImageKey.value) return - isGettingImageKey.value = true - manualKeyErrors.xor_key = '' - manualKeyErrors.aes_key = '' - - error.value = '' - warning.value = '' - - try { - const res = await getImageKey() - - if (res && res.status === 0) { - if (res.data?.aes_key) { - manualKeys.aes_key = res.data.aes_key - } - if (res.data?.xor_key) { - // 后端记得处理为16进制再返回!!! - manualKeys.xor_key = res.data.xor_key - } - - if (res.errmsg && res.errmsg !== 'ok') { - error.value = res.errmsg - } - } else { - error.value = '获取失败: ' + (res?.errmsg || '未知错误') - } - } catch (e) { - console.error(e) - error.value = '系统错误: ' + e.message - } finally { - isGettingImageKey.value = false - } -} const applyManualKeys = () => { manualKeyErrors.xor_key = '' @@ -749,13 +703,13 @@ const handleDecrypt = async () => { if (!validateForm()) { return } - + loading.value = true error.value = '' warning.value = '' resetDbDecryptProgress() - + try { const canSse = process.client && typeof window !== 'undefined' && typeof EventSource !== 'undefined' @@ -776,9 +730,26 @@ const handleDecrypt = async () => { if (accounts.length > 0) mediaAccount.value = accounts[0] } catch (e) {} - clearManualKeys() currentStep.value = 1 await prefillKeysForAccount(mediaAccount.value) + + if (!manualKeys.xor_key && !manualKeys.aes_key) { + warning.value = '正在通过云端备选方案自动获取图片密钥,请稍候...' + try { + const imgRes = await getImageKey({ account: mediaAccount.value }) + if (imgRes && imgRes.status === 0) { + if (imgRes.data?.xor_key) manualKeys.xor_key = imgRes.data.xor_key + if (imgRes.data?.aes_key) manualKeys.aes_key = imgRes.data.aes_key + warning.value = '已通过云端成功获取图片密钥!' + setTimeout(() => { if(warning.value.includes('成功获取')) warning.value = '' }, 3000) + } else { + warning.value = '云端获取图片密钥失败,您可以尝试手动填写。' + } + } catch (e) { + warning.value = '网络请求失败,请手动填写图片密钥。' + } + } + } else if (result.status === 'failed') { if (result.failure_count > 0 && result.success_count === 0) { error.value = result.message || '所有文件解密失败' @@ -855,9 +826,26 @@ const handleDecrypt = async () => { loading.value = false if (data.status === 'completed') { - clearManualKeys() currentStep.value = 1 await prefillKeysForAccount(mediaAccount.value) + + // 【重点】如果刚才没有通过双 Hook 拿到图片密钥,触发云端 API 备用方案自动获取 + if (!manualKeys.xor_key && !manualKeys.aes_key) { + warning.value = '正在通过云端备选方案自动获取图片密钥,请稍候...' + try { + const imgRes = await getImageKey({ account: mediaAccount.value }) + if (imgRes && imgRes.status === 0) { + if (imgRes.data?.xor_key) manualKeys.xor_key = imgRes.data.xor_key + if (imgRes.data?.aes_key) manualKeys.aes_key = imgRes.data.aes_key + warning.value = '已通过云端成功获取图片密钥!' + setTimeout(() => { if(warning.value.includes('成功获取')) warning.value = '' }, 3000) + } else { + warning.value = '云端获取图片密钥失败,您可以尝试手动填写。' + } + } catch (e) { + warning.value = '网络请求失败,请手动填写图片密钥。' + } + } } else if (data.status === 'failed') { error.value = data.message || '所有文件解密失败' } else { diff --git a/pyproject.toml b/pyproject.toml index 567fc1a..e37fa6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ "pilk>=0.2.4", "pypinyin>=0.53.0", "jieba>=0.42.1", - "wx_key", + "wx_key>=1.1.0", "packaging", "httpx", ] diff --git a/src/wechat_decrypt_tool/key_service.py b/src/wechat_decrypt_tool/key_service.py index 69a93d3..215003d 100644 --- a/src/wechat_decrypt_tool/key_service.py +++ b/src/wechat_decrypt_tool/key_service.py @@ -35,10 +35,12 @@ logger = logging.getLogger(__name__) @dataclass class HookConfig: min_version: str - pattern: str # 用 00 不要用 ? !!!! 否则C++内存会炸 + pattern: str mask: str offset: int - + md5_pattern: str = "" + md5_mask: str = "" + md5_offset: int = 0 class WeChatKeyFetcher: def __init__(self): @@ -50,13 +52,13 @@ class WeChatKeyFetcher: return " ".join([f"{b:02X}" for b in hex_array]) def _get_hook_config(self, version_str: str) -> Optional[HookConfig]: - """搬运自wx_key代码,未来用ida脚本直接获取即可""" try: v_curr = pkg_version.parse(version_str) except Exception as e: logger.error(f"版本号解析失败: {version_str} || {e}") return None + if v_curr > pkg_version.parse("4.1.6.14"): return HookConfig( min_version=">4.1.6.14", @@ -66,7 +68,10 @@ class WeChatKeyFetcher: 0x89, 0xCE, 0x48, 0x89 ]), mask="xxxxxxxxxxxxxxxxxxxxxxxx", - offset=-3 + offset=-3, + md5_pattern="48 8D 4D 00 48 89 4D B0 48 89 45 B8 48 8D 7D 00 48 8D 55 B0 48 89 F9", + md5_mask="xxx?xxxxxxxxxxx?xxxxxxx", + md5_offset=4 ) if pkg_version.parse("4.1.4") <= v_curr <= pkg_version.parse("4.1.6.14"): @@ -78,10 +83,14 @@ class WeChatKeyFetcher: 0x83, 0xec, 0x50, 0x41 ]), mask="xxxxxxxxxx?xxxx?xxxxxxxx", - offset=-3 + offset=-3, + md5_pattern="48 8D 4D 00 48 89 4D B0 48 89 45 B8 48 8D 7D 00 48 8D 55 B0 48 89 F9", + md5_mask="xxx?xxxxxxxxxxx?xxxxxxx", + md5_offset=4 ) if v_curr < pkg_version.parse("4.1.4"): + """图片密钥可能是错的,版本过低没有测试""" return HookConfig( min_version="<4.1.4", pattern=self._hex_array_to_str([ @@ -90,7 +99,10 @@ class WeChatKeyFetcher: 0x89, 0xce, 0x48, 0x89 ]), mask="xxxxxxxxxxxxxxxxxxxxxxxx", - offset=-15 # -0xf + offset=-15, # -0xf + md5_pattern="48 8D 4D 00 48 89 4D B0 48 89 45 B8 48 8D 7D 00 48 8D 55 B0 48 89 F9", + md5_mask="xxx?xxxxxxxxxxx?xxxxxxx", + md5_offset=4 ) return None @@ -134,13 +146,12 @@ class WeChatKeyFetcher: logger.error(f"启动微信失败: {e}") raise RuntimeError(f"无法启动微信: {e}") - def fetch_key(self) -> str: - """没有wx_key模块无法自动获取密钥""" + def fetch_key(self) -> dict: + """调用 wx_key 获取双密钥""" if wx_key is None: raise RuntimeError("wx_key 模块未安装或加载失败") install_info = detect_wechat_installation() - exe_path = install_info.get('wechat_exe_path') version = install_info.get('wechat_version') @@ -151,30 +162,34 @@ class WeChatKeyFetcher: config = self._get_hook_config(version) if not config: - raise RuntimeError(f"不支持的微信版本: {version}") + raise RuntimeError(f"原生获取失败:当前微信版本 ({version}) 过低,为保证稳定性,仅支持 4.1.5 及以上版本使用原生获取。") self.kill_wechat() - pid = self.launch_wechat(exe_path) logger.info(f"WeChat launched, PID: {pid}") - logger.info(f"Initializing Hook with pattern: {config.pattern[:20]}... Offset: {config.offset}") - - if not wx_key.initialize_hook(pid, "", config.pattern, config.mask, config.offset): + if not wx_key.initialize_hook(pid, "", config.pattern, config.mask, config.offset, + config.md5_pattern, config.md5_mask, config.md5_offset): err = wx_key.get_last_error_msg() raise RuntimeError(f"Hook初始化失败: {err}") start_time = time.time() - + found_db_key = None + found_md5_data = None try: while True: if time.time() - start_time > self.timeout_seconds: - raise TimeoutError("获取密钥超时 (60s)") + raise TimeoutError("获取密钥超时 (60s),请确保在弹出的微信中完成登录。") - key = wx_key.poll_key_data() - if key: - found_key = key + key_data = wx_key.poll_key_data() + if key_data: + if 'key' in key_data: + found_db_key = key_data['key'] + if 'md5' in key_data: + found_md5_data = key_data['md5'] + + if found_db_key and found_md5_data: break while True: @@ -185,15 +200,22 @@ class WeChatKeyFetcher: logger.error(f"[Hook Error] {msg}") time.sleep(0.1) - finally: logger.info("Cleaning up hook...") wx_key.cleanup_hook() - if found_key: - return found_key - else: - raise RuntimeError("未知错误,未获取到密钥") + aes_key = None # gemini !!! ??? + xor_key = None + + if found_md5_data and "|" in found_md5_data: + aes_key, xor_key_dec = found_md5_data.split("|") + xor_key = f"0x{int(xor_key_dec):02X}" + + return { + "db_key": found_db_key, + "aes_key": aes_key, + "xor_key": xor_key + } def get_db_key_workflow(): fetcher = WeChatKeyFetcher() @@ -202,73 +224,11 @@ def get_db_key_workflow(): # ============================== 以下是图片密钥逻辑 ===================================== - -# 远程 API 配置 -REMOTE_URL = "https://view.free.c3o.re/dashboard" -BASE_URL = "https://view.free.c3o.re" # 用于拼接js - -# NEXT_ACTION_ID = "7c8f99280c70626ccf5960cc4a68f368197e15f8e9" # 不可以硬编码 - - -async def fetch_js_and_scan(client: httpx.AsyncClient, js_path: str) -> Optional[str]: - """ - 异步下载单个 JS 文件并匹配 Action ID - """ - full_url = f"{BASE_URL}{js_path}" if js_path.startswith("/") else js_path - try: - response = await client.get(full_url) - if response.status_code != 200: - return None - - content = response.text - - action_id_pattern = re.compile(r'createServerReference.*?["\']([a-f0-9]{42})["\'].*?["\']getUserConfigFromBytes["\']') - - match = action_id_pattern.search(content) - if match: - found_id = match.group(1) - return found_id - - except Exception as e: - logger.error(f"Error fetching {js_path}: {e}") - return None - - -async def _get_next_action_id_async() -> str: - async with httpx.AsyncClient(timeout=10.0) as client: - - resp = await client.get(REMOTE_URL) - html = resp.text - - js_file_pattern = re.compile(r'src="(/_next/static/chunks/[^"]+\.js)"') - js_files = set(js_file_pattern.findall(html)) - - if not js_files: - raise Exception("未找到任何 Next.js chunk 文件,可能页面结构已变动。") - - tasks = [fetch_js_and_scan(client, js_path) for js_path in js_files] - - results = await asyncio.gather(*tasks) - - for res in results: - if res: - return res - - raise Exception("遍历了所有 JS 文件,但未找到匹配的 createServerReference ID。") - - def get_wechat_internal_global_config(wx_dir: Path, file_name1) -> bytes: - """ - 读微信目录下的主配置文件 - """ xwechat_files_root = wx_dir.parent - target_path = os.path.join(xwechat_files_root, "all_users", "config", file_name1) - if not os.path.exists(target_path): - logger.error(f"未找到微信内部 global_config: {target_path}") - raise FileNotFoundError(f"找不到文件: {target_path},请确认微信数据目录结构是否完整") - + raise FileNotFoundError(f"找不到配置文件: {target_path},请确认微信数据目录结构是否完整") return Path(target_path).read_bytes() @@ -278,90 +238,36 @@ async def fetch_and_save_remote_keys(account: Optional[str] = None) -> Dict[str, wx_id_dir = _resolve_account_wxid_dir(account_dir) wxid = wx_id_dir.name - logger.info("尝试获取next_action_id") - try: - next_action_id = await _get_next_action_id_async() - logger.info(f"获取next_action_id成功: {next_action_id}") - except Exception as e: - raise RuntimeError(f"获取next_action_id失败:{e}") + url = "https://view.free.c3o.re/api/key" + data = {"weixinIDFolder": wxid} - - logger.info(f"正在为账号 {wxid} 获取密钥...") + logger.info(f"正在为账号 {wxid} 获取云端备选图片密钥...") try: - blob1_bytes = get_wechat_internal_global_config(wx_id_dir, file_name1= "global_config") # 估计这是唯一有效的数据!! - logger.info(f"获取微信内部配置成功,大小: {len(blob1_bytes)} bytes") + blob1_bytes = get_wechat_internal_global_config(wx_id_dir, file_name1="global_config") + blob2_bytes = get_wechat_internal_global_config(wx_id_dir, file_name1="global_config.crc") except Exception as e: raise RuntimeError(f"读取微信内部文件失败: {e}") - try: - blob2_bytes = get_wechat_internal_global_config(wx_id_dir, file_name1= "global_config.crc") - logger.info(f"获取微信内部配置成功,大小: {len(blob2_bytes)} bytes") - except Exception as e: - raise RuntimeError(f"读取微信内部文件失败: {e}") - - blob3_bytes = b"" - - headers = { - "Accept": "text/x-component", - "Next-Action": next_action_id, - "Next-Router-State-Tree": "%5B%22%22%2C%7B%22children%22%3A%5B%22dashboard%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%2Ctrue%5D", - "Origin": "https://view.free.c3o.re", - "Referer": "https://view.free.c3o.re/dashboard", - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" - } - files = { - '1': ('blob', blob1_bytes, 'application/octet-stream'), - '2': ('blob', blob2_bytes, 'application/octet-stream'), - '3': ('blob', blob3_bytes, 'application/octet-stream'), - '0': (None, json.dumps([wxid, "$A1", "$A2", "$A3", 0],separators=(",",":")).encode('utf-8')), + 'fileBytes': ('file', blob1_bytes, 'application/octet-stream'), + 'crcBytes': ('file.crc', blob2_bytes, 'application/octet-stream'), } - async with httpx.AsyncClient(timeout=30) as client: - logger.info("向远程服务器发送请求...") - response = await client.post(REMOTE_URL, headers=headers, files=files) + logger.info("向云端 API 发送请求...") + response = await client.post(url, data=data, files=files) if response.status_code != 200: - raise RuntimeError(f"远程服务器错误: {response.status_code} - {response.text[:100]}") + raise RuntimeError(f"云端服务器错误: {response.status_code} - {response.text[:100]}") + config = response.json() + if not config: + raise RuntimeError("云端解析失败: 返回数据为空") - result_data = {} - lines = response.text.split('\n') - - found_config = False - for line in lines: - line = line.strip() - if not line: - continue - - if line.startswith('1:'): - try: - json_part = line[2:] # 去掉 "1:" - data_obj = json.loads(json_part) - - if "config" in data_obj: - config = data_obj["config"] - result_data = { - "xor_key": config.get("xor_key", ""), - "aes_key": config.get("aes_key", ""), - "nick_name": config.get("nick_name", ""), - "avatar_url": config.get("avatar_url", "") - } - found_config = True - break - except Exception as e: - logger.warning(f"解析响应行失败: {e}") - continue - - if not found_config or not result_data.get("aes_key"): - logger.error(f"响应中未找到密钥信息。Full Response: {response.text[:500]}") - raise RuntimeError("解析失败: 服务器未返回 config 数据") - - # 6. 处理并保存密钥 - xor_raw = str(result_data["xor_key"]) - aes_val = str(result_data["aes_key"]) + # 新 API 的字段兼容处理 + xor_raw = str(config.get("xorKey", config.get("xor_key", ""))) + aes_val = str(config.get("aesKey", config.get("aes_key", ""))) try: if xor_raw.startswith("0x"): @@ -382,6 +288,5 @@ async def fetch_and_save_remote_keys(account: Optional[str] = None) -> Dict[str, "wxid": wxid, "xor_key": xor_hex_str, "aes_key": aes_val, - "nick_name": result_data["nick_name"] - } - + "nick_name": config.get("nickName", config.get("nick_name", "")) + } \ No newline at end of file diff --git a/src/wechat_decrypt_tool/routers/chat.py b/src/wechat_decrypt_tool/routers/chat.py index f741b8a..4a00212 100644 --- a/src/wechat_decrypt_tool/routers/chat.py +++ b/src/wechat_decrypt_tool/routers/chat.py @@ -4114,8 +4114,7 @@ def _collect_chat_messages( render_type = "system" template = _extract_xml_tag_text(raw_text, "template") if template: - import re - + # import re pat_usernames.update({m.group(1) for m in re.finditer(r"\$\{([^}]+)\}", template) if m.group(1)}) content_text = "[拍一拍]" else: @@ -4255,8 +4254,7 @@ def _collect_chat_messages( elif local_type == 50: render_type = "voip" try: - import re - + # import re block = raw_text m_voip = re.search( r"(]*>.*?)", @@ -5013,7 +5011,7 @@ def list_chat_messages( render_type = "system" template = _extract_xml_tag_text(raw_text, "template") if template: - import re + # import re pat_usernames.update({m.group(1) for m in re.finditer(r"\$\{([^}]+)\}", template) if m.group(1)}) content_text = "[拍一拍]" @@ -5145,7 +5143,7 @@ def list_chat_messages( elif local_type == 50: render_type = "voip" try: - import re + # import re block = raw_text m_voip = re.search( @@ -5494,6 +5492,7 @@ def list_chat_messages( if existing_local: try: + # import re cur = str(m.get("emojiUrl") or "") if cur and re.match(r"^https?://", cur, flags=re.I) and ("/api/chat/media/emoji" not in cur): m["emojiRemoteUrl"] = cur diff --git a/src/wechat_decrypt_tool/routers/keys.py b/src/wechat_decrypt_tool/routers/keys.py index 101f9e5..2a4da9a 100644 --- a/src/wechat_decrypt_tool/routers/keys.py +++ b/src/wechat_decrypt_tool/routers/keys.py @@ -53,31 +53,28 @@ async def get_saved_keys(account: Optional[str] = None): } -@router.get("/api/get_db_key", summary="自动获取微信数据库密钥") +@router.get("/api/get_keys", summary="自动获取微信数据库与图片密钥") async def get_wechat_db_key(): """ 自动流程: 1. 结束微信进程 2. 启动微信 - 3. 根据版本注入 Hook - 4. 抓取密钥并返回 + 3. 根据版本注入双 Hook + 4. 抓取 DB 与 图片密钥(AES + XOR)并返回 """ try: - # 不需要async吧,我相信fastapi的线程池 - db_key = get_db_key_workflow() + keys_data = get_db_key_workflow() return { "status": 0, "errmsg": "ok", - "data": { - "db_key": db_key - } + "data": keys_data # 现在完美包含了 db_key, aes_key, xor_key } except TimeoutError: return { "status": -1, - "errmsg": "获取超时,请确保微信没有开启自动登录 或者 加快手速", + "errmsg": "获取超时,请确保微信没有开启自动登录并且在弹窗中完成了登录", "data": {} } except Exception as e: @@ -88,6 +85,7 @@ async def get_wechat_db_key(): } + @router.get("/api/get_image_key", summary="获取并保存微信图片密钥") async def get_image_key(account: Optional[str] = None): """ diff --git a/tools/key_wheels/wx_key-1.0.0-cp310-cp310-win_amd64.whl b/tools/key_wheels/wx_key-1.0.0-cp310-cp310-win_amd64.whl deleted file mode 100644 index 0276318..0000000 Binary files a/tools/key_wheels/wx_key-1.0.0-cp310-cp310-win_amd64.whl and /dev/null differ diff --git a/tools/key_wheels/wx_key-1.0.0-cp311-cp311-win_amd64.whl b/tools/key_wheels/wx_key-1.0.0-cp311-cp311-win_amd64.whl deleted file mode 100644 index b286145..0000000 Binary files a/tools/key_wheels/wx_key-1.0.0-cp311-cp311-win_amd64.whl and /dev/null differ diff --git a/tools/key_wheels/wx_key-1.0.0-cp312-cp312-win_amd64.whl b/tools/key_wheels/wx_key-1.0.0-cp312-cp312-win_amd64.whl deleted file mode 100644 index c049ea0..0000000 Binary files a/tools/key_wheels/wx_key-1.0.0-cp312-cp312-win_amd64.whl and /dev/null differ diff --git a/tools/key_wheels/wx_key-1.0.0-cp313-cp313-win_amd64.whl b/tools/key_wheels/wx_key-1.0.0-cp313-cp313-win_amd64.whl deleted file mode 100644 index 42afeda..0000000 Binary files a/tools/key_wheels/wx_key-1.0.0-cp313-cp313-win_amd64.whl and /dev/null differ diff --git a/tools/key_wheels/wx_key-1.0.0-cp314-cp314-win_amd64.whl b/tools/key_wheels/wx_key-1.0.0-cp314-cp314-win_amd64.whl deleted file mode 100644 index d19b6f4..0000000 Binary files a/tools/key_wheels/wx_key-1.0.0-cp314-cp314-win_amd64.whl and /dev/null differ diff --git a/tools/key_wheels/wx_key-1.1.0-cp310-cp310-win_amd64.whl b/tools/key_wheels/wx_key-1.1.0-cp310-cp310-win_amd64.whl new file mode 100644 index 0000000..3d8ae63 Binary files /dev/null and b/tools/key_wheels/wx_key-1.1.0-cp310-cp310-win_amd64.whl differ diff --git a/tools/key_wheels/wx_key-1.1.0-cp311-cp311-win_amd64.whl b/tools/key_wheels/wx_key-1.1.0-cp311-cp311-win_amd64.whl new file mode 100644 index 0000000..c628766 Binary files /dev/null and b/tools/key_wheels/wx_key-1.1.0-cp311-cp311-win_amd64.whl differ diff --git a/tools/key_wheels/wx_key-1.1.0-cp312-cp312-win_amd64.whl b/tools/key_wheels/wx_key-1.1.0-cp312-cp312-win_amd64.whl new file mode 100644 index 0000000..2a39ad4 Binary files /dev/null and b/tools/key_wheels/wx_key-1.1.0-cp312-cp312-win_amd64.whl differ diff --git a/tools/key_wheels/wx_key-1.1.0-cp313-cp313-win_amd64.whl b/tools/key_wheels/wx_key-1.1.0-cp313-cp313-win_amd64.whl new file mode 100644 index 0000000..f00eccc Binary files /dev/null and b/tools/key_wheels/wx_key-1.1.0-cp313-cp313-win_amd64.whl differ diff --git a/tools/key_wheels/wx_key-1.1.0-cp314-cp314-win_amd64.whl b/tools/key_wheels/wx_key-1.1.0-cp314-cp314-win_amd64.whl new file mode 100644 index 0000000..26cffc4 Binary files /dev/null and b/tools/key_wheels/wx_key-1.1.0-cp314-cp314-win_amd64.whl differ diff --git a/uv.lock b/uv.lock index 04b52ea..3dc6aee 100644 --- a/uv.lock +++ b/uv.lock @@ -919,7 +919,7 @@ requires-dist = [ { name = "requests", specifier = ">=2.32.4" }, { name = "typing-extensions", specifier = ">=4.8.0" }, { name = "uvicorn", extras = ["standard"], specifier = ">=0.24.0" }, - { name = "wx-key" }, + { name = "wx-key", specifier = ">=1.1.0" }, { name = "zstandard", specifier = ">=0.23.0" }, ] provides-extras = ["build"] @@ -935,13 +935,13 @@ wheels = [ [[package]] name = "wx-key" -version = "1.0.0" +version = "1.1.0" source = { registry = "tools/key_wheels" } wheels = [ - { path = "wx_key-1.0.0-cp311-cp311-win_amd64.whl" }, - { path = "wx_key-1.0.0-cp312-cp312-win_amd64.whl" }, - { path = "wx_key-1.0.0-cp313-cp313-win_amd64.whl" }, - { path = "wx_key-1.0.0-cp314-cp314-win_amd64.whl" }, + { path = "wx_key-1.1.0-cp311-cp311-win_amd64.whl" }, + { path = "wx_key-1.1.0-cp312-cp312-win_amd64.whl" }, + { path = "wx_key-1.1.0-cp313-cp313-win_amd64.whl" }, + { path = "wx_key-1.1.0-cp314-cp314-win_amd64.whl" }, ] [[package]]