From 709e15663bb841b28a0b24e71b9f6987c96b3a73 Mon Sep 17 00:00:00 2001 From: pluvium27 Date: Mon, 20 Apr 2026 06:37:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20ZMQ=20=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=B9=B6=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/config/global.toml | 6 +- data/config/interface/global.toml | 2 +- data/config/interface/puzzles/cloze.toml | 2 +- data/config/interface/puzzles/mcq.toml | 2 +- pyproject.toml | 1 + src/heurams/interface/__main__.py | 37 +++++++++++ src/heurams/interface/screens/setting.py | 42 ++++++++---- src/heurams/services/config.py | 54 ++++++++------- src/heurams/services/epath.py | 62 +++++++++++------ src/heurams/tools/zmqclient.py | 55 ++++++++++++++++ uv.lock | 84 ++++++++++++++++++++++++ 11 files changed, 289 insertions(+), 58 deletions(-) create mode 100644 src/heurams/tools/zmqclient.py diff --git a/data/config/global.toml b/data/config/global.toml index 257649b..47d5999 100644 --- a/data/config/global.toml +++ b/data/config/global.toml @@ -1,4 +1,8 @@ -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 = "用户数据路径定义" diff --git a/data/config/interface/global.toml b/data/config/interface/global.toml index e6e60fd..d9d800b 100644 --- a/data/config/interface/global.toml +++ b/data/config/interface/global.toml @@ -4,7 +4,7 @@ quick_pass = true _quick_pass_desc = "[调试] 启用快速应答功能(跳过测验)" auto_pass = false _auto_pass_desc = "[调试] 自动通过测试模式" -scheduled_num = 420 +scheduled_num = "420" _scheduled_num_desc = "默认记忆单元数量(可被单元集设置覆盖)" algorithm = "NSP-0" _algorithm_desc = "默认记忆调度算法(可被单元集设置覆盖)" diff --git a/data/config/interface/puzzles/cloze.toml b/data/config/interface/puzzles/cloze.toml index d467db1..a2e0ed8 100644 --- a/data/config/interface/puzzles/cloze.toml +++ b/data/config/interface/puzzles/cloze.toml @@ -1,2 +1,2 @@ -min_denominator = 3 +min_denominator = "3" _min_denominator_desc = "设空比例系数的倒数" diff --git a/data/config/interface/puzzles/mcq.toml b/data/config/interface/puzzles/mcq.toml index f0fe9ca..558ca8f 100644 --- a/data/config/interface/puzzles/mcq.toml +++ b/data/config/interface/puzzles/mcq.toml @@ -1,2 +1,2 @@ -max_riddles_num = 2 +max_riddles_num = "2" _max_riddles_num_desc = "单次生成的最大谜题数量" diff --git a/pyproject.toml b/pyproject.toml index 92772aa..18cc5b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ dependencies = [ "textual>=8.2.3", "toml>=0.10.2", "transitions>=0.9.3", + "zmq>=0.0.0", ] [project.scripts] diff --git a/src/heurams/interface/__main__.py b/src/heurams/interface/__main__.py index 209f0a3..65f8454 100644 --- a/src/heurams/interface/__main__.py +++ b/src/heurams/interface/__main__.py @@ -1,6 +1,9 @@ from heurams.interface import * from heurams.context import config_var from heurams.services.logger import get_logger +import threading +import zmq +import pickle logger = get_logger(__name__) @@ -20,9 +23,43 @@ def environment_check(): print(f"找到 {i}") 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(): environment_check() + 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) if __name__ == "__main__": diff --git a/src/heurams/interface/screens/setting.py b/src/heurams/interface/screens/setting.py index cdf3c6a..04c9f60 100644 --- a/src/heurams/interface/screens/setting.py +++ b/src/heurams/interface/screens/setting.py @@ -75,17 +75,17 @@ class SettingScreen(Screen): a = self._get_subcfg(f"{parent_epath}.{i}") if a: lst.append(Collapsible(*a, title=i + f'\n{parent.get(f"_{i}_desc", "")}')) - elif f'_{i}_candidate' in parent: + elif f'_{i}_candidate' in parent: # 选择框模式 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()), + 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']), + 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: @@ -102,7 +102,7 @@ class SettingScreen(Screen): elif isinstance(parent[i], bool): lst.append(Horizontal( 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): lst.append(Horizontal( @@ -122,6 +122,7 @@ class SettingScreen(Screen): def action_go_back(self) -> None: """返回上一屏幕""" + config_var.get().persist() self.app.pop_screen() def action_quit_app(self) -> None: @@ -132,12 +133,27 @@ class SettingScreen(Screen): """打开导航器""" self.app.push_screen(NavigatorScreen()) - def on_button_pressed(self, event: Button.Pressed) -> None: - logger.debug(f"event.button.id: {event.button.id}") - """处理按钮点击事件""" - if str(event.button.id) == 'apply': - pass - if str(event.button.id) == 'openfolder': - pass - if str(event.button.id) == 'cancel': - pass + + def on_input_changed(self, event: Input.Changed) -> None: + widget_id = event.input.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_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)) \ No newline at end of file diff --git a/src/heurams/services/config.py b/src/heurams/services/config.py index 7119fc2..9f6b2a5 100644 --- a/src/heurams/services/config.py +++ b/src/heurams/services/config.py @@ -9,17 +9,38 @@ from heurams.services.logger import get_logger from heurams.services.exceptions import WTFException # 我们的流程是: 找到文件名: 返回文件名里头的数据; 找不到: 继续查索引; 所以 self.data 除了存本级各种索引球用没得 -# 递归就是这么吊 +logger = get_logger(__name__) + 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): # 需要自己把自己提起来 + # 避免重复初始化 + if hasattr(self, '_initialized'): + return + self._initialized = True if dict: raise WTFException("不要放默认值...") super().__init__(dict) - self.logger = get_logger(__name__) + logger = get_logger(__name__) self.path = config_path self.is_dir = self.path.is_dir() if self.is_dir: - self.update_index() # 狗儿要唱狗儿歌 + self.update_index() else: with open(self.path, 'r+') as f: #TODO: 给这个做缓存 try: @@ -35,11 +56,6 @@ class ConfigDict(UserDict): # 舒服了 return ConfigDict(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): return super().__contains__(key) @@ -67,22 +83,16 @@ class ConfigDict(UserDict): # 舒服了 if i.suffix == '.toml': self.data[i.stem] = i else: - self.logger.log(f"配置目录中有无效的文件 {i.stem}") # what's up bro + logger.debug(f"配置目录中有无效的文件 {i.stem}") # what's up bro def persist(self): 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: - 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 \ No newline at end of file + toml.dump(self.data, f) \ No newline at end of file diff --git a/src/heurams/services/epath.py b/src/heurams/services/epath.py index 185b640..91d0504 100644 --- a/src/heurams/services/epath.py +++ b/src/heurams/services/epath.py @@ -2,35 +2,59 @@ from heurams.services.config import ConfigDict from heurams.services.logger import get_logger 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: return dct path = path.rstrip('.') path = path.lstrip('.') target = dct - - for i in path.split('.'): + keys = path.split('.') + logger.debug(f"处理 EPATH {path}, {new_value}") + for idx, i in enumerate(keys): + is_last = (idx == len(keys) - 1) + # 处理字典键 logger.debug(f'处理 {i}, {(isinstance(target, dict) or isinstance(target, ConfigDict))} {i in target}') - if (isinstance(target, dict) or isinstance(target, ConfigDict)) and i in target: - target = target[i] - # 处理列表索引 - elif i.startswith('[') and i.endswith(']') and isinstance(target, (list, tuple)): - idx = int(i[1:-1]) - if 0 <= idx < len(target): - target = target[idx] - elif parents: - while len(target) <= idx: - target.append(None) - target[idx] = {} - target = target[idx] + + 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 - elif parents: - target[i] = {} - target = target[i] else: - return default + if (isinstance(target, dict) or isinstance(target, ConfigDict)) and i in target: + target = target[i] + elif i.startswith('[') and i.endswith(']') and isinstance(target, (list, tuple)): + idx_num = int(i[1:-1]) + if 0 <= idx_num < len(target): + target = target[idx_num] + elif parents: + while len(target) <= idx_num: + target.append(None) + target[idx_num] = {} + target = target[idx_num] + else: + return default + elif parents: + target[i] = {} + target = target[i] + else: + return default return target \ No newline at end of file diff --git a/src/heurams/tools/zmqclient.py b/src/heurams/tools/zmqclient.py new file mode 100644 index 0000000..1a7a481 --- /dev/null +++ b/src/heurams/tools/zmqclient.py @@ -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() \ No newline at end of file diff --git a/uv.lock b/uv.lock index bca2161..961fbd6 100644 --- a/uv.lock +++ b/uv.lock @@ -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" }, ] +[[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]] name = "colorama" version = "0.4.6" @@ -211,6 +244,7 @@ dependencies = [ { name = "textual" }, { name = "toml" }, { name = "transitions" }, + { name = "zmq" }, ] [package.metadata] @@ -225,6 +259,7 @@ requires-dist = [ { name = "textual", specifier = ">=8.2.3" }, { name = "toml", specifier = ">=0.10.2" }, { name = "transitions", specifier = ">=0.9.3" }, + { name = "zmq", specifier = ">=0.0.0" }, ] [[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" }, ] +[[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]] name = "pydantic" 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" } +[[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]] name = "rich" 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/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" }