Compare commits

...

2 Commits

28 changed files with 399 additions and 90 deletions

6
data/config/_.toml Normal file
View File

@@ -0,0 +1,6 @@
_services_desc = '服务模块(services)设置'
_providers_desc = '驱动模块(providers)设置'
_repo_desc = '单元集独立设置'
_interface_desc = '基本用户界面设置'
_default_desc = '默认设置'
_global_desc = '底层设置'

View File

@@ -1,7 +1,17 @@
enable_built_in_interface = true zmq_debug = false
_zmq_debug_desc = "[调试] ZMQ 调试服务器, 这会在 zmq_debug_port 上打开调试服务器\n可作为 HeurAMS 执行任意 python 代码, 如无必要请关闭"
zmq_debug_port = 5555
_zmq_debug_port_desc = "ZMQ 调试服务器端口"
enable_built_in_interface = false
_enable_built_in_interface_desc = "启用内置基本用户界面\n(当且仅当 HeurAMS 作为程序库时禁用, 以跳过用户界面逻辑)"
_paths_desc = "用户数据路径定义"
[paths] [paths]
data = "./data" data = "./data"
_data_desc = "用户数据根目录"
cache = "./data/cache" cache = "./data/cache"
_cache_desc = "缓存根目录\n(如音频缓存在 voice 子目录)"
config = "./data/config" config = "./data/config"
_config_desc = "配置文件根目录"
repo = "./data/repo" repo = "./data/repo"
_repo_desc = "记忆单元集根目录"

View File

@@ -0,0 +1,4 @@
_global_desc = "用户界面通用设置"
_widgets_desc = "各组件设置"
_screens_desc = "各界面设置"
_puzzles_desc = "谜题生成器设置"

View File

@@ -1,6 +1,17 @@
persist_to_file = true persist_to_file = true
_persist_to_file_desc = "[调试] 将记忆更改保存到文件"
quick_pass = true quick_pass = true
_quick_pass_desc = "[调试] 启用快速应答功能(跳过测验)"
auto_pass = false auto_pass = false
scheduled_num = 420 _auto_pass_desc = "[调试] 自动通过测试模式"
scheduled_num = "420"
_scheduled_num_desc = "默认记忆单元数量(可被单元集设置覆盖)"
algorithm = "NSP-0" algorithm = "NSP-0"
_algorithm_candidate = [ "SM-2", "SM-15M", "FSRS", "NSP-0", "None",] _algorithm_desc = "默认记忆调度算法(可被单元集设置覆盖)"
[_algorithm_candidate]
NSP-0 = "筛选用非间隔重复调度器"
none = "不设置默认调度器"
SM-2 = "第二代 SuperMemo 简单间隔重复调度器"
SM-15M = "第15代 SuperMemo 复杂间隔重复调度器 (不稳定且逆向工程)"
FSRS = "先进开放间隔重复调度器"

View File

@@ -0,0 +1,2 @@
_cloze_desc = "填空题"
_mcq_desc = "选择题"

View File

@@ -1 +1,2 @@
min_denominator = 3 min_denominator = "3"
_min_denominator_desc = "设空比例系数的倒数"

View File

@@ -1 +1,2 @@
max_riddles_num = 2 max_riddles_num = "2"
_max_riddles_num_desc = "单次生成的最大谜题数量"

View File

@@ -1 +0,0 @@
autovoice = true

View File

@@ -0,0 +1 @@
_recognition_desc = "用于 '辨识' 组件的设置"

View File

@@ -1 +1,2 @@
autovoice = true autovoice = true
_autovoice_desc = "自动语音播放"

View File

@@ -0,0 +1 @@
_tts_desc = '文本转语音驱动'

View File

@@ -0,0 +1 @@
_edgetts_desc = "微软文本转语音驱动"

View File

@@ -1,4 +1,5 @@
voice = "zh-CN-XiaoxiaoNeural" voice = "zh-CN-XiaoxiaoNeural"
_voice_desc = "音色"
[_voice_candidate] [_voice_candidate]
zh-CN-XiaoxiaoNeural = "晓晓: 中文温柔女声" zh-CN-XiaoxiaoNeural = "晓晓: 中文温柔女声"

View File

@@ -0,0 +1,5 @@
_audio_desc = '音频服务'
_llm_desc = '语言模型服务'
_sync_desc = '数据同步服务'
_timer_desc = '时间服务'
_tts_desc = '文本转语音服务'

View File

@@ -1,4 +1,5 @@
provider = "playsound" provider = "playsound"
_provider_desc = "音频驱动类型"
[_provider_candidate] [_provider_candidate]
playsound = "python 跨平台音频系统" playsound = "python 跨平台音频系统"

View File

@@ -1,4 +1,5 @@
provider = "openai" provider = "openai"
_provider_desc = "模型接口类型"
[_provider_candidate] [_provider_candidate]
openai = "OpenAI 风格 API, 同时支持与其相容的模型服务 (如 deepseek)" openai = "OpenAI 风格 API, 同时支持与其相容的模型服务 (如 deepseek)"

View File

@@ -1,4 +1,5 @@
provider = "webdav" provider = "webdav"
_provider_desc = "同步服务驱动类型"
[_provider_candidate] [_provider_candidate]
webdav = "WebDAV 兼容网络文件系统 (包括 webdavs)" webdav = "WebDAV 兼容网络文件系统 (包括 webdavs)"

View File

@@ -1,3 +1,6 @@
daystamp_override = -1 daystamp_override = -1
_daystamp_override_desc = "[调试] 覆写 UNIX 日时间戳, 单位为日\n(设为 -1 禁用)"
timestamp_override = -1 timestamp_override = -1
_timestamp_override_desc = "[调试] 覆写 UNIX 时间戳, 单位为秒\n(设为 -1 禁用)"
timezone_offset = 28800 timezone_offset = 28800
_timezone_offset_desc = "时区偏移设置, 用于取消跨天时区误差, 单位为秒\n(如 28800 为 UTC+8.0, 中国标准时间)"

View File

@@ -1,4 +1,5 @@
provider = "edgetts" provider = "edgetts"
_provider_desc = "文本转语音驱动类型"
[_provider_candidate] [_provider_candidate]
edgetts = "微软神经网络语音合成, 依赖微软网络服务" edgetts = "微软神经网络语音合成, 依赖微软网络服务"

View File

@@ -1,4 +0,0 @@
[aa.bb]
str = "sq"
boolean = true
int = 123

View File

@@ -16,6 +16,7 @@ dependencies = [
"textual>=8.2.3", "textual>=8.2.3",
"toml>=0.10.2", "toml>=0.10.2",
"transitions>=0.9.3", "transitions>=0.9.3",
"zmq>=0.0.0",
] ]
[project.scripts] [project.scripts]

View File

@@ -1,6 +1,9 @@
from heurams.interface import * from heurams.interface import *
from heurams.context import config_var from heurams.context import config_var
from heurams.services.logger import get_logger from heurams.services.logger import get_logger
import threading
import zmq
import pickle
logger = get_logger(__name__) logger = get_logger(__name__)
@@ -20,9 +23,43 @@ def environment_check():
print(f"找到 {i}") print(f"找到 {i}")
logger.debug("环境检查完成") logger.debug("环境检查完成")
def start_debug_server(app):
logger = get_logger("zmq_debug")
context = zmq.Context()
socket = context.socket(zmq.REP)
port = config_var.get()['global'].get('zmq_debug_port', 5555)
socket.bind(f"tcp://*:{port}")
logger.info(f"ZMQ Debug server started on port {port}")
first = 1
while True:
msg = socket.recv()
code = pickle.loads(msg)
namespace = {"app": app, "logger": logger, "config_var": config_var}
if first:
app.title += ' [调试已连接]'
first = 0
try:
# 先尝试 eval
result = eval(code, namespace)
socket.send(pickle.dumps(f"成功: {result}"))
except SyntaxError:
# 再尝试 exec
try:
exec(code, namespace)
socket.send(pickle.dumps(f"成功: 执行完成"))
except Exception as e:
socket.send(pickle.dumps(f"错误: {e}"))
except Exception as e:
socket.send(pickle.dumps(f"错误: {e}"))
def main(): def main():
environment_check() environment_check()
app = HeurAMSApp() app = HeurAMSApp()
if config_var.get()['global'].get('zmq_debug', False):
threading.Thread(target=start_debug_server, args=(app,), daemon=True).start()
app.run(inline=False) app.run(inline=False)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -39,7 +39,10 @@ NavigatorScreen {
margin-top: 1; margin-top: 1;
align: center middle; align: center middle;
} }
.container {
height: auto;
padding: 0 0 1 0;
}
#message-input { #message-input {
width: 1fr; width: 1fr;
margin-right: 1; margin-right: 1;

View File

@@ -8,7 +8,7 @@ import os
from textual.app import ComposeResult from textual.app import ComposeResult
from textual.containers import ScrollableContainer, Container, Horizontal, Vertical from textual.containers import ScrollableContainer, Container, Horizontal, Vertical
from textual.screen import Screen from textual.screen import Screen
from textual.widgets import Button, Footer, Header, Label, ListItem, ListView, Static, Collapsible, Input, Switch from textual.widgets import Button, Footer, Header, Label, ListItem, ListView, Static, Collapsible, Input, Switch, Select
from textual.layouts import horizontal from textual.layouts import horizontal
import heurams.kernel.particles as pt import heurams.kernel.particles as pt
@@ -45,9 +45,13 @@ class SettingScreen(Screen):
"""组合界面组件""" """组合界面组件"""
yield Header(show_clock=True) yield Header(show_clock=True)
with ScrollableContainer(): with ScrollableContainer():
yield Label('设置页面') yield Label('[b]设置页面[/b]')
for i in config_var.get(): for i in config_var.get():
yield Collapsible(*self._get_subcfg(f'{i}'), title=i) if i.startswith('_'):
continue
a = self._get_subcfg(f'{i}')
if a:
yield Collapsible(*a, title=i + f'\n{config_var.get().get(f"_{i}_desc", "")}')
yield Footer() yield Footer()
def _get_subcfg(self, parent_epath: str): def _get_subcfg(self, parent_epath: str):
@@ -56,7 +60,11 @@ class SettingScreen(Screen):
if parent.is_dir: if parent.is_dir:
lst = list() lst = list()
for i in parent: for i in parent:
lst.append(Collapsible(*self._get_subcfg(f"{parent_epath}.{i}"), title=i)) if i.startswith('_'):
continue
a = self._get_subcfg(f"{parent_epath}.{i}")
if a:
lst.append(Collapsible(*a, title=i + f'\n{parent.get(f"_{i}_desc", "")}'))
return lst return lst
if isinstance(parent, dict) or (isinstance(parent, ConfigDict) and not parent.is_dir): if isinstance(parent, dict) or (isinstance(parent, ConfigDict) and not parent.is_dir):
lst = list() lst = list()
@@ -64,32 +72,47 @@ class SettingScreen(Screen):
if i.startswith('_'): if i.startswith('_'):
continue continue
if isinstance(parent[i], dict): if isinstance(parent[i], dict):
lst.append(Collapsible(*self._get_subcfg(f"{parent_epath}.{i}"), title=i)) a = self._get_subcfg(f"{parent_epath}.{i}")
elif isinstance(parent[i], float): if a:
lst.extend([ lst.append(Collapsible(*a, title=i + f'\n{parent.get(f"_{i}_desc", "")}'))
Label(i), elif f'_{i}_candidate' in parent: # 选择框模式
Input(value=str(parent[i]), placeholder='要求一个浮点数', type='number', id=domize(f"{parent_epath}.{i}")) if isinstance(parent[f'_{i}_candidate'], dict):
]) lst.append(Horizontal(
Label(i + f'\n{parent.get(f"_{i}_desc", "")}'),
Select(((f"{j} ({k})", j) for j, k in parent[f'_{i}_candidate'].items()), prompt=f'{parent.get(f"{i}", "")}', id=domize(f"{parent_epath}.{i}")),
classes='container'
))
elif isinstance(parent[f'_{i}_candidate'], list):
lst.append(Horizontal(
Label(i + f'\n{parent.get(f"_{i}_desc", "")}'),
Select(((j, j) for j in parent[f'_{i}_candidate']), prompt=f'{parent.get(f"{i}", "")}', id=domize(f"{parent_epath}.{i}")),
classes='container'
))
else:
if isinstance(parent[i], float):
lst.append(Horizontal(
Label(i + f'\n{parent.get(f"_{i}_desc", "")}'),
Input(value=str(parent[i]), placeholder='要求一个浮点数', type='number', id=domize(f"{parent_epath}.{i}")),
classes='container'))
elif isinstance(parent[i], str): elif isinstance(parent[i], str):
lst.extend([ lst.append(Horizontal(
Label(i), Label(i + f'\n{parent.get(f"_{i}_desc", "")}'),
Input(value=parent[i], placeholder='要求一个字符串', type='text', id=domize(f"{parent_epath}.{i}")) Input(value=parent[i], placeholder='要求一个字符串', type='text', id=domize(f"{parent_epath}.{i}")),
]) classes='container'))
elif isinstance(parent[i], bool): elif isinstance(parent[i], bool):
lst.extend([ lst.append(Horizontal(
Label(i), Label(i + f'\n{parent.get(f"_{i}_desc", "")}'),
Switch(value=str(parent[i]), id=domize(f"{parent_epath}.{i}")) Switch(value=parent[i], id=domize(f"{parent_epath}.{i}")),
]) classes='container'))
elif isinstance(parent[i], int): elif isinstance(parent[i], int):
lst.extend([ lst.append(Horizontal(
Label(i), Label(i + f'\n{parent.get(f"_{i}_desc", "")}'),
Input(value=str(parent[i]), placeholder='要求一个整数', type='integer', id=domize(f"{parent_epath}.{i}")) Input(value=str(parent[i]), placeholder='要求一个整数', type='integer', id=domize(f"{parent_epath}.{i}")),
]) classes='container'))
elif isinstance(parent[i], list): elif isinstance(parent[i], list):
pass pass
else: else:
lst.append(Label('未知类型')) lst.append(Label('未知类型'))
return lst return lst
return [Label('无子项')] return [Label('无子项')]
@@ -97,6 +120,11 @@ class SettingScreen(Screen):
"""挂载组件时初始化""" """挂载组件时初始化"""
pass pass
def action_go_back(self) -> None:
"""返回上一屏幕"""
config_var.get().persist()
self.app.pop_screen()
def action_quit_app(self) -> None: def action_quit_app(self) -> None:
"""退出应用程序""" """退出应用程序"""
self.app.exit() self.app.exit()
@@ -105,12 +133,27 @@ class SettingScreen(Screen):
"""打开导航器""" """打开导航器"""
self.app.push_screen(NavigatorScreen()) self.app.push_screen(NavigatorScreen())
def on_button_pressed(self, event: Button.Pressed) -> None:
logger.debug(f"event.button.id: {event.button.id}") def on_input_changed(self, event: Input.Changed) -> None:
"""处理按钮点击事件""" widget_id = event.input.id
if str(event.button.id) == 'apply': if not widget_id:
pass return
if str(event.button.id) == 'openfolder': eepath = undomize(widget_id)
pass value = event.value
if str(event.button.id) == 'cancel': epath(config_var.get(), eepath, enable_modify=True, new_value=type(epath(config_var.get(), eepath))(value))
pass
def on_switch_changed(self, event: Switch.Changed) -> None:
widget_id = event.switch.id
if not widget_id:
return
eepath = undomize(widget_id)
value = event.value
epath(config_var.get(), eepath, enable_modify=True, new_value=type(epath(config_var.get(), eepath))(value))
def on_select_changed(self, event: Select.Changed) -> None:
widget_id = event.select.id
if not widget_id:
return
eepath = undomize(widget_id)
value = event.value
epath(config_var.get(), eepath, enable_modify=True, new_value=type(epath(config_var.get(), eepath))(value))

View File

@@ -9,17 +9,38 @@ from heurams.services.logger import get_logger
from heurams.services.exceptions import WTFException from heurams.services.exceptions import WTFException
# 我们的流程是: 找到文件名: 返回文件名里头的数据; 找不到: 继续查索引; 所以 self.data 除了存本级各种索引球用没得 # 我们的流程是: 找到文件名: 返回文件名里头的数据; 找不到: 继续查索引; 所以 self.data 除了存本级各种索引球用没得
# 递归就是这么吊 logger = get_logger(__name__)
class ConfigDict(UserDict): # 舒服了 class ConfigDict(UserDict): # 舒服了
_instances = {} # 必须使用单例模式, 不然有严重的多实例导致的配置无法持久化问题
def __new__(cls, config_path: pathlib.Path, dict=None):
if dict:
raise WTFException("不要放默认值...")
# 规范化路径, 免得单例存在"别名"
path_key = config_path.resolve()
if path_key in cls._instances:
return cls._instances[path_key]
instance = super().__new__(cls)
cls._instances[path_key] = instance
return instance
def __init__(self, config_path: pathlib.Path, dict = None): # 需要自己把自己提起来 def __init__(self, config_path: pathlib.Path, dict = None): # 需要自己把自己提起来
# 避免重复初始化
if hasattr(self, '_initialized'):
return
self._initialized = True
if dict: if dict:
raise WTFException("不要放默认值...") raise WTFException("不要放默认值...")
super().__init__(dict) super().__init__(dict)
self.logger = get_logger(__name__) logger = get_logger(__name__)
self.path = config_path self.path = config_path
self.is_dir = self.path.is_dir() self.is_dir = self.path.is_dir()
if self.is_dir: if self.is_dir:
self.update_index() # 狗儿要唱狗儿歌 self.update_index()
else: else:
with open(self.path, 'r+') as f: #TODO: 给这个做缓存 with open(self.path, 'r+') as f: #TODO: 给这个做缓存
try: try:
@@ -35,11 +56,6 @@ class ConfigDict(UserDict): # 舒服了
return ConfigDict(value) return ConfigDict(value)
return value return value
def converter(self, s):
if (self.path / s).exists:
return self.path / s
return self.path / s+'.toml'
def __contains__(self, key): def __contains__(self, key):
return super().__contains__(key) return super().__contains__(key)
@@ -56,28 +72,27 @@ class ConfigDict(UserDict): # 舒服了
def update_index(self): # 如果有人没事干在config里面创建指向config的符号链接 这玩意会崩溃 但是不要修复: 需要这个符号链接特性 def update_index(self): # 如果有人没事干在config里面创建指向config的符号链接 这玩意会崩溃 但是不要修复: 需要这个符号链接特性
for i in self.path.iterdir(): for i in self.path.iterdir():
if i.name.startswith('_'):
if i.name == '_.toml' and not i.is_dir():
with open(self.path/'_.toml', 'r+') as f:
self.data.update(dict(toml.load(f)))
continue
if i.is_dir(): if i.is_dir():
self.data[i.name] = i self.data[i.name] = i
else: else:
if i.suffix == '.toml': if i.suffix == '.toml':
self.data[i.stem] = i self.data[i.stem] = i
else: else:
self.logger.log(f"配置目录中有无效的文件 {i.stem}") # what's up bro logger.debug(f"配置目录中有无效的文件 {i.stem}") # what's up bro
def persist(self): def persist(self):
if self.is_dir: if self.is_dir:
raise WTFException("不准这样浪费性能...") for i in self.data.keys():
j = self[i]
if isinstance(j, ConfigDict):
j.persist()
logger.debug("完成配置持久化")
return
with open(self.path, 'w+') as f: with open(self.path, 'w+') as f:
toml.dump(self.data, f) toml.dump(self.data, f)
def __del__(self):
if not self.is_dir:
self.persist() # 不准循环引用, 懂了吧
@staticmethod
def titleize(objt):
if isinstance(objt, pathlib.Path):
return objt.stem
else:
return objt

View File

@@ -2,29 +2,53 @@ from heurams.services.config import ConfigDict
from heurams.services.logger import get_logger from heurams.services.logger import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
def epath(dct, path: str = '', default=None, parents=False):
def epath(dct, path: str = '', default=None, parents=False, enable_modify=False, new_value=None):
if not path: if not path:
return dct return dct
path = path.rstrip('.') path = path.rstrip('.')
path = path.lstrip('.') path = path.lstrip('.')
target = dct target = dct
keys = path.split('.')
logger.debug(f"处理 EPATH {path}, {new_value}")
for idx, i in enumerate(keys):
is_last = (idx == len(keys) - 1)
for i in path.split('.'):
# 处理字典键 # 处理字典键
logger.debug(f'处理 {i}, {(isinstance(target, dict) or isinstance(target, ConfigDict))} {i in target}') logger.debug(f'处理 {i}, {(isinstance(target, dict) or isinstance(target, ConfigDict))} {i in target}')
if is_last and enable_modify:
# 最后一次循环执行修改
if (isinstance(target, dict) or isinstance(target, ConfigDict)):
target[i] = new_value
return new_value
elif i.startswith('[') and i.endswith(']') and isinstance(target, (list, tuple)):
idx_num = int(i[1:-1])
if 0 <= idx_num < len(target):
target[idx_num] = new_value
return new_value
elif parents:
while len(target) <= idx_num:
target.append(None)
target[idx_num] = new_value
return new_value
else:
return default
else:
return default
else:
if (isinstance(target, dict) or isinstance(target, ConfigDict)) and i in target: if (isinstance(target, dict) or isinstance(target, ConfigDict)) and i in target:
target = target[i] target = target[i]
# 处理列表索引
elif i.startswith('[') and i.endswith(']') and isinstance(target, (list, tuple)): elif i.startswith('[') and i.endswith(']') and isinstance(target, (list, tuple)):
idx = int(i[1:-1]) idx_num = int(i[1:-1])
if 0 <= idx < len(target): if 0 <= idx_num < len(target):
target = target[idx] target = target[idx_num]
elif parents: elif parents:
while len(target) <= idx: while len(target) <= idx_num:
target.append(None) target.append(None)
target[idx] = {} target[idx_num] = {}
target = target[idx] target = target[idx_num]
else: else:
return default return default
elif parents: elif parents:

View File

@@ -0,0 +1,55 @@
import zmq
import pickle
import readline
import sys
class DebugClient:
def __init__(self, port=5555):
self.context = zmq.Context()
self.socket = self.context.socket(zmq.REQ)
self.socket.connect(f"tcp://localhost:{port}")
print(f"已连接到调试服务器 (端口 {port})")
print("输入Python代码并按回车执行, 输入 'exit' 退出")
print("可用变量: app, logger")
print("-" * 50)
def execute(self, code):
"""执行代码并返回结果"""
try:
self.socket.send(pickle.dumps(code))
response = pickle.loads(self.socket.recv())
return response
except Exception as e:
return f"连接错误: {e}"
def repl(self):
"""交互式REPL循环"""
self.execute('print("test")')
while True:
try:
# 获取用户输入
code = input(">>> ").strip()
if not code:
continue
if code.lower() in ['exit', 'quit']:
print("退出调试客户端")
break
# 执行代码
result = self.execute(code)
print(f"结果: {result}\n")
except KeyboardInterrupt:
print("\n退出调试客户端")
break
except EOFError:
break
if __name__ == "__main__":
# 从命令行参数获取端口
port = int(sys.argv[1]) if len(sys.argv) > 1 else 5555
client = DebugClient(port)
client.repl()

84
uv.lock generated
View File

@@ -113,6 +113,39 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" },
] ]
[[package]]
name = "cffi"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
{ url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
{ url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
{ url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
{ url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
{ url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
{ url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
{ url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
{ url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
{ url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
{ url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
{ url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
{ url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
{ url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
{ url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
{ url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
{ url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
{ url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
{ url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
{ url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
{ url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
]
[[package]] [[package]]
name = "colorama" name = "colorama"
version = "0.4.6" version = "0.4.6"
@@ -211,6 +244,7 @@ dependencies = [
{ name = "textual" }, { name = "textual" },
{ name = "toml" }, { name = "toml" },
{ name = "transitions" }, { name = "transitions" },
{ name = "zmq" },
] ]
[package.metadata] [package.metadata]
@@ -225,6 +259,7 @@ requires-dist = [
{ name = "textual", specifier = ">=8.2.3" }, { name = "textual", specifier = ">=8.2.3" },
{ name = "toml", specifier = ">=0.10.2" }, { name = "toml", specifier = ">=0.10.2" },
{ name = "transitions", specifier = ">=0.9.3" }, { name = "transitions", specifier = ">=0.9.3" },
{ name = "zmq", specifier = ">=0.0.0" },
] ]
[[package]] [[package]]
@@ -510,6 +545,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d7/54/123f6239685f5f3f2edc123f1e38d2eefacebee18cf3c532d2f4bd51d0ef/pycairo-1.29.0-cp314-cp314t-win_arm64.whl", hash = "sha256:caba0837a4b40d47c8dfb0f24cccc12c7831e3dd450837f2a356c75f21ce5a15", size = 721404, upload-time = "2025-11-11T19:12:36.919Z" }, { url = "https://files.pythonhosted.org/packages/d7/54/123f6239685f5f3f2edc123f1e38d2eefacebee18cf3c532d2f4bd51d0ef/pycairo-1.29.0-cp314-cp314t-win_arm64.whl", hash = "sha256:caba0837a4b40d47c8dfb0f24cccc12c7831e3dd450837f2a356c75f21ce5a15", size = 721404, upload-time = "2025-11-11T19:12:36.919Z" },
] ]
[[package]]
name = "pycparser"
version = "3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
]
[[package]] [[package]]
name = "pydantic" name = "pydantic"
version = "2.13.2" version = "2.13.2"
@@ -584,6 +628,37 @@ dependencies = [
] ]
sdist = { url = "https://files.pythonhosted.org/packages/a2/80/09247a2be28af2c2240132a0af6c1005a2b1d089242b13a2cd782d2de8d7/pygobject-3.56.2.tar.gz", hash = "sha256:b816098969544081de9eecedb94ad6ac59c77e4d571fe7051f18bebcec074313", size = 1409059, upload-time = "2026-03-25T16:14:04.008Z" } sdist = { url = "https://files.pythonhosted.org/packages/a2/80/09247a2be28af2c2240132a0af6c1005a2b1d089242b13a2cd782d2de8d7/pygobject-3.56.2.tar.gz", hash = "sha256:b816098969544081de9eecedb94ad6ac59c77e4d571fe7051f18bebcec074313", size = 1409059, upload-time = "2026-03-25T16:14:04.008Z" }
[[package]]
name = "pyzmq"
version = "27.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "implementation_name == 'pypy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" },
{ url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" },
{ url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" },
{ url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" },
{ url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" },
{ url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" },
{ url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" },
{ url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" },
{ url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" },
{ url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" },
{ url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197, upload-time = "2025-09-08T23:08:44.973Z" },
{ url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175, upload-time = "2025-09-08T23:08:46.601Z" },
{ url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427, upload-time = "2025-09-08T23:08:48.187Z" },
{ url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929, upload-time = "2025-09-08T23:08:49.76Z" },
{ url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193, upload-time = "2025-09-08T23:08:51.7Z" },
{ url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388, upload-time = "2025-09-08T23:08:53.393Z" },
{ url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316, upload-time = "2025-09-08T23:08:55.702Z" },
{ url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472, upload-time = "2025-09-08T23:08:58.18Z" },
{ url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401, upload-time = "2025-09-08T23:08:59.802Z" },
{ url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170, upload-time = "2025-09-08T23:09:01.418Z" },
]
[[package]] [[package]]
name = "rich" name = "rich"
version = "15.0.0" version = "15.0.0"
@@ -753,3 +828,12 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" },
{ url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" },
] ]
[[package]]
name = "zmq"
version = "0.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyzmq" },
]
sdist = { url = "https://files.pythonhosted.org/packages/6e/78/833b2808793c1619835edb1a4e17a023d5d625f4f97ff25ffff986d1f472/zmq-0.0.0.tar.gz", hash = "sha256:6b1a1de53338646e8c8405803cffb659e8eb7bb02fff4c9be62a7acfac8370c9", size = 966, upload-time = "2015-05-21T17:34:26.603Z" }