Refactor plugin host

This commit is contained in:
qianlifeng 2024-12-22 22:24:04 +08:00
parent 6feb7cfb15
commit ccfdbc5e4a
No known key found for this signature in database
52 changed files with 1411 additions and 1230 deletions

18
.cursorrules Normal file
View File

@ -0,0 +1,18 @@
项目基本结构如下:
wox.core Go实现的Wox后端,通过websocket与http与wox.ui.flutter通信
wox.core/setting wox.core的设置相关定义
wox.core/plugin wox.core的API定义与实现
wox.plugin.python python插件需要引用的类库
wox.plugin.host.python python插件的host,通过websocket与wox.core通信,负责加载python插件
wox.plugin.nodejs nodejs插件需要引用的类库
wox.plugin.host.nodejs nodejs插件的host,通过websocket与wox.core通信,负责加载nodejs插件
wox.ui.flutter flutter实现的Wox前端,通过websocket与wox.core通信
所有的插件类库(wox.plugin.python,wox.plugin.nodejs)的定义都必须对齐wox.core的定义, 例如:
wox.core/plugin/api.go 定义了API接口, 所有的插件类库都必须定义这些接口, 且名称,类型,参数,返回值必须完全一致
本项目中所有的python项目都使用如下类库:
* orjson进行数据序列化, 请参考wox.plugin.python/src/wox_plugin/models/query.py
* dataclass进行数据模型定义, 请参考wox.plugin.python/src/wox_plugin/models/query.py

2
.gitignore vendored
View File

@ -16,3 +16,5 @@ wox.core/log/
wox.plugin.python/dist/
wox.plugin.python/wox_plugin.egg-info/
.venv/
.ruff_cache/
.mypy_cache/

4
.vscode/launch.json vendored
View File

@ -9,14 +9,14 @@
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/Wox",
"program": "${workspaceFolder}/wox.core",
"env": {
"CGO_ENABLED": "1"
}
},
{
"name": "Run Flutter",
"cwd": "Wox.UI.Flutter/wox",
"cwd": "${workspaceFolder}/wox.ui.flutter/wox",
"request": "launch",
"type": "dart",
// "flutterMode": "release"

32
.vscode/settings.json vendored
View File

@ -1,26 +1,4 @@
{
"dart.lineLength": 200,
"python.languageServer": "Pylance",
"python.analysis.typeCheckingMode": "strict",
"python.analysis.diagnosticMode": "workspace",
"python.analysis.inlayHints.functionReturnTypes": true,
"python.analysis.inlayHints.variableTypes": true,
"python.analysis.autoFormatStrings": true,
"python.analysis.autoImportCompletions": true,
"python.analysis.diagnosticSeverityOverrides": {
"reportUnknownMemberType": "error",
"reportUnknownVariableType": "error",
"reportUnknownArgumentType": "error",
"reportUnknownParameterType": "error",
"reportMissingTypeStubs": "error",
"reportUnknownLambdaType": "error",
"reportOptionalCall": "error",
"reportOptionalMemberAccess": "error",
"reportOptionalSubscript": "error",
"reportOptionalIterable": "error",
"reportOptionalContextManager": "error",
"reportOptionalOperand": "error"
},
"files.exclude": {
"wox.plugin.host.nodejs": true,
"wox.plugin.host.python": true,
@ -28,5 +6,13 @@
"wox.plugin.python": true,
"wox.ui.flutter": true,
"wox.core": true
}
},
"[python]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
},
"editor.defaultFormatter": "charliermarsh.ruff"
},
}

View File

@ -30,7 +30,8 @@
}
],
"settings": {
"python.languageServer": "Pylance"
"python.languageServer": "Default",
"makefile.configureOnOpen": false
},
"extensions": {
"recommendations": [
@ -38,8 +39,8 @@
"golang.go",
// for python
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.black-formatter",
"charliermarsh.ruff",
"ms-python.mypy-type-checker",
// for flutter
"dart-code.flutter",
// for js

View File

@ -101,7 +101,6 @@ func (m *Manager) TranslatePlugin(ctx context.Context, key string, pluginDirecto
jsonPath := path.Join(pluginDirectory, "lang", fmt.Sprintf("%s.json", m.currentLangCode))
if _, err := os.Stat(jsonPath); os.IsNotExist(err) {
util.GetLogger().Error(ctx, fmt.Sprintf("lang file not found: %s", jsonPath))
return key
}

View File

@ -9,7 +9,6 @@ import (
"wox/ai"
"wox/i18n"
"wox/setting"
"wox/setting/definition"
"wox/share"
"wox/util"
@ -36,7 +35,7 @@ type API interface {
GetSetting(ctx context.Context, key string) string
SaveSetting(ctx context.Context, key string, value string, isPlatformSpecific bool)
OnSettingChanged(ctx context.Context, callback func(key string, value string))
OnGetDynamicSetting(ctx context.Context, callback func(key string) definition.PluginSettingDefinitionItem)
OnGetDynamicSetting(ctx context.Context, callback func(key string) string)
OnDeepLink(ctx context.Context, callback func(arguments map[string]string))
OnUnload(ctx context.Context, callback func())
RegisterQueryCommands(ctx context.Context, commands []MetadataCommand)
@ -148,7 +147,7 @@ func (a *APIImpl) OnSettingChanged(ctx context.Context, callback func(key string
a.pluginInstance.SettingChangeCallbacks = append(a.pluginInstance.SettingChangeCallbacks, callback)
}
func (a *APIImpl) OnGetDynamicSetting(ctx context.Context, callback func(key string) definition.PluginSettingDefinitionItem) {
func (a *APIImpl) OnGetDynamicSetting(ctx context.Context, callback func(key string) string) {
a.pluginInstance.DynamicSettingCallbacks = append(a.pluginInstance.DynamicSettingCallbacks, callback)
}

View File

@ -3,12 +3,13 @@ package host
import (
"context"
"fmt"
"github.com/Masterminds/semver/v3"
"github.com/mitchellh/go-homedir"
"path"
"strings"
"wox/plugin"
"wox/util"
"github.com/Masterminds/semver/v3"
"github.com/mitchellh/go-homedir"
)
func init() {
@ -29,7 +30,7 @@ func (n *NodejsHost) GetRuntime(ctx context.Context) plugin.Runtime {
}
func (n *NodejsHost) Start(ctx context.Context) error {
return n.websocketHost.StartHost(ctx, n.findNodejsPath(ctx), path.Join(util.GetLocation().GetHostDirectory(), "node-host.js"))
return n.websocketHost.StartHost(ctx, n.findNodejsPath(ctx), path.Join(util.GetLocation().GetHostDirectory(), "node-host.js"), nil)
}
func (n *NodejsHost) findNodejsPath(ctx context.Context) string {

View File

@ -30,7 +30,7 @@ func (n *PythonHost) GetRuntime(ctx context.Context) plugin.Runtime {
}
func (n *PythonHost) Start(ctx context.Context) error {
return n.websocketHost.StartHost(ctx, n.findPythonPath(ctx), path.Join(util.GetLocation().GetHostDirectory(), "python-host.pyz"))
return n.websocketHost.StartHost(ctx, n.findPythonPath(ctx), path.Join(util.GetLocation().GetHostDirectory(), "python-host.pyz"), []string{"SHIV_ROOT=" + util.GetLocation().GetCacheDirectory()})
}
func (n *PythonHost) findPythonPath(ctx context.Context) string {

View File

@ -28,7 +28,7 @@ func (w *WebsocketHost) getHostName(ctx context.Context) string {
return fmt.Sprintf("%s Host Impl", w.host.GetRuntime(ctx))
}
func (w *WebsocketHost) StartHost(ctx context.Context, executablePath string, entry string, executableArgs ...string) error {
func (w *WebsocketHost) StartHost(ctx context.Context, executablePath string, entry string, envs []string, executableArgs ...string) error {
port, portErr := util.GetAvailableTcpPort(ctx)
if portErr != nil {
return fmt.Errorf("failed to get available port: %w", portErr)
@ -44,7 +44,7 @@ func (w *WebsocketHost) StartHost(ctx context.Context, executablePath string, en
args = append(args, executableArgs...)
args = append(args, entry, fmt.Sprintf("%d", port), util.GetLocation().GetLogHostsDirectory(), fmt.Sprintf("%d", os.Getpid()))
cmd, err := util.ShellRun(executablePath, args...)
cmd, err := util.ShellRunWithEnv(executablePath, envs, args...)
if err != nil {
return fmt.Errorf("failed to start host: %w", err)
}
@ -98,7 +98,7 @@ func (w *WebsocketHost) UnloadPlugin(ctx context.Context, metadata plugin.Metada
}
func (w *WebsocketHost) invokeMethod(ctx context.Context, metadata plugin.Metadata, method string, params map[string]string) (result any, err error) {
if !w.ws.IsConnected() {
if w.ws == nil || !w.ws.IsConnected() {
return "", fmt.Errorf("host is not connected")
}
@ -342,34 +342,45 @@ func (w *WebsocketHost) handleRequestFromPlugin(ctx context.Context, request Jso
}
metadata := pluginInstance.Metadata
pluginInstance.API.OnGetDynamicSetting(ctx, func(key string) definition.PluginSettingDefinitionItem {
pluginInstance.API.OnGetDynamicSetting(ctx, func(key string) string {
result, err := w.invokeMethod(ctx, metadata, "onGetDynamicSetting", map[string]string{
"CallbackId": callbackId,
"Key": key,
})
if err != nil {
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to get dynamic setting: %s", request.PluginName, err))
return definition.PluginSettingDefinitionItem{
settingJson, marshalErr := json.Marshal(definition.PluginSettingDefinitionItem{
Type: definition.PluginSettingDefinitionTypeLabel,
Value: &definition.PluginSettingValueLabel{
Content: fmt.Sprintf("failed to get dynamic setting: %s", err),
},
})
if marshalErr != nil {
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to marshal dynamic setting: %s", request.PluginName, marshalErr))
return ""
}
return string(settingJson)
}
// validate the result is a valid definition.PluginSettingDefinitionItem json string
var setting definition.PluginSettingDefinitionItem
unmarshalErr := json.Unmarshal([]byte(result.(string)), &setting)
if unmarshalErr != nil {
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to unmarshal dynamic setting: %s", request.PluginName, unmarshalErr))
return definition.PluginSettingDefinitionItem{
settingJson, marshalErr := json.Marshal(definition.PluginSettingDefinitionItem{
Type: definition.PluginSettingDefinitionTypeLabel,
Value: &definition.PluginSettingValueLabel{
Content: fmt.Sprintf("failed to unmarshal dynamic setting: %s", unmarshalErr),
},
})
if marshalErr != nil {
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to marshal dynamic setting: %s", request.PluginName, marshalErr))
return ""
}
return string(settingJson)
}
return setting
return result.(string)
})
w.sendResponseToHost(ctx, request, "")
case "OnDeepLink":

View File

@ -3,7 +3,6 @@ package plugin
import (
"context"
"wox/setting"
"wox/setting/definition"
)
type Instance struct {
@ -17,7 +16,7 @@ type Instance struct {
Host Host // plugin host to run this plugin
Setting *setting.PluginSetting // setting for this plugin
DynamicSettingCallbacks []func(key string) definition.PluginSettingDefinitionItem // dynamic setting callbacks
DynamicSettingCallbacks []func(key string) string // dynamic setting callbacks
SettingChangeCallbacks []func(key string, value string)
DeepLinkCallbacks []func(arguments map[string]string)
UnloadCallbacks []func()

View File

@ -589,7 +589,7 @@ func (m *Manager) PolishResult(ctx context.Context, pluginInstance *Instance, qu
// store preview for ui invoke later
// because preview may contain some heavy data (E.g. image or large text), we will store preview in cache and only send preview to ui when user select the result
if result.Preview.PreviewType != "" && result.Preview.PreviewType != WoxPreviewTypeRemote {
if result.Preview.PreviewType != "" && result.Preview.PreviewData != "" && result.Preview.PreviewType != WoxPreviewTypeRemote {
resultCache.Preview = result.Preview
result.Preview = WoxPreview{
PreviewType: WoxPreviewTypeRemote,

View File

@ -5,7 +5,6 @@ import (
"testing"
"wox/ai"
"wox/plugin"
"wox/setting/definition"
"wox/share"
"wox/util"
@ -15,7 +14,7 @@ import (
type emptyAPIImpl struct {
}
func (e emptyAPIImpl) OnGetDynamicSetting(ctx context.Context, callback func(key string) definition.PluginSettingDefinitionItem) {
func (e emptyAPIImpl) OnGetDynamicSetting(ctx context.Context, callback func(key string) string) {
}
func (e emptyAPIImpl) ChangeQuery(ctx context.Context, query share.PlainQuery) {

View File

@ -4,8 +4,9 @@ import (
"context"
"encoding/json"
"errors"
"github.com/tidwall/gjson"
"wox/util"
"github.com/tidwall/gjson"
)
type PluginSettingDefinitionType string

View File

@ -189,7 +189,13 @@ func convertPluginDto(ctx context.Context, pluginDto dto.PluginDto, pluginInstan
if settingDefinition.Type == definition.PluginSettingDefinitionTypeDynamic {
replaced := false
for _, callback := range pluginInstance.DynamicSettingCallbacks {
newSettingDefinition := callback(settingDefinition.Value.GetKey())
newSettingDefinitionJson := callback(settingDefinition.Value.GetKey())
var newSettingDefinition definition.PluginSettingDefinitionItem
unmarshalErr := json.Unmarshal([]byte(newSettingDefinitionJson), &newSettingDefinition)
if unmarshalErr != nil {
logger.Error(ctx, fmt.Sprintf("failed to unmarshal dynamic setting: %s", unmarshalErr.Error()))
continue
}
if newSettingDefinition.Value != nil && newSettingDefinition.Type != definition.PluginSettingDefinitionTypeDynamic {
logger.Debug(ctx, fmt.Sprintf("dynamic setting replaced: %s(%s) -> %s(%s)", settingDefinition.Value.GetKey(), settingDefinition.Type, newSettingDefinition.Value.GetKey(), newSettingDefinition.Type))
pluginDto.SettingDefinitions[i] = newSettingDefinition

View File

@ -2,11 +2,12 @@ package util
import (
"fmt"
"github.com/mitchellh/go-homedir"
"os"
"path"
"strings"
"sync"
"github.com/mitchellh/go-homedir"
)
var locationInstance *Location

View File

@ -2,6 +2,7 @@ package util
import (
"fmt"
"os"
"os/exec"
)
@ -21,6 +22,23 @@ func ShellRun(name string, arg ...string) (*exec.Cmd, error) {
return cmd, nil
}
func ShellRunWithEnv(name string, envs []string, arg ...string) (*exec.Cmd, error) {
if len(envs) == 0 {
return ShellRun(name, arg...)
}
cmd := exec.Command(name, arg...)
cmd.Stdout = GetLogger().GetWriter()
cmd.Stderr = GetLogger().GetWriter()
cmd.Env = append(os.Environ(), envs...)
cmdErr := cmd.Start()
if cmdErr != nil {
return nil, cmdErr
}
return cmd, nil
}
func ShellRunOutput(name string, arg ...string) ([]byte, error) {
cmd := exec.Command(name, arg...)
output, err := cmd.CombinedOutput()

View File

@ -3,9 +3,10 @@ package util
import (
"context"
"fmt"
"github.com/gorilla/websocket"
"sync"
"time"
"github.com/gorilla/websocket"
)
type WebsocketClient struct {

View File

@ -7,22 +7,22 @@ help:
@echo " make clean - Remove build artifacts and cache files"
@echo " make build - Build Python host"
@echo " make install - Install development dependencies"
@echo " make lint - Run linting"
@echo " make format - Run formatting"
clean:
rm -rf python-host.pyz .venv/ __pycache__/ *.pyc python-host/
find . -type d -name "__pycache__" -exec rm -rf {} +
find . -type f -name "*.pyc" -delete
rm -rf python-host.pyz python-host/
install:
uv cache clean
uv sync --all-extras
build: install clean
uv pip freeze > requirements.txt
uv pip install -r requirements.txt --target python-host
rm requirements.txt
cp -r src/wox_plugin_host/*.py python-host/
uv run python -m zipapp -p "interpreter" python-host
rm -rf python-host
mkdir -p $(HOSTS_DIR)
mv python-host.pyz $(HOSTS_DIR)/python-host.pyz
lint:
uv run ruff check src
uv run mypy src
format:
uv run ruff format src
build: clean install lint format
uv run shiv --compressed -c run -o $(HOSTS_DIR)/python-host.pyz .

View File

@ -4,17 +4,19 @@ version = "0.0.1"
description = "Python host for Wox plugins"
readme = "README.md"
requires-python = ">=3.10"
license = "MIT"
license = "GPL-3.0"
authors = [{ name = "Wox Team", email = "qianlifeng@gmail.com" }]
dependencies = ["loguru", "websockets", "wox-plugin==0.0.30"]
dependencies = ["loguru", "websockets", "wox-plugin==0.0.45"]
[project.scripts]
run = "wox_plugin_host.__main__:run"
[project.optional-dependencies]
dev = ["black"]
dev = ["shiv", "ruff", "mypy"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.black]
line-length = 100
target-version = ["py310"]
[tool.ruff]
line-length = 140

View File

@ -0,0 +1,3 @@
from .__main__ import run
__all__ = ["run"]

View File

@ -3,11 +3,11 @@ import sys
import uuid
import os
import logger
from host import start_websocket
from . import logger
from .host import start_websocket
if len(sys.argv) != 4:
print('Usage: python python-host.pyz <port> <logDirectory> <woxPid>')
print("Usage: python python-host.pyz <port> <logDirectory> <woxPid>")
sys.exit(1)
port = int(sys.argv[1])
@ -18,6 +18,7 @@ trace_id = str(uuid.uuid4())
host_id = f"python-{uuid.uuid4()}"
logger.update_log_directory(log_directory)
def check_wox_process():
"""Check if Wox process is still alive"""
try:
@ -26,6 +27,7 @@ def check_wox_process():
except OSError:
return False
async def monitor_wox_process():
"""Monitor Wox process and exit if it's not alive"""
await logger.info(trace_id, "start monitor wox process")
@ -35,9 +37,11 @@ async def monitor_wox_process():
sys.exit(1)
await asyncio.sleep(1)
async def main():
"""Main function"""
# Log startup information
await logger.info(trace_id, "----------------------------------------")
await logger.info(trace_id, f"start python host: {host_id}")
await logger.info(trace_id, f"port: {port}")
@ -48,4 +52,6 @@ async def main():
websocket_task = asyncio.create_task(start_websocket(port))
await asyncio.gather(monitor_task, websocket_task)
def run():
asyncio.run(main())

View File

@ -1,19 +1,49 @@
#!/usr/bin/env python
import asyncio
import json
import uuid
from typing import Dict, Any
import traceback
from wox_plugin import Context
import websockets
import logger
from wox_plugin import Context, new_context_with_value
from constants import PLUGIN_JSONRPC_TYPE_REQUEST, PLUGIN_JSONRPC_TYPE_RESPONSE
from plugin_manager import waiting_for_response
from jsonrpc import handle_request_from_wox
async def handle_message(ws: websockets.WebSocketServerProtocol, message: str):
from . import logger
from .constants import PLUGIN_JSONRPC_TYPE_REQUEST, PLUGIN_JSONRPC_TYPE_RESPONSE
from .plugin_manager import waiting_for_response
from .jsonrpc import handle_request_from_wox
def _clean_for_serialization(obj):
"""Remove non-serializable properties from any object recursively"""
if obj is None:
return obj
if isinstance(obj, (str, int, float, bool)):
return obj
if isinstance(obj, (list, tuple)):
return [_clean_for_serialization(item) for item in obj]
if isinstance(obj, dict):
return {k: _clean_for_serialization(v) for k, v in obj.items()}
# Handle custom objects
if hasattr(obj, "__dict__"):
# Create a copy of the object's dict
obj_dict = obj.__dict__.copy()
# Remove callable (methods/functions) and handle nested objects
cleaned_dict = {}
for k, v in obj_dict.items():
if callable(v):
continue
cleaned_dict[k] = _clean_for_serialization(v)
return cleaned_dict
# If we can't handle it, just return None
return None
async def handle_message(ws: websockets.asyncio.server.ServerConnection, message: str):
"""Handle incoming WebSocket message"""
trace_id = str(uuid.uuid4())
@ -22,7 +52,7 @@ async def handle_message(ws: websockets.WebSocketServerProtocol, message: str):
if msg_data.get("TraceId"):
trace_id = msg_data.get("TraceId")
ctx = new_context_with_value("traceId", trace_id)
ctx = Context.new_with_value("TraceId", trace_id)
if PLUGIN_JSONRPC_TYPE_RESPONSE in message:
# Handle response from Wox
@ -37,14 +67,17 @@ async def handle_message(ws: websockets.WebSocketServerProtocol, message: str):
# Handle request from Wox
try:
result = await handle_request_from_wox(ctx, msg_data, ws)
# Clean result for serialization
cleaned_result = _clean_for_serialization(result)
response = {
"TraceId": trace_id,
"Id": msg_data["Id"],
"Method": msg_data["Method"],
"Type": PLUGIN_JSONRPC_TYPE_RESPONSE,
"Result": result
"Result": cleaned_result,
}
await ws.send(json.dumps(response, default=lambda o: '<not serializable>'))
await ws.send(json.dumps(response))
except Exception as e:
error_stack = traceback.format_exc()
error_response = {
@ -52,17 +85,21 @@ async def handle_message(ws: websockets.WebSocketServerProtocol, message: str):
"Id": msg_data["Id"],
"Method": msg_data["Method"],
"Type": PLUGIN_JSONRPC_TYPE_RESPONSE,
"Error": str(e)
"Error": str(e),
}
await logger.error(trace_id, f"handle request failed: {str(e)}\nStack trace:\n{error_stack}")
await ws.send(json.dumps(error_response, default=lambda o: '<not serializable>'))
await ws.send(json.dumps(error_response))
else:
await logger.error(trace_id, f"unknown message type: {message}")
except Exception as e:
error_stack = traceback.format_exc()
await logger.error(trace_id, f"receive and handle msg error: {message}, err: {str(e)}\nStack trace:\n{error_stack}")
await logger.error(
trace_id,
f"receive and handle msg error: {message}, err: {str(e)}\nStack trace:\n{error_stack}",
)
async def handler(websocket: websockets.WebSocketServerProtocol):
async def handler(websocket: websockets.asyncio.server.ServerConnection):
"""WebSocket connection handler"""
logger.update_websocket(websocket)
@ -70,7 +107,7 @@ async def handler(websocket: websockets.WebSocketServerProtocol):
while True:
try:
message = await websocket.recv()
asyncio.create_task(handle_message(websocket, message))
asyncio.create_task(handle_message(websocket, str(message)))
except websockets.exceptions.ConnectionClosed:
await logger.info(str(uuid.uuid4()), "connection closed")
break
@ -80,6 +117,7 @@ async def handler(websocket: websockets.WebSocketServerProtocol):
finally:
logger.update_websocket(None)
async def start_websocket(websocket_port: int):
"""Start WebSocket server"""
await logger.info(str(uuid.uuid4()), "start websocket server")

View File

@ -5,37 +5,27 @@ import sys
from typing import Any, Dict
import uuid
import websockets
import logger
from wox_plugin.types import (
from . import logger
from wox_plugin import (
Context,
Query,
QueryType,
Selection,
QueryEnv,
RefreshableResult,
WoxImage,
WoxPreview,
ResultTail,
ResultAction,
PluginInitParams,
ActionContext,
Result,
)
from plugin_manager import plugin_instances, PluginInstance
from plugin_api import PluginAPI
from .plugin_manager import plugin_instances, PluginInstance
from .plugin_api import PluginAPI
import traceback
import asyncio
async def handle_request_from_wox(
ctx: Context, request: Dict[str, Any], ws: websockets.WebSocketServerProtocol
) -> Any:
async def handle_request_from_wox(ctx: Context, request: Dict[str, Any], ws: websockets.asyncio.server.ServerConnection) -> Any:
"""Handle incoming request from Wox"""
method = request.get("Method")
plugin_name = request.get("PluginName")
await logger.info(
ctx["Values"]["traceId"], f"invoke <{plugin_name}> method: {method}"
)
await logger.info(ctx.get_trace_id(), f"invoke <{plugin_name}> method: {method}")
if method == "loadPlugin":
return await load_plugin(ctx, request)
@ -50,19 +40,20 @@ async def handle_request_from_wox(
elif method == "unloadPlugin":
return await unload_plugin(ctx, request)
else:
await logger.info(ctx["Values"]["traceId"], f"unknown method handler: {method}")
await logger.info(ctx.get_trace_id(), f"unknown method handler: {method}")
raise Exception(f"unknown method handler: {method}")
async def load_plugin(ctx: Context, request: Dict[str, Any]) -> None:
"""Load a plugin"""
plugin_directory: str = request.get("Params", {}).get("PluginDirectory", "")
entry: str = request.get("Params", {}).get("Entry", "")
params: Dict[str, str] = request.get("Params", {})
plugin_directory: str = params.get("PluginDirectory", "")
entry: str = params.get("Entry", "")
plugin_id: str = request.get("PluginId", "")
plugin_name: str = request.get("PluginName", "")
await logger.info(
ctx["Values"]["traceId"],
ctx.get_trace_id(),
f"<{plugin_name}> load plugin, directory: {plugin_directory}, entry: {entry}",
)
@ -94,27 +85,23 @@ async def load_plugin(ctx: Context, request: Dict[str, Any]) -> None:
plugin_instances[plugin_id] = PluginInstance(
plugin=module.plugin,
api=None, # type: ignore , Will be set in init_plugin
api=None,
module_path=full_entry_path,
actions={},
refreshes={},
)
await logger.info(
ctx["Values"]["traceId"], f"<{plugin_name}> load plugin successfully"
)
await logger.info(ctx.get_trace_id(), f"<{plugin_name}> load plugin successfully")
except Exception as e:
error_stack = traceback.format_exc()
await logger.error(
ctx["Values"]["traceId"],
ctx.get_trace_id(),
f"<{plugin_name}> load plugin failed: {str(e)}\nStack trace:\n{error_stack}",
)
raise e
async def init_plugin(
ctx: Context, request: Dict[str, Any], ws: websockets.WebSocketServerProtocol
) -> None:
async def init_plugin(ctx: Context, request: Dict[str, Any], ws: websockets.asyncio.server.ServerConnection) -> None:
"""Initialize a plugin"""
plugin_id = request.get("PluginId", "")
plugin_name = request.get("PluginName", "")
@ -126,26 +113,24 @@ async def init_plugin(
# Create plugin API instance
api = PluginAPI(ws, plugin_id, plugin_name)
plugin_instance.api = api
params: Dict[str, str] = request.get("Params", {})
plugin_directory: str = params.get("PluginDirectory", "")
# Call plugin's init method
init_params = PluginInitParams(
API=api, PluginDirectory=request.get("Params", {}).get("PluginDirectory")
)
init_params = PluginInitParams(api=api, plugin_directory=plugin_directory)
await plugin_instance.plugin.init(ctx, init_params)
await logger.info(
ctx["Values"]["traceId"], f"<{plugin_name}> init plugin successfully"
)
await logger.info(ctx.get_trace_id(), f"<{plugin_name}> init plugin successfully")
except Exception as e:
error_stack = traceback.format_exc()
await logger.error(
ctx["Values"]["traceId"],
ctx.get_trace_id(),
f"<{plugin_name}> init plugin failed: {str(e)}\nStack trace:\n{error_stack}",
)
raise e
async def query(ctx: Context, request: Dict[str, Any]) -> list[Any]:
async def query(ctx: Context, request: Dict[str, Any]) -> list[Result]:
"""Handle query request"""
plugin_id = request.get("PluginId", "")
plugin_name = request.get("PluginName", "")
@ -158,50 +143,36 @@ async def query(ctx: Context, request: Dict[str, Any]) -> list[Any]:
plugin_instance.actions.clear()
plugin_instance.refreshes.clear()
params = request.get("Params", {})
results = await plugin_instance.plugin.query(
ctx,
Query(
Type=QueryType(params.get("Type")),
RawQuery=params.get("RawQuery"),
TriggerKeyword=params.get("TriggerKeyword"),
Command=params.get("Command"),
Search=params.get("Search"),
Selection=Selection(**json.loads(params.get("Selection"))),
Env=QueryEnv(**json.loads(params.get("Env"))),
),
)
params: Dict[str, str] = request.get("Params", {})
results = await plugin_instance.plugin.query(ctx, Query.from_json(json.dumps(params)))
# Ensure each result has an ID and cache actions and refreshes
if results:
for result in results:
if not result.Id:
result.Id = str(uuid.uuid4())
if result.Actions:
for action in result.Actions:
if not action.Id:
action.Id = str(uuid.uuid4())
if not result.id:
result.id = str(uuid.uuid4())
if result.actions:
for action in result.actions:
if action.action:
if not action.id:
action.id = str(uuid.uuid4())
# Cache action
plugin_instance.actions[action.Id] = action.Action
plugin_instance.actions[action.id] = action.action
# Cache refresh callback if exists
if (
result.RefreshInterval
and result.RefreshInterval > 0
and result.OnRefresh
):
plugin_instance.refreshes[result.Id] = result.OnRefresh
if result.refresh_interval and result.refresh_interval > 0 and result.on_refresh:
plugin_instance.refreshes[result.id] = result.on_refresh
return [result.to_dict() for result in results]
return results
except Exception as e:
error_stack = traceback.format_exc()
await logger.error(
ctx["Values"]["traceId"],
ctx.get_trace_id(),
f"<{plugin_name}> query failed: {str(e)}\nStack trace:\n{error_stack}",
)
raise e
async def action(ctx: Context, request: Dict[str, Any]) -> Any:
async def action(ctx: Context, request: Dict[str, Any]) -> None:
"""Handle action request"""
plugin_id = request.get("PluginId", "")
plugin_name = request.get("PluginName", "")
@ -210,29 +181,28 @@ async def action(ctx: Context, request: Dict[str, Any]) -> Any:
raise Exception(f"plugin not found: {plugin_name}, forget to load plugin?")
try:
params = request.get("Params", {})
action_id = params.get("ActionId")
context_data = params.get("ContextData")
params: Dict[str, str] = request.get("Params", {})
action_id = params.get("ActionId", "")
context_data = params.get("ContextData", "")
# Get action from cache
action_func = plugin_instance.actions.get(action_id)
if action_func:
# Handle both coroutine and regular functions
result = action_func(ActionContext(ContextData=context_data))
result = action_func(ActionContext(context_data=context_data))
if asyncio.iscoroutine(result):
asyncio.create_task(result)
return None
except Exception as e:
error_stack = traceback.format_exc()
await logger.error(
ctx["Values"]["traceId"],
ctx.get_trace_id(),
f"<{plugin_name}> action failed: {str(e)}\nStack trace:\n{error_stack}",
)
raise e
async def refresh(ctx: Context, request: Dict[str, Any]) -> Any:
async def refresh(ctx: Context, request: Dict[str, Any]) -> RefreshableResult:
"""Handle refresh request"""
plugin_id = request.get("PluginId", "")
plugin_name = request.get("PluginName", "")
@ -241,50 +211,37 @@ async def refresh(ctx: Context, request: Dict[str, Any]) -> Any:
raise Exception(f"plugin not found: {plugin_name}, forget to load plugin?")
try:
params = request.get("Params", {})
result_id = params.get("ResultId")
refreshable_result_dict = json.loads(params.get("RefreshableResult"))
params: Dict[str, str] = request.get("Params", {})
result_id = params.get("ResultId", "")
refreshable_result_dict = json.loads(params.get("RefreshableResult", ""))
# Convert dict to RefreshableResult object
refreshable_result = RefreshableResult(
Title=refreshable_result_dict.get("Title"),
SubTitle=refreshable_result_dict.get("SubTitle", ""),
Icon=WoxImage.from_dict(refreshable_result_dict.get("Icon", {})),
Preview=WoxPreview.from_dict(refreshable_result_dict.get("Preview", {})),
Tails=[
ResultTail.from_dict(tail)
for tail in refreshable_result_dict.get("Tails", [])
],
ContextData=refreshable_result_dict.get("ContextData", ""),
RefreshInterval=refreshable_result_dict.get("RefreshInterval", 0),
Actions=[
ResultAction.from_dict(action)
for action in refreshable_result_dict.get("Actions", [])
],
)
refreshable_result = RefreshableResult.from_json(json.dumps(refreshable_result_dict))
# replace action with cached action
for action in refreshable_result.Actions:
action.Action = plugin_instance.actions.get(action.Id)
for action in refreshable_result.actions:
action.action = plugin_instance.actions.get(action.id)
refresh_func = plugin_instance.refreshes.get(result_id)
if refresh_func:
refreshed_result = await refresh_func(refreshable_result)
# Cache any new actions from the refreshed result
if refreshed_result.Actions:
for action in refreshed_result.Actions:
if not action.Id:
action.Id = str(uuid.uuid4())
plugin_instance.actions[action.Id] = action.Action
if refreshed_result.actions:
for action in refreshed_result.actions:
if not action.id:
action.id = str(uuid.uuid4())
return refreshed_result.to_dict()
if action.action:
plugin_instance.actions[action.id] = action.action
return None
return refreshed_result
raise Exception(f"refresh function not found for result id: {result_id}")
except Exception as e:
error_stack = traceback.format_exc()
await logger.error(
ctx["Values"]["traceId"],
ctx.get_trace_id(),
f"<{plugin_name}> refresh failed: {str(e)}\nStack trace:\n{error_stack}",
)
raise e
@ -292,8 +249,8 @@ async def refresh(ctx: Context, request: Dict[str, Any]) -> Any:
async def unload_plugin(ctx: Context, request: Dict[str, Any]) -> None:
"""Unload a plugin"""
plugin_id = request.get("PluginId")
plugin_name = request.get("PluginName")
plugin_id = request.get("PluginId", "")
plugin_name = request.get("PluginName", "")
plugin_instance = plugin_instances.get(plugin_id)
if not plugin_instance:
raise Exception(f"plugin not found: {plugin_name}, forget to load plugin?")
@ -307,13 +264,11 @@ async def unload_plugin(ctx: Context, request: Dict[str, Any]) -> None:
if plugin_dir in sys.path:
sys.path.remove(plugin_dir)
await logger.info(
ctx["Values"]["traceId"], f"<{plugin_name}> unload plugin successfully"
)
await logger.info(ctx.get_trace_id(), f"<{plugin_name}> unload plugin successfully")
except Exception as e:
error_stack = traceback.format_exc()
await logger.error(
ctx["Values"]["traceId"],
ctx.get_trace_id(),
f"<{plugin_name}> unload plugin failed: {str(e)}\nStack trace:\n{error_stack}",
)
raise e

View File

@ -1,41 +1,51 @@
import json
from typing import Optional
import websockets
from loguru import logger
from websockets.asyncio.server import ServerConnection
PLUGIN_JSONRPC_TYPE_SYSTEM_LOG = "WOX_JSONRPC_SYSTEM_LOG"
websocket: Optional[websockets.WebSocketServerProtocol] = None
websocket: Optional[ServerConnection] = None
def update_log_directory(log_directory: str):
"""Update the log directory for the logger"""
logger.remove()
logger.add(f"{log_directory}/python.log", format="{time} {message}")
def update_websocket(ws: Optional[websockets.WebSocketServerProtocol]):
def update_websocket(ws: Optional[ServerConnection]):
"""Update the websocket connection for logging"""
global websocket
websocket = ws
async def log(trace_id: str, level: str, msg: str):
"""Log a message to both file and websocket if available"""
logger.log(level.upper(), f"{trace_id} [{level}] {msg}")
if websocket:
try:
await websocket.send(json.dumps({
await websocket.send(
json.dumps(
{
"Type": PLUGIN_JSONRPC_TYPE_SYSTEM_LOG,
"TraceId": trace_id,
"Level": level,
"Message": msg
}))
"Message": msg,
}
)
)
except Exception as e:
logger.error(f"Failed to send log message through websocket: {e}")
async def debug(trace_id: str, msg: str):
await log(trace_id, "debug", msg)
async def info(trace_id: str, msg: str):
await log(trace_id, "info", msg)
async def error(trace_id: str, msg: str):
await log(trace_id, "error", msg)

View File

@ -3,42 +3,35 @@ import json
import uuid
from typing import Any, Dict, Callable
import websockets
import logger
from wox_plugin.types import (
from . import logger
from wox_plugin import (
Context,
PublicAPI,
ChangeQueryParam,
MetadataCommand,
PluginSettingDefinitionItem,
MapString,
Conversation,
ChatStreamFunc,
AIModel,
ChatStreamCallback,
)
from constants import PLUGIN_JSONRPC_TYPE_REQUEST
from plugin_manager import waiting_for_response
from .constants import PLUGIN_JSONRPC_TYPE_REQUEST
from .plugin_manager import waiting_for_response
class PluginAPI(PublicAPI):
def __init__(
self, ws: websockets.WebSocketServerProtocol, plugin_id: str, plugin_name: str
):
def __init__(self, ws: websockets.asyncio.server.ServerConnection, plugin_id: str, plugin_name: str):
self.ws = ws
self.plugin_id = plugin_id
self.plugin_name = plugin_name
self.setting_change_callbacks: Dict[str, Callable[[str, str], None]] = {}
self.get_dynamic_setting_callbacks: Dict[
str, Callable[[str], PluginSettingDefinitionItem]
] = {}
self.deep_link_callbacks: Dict[str, Callable[[MapString], None]] = {}
self.get_dynamic_setting_callbacks: Dict[str, Callable[[str], str]] = {}
self.deep_link_callbacks: Dict[str, Callable[[Dict[str, str]], None]] = {}
self.unload_callbacks: Dict[str, Callable[[], None]] = {}
self.llm_stream_callbacks: Dict[str, ChatStreamFunc] = {}
self.llm_stream_callbacks: Dict[str, ChatStreamCallback] = {}
async def invoke_method(
self, ctx: Context, method: str, params: Dict[str, Any]
) -> Any:
async def invoke_method(self, ctx: Context, method: str, params: Dict[str, Any]) -> Any:
"""Invoke a method on Wox"""
request_id = str(uuid.uuid4())
trace_id = ctx["Values"]["traceId"]
trace_id = ctx.get_trace_id()
if method != "Log":
await logger.info(
@ -71,11 +64,9 @@ class PluginAPI(PublicAPI):
async def change_query(self, ctx: Context, query: ChangeQueryParam) -> None:
"""Change the query in Wox"""
params = {
"QueryType": query.QueryType,
"QueryText": query.QueryText,
"QuerySelection": (
query.QuerySelection.__dict__ if query.QuerySelection else None
),
"QueryType": query.query_type,
"QueryText": query.query_text,
"QuerySelection": (query.query_selection.__dict__ if query.query_selection else None),
}
await self.invoke_method(ctx, "ChangeQuery", params)
@ -105,9 +96,7 @@ class PluginAPI(PublicAPI):
result = await self.invoke_method(ctx, "GetSetting", {"key": key})
return str(result) if result is not None else ""
async def save_setting(
self, ctx: Context, key: str, value: str, is_platform_specific: bool
) -> None:
async def save_setting(self, ctx: Context, key: str, value: str, is_platform_specific: bool) -> None:
"""Save a setting value"""
await self.invoke_method(
ctx,
@ -115,27 +104,19 @@ class PluginAPI(PublicAPI):
{"key": key, "value": value, "isPlatformSpecific": is_platform_specific},
)
async def on_setting_changed(
self, ctx: Context, callback: Callable[[str, str], None]
) -> None:
async def on_setting_changed(self, ctx: Context, callback: Callable[[str, str], None]) -> None:
"""Register setting changed callback"""
callback_id = str(uuid.uuid4())
self.setting_change_callbacks[callback_id] = callback
await self.invoke_method(ctx, "OnSettingChanged", {"callbackId": callback_id})
async def on_get_dynamic_setting(
self, ctx: Context, callback: Callable[[str], PluginSettingDefinitionItem]
) -> None:
async def on_get_dynamic_setting(self, ctx: Context, callback: Callable[[str], str]) -> None:
"""Register dynamic setting callback"""
callback_id = str(uuid.uuid4())
self.get_dynamic_setting_callbacks[callback_id] = callback
await self.invoke_method(
ctx, "OnGetDynamicSetting", {"callbackId": callback_id}
)
await self.invoke_method(ctx, "OnGetDynamicSetting", {"callbackId": callback_id})
async def on_deep_link(
self, ctx: Context, callback: Callable[[MapString], None]
) -> None:
async def on_deep_link(self, ctx: Context, callback: Callable[[Dict[str, str]], None]) -> None:
"""Register deep link callback"""
callback_id = str(uuid.uuid4())
self.deep_link_callbacks[callback_id] = callback
@ -147,9 +128,7 @@ class PluginAPI(PublicAPI):
self.unload_callbacks[callback_id] = callback
await self.invoke_method(ctx, "OnUnload", {"callbackId": callback_id})
async def register_query_commands(
self, ctx: Context, commands: list[MetadataCommand]
) -> None:
async def register_query_commands(self, ctx: Context, commands: list[MetadataCommand]) -> None:
"""Register query commands"""
await self.invoke_method(
ctx,
@ -157,8 +136,12 @@ class PluginAPI(PublicAPI):
{"commands": json.dumps([command.__dict__ for command in commands])},
)
async def llm_stream(
self, ctx: Context, conversations: list[Conversation], callback: ChatStreamFunc
async def ai_chat_stream(
self,
ctx: Context,
model: AIModel,
conversations: list[Conversation],
callback: ChatStreamCallback,
) -> None:
"""Chat using LLM"""
callback_id = str(uuid.uuid4())

View File

@ -1,18 +1,18 @@
from typing import Dict, TypeVar, Callable
from typing import Dict, Any, Callable, Optional, Awaitable
from dataclasses import dataclass
import asyncio
from wox_plugin import PublicAPI, Plugin, RefreshableResult, ActionContext
@dataclass
class PluginInstance:
plugin: Plugin
api: PublicAPI
api: Optional[PublicAPI]
module_path: str
actions: Dict[str, Callable[[ActionContext], None]]
refreshes: Dict[str, Callable[[RefreshableResult], RefreshableResult]]
actions: Dict[str, Callable[[ActionContext], Awaitable[None]]]
refreshes: Dict[str, Callable[[RefreshableResult], Awaitable[RefreshableResult]]]
T = TypeVar('T')
# Global state with strong typing
plugin_instances: Dict[str, PluginInstance] = {}
waiting_for_response: Dict[str, asyncio.Future[T]] = {}
waiting_for_response: Dict[str, asyncio.Future[Any]] = {}

View File

@ -1,49 +1,6 @@
version = 1
requires-python = ">=3.10"
[[package]]
name = "annotated-types"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
]
[[package]]
name = "black"
version = "24.10.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "mypy-extensions" },
{ name = "packaging" },
{ name = "pathspec" },
{ name = "platformdirs" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d8/0d/cc2fb42b8c50d80143221515dd7e4766995bd07c56c9a3ed30baf080b6dc/black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", size = 645813 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a3/f3/465c0eb5cddf7dbbfe1fecd9b875d1dcf51b88923cd2c1d7e9ab95c6336b/black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812", size = 1623211 },
{ url = "https://files.pythonhosted.org/packages/df/57/b6d2da7d200773fdfcc224ffb87052cf283cec4d7102fab450b4a05996d8/black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea", size = 1457139 },
{ url = "https://files.pythonhosted.org/packages/6e/c5/9023b7673904a5188f9be81f5e129fff69f51f5515655fbd1d5a4e80a47b/black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f", size = 1753774 },
{ url = "https://files.pythonhosted.org/packages/e1/32/df7f18bd0e724e0d9748829765455d6643ec847b3f87e77456fc99d0edab/black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e", size = 1414209 },
{ url = "https://files.pythonhosted.org/packages/c2/cc/7496bb63a9b06a954d3d0ac9fe7a73f3bf1cd92d7a58877c27f4ad1e9d41/black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad", size = 1607468 },
{ url = "https://files.pythonhosted.org/packages/2b/e3/69a738fb5ba18b5422f50b4f143544c664d7da40f09c13969b2fd52900e0/black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50", size = 1437270 },
{ url = "https://files.pythonhosted.org/packages/c9/9b/2db8045b45844665c720dcfe292fdaf2e49825810c0103e1191515fc101a/black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392", size = 1737061 },
{ url = "https://files.pythonhosted.org/packages/a3/95/17d4a09a5be5f8c65aa4a361444d95edc45def0de887810f508d3f65db7a/black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175", size = 1423293 },
{ url = "https://files.pythonhosted.org/packages/90/04/bf74c71f592bcd761610bbf67e23e6a3cff824780761f536512437f1e655/black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", size = 1644256 },
{ url = "https://files.pythonhosted.org/packages/4c/ea/a77bab4cf1887f4b2e0bce5516ea0b3ff7d04ba96af21d65024629afedb6/black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", size = 1448534 },
{ url = "https://files.pythonhosted.org/packages/4e/3e/443ef8bc1fbda78e61f79157f303893f3fddf19ca3c8989b163eb3469a12/black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", size = 1761892 },
{ url = "https://files.pythonhosted.org/packages/52/93/eac95ff229049a6901bc84fec6908a5124b8a0b7c26ea766b3b8a5debd22/black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", size = 1434796 },
{ url = "https://files.pythonhosted.org/packages/d0/a0/a993f58d4ecfba035e61fca4e9f64a2ecae838fc9f33ab798c62173ed75c/black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", size = 1643986 },
{ url = "https://files.pythonhosted.org/packages/37/d5/602d0ef5dfcace3fb4f79c436762f130abd9ee8d950fa2abdbf8bbc555e0/black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", size = 1448085 },
{ url = "https://files.pythonhosted.org/packages/47/6d/a3a239e938960df1a662b93d6230d4f3e9b4a22982d060fc38c42f45a56b/black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", size = 1760928 },
{ url = "https://files.pythonhosted.org/packages/dd/cf/af018e13b0eddfb434df4d9cd1b2b7892bab119f7a20123e93f6910982e8/black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", size = 1436875 },
{ url = "https://files.pythonhosted.org/packages/8d/a7/4b27c50537ebca8bec139b872861f9d2bf501c5ec51fcf897cb924d9e264/black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", size = 206898 },
]
[[package]]
name = "click"
version = "8.1.7"
@ -78,6 +35,40 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595 },
]
[[package]]
name = "mypy"
version = "1.14.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mypy-extensions" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8c/7b/08046ef9330735f536a09a2e31b00f42bccdb2795dcd979636ba43bb2d63/mypy-1.14.0.tar.gz", hash = "sha256:822dbd184d4a9804df5a7d5335a68cf7662930e70b8c1bc976645d1509f9a9d6", size = 3215684 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/97/f00ded038482230e0beaaa08f9c5483a54530b362ad1b0d752d5d2b2f211/mypy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e971c1c667007f9f2b397ffa80fa8e1e0adccff336e5e77e74cb5f22868bee87", size = 11207956 },
{ url = "https://files.pythonhosted.org/packages/68/67/8b4db0da19c9e3fa6264e948f1c135ab4dd45bede1809f4fdb613dc119f6/mypy-1.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e86aaeaa3221a278c66d3d673b297232947d873773d61ca3ee0e28b2ff027179", size = 10363681 },
{ url = "https://files.pythonhosted.org/packages/f5/00/56b1619ff1f3fcad2d411eccda60d74d20e73bda39c218d5ad2769980682/mypy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1628c5c3ce823d296e41e2984ff88c5861499041cb416a8809615d0c1f41740e", size = 12832976 },
{ url = "https://files.pythonhosted.org/packages/e7/8b/9247838774b0bd865f190cc221822212091317f16310305ef924d9772532/mypy-1.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fadb29b77fc14a0dd81304ed73c828c3e5cde0016c7e668a86a3e0dfc9f3af3", size = 13013704 },
{ url = "https://files.pythonhosted.org/packages/b2/69/0c0868a6f3d9761d2f704d1fb6ef84d75998c27d342738a8b20f109a411f/mypy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:3fa76988dc760da377c1e5069200a50d9eaaccf34f4ea18428a3337034ab5a44", size = 9782230 },
{ url = "https://files.pythonhosted.org/packages/34/c1/b9dd3e955953aec1c728992545b7877c9f6fa742a623ce4c200da0f62540/mypy-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6e73c8a154eed31db3445fe28f63ad2d97b674b911c00191416cf7f6459fd49a", size = 11121032 },
{ url = "https://files.pythonhosted.org/packages/ee/96/c52d5d516819ab95bf41f4a1ada828a3decc302f8c152ff4fc5feb0e4529/mypy-1.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:273e70fcb2e38c5405a188425aa60b984ffdcef65d6c746ea5813024b68c73dc", size = 10286294 },
{ url = "https://files.pythonhosted.org/packages/69/2c/3dbe51877a24daa467f8d8631f9ffd1aabbf0f6d9367a01c44a59df81fe0/mypy-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1daca283d732943731a6a9f20fdbcaa927f160bc51602b1d4ef880a6fb252015", size = 12746528 },
{ url = "https://files.pythonhosted.org/packages/a1/a8/eb20cde4ba9c4c3e20d958918a7c5d92210f4d1a0200c27de9a641f70996/mypy-1.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7e68047bedb04c1c25bba9901ea46ff60d5eaac2d71b1f2161f33107e2b368eb", size = 12883489 },
{ url = "https://files.pythonhosted.org/packages/91/17/a1fc6c70f31d52c99299320cf81c3cb2c6b91ec7269414e0718a6d138e34/mypy-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:7a52f26b9c9b1664a60d87675f3bae00b5c7f2806e0c2800545a32c325920bcc", size = 9780113 },
{ url = "https://files.pythonhosted.org/packages/fe/d8/0e72175ee0253217f5c44524f5e95251c02e95ba9749fb87b0e2074d203a/mypy-1.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d5326ab70a6db8e856d59ad4cb72741124950cbbf32e7b70e30166ba7bbf61dd", size = 11269011 },
{ url = "https://files.pythonhosted.org/packages/e9/6d/4ea13839dabe5db588dc6a1b766da16f420d33cf118a7b7172cdf6c7fcb2/mypy-1.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bf4ec4980bec1e0e24e5075f449d014011527ae0055884c7e3abc6a99cd2c7f1", size = 10253076 },
{ url = "https://files.pythonhosted.org/packages/3e/38/7db2c5d0f4d290e998f7a52b2e2616c7bbad96b8e04278ab09d11978a29e/mypy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:390dfb898239c25289495500f12fa73aa7f24a4c6d90ccdc165762462b998d63", size = 12862786 },
{ url = "https://files.pythonhosted.org/packages/bf/4b/62d59c801b34141040989949c2b5c157d0408b45357335d3ec5b2845b0f6/mypy-1.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7e026d55ddcd76e29e87865c08cbe2d0104e2b3153a523c529de584759379d3d", size = 12971568 },
{ url = "https://files.pythonhosted.org/packages/f1/9c/e0f281b32d70c87b9e4d2939e302b1ff77ada4d7b0f2fb32890c144bc1d6/mypy-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:585ed36031d0b3ee362e5107ef449a8b5dfd4e9c90ccbe36414ee405ee6b32ba", size = 9879477 },
{ url = "https://files.pythonhosted.org/packages/13/33/8380efd0ebdfdfac7fc0bf065f03a049800ca1e6c296ec1afc634340d992/mypy-1.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9f6f4c0b27401d14c483c622bc5105eff3911634d576bbdf6695b9a7c1ba741", size = 11251509 },
{ url = "https://files.pythonhosted.org/packages/15/6d/4e1c21c60fee11af7d8e4f2902a29886d1387d6a836be16229eb3982a963/mypy-1.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b2280cedcb312c7a79f5001ae5325582d0d339bce684e4a529069d0e7ca1e7", size = 10244282 },
{ url = "https://files.pythonhosted.org/packages/8b/cf/7a8ae5c0161edae15d25c2c67c68ce8b150cbdc45aefc13a8be271ee80b2/mypy-1.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:342de51c48bab326bfc77ce056ba08c076d82ce4f5a86621f972ed39970f94d8", size = 12867676 },
{ url = "https://files.pythonhosted.org/packages/9c/d0/71f7bbdcc7cfd0f2892db5b13b1e8857673f2cc9e0c30e3e4340523dc186/mypy-1.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:00df23b42e533e02a6f0055e54de9a6ed491cd8b7ea738647364fd3a39ea7efc", size = 12964189 },
{ url = "https://files.pythonhosted.org/packages/a7/40/fb4ad65d6d5f8c51396ecf6305ec0269b66013a5bf02d0e9528053640b4a/mypy-1.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:e8c8387e5d9dff80e7daf961df357c80e694e942d9755f3ad77d69b0957b8e3f", size = 9888247 },
{ url = "https://files.pythonhosted.org/packages/39/32/0214608af400cdf8f5102144bb8af10d880675c65ed0b58f7e0e77175d50/mypy-1.14.0-py3-none-any.whl", hash = "sha256:2238d7f93fc4027ed1efc944507683df3ba406445a2b6c96e79666a045aadfab", size = 2752803 },
]
[[package]]
name = "mypy-extensions"
version = "1.0.0"
@ -88,119 +79,60 @@ wheels = [
]
[[package]]
name = "packaging"
version = "24.2"
name = "pip"
version = "24.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
sdist = { url = "https://files.pythonhosted.org/packages/f4/b1/b422acd212ad7eedddaf7981eee6e5de085154ff726459cf2da7c5a184c1/pip-24.3.1.tar.gz", hash = "sha256:ebcb60557f2aefabc2e0f918751cd24ea0d56d8ec5445fe1807f1d2109660b99", size = 1931073 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
{ url = "https://files.pythonhosted.org/packages/ef/7d/500c9ad20238fcfcb4cb9243eede163594d7020ce87bd9610c9e02771876/pip-24.3.1-py3-none-any.whl", hash = "sha256:3790624780082365f47549d032f3770eeb2b1e8bd1f7b2e02dace1afa361b4ed", size = 1822182 },
]
[[package]]
name = "pathspec"
version = "0.12.1"
name = "ruff"
version = "0.8.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 }
sdist = { url = "https://files.pythonhosted.org/packages/34/37/9c02181ef38d55b77d97c68b78e705fd14c0de0e5d085202bb2b52ce5be9/ruff-0.8.4.tar.gz", hash = "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8", size = 3402103 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 },
{ url = "https://files.pythonhosted.org/packages/05/67/f480bf2f2723b2e49af38ed2be75ccdb2798fca7d56279b585c8f553aaab/ruff-0.8.4-py3-none-linux_armv6l.whl", hash = "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60", size = 10546415 },
{ url = "https://files.pythonhosted.org/packages/eb/7a/5aba20312c73f1ce61814e520d1920edf68ca3b9c507bd84d8546a8ecaa8/ruff-0.8.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac", size = 10346113 },
{ url = "https://files.pythonhosted.org/packages/76/f4/c41de22b3728486f0aa95383a44c42657b2db4062f3234ca36fc8cf52d8b/ruff-0.8.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296", size = 9943564 },
{ url = "https://files.pythonhosted.org/packages/0e/f0/afa0d2191af495ac82d4cbbfd7a94e3df6f62a04ca412033e073b871fc6d/ruff-0.8.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643", size = 10805522 },
{ url = "https://files.pythonhosted.org/packages/12/57/5d1e9a0fd0c228e663894e8e3a8e7063e5ee90f8e8e60cf2085f362bfa1a/ruff-0.8.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e", size = 10306763 },
{ url = "https://files.pythonhosted.org/packages/04/df/f069fdb02e408be8aac6853583572a2873f87f866fe8515de65873caf6b8/ruff-0.8.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3", size = 11359574 },
{ url = "https://files.pythonhosted.org/packages/d3/04/37c27494cd02e4a8315680debfc6dfabcb97e597c07cce0044db1f9dfbe2/ruff-0.8.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f", size = 12094851 },
{ url = "https://files.pythonhosted.org/packages/81/b1/c5d7fb68506cab9832d208d03ea4668da9a9887a4a392f4f328b1bf734ad/ruff-0.8.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604", size = 11655539 },
{ url = "https://files.pythonhosted.org/packages/ef/38/8f8f2c8898dc8a7a49bc340cf6f00226917f0f5cb489e37075bcb2ce3671/ruff-0.8.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf", size = 12912805 },
{ url = "https://files.pythonhosted.org/packages/06/dd/fa6660c279f4eb320788876d0cff4ea18d9af7d9ed7216d7bd66877468d0/ruff-0.8.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720", size = 11205976 },
{ url = "https://files.pythonhosted.org/packages/a8/d7/de94cc89833b5de455750686c17c9e10f4e1ab7ccdc5521b8fe911d1477e/ruff-0.8.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae", size = 10792039 },
{ url = "https://files.pythonhosted.org/packages/6d/15/3e4906559248bdbb74854af684314608297a05b996062c9d72e0ef7c7097/ruff-0.8.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7", size = 10400088 },
{ url = "https://files.pythonhosted.org/packages/a2/21/9ed4c0e8133cb4a87a18d470f534ad1a8a66d7bec493bcb8bda2d1a5d5be/ruff-0.8.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111", size = 10900814 },
{ url = "https://files.pythonhosted.org/packages/0d/5d/122a65a18955bd9da2616b69bc839351f8baf23b2805b543aa2f0aed72b5/ruff-0.8.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8", size = 11268828 },
{ url = "https://files.pythonhosted.org/packages/43/a9/1676ee9106995381e3d34bccac5bb28df70194167337ed4854c20f27c7ba/ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835", size = 8805621 },
{ url = "https://files.pythonhosted.org/packages/10/98/ed6b56a30ee76771c193ff7ceeaf1d2acc98d33a1a27b8479cbdb5c17a23/ruff-0.8.4-py3-none-win_amd64.whl", hash = "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d", size = 9660086 },
{ url = "https://files.pythonhosted.org/packages/13/9f/026e18ca7d7766783d779dae5e9c656746c6ede36ef73c6d934aaf4a6dec/ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08", size = 9074500 },
]
[[package]]
name = "platformdirs"
version = "4.3.6"
name = "setuptools"
version = "75.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 }
sdist = { url = "https://files.pythonhosted.org/packages/43/54/292f26c208734e9a7f067aea4a7e282c080750c4546559b58e2e45413ca0/setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", size = 1337429 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
{ url = "https://files.pythonhosted.org/packages/55/21/47d163f615df1d30c094f6c8bbb353619274edccf0327b185cc2493c2c33/setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d", size = 1224032 },
]
[[package]]
name = "pydantic"
version = "2.10.4"
name = "shiv"
version = "1.0.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
{ name = "click" },
{ name = "pip" },
{ name = "setuptools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/70/7e/fb60e6fee04d0ef8f15e4e01ff187a196fa976eb0f0ab524af4599e5754c/pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06", size = 762094 }
sdist = { url = "https://files.pythonhosted.org/packages/06/85/004e7123b4821c64be6d9bfed27f63147b4dde929a1cd848f137befeada4/shiv-1.0.8.tar.gz", hash = "sha256:2a68d69e98ce81cb5b8fdafbfc1e27efa93e6d89ca14bfae33482e4176f561d6", size = 32806 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f3/26/3e1bbe954fde7ee22a6e7d31582c642aad9e84ffe4b5fb61e63b87cd326f/pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d", size = 431765 },
]
[[package]]
name = "pydantic-core"
version = "2.27.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3a/bc/fed5f74b5d802cf9a03e83f60f18864e90e3aed7223adaca5ffb7a8d8d64/pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa", size = 1895938 },
{ url = "https://files.pythonhosted.org/packages/71/2a/185aff24ce844e39abb8dd680f4e959f0006944f4a8a0ea372d9f9ae2e53/pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c", size = 1815684 },
{ url = "https://files.pythonhosted.org/packages/c3/43/fafabd3d94d159d4f1ed62e383e264f146a17dd4d48453319fd782e7979e/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a", size = 1829169 },
{ url = "https://files.pythonhosted.org/packages/a2/d1/f2dfe1a2a637ce6800b799aa086d079998959f6f1215eb4497966efd2274/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5", size = 1867227 },
{ url = "https://files.pythonhosted.org/packages/7d/39/e06fcbcc1c785daa3160ccf6c1c38fea31f5754b756e34b65f74e99780b5/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c", size = 2037695 },
{ url = "https://files.pythonhosted.org/packages/7a/67/61291ee98e07f0650eb756d44998214231f50751ba7e13f4f325d95249ab/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7", size = 2741662 },
{ url = "https://files.pythonhosted.org/packages/32/90/3b15e31b88ca39e9e626630b4c4a1f5a0dfd09076366f4219429e6786076/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a", size = 1993370 },
{ url = "https://files.pythonhosted.org/packages/ff/83/c06d333ee3a67e2e13e07794995c1535565132940715931c1c43bfc85b11/pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236", size = 1996813 },
{ url = "https://files.pythonhosted.org/packages/7c/f7/89be1c8deb6e22618a74f0ca0d933fdcb8baa254753b26b25ad3acff8f74/pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962", size = 2005287 },
{ url = "https://files.pythonhosted.org/packages/b7/7d/8eb3e23206c00ef7feee17b83a4ffa0a623eb1a9d382e56e4aa46fd15ff2/pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9", size = 2128414 },
{ url = "https://files.pythonhosted.org/packages/4e/99/fe80f3ff8dd71a3ea15763878d464476e6cb0a2db95ff1c5c554133b6b83/pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af", size = 2155301 },
{ url = "https://files.pythonhosted.org/packages/2b/a3/e50460b9a5789ca1451b70d4f52546fa9e2b420ba3bfa6100105c0559238/pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4", size = 1816685 },
{ url = "https://files.pythonhosted.org/packages/57/4c/a8838731cb0f2c2a39d3535376466de6049034d7b239c0202a64aaa05533/pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31", size = 1982876 },
{ url = "https://files.pythonhosted.org/packages/c2/89/f3450af9d09d44eea1f2c369f49e8f181d742f28220f88cc4dfaae91ea6e/pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc", size = 1893421 },
{ url = "https://files.pythonhosted.org/packages/9e/e3/71fe85af2021f3f386da42d291412e5baf6ce7716bd7101ea49c810eda90/pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7", size = 1814998 },
{ url = "https://files.pythonhosted.org/packages/a6/3c/724039e0d848fd69dbf5806894e26479577316c6f0f112bacaf67aa889ac/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15", size = 1826167 },
{ url = "https://files.pythonhosted.org/packages/2b/5b/1b29e8c1fb5f3199a9a57c1452004ff39f494bbe9bdbe9a81e18172e40d3/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306", size = 1865071 },
{ url = "https://files.pythonhosted.org/packages/89/6c/3985203863d76bb7d7266e36970d7e3b6385148c18a68cc8915fd8c84d57/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99", size = 2036244 },
{ url = "https://files.pythonhosted.org/packages/0e/41/f15316858a246b5d723f7d7f599f79e37493b2e84bfc789e58d88c209f8a/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459", size = 2737470 },
{ url = "https://files.pythonhosted.org/packages/a8/7c/b860618c25678bbd6d1d99dbdfdf0510ccb50790099b963ff78a124b754f/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048", size = 1992291 },
{ url = "https://files.pythonhosted.org/packages/bf/73/42c3742a391eccbeab39f15213ecda3104ae8682ba3c0c28069fbcb8c10d/pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d", size = 1994613 },
{ url = "https://files.pythonhosted.org/packages/94/7a/941e89096d1175d56f59340f3a8ebaf20762fef222c298ea96d36a6328c5/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b", size = 2002355 },
{ url = "https://files.pythonhosted.org/packages/6e/95/2359937a73d49e336a5a19848713555605d4d8d6940c3ec6c6c0ca4dcf25/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474", size = 2126661 },
{ url = "https://files.pythonhosted.org/packages/2b/4c/ca02b7bdb6012a1adef21a50625b14f43ed4d11f1fc237f9d7490aa5078c/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6", size = 2153261 },
{ url = "https://files.pythonhosted.org/packages/72/9d/a241db83f973049a1092a079272ffe2e3e82e98561ef6214ab53fe53b1c7/pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c", size = 1812361 },
{ url = "https://files.pythonhosted.org/packages/e8/ef/013f07248041b74abd48a385e2110aa3a9bbfef0fbd97d4e6d07d2f5b89a/pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc", size = 1982484 },
{ url = "https://files.pythonhosted.org/packages/10/1c/16b3a3e3398fd29dca77cea0a1d998d6bde3902fa2706985191e2313cc76/pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4", size = 1867102 },
{ url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 },
{ url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 },
{ url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 },
{ url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 },
{ url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 },
{ url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 },
{ url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 },
{ url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 },
{ url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 },
{ url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 },
{ url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 },
{ url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 },
{ url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 },
{ url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 },
{ url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 },
{ url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 },
{ url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 },
{ url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 },
{ url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 },
{ url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 },
{ url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 },
{ url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 },
{ url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 },
{ url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 },
{ url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 },
{ url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 },
{ url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 },
{ url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 },
{ url = "https://files.pythonhosted.org/packages/46/72/af70981a341500419e67d5cb45abe552a7c74b66326ac8877588488da1ac/pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e", size = 1891159 },
{ url = "https://files.pythonhosted.org/packages/ad/3d/c5913cccdef93e0a6a95c2d057d2c2cba347815c845cda79ddd3c0f5e17d/pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8", size = 1768331 },
{ url = "https://files.pythonhosted.org/packages/f6/f0/a3ae8fbee269e4934f14e2e0e00928f9346c5943174f2811193113e58252/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3", size = 1822467 },
{ url = "https://files.pythonhosted.org/packages/d7/7a/7bbf241a04e9f9ea24cd5874354a83526d639b02674648af3f350554276c/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f", size = 1979797 },
{ url = "https://files.pythonhosted.org/packages/4f/5f/4784c6107731f89e0005a92ecb8a2efeafdb55eb992b8e9d0a2be5199335/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133", size = 1987839 },
{ url = "https://files.pythonhosted.org/packages/6d/a7/61246562b651dff00de86a5f01b6e4befb518df314c54dec187a78d81c84/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc", size = 1998861 },
{ url = "https://files.pythonhosted.org/packages/86/aa/837821ecf0c022bbb74ca132e117c358321e72e7f9702d1b6a03758545e2/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50", size = 2116582 },
{ url = "https://files.pythonhosted.org/packages/81/b0/5e74656e95623cbaa0a6278d16cf15e10a51f6002e3ec126541e95c29ea3/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9", size = 2151985 },
{ url = "https://files.pythonhosted.org/packages/63/37/3e32eeb2a451fddaa3898e2163746b0cffbbdbb4740d38372db0490d67f3/pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151", size = 2004715 },
{ url = "https://files.pythonhosted.org/packages/e7/ec/afbb46f7c1ab071a50d92424daf149420ca1f0e02dc51239485747151d6c/shiv-1.0.8-py2.py3-none-any.whl", hash = "sha256:a60e4b05a2d2f8b820d567b1d89ee59af731759771c32c282d03c4ceae6aba24", size = 20516 },
]
[[package]]
@ -321,14 +253,11 @@ wheels = [
[[package]]
name = "wox-plugin"
version = "0.0.30"
version = "0.0.45"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pydantic" },
]
sdist = { url = "https://files.pythonhosted.org/packages/30/58/c09cc97cee3b3253dd1d332fe95c828b241e8f788b5d81dbda6e38767568/wox_plugin-0.0.30.tar.gz", hash = "sha256:e23d7b85ba2845f11047b3fa049edf08ebc6d5435ecd6aa486cbfdcb0b1dd3c0", size = 46011 }
sdist = { url = "https://files.pythonhosted.org/packages/55/0f/88e083cda63dfeda66bf4a506322bfa161789cb268f9f7e5d366a6982860/wox_plugin-0.0.45.tar.gz", hash = "sha256:38a543336b2eb32a22cbd85b5b288e63d38c5e67c977f02c6ac8f59c7ba6ca8e", size = 34974 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0e/22/a418ccaea3dfaa7f25b03ed9012ae9fa0ca94b2c8fdd7710342862555093/wox_plugin-0.0.30-py3-none-any.whl", hash = "sha256:deea70fce050fc969ed90584d3635fde96ea76e3c562fca0a1a5b83cf634791e", size = 7850 },
{ url = "https://files.pythonhosted.org/packages/dd/9f/1f0a833675292b77a6fa3276749125ea4100f5caccdd3cb4584e00878ad5/wox_plugin-0.0.45-py3-none-any.whl", hash = "sha256:2ac9d1fd84e73be0c3af6ba5f9b2f551052196543d9d6b60ae2e6b2ddeb3f8f9", size = 10304 },
]
[[package]]
@ -343,13 +272,17 @@ dependencies = [
[package.optional-dependencies]
dev = [
{ name = "black" },
{ name = "mypy" },
{ name = "ruff" },
{ name = "shiv" },
]
[package.metadata]
requires-dist = [
{ name = "black", marker = "extra == 'dev'" },
{ name = "loguru" },
{ name = "mypy", marker = "extra == 'dev'" },
{ name = "ruff", marker = "extra == 'dev'" },
{ name = "shiv", marker = "extra == 'dev'" },
{ name = "websockets" },
{ name = "wox-plugin", specifier = "==0.0.30" },
{ name = "wox-plugin", specifier = "==0.0.45" },
]

View File

@ -216,7 +216,7 @@ export interface PublicAPI {
/**
* Get dynamic setting definition
*/
OnGetDynamicSetting: (ctx: Context, callback: (key: string) => PluginSettingDefinitionItem) => Promise<void>
OnGetDynamicSetting: (ctx: Context, callback: (key: string) => string) => Promise<void>
/**
* Register deep link callback

View File

@ -1 +1 @@
3.12
3.10

View File

@ -9,6 +9,8 @@ help:
@echo " make build - Build package"
@echo " make publish - Publish package to PyPI"
@echo " make install - Install development dependencies"
@echo " make lint - Run linting"
@echo " make format - Run formatting"
clean:
rm -rf dist/ build/ *.egg-info/
@ -16,7 +18,14 @@ clean:
install:
uv sync --all-extras
build: install clean
lint:
uv run ruff check src
uv run mypy src
format:
uv run ruff format src
build: install clean lint format
uv run hatchling build
publish: build

View File

@ -1,33 +1,26 @@
[project]
name = "wox-plugin"
version = "0.0.30"
version = "0.0.45"
description = "Python plugin SDK for Wox launcher"
authors = [{ name = "Wox Team" }]
dependencies = ["pydantic>=2.0.0"]
requires-python = ">=3.8"
readme = "README.md"
requires-python = ">=3.10"
license = { text = "GPL-3.0" }
authors = [{ name = "Wox Team" }]
dependencies = []
[project.urls]
Homepage = "https://github.com/Wox-launcher/Wox"
Repository = "https://github.com/Wox-launcher/Wox"
[project.optional-dependencies]
dev = ["black>=23.0.0", "hatchling>=1.18.0", "twine>=4.0.0"]
dev = ["hatchling", "twine", "ruff", "mypy"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.black]
line-length = 88
target-version = ['py38']
[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
[tool.setuptools]
package-data = { "wox_plugin" = ["py.typed"] }
[tool.ruff]
line-length = 140

View File

@ -6,47 +6,43 @@ This package provides the SDK for developing Wox plugins in Python.
from typing import List
from .plugin import Plugin, BasePlugin, PluginInitParams
from .api import PublicAPI
from .plugin import Plugin, PluginInitParams
from .api import PublicAPI, ChatStreamCallback
from .models.context import Context
from .models.query import Query, QueryEnv, Selection
from .models.query import (
Query,
QueryEnv,
Selection,
ChangeQueryParam,
QueryType,
SelectionType,
MetadataCommand,
)
from .models.result import (
Result,
WoxImage,
WoxPreview,
ResultTail,
ResultAction,
ActionContext,
RefreshableResult,
)
from .models.settings import (
PluginSettingDefinitionItem,
PluginSettingDefinitionValue,
PluginSettingValueStyle,
MetadataCommand,
)
from .types import (
Platform,
SelectionType,
QueryType,
WoxImageType,
WoxPreviewType,
ResultTailType,
)
from .models.ai import (
AIModel,
Conversation,
ConversationRole,
ChatStreamDataType,
PluginSettingDefinitionType,
)
from .utils.helpers import new_base64_wox_image
from .exceptions import WoxPluginError, InvalidQueryError, PluginInitError, APIError
from .models.image import WoxImage, WoxImageType
from .models.preview import WoxPreview, WoxPreviewType, WoxPreviewScrollPosition
__version__: str = "0.1.0"
__all__: List[str] = [
# Plugin
"Plugin",
"BasePlugin",
"PluginInitParams",
# API
"PublicAPI",
"ChatStreamCallback",
# Models
"Context",
"Query",
@ -59,25 +55,33 @@ __all__: List[str] = [
"ResultAction",
"ActionContext",
"RefreshableResult",
"PluginSettingDefinitionItem",
"PluginSettingDefinitionValue",
"PluginSettingValueStyle",
"MetadataCommand",
# Types
"Platform",
"SelectionType",
"QueryType",
"WoxImageType",
"WoxPreviewType",
"ResultTailType",
"PluginSettingDefinitionItem",
"PluginSettingValueStyle",
# AI
"AIModel",
"Conversation",
"ConversationRole",
"ChatStreamDataType",
"PluginSettingDefinitionType",
# Utils
"new_base64_wox_image",
"user_message",
"ai_message",
# Query
"ChangeQueryParam",
"QueryType",
"Selection",
"SelectionType",
# Exceptions
"WoxPluginError",
"InvalidQueryError",
"PluginInitError",
"APIError",
# Image
"WoxImage",
"WoxImageType",
# Preview
"WoxPreview",
"WoxPreviewType",
"WoxPreviewScrollPosition",
# Result
"ResultTailType",
]

View File

@ -1,17 +1,16 @@
from typing import Protocol, List, Callable
from typing import Protocol, Callable, Dict, List
from .types import MapString, ChatStreamFunc
from .models.query import MetadataCommand
from .models.context import Context
from .models.query import ChangeQueryParam
from .models.settings import MetadataCommand, PluginSettingDefinitionItem
from .models.result import Conversation
from .models.ai import AIModel, Conversation, ChatStreamCallback
class PublicAPI(Protocol):
"""Public API interface for Wox plugins"""
async def change_query(self, ctx: Context, query: ChangeQueryParam) -> None:
"""Change the current query"""
"""Change the current query in Wox"""
...
async def hide_app(self, ctx: Context) -> None:
@ -23,11 +22,11 @@ class PublicAPI(Protocol):
...
async def notify(self, ctx: Context, message: str) -> None:
"""Show a notification"""
"""Show a notification message"""
...
async def log(self, ctx: Context, level: str, msg: str) -> None:
"""Log a message"""
"""Write log message"""
...
async def get_translation(self, ctx: Context, key: str) -> str:
@ -38,27 +37,19 @@ class PublicAPI(Protocol):
"""Get setting value"""
...
async def save_setting(
self, ctx: Context, key: str, value: str, is_platform_specific: bool
) -> None:
async def save_setting(self, ctx: Context, key: str, value: str, is_platform_specific: bool) -> None:
"""Save setting value"""
...
async def on_setting_changed(
self, ctx: Context, callback: Callable[[str, str], None]
) -> None:
async def on_setting_changed(self, ctx: Context, callback: Callable[[str, str], None]) -> None:
"""Register setting change callback"""
...
async def on_get_dynamic_setting(
self, ctx: Context, callback: Callable[[str], PluginSettingDefinitionItem]
) -> None:
async def on_get_dynamic_setting(self, ctx: Context, callback: Callable[[str], str]) -> None:
"""Register dynamic setting callback"""
...
async def on_deep_link(
self, ctx: Context, callback: Callable[[MapString], None]
) -> None:
async def on_deep_link(self, ctx: Context, callback: Callable[[Dict[str, str]], None]) -> None:
"""Register deep link callback"""
...
@ -66,14 +57,27 @@ class PublicAPI(Protocol):
"""Register unload callback"""
...
async def register_query_commands(
self, ctx: Context, commands: List[MetadataCommand]
) -> None:
async def register_query_commands(self, ctx: Context, commands: List[MetadataCommand]) -> None:
"""Register query commands"""
...
async def llm_stream(
self, ctx: Context, conversations: List[Conversation], callback: ChatStreamFunc
async def ai_chat_stream(
self,
ctx: Context,
model: AIModel,
conversations: List[Conversation],
callback: ChatStreamCallback,
) -> None:
"""Stream LLM responses"""
"""
Start an AI chat stream.
Args:
ctx: Context
model: AI model to use
conversations: Conversation history
callback: Stream callback function to receive AI responses
The callback takes two parameters:
- stream_type: ChatStreamDataType, indicates the stream status
- data: str, the stream content
"""
...

View File

@ -1,22 +0,0 @@
class WoxPluginError(Exception):
"""Base exception for Wox plugin errors"""
pass
class InvalidQueryError(WoxPluginError):
"""Raised when query is invalid"""
pass
class PluginInitError(WoxPluginError):
"""Raised when plugin initialization fails"""
pass
class APIError(WoxPluginError):
"""Raised when API call fails"""
pass

View File

@ -0,0 +1,98 @@
from enum import Enum
from typing import List, Callable, Optional
import time
from dataclasses import dataclass, field
import json
class ConversationRole(str, Enum):
"""Role in the conversation"""
USER = "user"
AI = "ai"
class ChatStreamDataType(str, Enum):
"""Type of chat stream data"""
STREAMING = "streaming" # Currently streaming
FINISHED = "finished" # Stream completed
ERROR = "error" # Error occurred
ChatStreamCallback = Callable[[ChatStreamDataType, str], None]
@dataclass
class AIModel:
"""AI model definition"""
name: str
provider: str
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"Name": self.name,
"Provider": self.provider,
}
)
@classmethod
def from_json(cls, json_str: str) -> "AIModel":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
return cls(
name=data.get("Name", ""),
provider=data.get("Provider", ""),
)
@dataclass
class Conversation:
"""Conversation content"""
role: ConversationRole
text: str
images: List[bytes] = field(default_factory=list) # PNG format image data
timestamp: int = field(default_factory=lambda: int(time.time() * 1000))
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"Role": self.role,
"Text": self.text,
"Images": [image.hex() for image in self.images] if self.images else [],
"Timestamp": self.timestamp,
}
)
@classmethod
def from_json(cls, json_str: str) -> "Conversation":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
return cls(
role=ConversationRole(data.get("Role", ConversationRole.USER)),
text=data.get("Text", ""),
images=[bytes.fromhex(img) for img in data.get("Images", [])] if data.get("Images") else [],
timestamp=data.get("Timestamp", int(time.time() * 1000)),
)
@classmethod
def new_user_message(cls, text: str, images: Optional[List[bytes]] = None) -> "Conversation":
"""Create a user message"""
return cls(
role=ConversationRole.USER,
text=text,
images=images if images is not None else [],
)
@classmethod
def new_ai_message(cls, text: str) -> "Conversation":
"""Create an AI message"""
return cls(
role=ConversationRole.AI,
text=text,
)

View File

@ -1,27 +1,45 @@
import uuid
from dataclasses import dataclass, field
from typing import Dict
from pydantic import BaseModel
import json
class Context(BaseModel):
@dataclass
class Context:
"""
Context object that carries request-scoped values across the plugin execution
"""
Values: Dict[str, str]
values: Dict[str, str] = field(default_factory=dict)
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"Values": self.values,
}
)
@classmethod
def from_json(cls, json_str: str) -> "Context":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
return cls(
values=data.get("Values", {}),
)
def get_trace_id(self) -> str:
"""Get the trace ID from context"""
return self.Values["traceId"]
return self.values.get("TraceId", "")
@staticmethod
def new() -> "Context":
@classmethod
def new(cls) -> "Context":
"""Create a new context with a random trace ID"""
return Context(Values={"traceId": str(uuid.uuid4())})
return cls(values={"TraceId": str(uuid.uuid4())})
@staticmethod
def new_with_value(key: str, value: str) -> "Context":
@classmethod
def new_with_value(cls, key: str, value: str) -> "Context":
"""Create a new context with a specific key-value pair"""
ctx = Context.new()
ctx.Values[key] = value
ctx = cls.new()
ctx.values[key] = value
return ctx

View File

@ -0,0 +1,86 @@
from dataclasses import dataclass, field
from enum import Enum
import json
class WoxImageType(str, Enum):
"""Image type enum for Wox"""
ABSOLUTE = "absolute"
RELATIVE = "relative"
BASE64 = "base64"
SVG = "svg"
LOTTIE = "lottie" # only support lottie json data
EMOJI = "emoji"
URL = "url"
THEME = "theme"
@dataclass
class WoxImage:
"""Image model for Wox"""
image_type: WoxImageType = field(default=WoxImageType.ABSOLUTE)
image_data: str = field(default="")
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"Data": self.image_data,
"Type": self.image_type,
}
)
@classmethod
def from_json(cls, json_str: str) -> "WoxImage":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
return cls(
image_type=WoxImageType(data.get("Type", WoxImageType.ABSOLUTE)),
image_data=data.get("Data", ""),
)
@classmethod
def new_base64(cls, data: str) -> "WoxImage":
"""Create a new base64 image"""
return cls(image_type=WoxImageType.BASE64, image_data=data)
@classmethod
def new_svg(cls, data: str) -> "WoxImage":
"""Create a new svg image"""
return cls(image_type=WoxImageType.SVG, image_data=data)
@classmethod
def new_lottie(cls, data: str) -> "WoxImage":
"""Create a new lottie image"""
return cls(image_type=WoxImageType.LOTTIE, image_data=data)
@classmethod
def new_emoji(cls, data: str) -> "WoxImage":
"""Create a new emoji image"""
return cls(image_type=WoxImageType.EMOJI, image_data=data)
@classmethod
def new_url(cls, data: str) -> "WoxImage":
"""Create a new url image"""
return cls(image_type=WoxImageType.URL, image_data=data)
@classmethod
def new_absolute(cls, data: str) -> "WoxImage":
"""Create a new absolute image"""
return cls(image_type=WoxImageType.ABSOLUTE, image_data=data)
@classmethod
def new_relative(cls, data: str) -> "WoxImage":
"""Create a new relative image"""
return cls(image_type=WoxImageType.RELATIVE, image_data=data)
@classmethod
def new_theme(cls, data: str) -> "WoxImage":
"""Create a new theme image"""
return cls(image_type=WoxImageType.THEME, image_data=data)
def __str__(self) -> str:
"""Convert image to string"""
return f"{self.image_type}:{self.image_data}"

View File

@ -0,0 +1,53 @@
from typing import Dict
from dataclasses import dataclass, field
from enum import Enum
import json
class WoxPreviewType(str, Enum):
"""Preview type enum for Wox"""
MARKDOWN = "markdown"
TEXT = "text"
IMAGE = "image" # when type is image, data should be WoxImage.String()
URL = "url"
FILE = "file" # when type is file(can be *.md, *.jpg, *.pdf and so on), data should be url/filepath
REMOTE = "remote" # when type is remote, data should be url to load WoxPreview
class WoxPreviewScrollPosition(str, Enum):
"""Preview scroll position enum for Wox"""
BOTTOM = "bottom" # scroll to bottom after preview first show
@dataclass
class WoxPreview:
"""Preview model for Wox results"""
preview_type: WoxPreviewType = field(default=WoxPreviewType.TEXT)
preview_data: str = field(default="")
preview_properties: Dict[str, str] = field(default_factory=dict)
scroll_position: WoxPreviewScrollPosition = field(default=WoxPreviewScrollPosition.BOTTOM)
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"PreviewType": self.preview_type,
"PreviewData": self.preview_data,
"PreviewProperties": self.preview_properties,
"ScrollPosition": self.scroll_position,
}
)
@classmethod
def from_json(cls, json_str: str) -> "WoxPreview":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
return cls(
preview_type=WoxPreviewType(data.get("PreviewType", WoxPreviewType.TEXT)),
preview_data=data.get("PreviewData", ""),
preview_properties=data.get("PreviewProperties", {}),
scroll_position=WoxPreviewScrollPosition(data.get("ScrollPosition", WoxPreviewScrollPosition.BOTTOM)),
)

View File

@ -1,48 +1,212 @@
from typing import Optional, List
from pydantic import BaseModel
from ..types import SelectionType, QueryType
from typing import List
from dataclasses import dataclass, field
from enum import Enum
import json
class Selection(BaseModel):
class SelectionType(str, Enum):
"""Selection type enum"""
TEXT = "text"
FILE = "file"
class QueryType(str, Enum):
"""Query type enum"""
INPUT = "input"
SELECTION = "selection"
@dataclass
class MetadataCommand:
"""Metadata command"""
command: str
description: str
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"Command": self.command,
"Description": self.description,
}
)
@classmethod
def from_json(cls, json_str: str) -> "MetadataCommand":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
return cls(
command=data.get("Command", ""),
description=data.get("Description", ""),
)
@dataclass
class Selection:
"""Selection model representing text or file selection"""
Type: SelectionType
Text: Optional[str] = None
FilePaths: Optional[List[str]] = None
type: SelectionType = field(default=SelectionType.TEXT)
text: str = field(default="")
file_paths: List[str] = field(default_factory=list)
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"Type": self.type,
"Text": self.text,
"FilePaths": self.file_paths,
}
)
@classmethod
def from_json(cls, json_str: str) -> "Selection":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
if data.get("Type", "") == "":
data["Type"] = SelectionType.TEXT
return cls(
type=SelectionType(data.get("Type", SelectionType.TEXT)),
text=data.get("Text", ""),
file_paths=data.get("FilePaths", []),
)
def __str__(self) -> str:
"""Convert selection to string"""
if self.type == SelectionType.TEXT and self.text:
return self.text
elif self.type == SelectionType.FILE and self.file_paths:
return ",".join(self.file_paths)
return ""
class QueryEnv(BaseModel):
@dataclass
class QueryEnv:
"""
Query environment information
"""
ActiveWindowTitle: str
active_window_title: str = field(default="")
"""Active window title when user query"""
ActiveWindowPid: int
active_window_pid: int = field(default=0)
"""Active window pid when user query, 0 if not available"""
ActiveBrowserUrl: str
active_browser_url: str = field(default="")
"""
Active browser url when user query
Only available when active window is browser and https://github.com/Wox-launcher/Wox.Chrome.Extension is installed
"""
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"ActiveWindowTitle": self.active_window_title,
"ActiveWindowPid": self.active_window_pid,
"ActiveBrowserUrl": self.active_browser_url,
}
)
class Query(BaseModel):
@classmethod
def from_json(cls, json_str: str) -> "QueryEnv":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
return cls(
active_window_title=data.get("ActiveWindowTitle", ""),
active_window_pid=data.get("ActiveWindowPid", 0),
active_browser_url=data.get("ActiveBrowserUrl", ""),
)
@dataclass
class Query:
"""
Query model representing a user query
"""
Type: QueryType
RawQuery: str
TriggerKeyword: Optional[str]
Command: Optional[str]
Search: str
Selection: Selection
Env: QueryEnv
type: QueryType
raw_query: str
selection: Selection
env: QueryEnv
trigger_keyword: str = field(default="")
command: str = field(default="")
search: str = field(default="")
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"Type": self.type,
"RawQuery": self.raw_query,
"Selection": json.loads(self.selection.to_json()),
"Env": json.loads(self.env.to_json()),
"TriggerKeyword": self.trigger_keyword,
"Command": self.command,
"Search": self.search,
}
)
@classmethod
def from_json(cls, json_str: str) -> "Query":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
if data.get("Type", "") == "":
data["Type"] = QueryType.INPUT
return cls(
type=QueryType(data.get("Type", QueryType.INPUT)),
raw_query=data.get("RawQuery", ""),
selection=Selection.from_json(data.get("Selection", Selection().to_json())),
env=QueryEnv.from_json(data.get("Env", QueryEnv().to_json())),
trigger_keyword=data.get("TriggerKeyword", ""),
command=data.get("Command", ""),
search=data.get("Search", ""),
)
def is_global_query(self) -> bool:
"""Check if this is a global query without trigger keyword"""
return self.Type == QueryType.INPUT and not self.TriggerKeyword
return self.type == QueryType.INPUT and not self.trigger_keyword
def __str__(self) -> str:
"""Convert query to string"""
if self.type == QueryType.INPUT:
return self.raw_query
elif self.type == QueryType.SELECTION:
return str(self.selection)
return ""
@dataclass
class ChangeQueryParam:
"""Change query parameter"""
query_type: QueryType
query_text: str = field(default="")
query_selection: Selection = field(default_factory=Selection)
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
data = {
"QueryType": self.query_type,
"QueryText": self.query_text,
}
if self.query_selection:
data["QuerySelection"] = json.loads(self.query_selection.to_json())
return json.dumps(data)
@classmethod
def from_json(cls, json_str: str) -> "ChangeQueryParam":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
return cls(
query_type=QueryType(data.get("QueryType", QueryType.INPUT)),
query_text=data.get("QueryText", ""),
query_selection=Selection.from_json(data.get("QuerySelection", Selection().to_json())),
)

View File

@ -1,87 +1,223 @@
from typing import Optional, List, Dict, Callable, Awaitable
from pydantic import BaseModel
from ..types import WoxImageType, WoxPreviewType, ResultTailType
from typing import List, Callable, Awaitable, Optional
from dataclasses import dataclass, field
from enum import Enum
import json
from .image import WoxImage
from .preview import WoxPreview
class WoxImage(BaseModel):
"""Image model for Wox"""
class ResultTailType(str, Enum):
"""Result tail type enum for Wox"""
ImageType: WoxImageType
ImageData: str
TEXT = "text" # string type
IMAGE = "image" # WoxImage type
class WoxPreview(BaseModel):
"""Preview model for Wox results"""
PreviewType: WoxPreviewType
PreviewData: str
PreviewProperties: Dict[str, str]
class ResultTail(BaseModel):
@dataclass
class ResultTail:
"""Tail model for Wox results"""
Type: ResultTailType
Text: Optional[str] = None
Image: Optional[WoxImage] = None
type: ResultTailType = field(default=ResultTailType.TEXT)
text: str = field(default="")
image: WoxImage = field(default_factory=WoxImage)
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"Type": self.type,
"Text": self.text,
"Image": json.loads(self.image.to_json()),
}
)
@classmethod
def from_json(cls, json_str: str) -> "ResultTail":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
image = WoxImage()
if "Image" in data:
image = WoxImage.from_json(json.dumps(data["Image"]))
return cls(
type=ResultTailType(data.get("Type", ResultTailType.TEXT)),
text=data.get("Text", ""),
image=image,
)
class ActionContext(BaseModel):
@dataclass
class ActionContext:
"""Context for result actions"""
ContextData: str
context_data: str
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"ContextData": self.context_data,
}
)
@classmethod
def from_json(cls, json_str: str) -> "ActionContext":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
return cls(
context_data=data.get("ContextData", ""),
)
class ResultAction(BaseModel):
@dataclass
class ResultAction:
"""Action model for Wox results"""
Name: str
Action: Callable[[ActionContext], Awaitable[None]]
Id: Optional[str] = None
Icon: Optional[WoxImage] = None
IsDefault: Optional[bool] = None
PreventHideAfterAction: Optional[bool] = None
Hotkey: Optional[str] = None
name: str
action: Optional[Callable[[ActionContext], Awaitable[None]]] = None
id: str = field(default="")
icon: WoxImage = field(default_factory=WoxImage)
is_default: bool = field(default=False)
prevent_hide_after_action: bool = field(default=False)
hotkey: str = field(default="")
class Config:
arbitrary_types_allowed = True
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"Name": self.name,
"Id": self.id,
"IsDefault": self.is_default,
"PreventHideAfterAction": self.prevent_hide_after_action,
"Hotkey": self.hotkey,
"Icon": json.loads(self.icon.to_json()),
}
)
@classmethod
def from_json(cls, json_str: str) -> "ResultAction":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
return cls(
name=data.get("Name", ""),
id=data.get("Id", ""),
icon=WoxImage.from_json(json.dumps(data.get("Icon", {}))),
is_default=data.get("IsDefault", False),
prevent_hide_after_action=data.get("PreventHideAfterAction", False),
hotkey=data.get("Hotkey", ""),
)
class Result(BaseModel):
@dataclass
class Result:
"""Result model for Wox"""
Title: str
Icon: WoxImage
Id: Optional[str] = None
SubTitle: Optional[str] = None
Preview: Optional[WoxPreview] = None
Score: Optional[float] = None
Group: Optional[str] = None
GroupScore: Optional[float] = None
Tails: Optional[List[ResultTail]] = None
ContextData: Optional[str] = None
Actions: Optional[List[ResultAction]] = None
RefreshInterval: Optional[int] = None
OnRefresh: Optional[
Callable[["RefreshableResult"], Awaitable["RefreshableResult"]]
] = None
title: str
icon: WoxImage
id: str = field(default="")
sub_title: str = field(default="")
preview: WoxPreview = field(default_factory=WoxPreview)
score: float = field(default=0.0)
group: str = field(default="")
group_score: float = field(default=0.0)
tails: List[ResultTail] = field(default_factory=list)
context_data: str = field(default="")
actions: List[ResultAction] = field(default_factory=list)
refresh_interval: int = field(default=0)
on_refresh: Optional[Callable[["RefreshableResult"], Awaitable["RefreshableResult"]]] = None
class Config:
arbitrary_types_allowed = True
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
data = {
"Title": self.title,
"Icon": json.loads(self.icon.to_json()),
"Id": self.id,
"SubTitle": self.sub_title,
"Score": self.score,
"Group": self.group,
"GroupScore": self.group_score,
"ContextData": self.context_data,
"RefreshInterval": self.refresh_interval,
}
if self.preview:
data["Preview"] = json.loads(self.preview.to_json())
if self.tails:
data["Tails"] = [json.loads(tail.to_json()) for tail in self.tails]
if self.actions:
data["Actions"] = [json.loads(action.to_json()) for action in self.actions]
return json.dumps(data)
@classmethod
def from_json(cls, json_str: str) -> "Result":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
preview = WoxPreview.from_json(json.dumps(data["Preview"]))
tails = []
if "Tails" in data:
tails = [ResultTail.from_json(json.dumps(tail)) for tail in data["Tails"]]
actions = []
if "Actions" in data:
actions = [ResultAction.from_json(json.dumps(action)) for action in data["Actions"]]
return cls(
title=data.get("Title", ""),
icon=WoxImage.from_json(json.dumps(data.get("Icon", {}))),
id=data.get("Id", ""),
sub_title=data.get("SubTitle", ""),
preview=preview,
score=data.get("Score", 0.0),
group=data.get("Group", ""),
group_score=data.get("GroupScore", 0.0),
tails=tails,
context_data=data.get("ContextData", ""),
actions=actions,
refresh_interval=data.get("RefreshInterval", 0),
)
class RefreshableResult(BaseModel):
@dataclass
class RefreshableResult:
"""Result that can be refreshed periodically"""
Title: str
SubTitle: str
Icon: WoxImage
Preview: WoxPreview
Tails: List[ResultTail]
ContextData: str
RefreshInterval: int
Actions: List[ResultAction]
title: str
sub_title: str
icon: WoxImage
preview: WoxPreview
tails: List[ResultTail] = field(default_factory=list)
context_data: str = field(default="")
refresh_interval: int = field(default=0)
actions: List[ResultAction] = field(default_factory=list)
def to_json(self) -> str:
"""Convert to JSON string with camelCase naming"""
return json.dumps(
{
"Title": self.title,
"SubTitle": self.sub_title,
"Icon": json.loads(self.icon.to_json()),
"Preview": json.loads(self.preview.to_json()),
"Tails": [json.loads(tail.to_json()) for tail in self.tails],
"ContextData": self.context_data,
"RefreshInterval": self.refresh_interval,
"Actions": [json.loads(action.to_json()) for action in self.actions],
}
)
@classmethod
def from_json(cls, json_str: str) -> "RefreshableResult":
"""Create from JSON string with camelCase naming"""
data = json.loads(json_str)
return cls(
title=data["Title"],
sub_title=data["SubTitle"],
icon=WoxImage.from_json(json.dumps(data.get("Icon", {}))),
preview=WoxPreview.from_json(json.dumps(data.get("Preview", {}))),
tails=[ResultTail.from_json(json.dumps(tail)) for tail in data.get("Tails", [])],
context_data=data.get("ContextData", ""),
refresh_interval=data.get("RefreshInterval", 0),
actions=[ResultAction.from_json(json.dumps(action)) for action in data["Actions"]],
)
def __await__(self):
# Make RefreshableResult awaitable by returning itself

View File

@ -1,48 +0,0 @@
from typing import List, Callable
from pydantic import BaseModel
from ..types import Platform, PluginSettingDefinitionType
from .context import Context
class PluginSettingValueStyle(BaseModel):
"""Style configuration for plugin settings"""
PaddingLeft: int
PaddingTop: int
PaddingRight: int
PaddingBottom: int
Width: int
LabelWidth: int
class PluginSettingDefinitionValue(BaseModel):
"""Base class for plugin setting definition values"""
def get_key(self) -> str:
"""Get the key of the setting"""
raise NotImplementedError
def get_default_value(self) -> str:
"""Get the default value of the setting"""
raise NotImplementedError
def translate(self, translator: Callable[[Context, str], str]) -> None:
"""Translate the setting using the provided translator"""
raise NotImplementedError
class PluginSettingDefinitionItem(BaseModel):
"""Plugin setting definition item"""
Type: PluginSettingDefinitionType
Value: PluginSettingDefinitionValue
DisabledInPlatforms: List[Platform]
IsPlatformSpecific: bool
class MetadataCommand(BaseModel):
"""Metadata for plugin commands"""
Command: str
Description: str

View File

@ -1,4 +1,5 @@
from typing import Protocol, List
from dataclasses import dataclass
from .models.context import Context
from .models.query import Query
@ -6,11 +7,12 @@ from .models.result import Result
from .api import PublicAPI
@dataclass
class PluginInitParams:
"""Parameters for plugin initialization"""
API: PublicAPI
PluginDirectory: str
api: PublicAPI
plugin_directory: str
class Plugin(Protocol):
@ -23,20 +25,3 @@ class Plugin(Protocol):
async def query(self, ctx: Context, query: Query) -> List[Result]:
"""Handle user query"""
...
class BasePlugin:
"""Base implementation of Plugin with common functionality"""
def __init__(self):
self.api: PublicAPI = None
self.plugin_dir: str = None
async def init(self, ctx: Context, init_params: PluginInitParams) -> None:
"""Initialize the plugin with API and plugin directory"""
self.api = init_params.API
self.plugin_dir = init_params.PluginDirectory
async def query(self, ctx: Context, query: Query) -> List[Result]:
"""Handle user query - must be implemented by subclasses"""
raise NotImplementedError("Subclasses must implement query method")

View File

@ -1,70 +0,0 @@
from enum import StrEnum
from typing import Dict, Callable
# Basic types
MapString = Dict[str, str]
class Platform(StrEnum):
WINDOWS = "windows"
DARWIN = "darwin"
LINUX = "linux"
class SelectionType(StrEnum):
TEXT = "text"
FILE = "file"
class QueryType(StrEnum):
INPUT = "input"
SELECTION = "selection"
class WoxImageType(StrEnum):
ABSOLUTE = "absolute"
RELATIVE = "relative"
BASE64 = "base64"
SVG = "svg"
URL = "url"
EMOJI = "emoji"
LOTTIE = "lottie"
class WoxPreviewType(StrEnum):
MARKDOWN = "markdown"
TEXT = "text"
IMAGE = "image"
URL = "url"
FILE = "file"
class ResultTailType(StrEnum):
TEXT = "text"
IMAGE = "image"
class ConversationRole(StrEnum):
USER = "user"
SYSTEM = "system"
class ChatStreamDataType(StrEnum):
STREAMING = "streaming"
FINISHED = "finished"
ERROR = "error"
class PluginSettingDefinitionType(StrEnum):
HEAD = "head"
TEXTBOX = "textbox"
CHECKBOX = "checkbox"
SELECT = "select"
LABEL = "label"
NEWLINE = "newline"
TABLE = "table"
DYNAMIC = "dynamic"
# Type aliases
ChatStreamFunc = Callable[[ChatStreamDataType, str], None]

View File

@ -1,7 +0,0 @@
from ..models.result import WoxImage
from ..types import WoxImageType
def new_base64_wox_image(image_data: str) -> WoxImage:
"""Create a new WoxImage with BASE64 type"""
return WoxImage(ImageType=WoxImageType.BASE64, ImageData=image_data)

View File

@ -1,17 +1,5 @@
version = 1
requires-python = ">=3.8"
[[package]]
name = "annotated-types"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.9'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
]
requires-python = ">=3.10"
[[package]]
name = "backports-tarfile"
@ -22,44 +10,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181 },
]
[[package]]
name = "black"
version = "24.8.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "mypy-extensions" },
{ name = "packaging" },
{ name = "pathspec" },
{ name = "platformdirs" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/04/b0/46fb0d4e00372f4a86a6f8efa3cb193c9f64863615e39010b1477e010578/black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f", size = 644810 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/47/6e/74e29edf1fba3887ed7066930a87f698ffdcd52c5dbc263eabb06061672d/black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6", size = 1632092 },
{ url = "https://files.pythonhosted.org/packages/ab/49/575cb6c3faee690b05c9d11ee2e8dba8fbd6d6c134496e644c1feb1b47da/black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb", size = 1457529 },
{ url = "https://files.pythonhosted.org/packages/7a/b4/d34099e95c437b53d01c4aa37cf93944b233066eb034ccf7897fa4e5f286/black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42", size = 1757443 },
{ url = "https://files.pythonhosted.org/packages/87/a0/6d2e4175ef364b8c4b64f8441ba041ed65c63ea1db2720d61494ac711c15/black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a", size = 1418012 },
{ url = "https://files.pythonhosted.org/packages/08/a6/0a3aa89de9c283556146dc6dbda20cd63a9c94160a6fbdebaf0918e4a3e1/black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1", size = 1615080 },
{ url = "https://files.pythonhosted.org/packages/db/94/b803d810e14588bb297e565821a947c108390a079e21dbdcb9ab6956cd7a/black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af", size = 1438143 },
{ url = "https://files.pythonhosted.org/packages/a5/b5/f485e1bbe31f768e2e5210f52ea3f432256201289fd1a3c0afda693776b0/black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4", size = 1738774 },
{ url = "https://files.pythonhosted.org/packages/a8/69/a000fc3736f89d1bdc7f4a879f8aaf516fb03613bb51a0154070383d95d9/black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af", size = 1427503 },
{ url = "https://files.pythonhosted.org/packages/a2/a8/05fb14195cfef32b7c8d4585a44b7499c2a4b205e1662c427b941ed87054/black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368", size = 1646132 },
{ url = "https://files.pythonhosted.org/packages/41/77/8d9ce42673e5cb9988f6df73c1c5c1d4e9e788053cccd7f5fb14ef100982/black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed", size = 1448665 },
{ url = "https://files.pythonhosted.org/packages/cc/94/eff1ddad2ce1d3cc26c162b3693043c6b6b575f538f602f26fe846dfdc75/black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018", size = 1762458 },
{ url = "https://files.pythonhosted.org/packages/28/ea/18b8d86a9ca19a6942e4e16759b2fa5fc02bbc0eb33c1b866fcd387640ab/black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2", size = 1436109 },
{ url = "https://files.pythonhosted.org/packages/9f/d4/ae03761ddecc1a37d7e743b89cccbcf3317479ff4b88cfd8818079f890d0/black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd", size = 1617322 },
{ url = "https://files.pythonhosted.org/packages/14/4b/4dfe67eed7f9b1ddca2ec8e4418ea74f0d1dc84d36ea874d618ffa1af7d4/black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2", size = 1442108 },
{ url = "https://files.pythonhosted.org/packages/97/14/95b3f91f857034686cae0e73006b8391d76a8142d339b42970eaaf0416ea/black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e", size = 1745786 },
{ url = "https://files.pythonhosted.org/packages/95/54/68b8883c8aa258a6dde958cd5bdfada8382bec47c5162f4a01e66d839af1/black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920", size = 1426754 },
{ url = "https://files.pythonhosted.org/packages/13/b2/b3f24fdbb46f0e7ef6238e131f13572ee8279b70f237f221dd168a9dba1a/black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c", size = 1631706 },
{ url = "https://files.pythonhosted.org/packages/d9/35/31010981e4a05202a84a3116423970fd1a59d2eda4ac0b3570fbb7029ddc/black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e", size = 1457429 },
{ url = "https://files.pythonhosted.org/packages/27/25/3f706b4f044dd569a20a4835c3b733dedea38d83d2ee0beb8178a6d44945/black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47", size = 1756488 },
{ url = "https://files.pythonhosted.org/packages/63/72/79375cd8277cbf1c5670914e6bd4c1b15dea2c8f8e906dc21c448d0535f0/black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb", size = 1417721 },
{ url = "https://files.pythonhosted.org/packages/27/1e/83fa8a787180e1632c3d831f7e58994d7aaf23a0961320d21e84f922f919/black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed", size = 206504 },
]
[[package]]
name = "certifi"
version = "2024.12.14"
@ -108,19 +58,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 },
{ url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 },
{ url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 },
{ url = "https://files.pythonhosted.org/packages/c2/5b/f1523dd545f92f7df468e5f653ffa4df30ac222f3c884e51e139878f1cb5/cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964", size = 425932 },
{ url = "https://files.pythonhosted.org/packages/53/93/7e547ab4105969cc8c93b38a667b82a835dd2cc78f3a7dad6130cfd41e1d/cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9", size = 448585 },
{ url = "https://files.pythonhosted.org/packages/56/c4/a308f2c332006206bb511de219efeff090e9d63529ba0a77aae72e82248b/cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc", size = 456268 },
{ url = "https://files.pythonhosted.org/packages/ca/5b/b63681518265f2f4060d2b60755c1c77ec89e5e045fc3773b72735ddaad5/cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c", size = 436592 },
{ url = "https://files.pythonhosted.org/packages/bb/19/b51af9f4a4faa4a8ac5a0e5d5c2522dcd9703d07fac69da34a36c4d960d3/cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1", size = 446512 },
{ url = "https://files.pythonhosted.org/packages/ed/65/25a8dc32c53bf5b7b6c2686b42ae2ad58743f7ff644844af7cdb29b49361/cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", size = 424910 },
{ url = "https://files.pythonhosted.org/packages/42/7a/9d086fab7c66bd7c4d0f27c57a1b6b068ced810afc498cc8c49e0088661c/cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", size = 447200 },
{ url = "https://files.pythonhosted.org/packages/da/63/1785ced118ce92a993b0ec9e0d0ac8dc3e5dbfbcaa81135be56c69cabbb6/cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", size = 454565 },
{ url = "https://files.pythonhosted.org/packages/74/06/90b8a44abf3556599cdec107f7290277ae8901a58f75e6fe8f970cd72418/cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", size = 435635 },
{ url = "https://files.pythonhosted.org/packages/bd/62/a1f468e5708a70b1d86ead5bab5520861d9c7eacce4a885ded9faa7729c3/cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", size = 445218 },
{ url = "https://files.pythonhosted.org/packages/5b/95/b34462f3ccb09c2594aa782d90a90b045de4ff1f70148ee79c69d37a0a5a/cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", size = 460486 },
{ url = "https://files.pythonhosted.org/packages/fc/fc/a1e4bebd8d680febd29cf6c8a40067182b64f00c7d105f8f26b5bc54317b/cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", size = 437911 },
{ url = "https://files.pythonhosted.org/packages/e6/c3/21cab7a6154b6a5ea330ae80de386e7665254835b9e98ecc1340b3a7de9a/cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", size = 460632 },
]
[[package]]
@ -189,60 +126,9 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 },
{ url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 },
{ url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 },
{ url = "https://files.pythonhosted.org/packages/86/f4/ccab93e631e7293cca82f9f7ba39783c967f823a0000df2d8dd743cad74f/charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", size = 193961 },
{ url = "https://files.pythonhosted.org/packages/94/d4/2b21cb277bac9605026d2d91a4a8872bc82199ed11072d035dc674c27223/charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", size = 124507 },
{ url = "https://files.pythonhosted.org/packages/9a/e0/a7c1fcdff20d9c667342e0391cfeb33ab01468d7d276b2c7914b371667cc/charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", size = 119298 },
{ url = "https://files.pythonhosted.org/packages/70/de/1538bb2f84ac9940f7fa39945a5dd1d22b295a89c98240b262fc4b9fcfe0/charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", size = 139328 },
{ url = "https://files.pythonhosted.org/packages/e9/ca/288bb1a6bc2b74fb3990bdc515012b47c4bc5925c8304fc915d03f94b027/charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", size = 149368 },
{ url = "https://files.pythonhosted.org/packages/aa/75/58374fdaaf8406f373e508dab3486a31091f760f99f832d3951ee93313e8/charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", size = 141944 },
{ url = "https://files.pythonhosted.org/packages/32/c8/0bc558f7260db6ffca991ed7166494a7da4fda5983ee0b0bfc8ed2ac6ff9/charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", size = 143326 },
{ url = "https://files.pythonhosted.org/packages/0e/dd/7f6fec09a1686446cee713f38cf7d5e0669e0bcc8288c8e2924e998cf87d/charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", size = 146171 },
{ url = "https://files.pythonhosted.org/packages/4c/a8/440f1926d6d8740c34d3ca388fbd718191ec97d3d457a0677eb3aa718fce/charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", size = 139711 },
{ url = "https://files.pythonhosted.org/packages/e9/7f/4b71e350a3377ddd70b980bea1e2cc0983faf45ba43032b24b2578c14314/charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", size = 148348 },
{ url = "https://files.pythonhosted.org/packages/1e/70/17b1b9202531a33ed7ef41885f0d2575ae42a1e330c67fddda5d99ad1208/charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", size = 151290 },
{ url = "https://files.pythonhosted.org/packages/44/30/574b5b5933d77ecb015550aafe1c7d14a8cd41e7e6c4dcea5ae9e8d496c3/charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", size = 149114 },
{ url = "https://files.pythonhosted.org/packages/0b/11/ca7786f7e13708687443082af20d8341c02e01024275a28bc75032c5ce5d/charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", size = 143856 },
{ url = "https://files.pythonhosted.org/packages/f9/c2/1727c1438256c71ed32753b23ec2e6fe7b6dff66a598f6566cfe8139305e/charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", size = 94333 },
{ url = "https://files.pythonhosted.org/packages/09/c8/0e17270496a05839f8b500c1166e3261d1226e39b698a735805ec206967b/charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", size = 101454 },
{ url = "https://files.pythonhosted.org/packages/54/2f/28659eee7f5d003e0f5a3b572765bf76d6e0fe6601ab1f1b1dd4cba7e4f1/charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", size = 196326 },
{ url = "https://files.pythonhosted.org/packages/d1/18/92869d5c0057baa973a3ee2af71573be7b084b3c3d428fe6463ce71167f8/charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", size = 125614 },
{ url = "https://files.pythonhosted.org/packages/d6/27/327904c5a54a7796bb9f36810ec4173d2df5d88b401d2b95ef53111d214e/charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", size = 120450 },
{ url = "https://files.pythonhosted.org/packages/a4/23/65af317914a0308495133b2d654cf67b11bbd6ca16637c4e8a38f80a5a69/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", size = 140135 },
{ url = "https://files.pythonhosted.org/packages/f2/41/6190102ad521a8aa888519bb014a74251ac4586cde9b38e790901684f9ab/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", size = 150413 },
{ url = "https://files.pythonhosted.org/packages/7b/ab/f47b0159a69eab9bd915591106859f49670c75f9a19082505ff16f50efc0/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", size = 142992 },
{ url = "https://files.pythonhosted.org/packages/28/89/60f51ad71f63aaaa7e51a2a2ad37919985a341a1d267070f212cdf6c2d22/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", size = 144871 },
{ url = "https://files.pythonhosted.org/packages/0c/48/0050550275fea585a6e24460b42465020b53375017d8596c96be57bfabca/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", size = 146756 },
{ url = "https://files.pythonhosted.org/packages/dc/b5/47f8ee91455946f745e6c9ddbb0f8f50314d2416dd922b213e7d5551ad09/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", size = 141034 },
{ url = "https://files.pythonhosted.org/packages/84/79/5c731059ebab43e80bf61fa51666b9b18167974b82004f18c76378ed31a3/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", size = 149434 },
{ url = "https://files.pythonhosted.org/packages/ca/f3/0719cd09fc4dc42066f239cb3c48ced17fc3316afca3e2a30a4756fe49ab/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", size = 152443 },
{ url = "https://files.pythonhosted.org/packages/f7/0e/c6357297f1157c8e8227ff337e93fd0a90e498e3d6ab96b2782204ecae48/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", size = 150294 },
{ url = "https://files.pythonhosted.org/packages/54/9a/acfa96dc4ea8c928040b15822b59d0863d6e1757fba8bd7de3dc4f761c13/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", size = 145314 },
{ url = "https://files.pythonhosted.org/packages/73/1c/b10a63032eaebb8d7bcb8544f12f063f41f5f463778ac61da15d9985e8b6/charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", size = 94724 },
{ url = "https://files.pythonhosted.org/packages/c5/77/3a78bf28bfaa0863f9cfef278dbeadf55efe064eafff8c7c424ae3c4c1bf/charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", size = 102159 },
{ url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 },
]
[[package]]
name = "click"
version = "8.1.7"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "platform_system == 'Windows'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
]
[[package]]
name = "cryptography"
version = "44.0.0"
@ -318,18 +204,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514 },
]
[[package]]
name = "importlib-resources"
version = "6.4.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "zipp", marker = "python_full_version < '3.10'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/98/be/f3e8c6081b684f176b761e6a2fef02a0be939740ed6f54109a2951d806f3/importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065", size = 43372 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e1/6a/4604f9ae2fa62ef47b9de2fa5ad599589d28c9fd1d335f32759813dfa91e/importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717", size = 36115 },
]
[[package]]
name = "jaraco-classes"
version = "3.4.0"
@ -381,7 +255,6 @@ version = "25.5.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "importlib-metadata", marker = "python_full_version < '3.12'" },
{ name = "importlib-resources", marker = "python_full_version < '3.9'" },
{ name = "jaraco-classes" },
{ name = "jaraco-context" },
{ name = "jaraco-functools" },
@ -424,6 +297,40 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/48/7e/3a64597054a70f7c86eb0a7d4fc315b8c1ab932f64883a297bdffeb5f967/more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef", size = 60952 },
]
[[package]]
name = "mypy"
version = "1.14.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mypy-extensions" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8c/7b/08046ef9330735f536a09a2e31b00f42bccdb2795dcd979636ba43bb2d63/mypy-1.14.0.tar.gz", hash = "sha256:822dbd184d4a9804df5a7d5335a68cf7662930e70b8c1bc976645d1509f9a9d6", size = 3215684 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/97/f00ded038482230e0beaaa08f9c5483a54530b362ad1b0d752d5d2b2f211/mypy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e971c1c667007f9f2b397ffa80fa8e1e0adccff336e5e77e74cb5f22868bee87", size = 11207956 },
{ url = "https://files.pythonhosted.org/packages/68/67/8b4db0da19c9e3fa6264e948f1c135ab4dd45bede1809f4fdb613dc119f6/mypy-1.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e86aaeaa3221a278c66d3d673b297232947d873773d61ca3ee0e28b2ff027179", size = 10363681 },
{ url = "https://files.pythonhosted.org/packages/f5/00/56b1619ff1f3fcad2d411eccda60d74d20e73bda39c218d5ad2769980682/mypy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1628c5c3ce823d296e41e2984ff88c5861499041cb416a8809615d0c1f41740e", size = 12832976 },
{ url = "https://files.pythonhosted.org/packages/e7/8b/9247838774b0bd865f190cc221822212091317f16310305ef924d9772532/mypy-1.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fadb29b77fc14a0dd81304ed73c828c3e5cde0016c7e668a86a3e0dfc9f3af3", size = 13013704 },
{ url = "https://files.pythonhosted.org/packages/b2/69/0c0868a6f3d9761d2f704d1fb6ef84d75998c27d342738a8b20f109a411f/mypy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:3fa76988dc760da377c1e5069200a50d9eaaccf34f4ea18428a3337034ab5a44", size = 9782230 },
{ url = "https://files.pythonhosted.org/packages/34/c1/b9dd3e955953aec1c728992545b7877c9f6fa742a623ce4c200da0f62540/mypy-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6e73c8a154eed31db3445fe28f63ad2d97b674b911c00191416cf7f6459fd49a", size = 11121032 },
{ url = "https://files.pythonhosted.org/packages/ee/96/c52d5d516819ab95bf41f4a1ada828a3decc302f8c152ff4fc5feb0e4529/mypy-1.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:273e70fcb2e38c5405a188425aa60b984ffdcef65d6c746ea5813024b68c73dc", size = 10286294 },
{ url = "https://files.pythonhosted.org/packages/69/2c/3dbe51877a24daa467f8d8631f9ffd1aabbf0f6d9367a01c44a59df81fe0/mypy-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1daca283d732943731a6a9f20fdbcaa927f160bc51602b1d4ef880a6fb252015", size = 12746528 },
{ url = "https://files.pythonhosted.org/packages/a1/a8/eb20cde4ba9c4c3e20d958918a7c5d92210f4d1a0200c27de9a641f70996/mypy-1.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7e68047bedb04c1c25bba9901ea46ff60d5eaac2d71b1f2161f33107e2b368eb", size = 12883489 },
{ url = "https://files.pythonhosted.org/packages/91/17/a1fc6c70f31d52c99299320cf81c3cb2c6b91ec7269414e0718a6d138e34/mypy-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:7a52f26b9c9b1664a60d87675f3bae00b5c7f2806e0c2800545a32c325920bcc", size = 9780113 },
{ url = "https://files.pythonhosted.org/packages/fe/d8/0e72175ee0253217f5c44524f5e95251c02e95ba9749fb87b0e2074d203a/mypy-1.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d5326ab70a6db8e856d59ad4cb72741124950cbbf32e7b70e30166ba7bbf61dd", size = 11269011 },
{ url = "https://files.pythonhosted.org/packages/e9/6d/4ea13839dabe5db588dc6a1b766da16f420d33cf118a7b7172cdf6c7fcb2/mypy-1.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bf4ec4980bec1e0e24e5075f449d014011527ae0055884c7e3abc6a99cd2c7f1", size = 10253076 },
{ url = "https://files.pythonhosted.org/packages/3e/38/7db2c5d0f4d290e998f7a52b2e2616c7bbad96b8e04278ab09d11978a29e/mypy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:390dfb898239c25289495500f12fa73aa7f24a4c6d90ccdc165762462b998d63", size = 12862786 },
{ url = "https://files.pythonhosted.org/packages/bf/4b/62d59c801b34141040989949c2b5c157d0408b45357335d3ec5b2845b0f6/mypy-1.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7e026d55ddcd76e29e87865c08cbe2d0104e2b3153a523c529de584759379d3d", size = 12971568 },
{ url = "https://files.pythonhosted.org/packages/f1/9c/e0f281b32d70c87b9e4d2939e302b1ff77ada4d7b0f2fb32890c144bc1d6/mypy-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:585ed36031d0b3ee362e5107ef449a8b5dfd4e9c90ccbe36414ee405ee6b32ba", size = 9879477 },
{ url = "https://files.pythonhosted.org/packages/13/33/8380efd0ebdfdfac7fc0bf065f03a049800ca1e6c296ec1afc634340d992/mypy-1.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9f6f4c0b27401d14c483c622bc5105eff3911634d576bbdf6695b9a7c1ba741", size = 11251509 },
{ url = "https://files.pythonhosted.org/packages/15/6d/4e1c21c60fee11af7d8e4f2902a29886d1387d6a836be16229eb3982a963/mypy-1.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b2280cedcb312c7a79f5001ae5325582d0d339bce684e4a529069d0e7ca1e7", size = 10244282 },
{ url = "https://files.pythonhosted.org/packages/8b/cf/7a8ae5c0161edae15d25c2c67c68ce8b150cbdc45aefc13a8be271ee80b2/mypy-1.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:342de51c48bab326bfc77ce056ba08c076d82ce4f5a86621f972ed39970f94d8", size = 12867676 },
{ url = "https://files.pythonhosted.org/packages/9c/d0/71f7bbdcc7cfd0f2892db5b13b1e8857673f2cc9e0c30e3e4340523dc186/mypy-1.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:00df23b42e533e02a6f0055e54de9a6ed491cd8b7ea738647364fd3a39ea7efc", size = 12964189 },
{ url = "https://files.pythonhosted.org/packages/a7/40/fb4ad65d6d5f8c51396ecf6305ec0269b66013a5bf02d0e9528053640b4a/mypy-1.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:e8c8387e5d9dff80e7daf961df357c80e694e942d9755f3ad77d69b0957b8e3f", size = 9888247 },
{ url = "https://files.pythonhosted.org/packages/39/32/0214608af400cdf8f5102144bb8af10d880675c65ed0b58f7e0e77175d50/mypy-1.14.0-py3-none-any.whl", hash = "sha256:2238d7f93fc4027ed1efc944507683df3ba406445a2b6c96e79666a045aadfab", size = 2752803 },
]
[[package]]
name = "mypy-extensions"
version = "1.0.0"
@ -491,15 +398,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/21/11/4af184fbd8ae13daa13953212b27a212f4e63772ca8a0dd84d08b60ed206/pkginfo-1.12.0-py3-none-any.whl", hash = "sha256:dcd589c9be4da8973eceffa247733c144812759aa67eaf4bbf97016a02f39088", size = 32322 },
]
[[package]]
name = "platformdirs"
version = "4.3.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
]
[[package]]
name = "pluggy"
version = "1.5.0"
@ -518,130 +416,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 },
]
[[package]]
name = "pydantic"
version = "2.10.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/45/0f/27908242621b14e649a84e62b133de45f84c255eecb350ab02979844a788/pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9", size = 786486 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/62/51/72c18c55cf2f46ff4f91ebcc8f75aa30f7305f3d726be3f4ebffb4ae972b/pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d", size = 456997 },
]
[[package]]
name = "pydantic-core"
version = "2.27.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a6/9f/7de1f19b6aea45aeb441838782d68352e71bfa98ee6fa048d5041991b33e/pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235", size = 412785 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6e/ce/60fd96895c09738648c83f3f00f595c807cb6735c70d3306b548cc96dd49/pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a", size = 1897984 },
{ url = "https://files.pythonhosted.org/packages/fd/b9/84623d6b6be98cc209b06687d9bca5a7b966ffed008d15225dd0d20cce2e/pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b", size = 1807491 },
{ url = "https://files.pythonhosted.org/packages/01/72/59a70165eabbc93b1111d42df9ca016a4aa109409db04304829377947028/pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278", size = 1831953 },
{ url = "https://files.pythonhosted.org/packages/7c/0c/24841136476adafd26f94b45bb718a78cb0500bd7b4f8d667b67c29d7b0d/pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05", size = 1856071 },
{ url = "https://files.pythonhosted.org/packages/53/5e/c32957a09cceb2af10d7642df45d1e3dbd8596061f700eac93b801de53c0/pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4", size = 2038439 },
{ url = "https://files.pythonhosted.org/packages/e4/8f/979ab3eccd118b638cd6d8f980fea8794f45018255a36044dea40fe579d4/pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f", size = 2787416 },
{ url = "https://files.pythonhosted.org/packages/02/1d/00f2e4626565b3b6d3690dab4d4fe1a26edd6a20e53749eb21ca892ef2df/pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08", size = 2134548 },
{ url = "https://files.pythonhosted.org/packages/9d/46/3112621204128b90898adc2e721a3cd6cf5626504178d6f32c33b5a43b79/pydantic_core-2.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6", size = 1989882 },
{ url = "https://files.pythonhosted.org/packages/49/ec/557dd4ff5287ffffdf16a31d08d723de6762bb1b691879dc4423392309bc/pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807", size = 1995829 },
{ url = "https://files.pythonhosted.org/packages/6e/b2/610dbeb74d8d43921a7234555e4c091cb050a2bdb8cfea86d07791ce01c5/pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c", size = 2091257 },
{ url = "https://files.pythonhosted.org/packages/8c/7f/4bf8e9d26a9118521c80b229291fa9558a07cdd9a968ec2d5c1026f14fbc/pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206", size = 2143894 },
{ url = "https://files.pythonhosted.org/packages/1f/1c/875ac7139c958f4390f23656fe696d1acc8edf45fb81e4831960f12cd6e4/pydantic_core-2.27.1-cp310-none-win32.whl", hash = "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c", size = 1816081 },
{ url = "https://files.pythonhosted.org/packages/d7/41/55a117acaeda25ceae51030b518032934f251b1dac3704a53781383e3491/pydantic_core-2.27.1-cp310-none-win_amd64.whl", hash = "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17", size = 1981109 },
{ url = "https://files.pythonhosted.org/packages/27/39/46fe47f2ad4746b478ba89c561cafe4428e02b3573df882334bd2964f9cb/pydantic_core-2.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8", size = 1895553 },
{ url = "https://files.pythonhosted.org/packages/1c/00/0804e84a78b7fdb394fff4c4f429815a10e5e0993e6ae0e0b27dd20379ee/pydantic_core-2.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330", size = 1807220 },
{ url = "https://files.pythonhosted.org/packages/01/de/df51b3bac9820d38371f5a261020f505025df732ce566c2a2e7970b84c8c/pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52", size = 1829727 },
{ url = "https://files.pythonhosted.org/packages/5f/d9/c01d19da8f9e9fbdb2bf99f8358d145a312590374d0dc9dd8dbe484a9cde/pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4", size = 1854282 },
{ url = "https://files.pythonhosted.org/packages/5f/84/7db66eb12a0dc88c006abd6f3cbbf4232d26adfd827a28638c540d8f871d/pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c", size = 2037437 },
{ url = "https://files.pythonhosted.org/packages/34/ac/a2537958db8299fbabed81167d58cc1506049dba4163433524e06a7d9f4c/pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de", size = 2780899 },
{ url = "https://files.pythonhosted.org/packages/4a/c1/3e38cd777ef832c4fdce11d204592e135ddeedb6c6f525478a53d1c7d3e5/pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025", size = 2135022 },
{ url = "https://files.pythonhosted.org/packages/7a/69/b9952829f80fd555fe04340539d90e000a146f2a003d3fcd1e7077c06c71/pydantic_core-2.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e", size = 1987969 },
{ url = "https://files.pythonhosted.org/packages/05/72/257b5824d7988af43460c4e22b63932ed651fe98804cc2793068de7ec554/pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919", size = 1994625 },
{ url = "https://files.pythonhosted.org/packages/73/c3/78ed6b7f3278a36589bcdd01243189ade7fc9b26852844938b4d7693895b/pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c", size = 2090089 },
{ url = "https://files.pythonhosted.org/packages/8d/c8/b4139b2f78579960353c4cd987e035108c93a78371bb19ba0dc1ac3b3220/pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc", size = 2142496 },
{ url = "https://files.pythonhosted.org/packages/3e/f8/171a03e97eb36c0b51981efe0f78460554a1d8311773d3d30e20c005164e/pydantic_core-2.27.1-cp311-none-win32.whl", hash = "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9", size = 1811758 },
{ url = "https://files.pythonhosted.org/packages/6a/fe/4e0e63c418c1c76e33974a05266e5633e879d4061f9533b1706a86f77d5b/pydantic_core-2.27.1-cp311-none-win_amd64.whl", hash = "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5", size = 1980864 },
{ url = "https://files.pythonhosted.org/packages/50/fc/93f7238a514c155a8ec02fc7ac6376177d449848115e4519b853820436c5/pydantic_core-2.27.1-cp311-none-win_arm64.whl", hash = "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89", size = 1864327 },
{ url = "https://files.pythonhosted.org/packages/be/51/2e9b3788feb2aebff2aa9dfbf060ec739b38c05c46847601134cc1fed2ea/pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f", size = 1895239 },
{ url = "https://files.pythonhosted.org/packages/7b/9e/f8063952e4a7d0127f5d1181addef9377505dcce3be224263b25c4f0bfd9/pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02", size = 1805070 },
{ url = "https://files.pythonhosted.org/packages/2c/9d/e1d6c4561d262b52e41b17a7ef8301e2ba80b61e32e94520271029feb5d8/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c", size = 1828096 },
{ url = "https://files.pythonhosted.org/packages/be/65/80ff46de4266560baa4332ae3181fffc4488ea7d37282da1a62d10ab89a4/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac", size = 1857708 },
{ url = "https://files.pythonhosted.org/packages/d5/ca/3370074ad758b04d9562b12ecdb088597f4d9d13893a48a583fb47682cdf/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb", size = 2037751 },
{ url = "https://files.pythonhosted.org/packages/b1/e2/4ab72d93367194317b99d051947c071aef6e3eb95f7553eaa4208ecf9ba4/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529", size = 2733863 },
{ url = "https://files.pythonhosted.org/packages/8a/c6/8ae0831bf77f356bb73127ce5a95fe115b10f820ea480abbd72d3cc7ccf3/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35", size = 2161161 },
{ url = "https://files.pythonhosted.org/packages/f1/f4/b2fe73241da2429400fc27ddeaa43e35562f96cf5b67499b2de52b528cad/pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089", size = 1993294 },
{ url = "https://files.pythonhosted.org/packages/77/29/4bb008823a7f4cc05828198153f9753b3bd4c104d93b8e0b1bfe4e187540/pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381", size = 2001468 },
{ url = "https://files.pythonhosted.org/packages/f2/a9/0eaceeba41b9fad851a4107e0cf999a34ae8f0d0d1f829e2574f3d8897b0/pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb", size = 2091413 },
{ url = "https://files.pythonhosted.org/packages/d8/36/eb8697729725bc610fd73940f0d860d791dc2ad557faaefcbb3edbd2b349/pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae", size = 2154735 },
{ url = "https://files.pythonhosted.org/packages/52/e5/4f0fbd5c5995cc70d3afed1b5c754055bb67908f55b5cb8000f7112749bf/pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c", size = 1833633 },
{ url = "https://files.pythonhosted.org/packages/ee/f2/c61486eee27cae5ac781305658779b4a6b45f9cc9d02c90cb21b940e82cc/pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16", size = 1986973 },
{ url = "https://files.pythonhosted.org/packages/df/a6/e3f12ff25f250b02f7c51be89a294689d175ac76e1096c32bf278f29ca1e/pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e", size = 1883215 },
{ url = "https://files.pythonhosted.org/packages/0f/d6/91cb99a3c59d7b072bded9959fbeab0a9613d5a4935773c0801f1764c156/pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073", size = 1895033 },
{ url = "https://files.pythonhosted.org/packages/07/42/d35033f81a28b27dedcade9e967e8a40981a765795c9ebae2045bcef05d3/pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08", size = 1807542 },
{ url = "https://files.pythonhosted.org/packages/41/c2/491b59e222ec7e72236e512108ecad532c7f4391a14e971c963f624f7569/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf", size = 1827854 },
{ url = "https://files.pythonhosted.org/packages/e3/f3/363652651779113189cefdbbb619b7b07b7a67ebb6840325117cc8cc3460/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737", size = 1857389 },
{ url = "https://files.pythonhosted.org/packages/5f/97/be804aed6b479af5a945daec7538d8bf358d668bdadde4c7888a2506bdfb/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2", size = 2037934 },
{ url = "https://files.pythonhosted.org/packages/42/01/295f0bd4abf58902917e342ddfe5f76cf66ffabfc57c2e23c7681a1a1197/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107", size = 2735176 },
{ url = "https://files.pythonhosted.org/packages/9d/a0/cd8e9c940ead89cc37812a1a9f310fef59ba2f0b22b4e417d84ab09fa970/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51", size = 2160720 },
{ url = "https://files.pythonhosted.org/packages/73/ae/9d0980e286627e0aeca4c352a60bd760331622c12d576e5ea4441ac7e15e/pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a", size = 1992972 },
{ url = "https://files.pythonhosted.org/packages/bf/ba/ae4480bc0292d54b85cfb954e9d6bd226982949f8316338677d56541b85f/pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc", size = 2001477 },
{ url = "https://files.pythonhosted.org/packages/55/b7/e26adf48c2f943092ce54ae14c3c08d0d221ad34ce80b18a50de8ed2cba8/pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960", size = 2091186 },
{ url = "https://files.pythonhosted.org/packages/ba/cc/8491fff5b608b3862eb36e7d29d36a1af1c945463ca4c5040bf46cc73f40/pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23", size = 2154429 },
{ url = "https://files.pythonhosted.org/packages/78/d8/c080592d80edd3441ab7f88f865f51dae94a157fc64283c680e9f32cf6da/pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05", size = 1833713 },
{ url = "https://files.pythonhosted.org/packages/83/84/5ab82a9ee2538ac95a66e51f6838d6aba6e0a03a42aa185ad2fe404a4e8f/pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337", size = 1987897 },
{ url = "https://files.pythonhosted.org/packages/df/c3/b15fb833926d91d982fde29c0624c9f225da743c7af801dace0d4e187e71/pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5", size = 1882983 },
{ url = "https://files.pythonhosted.org/packages/97/bb/c62074a65a32ed279bef44862e89fabb5ab1a81df8a9d383bddb4f49a1e0/pydantic_core-2.27.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62", size = 1901535 },
{ url = "https://files.pythonhosted.org/packages/9b/59/e224c93f95ffd4f5d37f1d148c569eda8ae23446ab8daf3a211ac0533e08/pydantic_core-2.27.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab", size = 1781287 },
{ url = "https://files.pythonhosted.org/packages/11/e2/33629134e577543b9335c5ca9bbfd2348f5023fda956737777a7a3b86788/pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864", size = 1834575 },
{ url = "https://files.pythonhosted.org/packages/fe/16/82e0849b3c6deb0330c07f1a8d55708d003ec8b1fd38ac84c7a830e25252/pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067", size = 1857948 },
{ url = "https://files.pythonhosted.org/packages/6b/4e/cdee588a7440bc58b6351e8b8dc2432e38b1144b5ae6625bfbdfb7fa76d9/pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd", size = 2041138 },
{ url = "https://files.pythonhosted.org/packages/1d/0e/73e0d1dff37a29c31e5b3e8587d228ced736cc7af9f81f6d7d06aa47576c/pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5", size = 2783820 },
{ url = "https://files.pythonhosted.org/packages/9a/b1/f164d05be347b99b91327ea9dd1118562951d2c86e1ea943ef73636b0810/pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78", size = 2138035 },
{ url = "https://files.pythonhosted.org/packages/72/44/cf1f20d3036d7e1545eafde0af4f3172075573a407a3a20313115c8990ff/pydantic_core-2.27.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f", size = 1991778 },
{ url = "https://files.pythonhosted.org/packages/5d/4c/486d8ddd595892e7d791f26dfd3e51bd8abea478eb7747fe2bbe890a2177/pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36", size = 1996644 },
{ url = "https://files.pythonhosted.org/packages/33/2a/9a1cd4c8aca242816be431583a3250797f2932fad32d35ad5aefcea179bc/pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a", size = 2091778 },
{ url = "https://files.pythonhosted.org/packages/8f/61/03576dac806c49e76a714c23f501420b0aeee80f97b995fc4b28fe63a010/pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b", size = 2146020 },
{ url = "https://files.pythonhosted.org/packages/72/82/e236d762052d24949aabad3952bc2c8635a470d6f3cbdd69498692afa679/pydantic_core-2.27.1-cp38-none-win32.whl", hash = "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618", size = 1819443 },
{ url = "https://files.pythonhosted.org/packages/6e/89/26816cad528ca5d4af9be33aa91507504c4576100e53b371b5bc6d3c797b/pydantic_core-2.27.1-cp38-none-win_amd64.whl", hash = "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4", size = 1979478 },
{ url = "https://files.pythonhosted.org/packages/bc/6a/d741ce0c7da75ce9b394636a406aace00ad992ae417935ef2ad2e67fb970/pydantic_core-2.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967", size = 1898376 },
{ url = "https://files.pythonhosted.org/packages/bd/68/6ba18e30f10c7051bc55f1dffeadbee51454b381c91846104892a6d3b9cd/pydantic_core-2.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60", size = 1777246 },
{ url = "https://files.pythonhosted.org/packages/36/b8/6f1b7c5f068c00dfe179b8762bc1d32c75c0e9f62c9372174b1b64a74aa8/pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854", size = 1832148 },
{ url = "https://files.pythonhosted.org/packages/d9/83/83ff64d599847f080a93df119e856e3bd93063cced04b9a27eb66d863831/pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9", size = 1856371 },
{ url = "https://files.pythonhosted.org/packages/72/e9/974e6c73f59627c446833ecc306cadd199edab40abcfa093372a5a5c0156/pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd", size = 2038686 },
{ url = "https://files.pythonhosted.org/packages/5e/bb/5e912d02dcf29aebb2da35e5a1a26088c39ffc0b1ea81242ee9db6f1f730/pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be", size = 2785725 },
{ url = "https://files.pythonhosted.org/packages/85/d7/936846087424c882d89c853711687230cd60179a67c79c34c99b64f92625/pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e", size = 2135177 },
{ url = "https://files.pythonhosted.org/packages/82/72/5a386e5ce8d3e933c3f283e61357474181c39383f38afffc15a6152fa1c5/pydantic_core-2.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792", size = 1989877 },
{ url = "https://files.pythonhosted.org/packages/ce/5c/b1c417a5fd67ce132d78d16a6ba7629dc7f188dbd4f7c30ef58111ee5147/pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01", size = 1996006 },
{ url = "https://files.pythonhosted.org/packages/dd/04/4e18f2c42b29929882f30e4c09a3a039555158995a4ac730a73585198a66/pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9", size = 2091441 },
{ url = "https://files.pythonhosted.org/packages/06/84/5a332345b7efb5ab361f916eaf7316ef010e72417e8c7dd3d34462ee9840/pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131", size = 2144471 },
{ url = "https://files.pythonhosted.org/packages/54/58/23caa58c35d36627156789c0fb562264c12cfdb451c75eb275535188a96f/pydantic_core-2.27.1-cp39-none-win32.whl", hash = "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3", size = 1816563 },
{ url = "https://files.pythonhosted.org/packages/f7/9c/e83f08adc8e222b43c7f11d98b27eba08f21bcb259bcbf74743ce903c49c/pydantic_core-2.27.1-cp39-none-win_amd64.whl", hash = "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c", size = 1983137 },
{ url = "https://files.pythonhosted.org/packages/7c/60/e5eb2d462595ba1f622edbe7b1d19531e510c05c405f0b87c80c1e89d5b1/pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6", size = 1894016 },
{ url = "https://files.pythonhosted.org/packages/61/20/da7059855225038c1c4326a840908cc7ca72c7198cb6addb8b92ec81c1d6/pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676", size = 1771648 },
{ url = "https://files.pythonhosted.org/packages/8f/fc/5485cf0b0bb38da31d1d292160a4d123b5977841ddc1122c671a30b76cfd/pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d", size = 1826929 },
{ url = "https://files.pythonhosted.org/packages/a1/ff/fb1284a210e13a5f34c639efc54d51da136074ffbe25ec0c279cf9fbb1c4/pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c", size = 1980591 },
{ url = "https://files.pythonhosted.org/packages/f1/14/77c1887a182d05af74f6aeac7b740da3a74155d3093ccc7ee10b900cc6b5/pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27", size = 1981326 },
{ url = "https://files.pythonhosted.org/packages/06/aa/6f1b2747f811a9c66b5ef39d7f02fbb200479784c75e98290d70004b1253/pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f", size = 1989205 },
{ url = "https://files.pythonhosted.org/packages/7a/d2/8ce2b074d6835f3c88d85f6d8a399790043e9fdb3d0e43455e72d19df8cc/pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed", size = 2079616 },
{ url = "https://files.pythonhosted.org/packages/65/71/af01033d4e58484c3db1e5d13e751ba5e3d6b87cc3368533df4c50932c8b/pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f", size = 2133265 },
{ url = "https://files.pythonhosted.org/packages/33/72/f881b5e18fbb67cf2fb4ab253660de3c6899dbb2dba409d0b757e3559e3d/pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c", size = 2001864 },
{ url = "https://files.pythonhosted.org/packages/85/3e/f6f75ba36678fee11dd07a7729e9ed172ecf31e3f50a5d636e9605eee2af/pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f", size = 1894250 },
{ url = "https://files.pythonhosted.org/packages/d3/2d/a40578918e2eb5b4ee0d206a4fb6c4040c2bf14e28d29fba9bd7e7659d16/pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31", size = 1772035 },
{ url = "https://files.pythonhosted.org/packages/7f/ee/0377e9f4ca5a47e8885f670a65c0a647ddf9ce98d50bf7547cf8e1ee5771/pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3", size = 1827025 },
{ url = "https://files.pythonhosted.org/packages/fe/0b/a24d9ef762d05bebdfafd6d5d176b990728fa9ec8ea7b6040d6fb5f3caaa/pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154", size = 1980927 },
{ url = "https://files.pythonhosted.org/packages/00/bd/deadc1722eb7dfdf787a3bbcd32eabbdcc36931fd48671a850e1b9f2cd77/pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd", size = 1980918 },
{ url = "https://files.pythonhosted.org/packages/f0/05/5d09d0b0e92053d538927308ea1d35cb25ab543d9c3e2eb2d7653bc73690/pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a", size = 1989990 },
{ url = "https://files.pythonhosted.org/packages/5b/7e/f7191346d1c3ac66049f618ee331359f8552a8b68a2daf916003c30b6dc8/pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97", size = 2079871 },
{ url = "https://files.pythonhosted.org/packages/f3/65/2caf4f7ad65413a137d43cb9578c54d1abd3224be786ad840263c1bf9e0f/pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2", size = 2133569 },
{ url = "https://files.pythonhosted.org/packages/fd/ab/718d9a1c41bb8d3e0e04d15b68b8afc135f8fcf552705b62f226225065c7/pydantic_core-2.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840", size = 2002035 },
]
[[package]]
name = "pygments"
version = "2.18.0"
@ -724,6 +498,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
]
[[package]]
name = "ruff"
version = "0.8.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/34/37/9c02181ef38d55b77d97c68b78e705fd14c0de0e5d085202bb2b52ce5be9/ruff-0.8.4.tar.gz", hash = "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8", size = 3402103 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/05/67/f480bf2f2723b2e49af38ed2be75ccdb2798fca7d56279b585c8f553aaab/ruff-0.8.4-py3-none-linux_armv6l.whl", hash = "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60", size = 10546415 },
{ url = "https://files.pythonhosted.org/packages/eb/7a/5aba20312c73f1ce61814e520d1920edf68ca3b9c507bd84d8546a8ecaa8/ruff-0.8.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac", size = 10346113 },
{ url = "https://files.pythonhosted.org/packages/76/f4/c41de22b3728486f0aa95383a44c42657b2db4062f3234ca36fc8cf52d8b/ruff-0.8.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296", size = 9943564 },
{ url = "https://files.pythonhosted.org/packages/0e/f0/afa0d2191af495ac82d4cbbfd7a94e3df6f62a04ca412033e073b871fc6d/ruff-0.8.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643", size = 10805522 },
{ url = "https://files.pythonhosted.org/packages/12/57/5d1e9a0fd0c228e663894e8e3a8e7063e5ee90f8e8e60cf2085f362bfa1a/ruff-0.8.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e", size = 10306763 },
{ url = "https://files.pythonhosted.org/packages/04/df/f069fdb02e408be8aac6853583572a2873f87f866fe8515de65873caf6b8/ruff-0.8.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3", size = 11359574 },
{ url = "https://files.pythonhosted.org/packages/d3/04/37c27494cd02e4a8315680debfc6dfabcb97e597c07cce0044db1f9dfbe2/ruff-0.8.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f", size = 12094851 },
{ url = "https://files.pythonhosted.org/packages/81/b1/c5d7fb68506cab9832d208d03ea4668da9a9887a4a392f4f328b1bf734ad/ruff-0.8.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604", size = 11655539 },
{ url = "https://files.pythonhosted.org/packages/ef/38/8f8f2c8898dc8a7a49bc340cf6f00226917f0f5cb489e37075bcb2ce3671/ruff-0.8.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf", size = 12912805 },
{ url = "https://files.pythonhosted.org/packages/06/dd/fa6660c279f4eb320788876d0cff4ea18d9af7d9ed7216d7bd66877468d0/ruff-0.8.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720", size = 11205976 },
{ url = "https://files.pythonhosted.org/packages/a8/d7/de94cc89833b5de455750686c17c9e10f4e1ab7ccdc5521b8fe911d1477e/ruff-0.8.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae", size = 10792039 },
{ url = "https://files.pythonhosted.org/packages/6d/15/3e4906559248bdbb74854af684314608297a05b996062c9d72e0ef7c7097/ruff-0.8.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7", size = 10400088 },
{ url = "https://files.pythonhosted.org/packages/a2/21/9ed4c0e8133cb4a87a18d470f534ad1a8a66d7bec493bcb8bda2d1a5d5be/ruff-0.8.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111", size = 10900814 },
{ url = "https://files.pythonhosted.org/packages/0d/5d/122a65a18955bd9da2616b69bc839351f8baf23b2805b543aa2f0aed72b5/ruff-0.8.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8", size = 11268828 },
{ url = "https://files.pythonhosted.org/packages/43/a9/1676ee9106995381e3d34bccac5bb28df70194167337ed4854c20f27c7ba/ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835", size = 8805621 },
{ url = "https://files.pythonhosted.org/packages/10/98/ed6b56a30ee76771c193ff7ceeaf1d2acc98d33a1a27b8479cbdb5c17a23/ruff-0.8.4-py3-none-win_amd64.whl", hash = "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d", size = 9660086 },
{ url = "https://files.pythonhosted.org/packages/13/9f/026e18ca7d7766783d779dae5e9c656746c6ede36ef73c6d934aaf4a6dec/ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08", size = 9074500 },
]
[[package]]
name = "secretstorage"
version = "3.3.3"
@ -790,7 +589,6 @@ name = "twine"
version = "6.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "importlib-metadata", marker = "python_full_version < '3.10'" },
{ name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" },
{ name = "packaging" },
{ name = "pkginfo" },
@ -826,25 +624,23 @@ wheels = [
[[package]]
name = "wox-plugin"
version = "0.0.30"
version = "0.0.45"
source = { editable = "." }
dependencies = [
{ name = "pydantic" },
]
[package.optional-dependencies]
dev = [
{ name = "black" },
{ name = "hatchling" },
{ name = "mypy" },
{ name = "ruff" },
{ name = "twine" },
]
[package.metadata]
requires-dist = [
{ name = "black", marker = "extra == 'dev'", specifier = ">=23.0.0" },
{ name = "hatchling", marker = "extra == 'dev'", specifier = ">=1.18.0" },
{ name = "pydantic", specifier = ">=2.0.0" },
{ name = "twine", marker = "extra == 'dev'", specifier = ">=4.0.0" },
{ name = "hatchling", marker = "extra == 'dev'" },
{ name = "mypy", marker = "extra == 'dev'" },
{ name = "ruff", marker = "extra == 'dev'" },
{ name = "twine", marker = "extra == 'dev'" },
]
[[package]]

View File

@ -5,9 +5,11 @@
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related

View File

@ -0,0 +1,3 @@
{
"dart.lineLength": 180
}

View File

@ -491,7 +491,9 @@ class WoxLauncherController extends GetxController {
if (activeResultIndex.value < prevResultIndex) {
resultScrollerController.jumpTo(0);
} else {
bool shouldJump = deviceType == WoxEventDeviceTypeEnum.WOX_EVENT_DEVEICE_TYPE_KEYBOARD.code ? isResultItemAtBottom(activeResultIndex.value - 1) : !isResultItemAtBottom(results.length - 1);
bool shouldJump = deviceType == WoxEventDeviceTypeEnum.WOX_EVENT_DEVEICE_TYPE_KEYBOARD.code
? isResultItemAtBottom(activeResultIndex.value - 1)
: !isResultItemAtBottom(results.length - 1);
if (shouldJump) {
resultScrollerController.jumpTo(resultScrollerController.offset.ceil() + WoxThemeUtil.instance.getResultItemHeight() * (activeResultIndex.value - prevResultIndex).abs());
}
@ -592,7 +594,8 @@ class WoxLauncherController extends GetxController {
RenderBox? renderBox = resultGlobalKeys[index].currentContext?.findRenderObject() as RenderBox?;
if (renderBox == null) return false;
if (renderBox.localToGlobal(Offset.zero).dy.ceil() >= WoxThemeUtil.instance.getQueryBoxHeight() + WoxThemeUtil.instance.getResultListViewHeightByCount(MAX_LIST_VIEW_ITEM_COUNT - 1)) {
if (renderBox.localToGlobal(Offset.zero).dy.ceil() >=
WoxThemeUtil.instance.getQueryBoxHeight() + WoxThemeUtil.instance.getResultListViewHeightByCount(MAX_LIST_VIEW_ITEM_COUNT - 1)) {
return true;
}
return false;
@ -774,7 +777,8 @@ class WoxLauncherController extends GetxController {
return;
}
Logger.instance.info(traceId, "update active actions, reason: $reason, current active result: ${activeQueryResult.title.value}, active action: ${activeQueryResult.actions.first.name.value}");
Logger.instance.info(
traceId, "update active actions, reason: $reason, current active result: ${activeQueryResult.title.value}, active action: ${activeQueryResult.actions.first.name.value}");
String? previousActionName;
if (remainIndex && actions.isNotEmpty && activeActionIndex.value >= 0 && activeActionIndex.value < actions.length) {
@ -870,6 +874,7 @@ class WoxLauncherController extends GetxController {
final resultIndex = results.indexWhere((element) => element.id == result.id);
if (isResultActiveByIndex(resultIndex)) {
currentPreview.value = result.preview;
isShowPreviewPanel.value = currentPreview.value.previewData != "";
resetActiveAction(traceId, "refresh active result", remainIndex: true);
}

View File

@ -77,10 +77,10 @@ packages:
dependency: transitive
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
url: "https://pub.dev"
source: hosted
version: "1.18.0"
version: "1.19.0"
convert:
dependency: transitive
description:
@ -417,18 +417,18 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06"
url: "https://pub.dev"
source: hosted
version: "10.0.5"
version: "10.0.7"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
version: "3.0.8"
leak_tracker_testing:
dependency: transitive
description:
@ -705,7 +705,7 @@ packages:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
version: "0.0.0"
source_span:
dependency: transitive
description:
@ -726,10 +726,10 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
url: "https://pub.dev"
source: hosted
version: "1.11.1"
version: "1.12.0"
stream_channel:
dependency: transitive
description:
@ -742,10 +742,10 @@ packages:
dependency: transitive
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.3.0"
syncfusion_flutter_core:
dependency: transitive
description:
@ -822,10 +822,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
url: "https://pub.dev"
source: hosted
version: "0.7.2"
version: "0.7.3"
tuple:
dependency: transitive
description:
@ -958,10 +958,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b
url: "https://pub.dev"
source: hosted
version: "14.2.5"
version: "14.3.0"
web:
dependency: transitive
description: