From 15e4b0da4e580bb530de784d306ab113fc237aa3 Mon Sep 17 00:00:00 2001 From: IxMxAMAR Date: Wed, 13 May 2026 21:36:54 +0530 Subject: [PATCH] release(v0.1.2): fix critical loader collision + 6 Gemini-review findings Critical: - rename nodes/ -> mp_nodes/, shared/ -> mp_shared/ to avoid shadowing ComfyUI core's nodes.py via sys.path injection (root cause of v0.1.1 'nodes is not a package' startup failure) - http_post now streams response and enforces 50MB cap mid-stream (DoS) - AES key derivation: HKDF -> PBKDF2-HMAC-SHA256 600k iters + random salt (work factor against weak-key brute force; ciphertext format changed) - fs_mkdir/copy/move/delete confined to ComfyUI input/output/temp + ~ by default; UTILITY_MEGAPACK_ALLOW_ARBITRARY_PATHS=1 to override UX / correctness: - combo settings now declare options array (themes dropdown was empty) - LATENT default 1x4x8x8 zeros -> {} (avoids Flux/SD3 shape crashes) - @op decorator rejects reserved widget names mode/theme at register time - _pad_outputs docstring updated to match relaxed opaque-type behavior --- CHANGELOG.md | 20 ++++- __init__.py | 24 +++--- {nodes => mp_nodes}/__init__.py | 0 {nodes => mp_nodes}/_base.py | 24 +++++- {nodes => mp_nodes}/convenience/__init__.py | 4 +- .../convenience/operations/__init__.py | 0 .../convenience/operations/convenience.py | 2 +- {nodes => mp_nodes}/image_pro/__init__.py | 4 +- .../image_pro/operations/__init__.py | 0 .../image_pro/operations/inspect.py | 2 +- .../image_pro/operations/load.py | 2 +- .../image_pro/operations/spatial_ops.py | 2 +- .../image_pro/operations/style_ops.py | 2 +- .../image_pro/operations/value_ops.py | 2 +- {nodes => mp_nodes}/io_workflow/__init__.py | 4 +- .../io_workflow/operations/__init__.py | 0 .../io_workflow/operations/filesystem.py | 52 +++++++++++++ .../io_workflow/operations/network.py | 14 +++- .../io_workflow/operations/system.py | 0 .../io_workflow/operations/workflow.py | 2 +- {nodes => mp_nodes}/mask_latent/__init__.py | 4 +- .../mask_latent/operations/__init__.py | 0 .../mask_latent/operations/latent_ops.py | 0 .../mask_latent/operations/mask_ops.py | 2 +- .../models_sampling/__init__.py | 4 +- .../models_sampling/operations/__init__.py | 0 .../models_sampling/operations/model_intel.py | 0 .../models_sampling/operations/resolution.py | 0 .../operations/seed_sampling.py | 0 {nodes => mp_nodes}/programming/__init__.py | 4 +- .../programming/operations/__init__.py | 0 .../programming/operations/control_flow.py | 0 .../programming/operations/crypto.py | 49 ++++++++---- .../programming/operations/data_struct.py | 0 .../programming/operations/encoding.py | 0 .../programming/operations/logic_bits.py | 0 .../programming/operations/math_signals.py | 0 .../programming/operations/text.py | 0 {nodes => mp_nodes}/prompt/__init__.py | 4 +- .../prompt/operations/__init__.py | 0 .../prompt/operations/prompt.py | 0 {nodes => mp_nodes}/smoke/__init__.py | 4 +- .../smoke/operations/__init__.py | 0 {nodes => mp_nodes}/smoke/operations/echo.py | 0 {shared => mp_shared}/__init__.py | 0 {shared => mp_shared}/conversions.py | 0 node.zip | Bin 0 -> 115611 bytes pyproject.toml | 4 +- tests/unit/test_base_anytype.py | 2 +- tests/unit/test_base_errors.py | 2 +- tests/unit/test_base_input_types.py | 2 +- tests/unit/test_base_opdef.py | 2 +- tests/unit/test_base_pad_outputs.py | 12 +-- tests/unit/test_base_process.py | 2 +- tests/unit/test_base_registry.py | 2 +- tests/unit/test_base_theme_choices.py | 2 +- tests/unit/test_convenience.py | 2 +- tests/unit/test_image_pro.py | 2 +- tests/unit/test_io_workflow.py | 70 ++++++++++++++++-- tests/unit/test_mask_latent.py | 2 +- tests/unit/test_models_sampling.py | 2 +- tests/unit/test_programming_control_flow.py | 2 +- tests/unit/test_programming_crypto.py | 14 +++- tests/unit/test_programming_data_struct.py | 2 +- tests/unit/test_programming_encoding.py | 2 +- tests/unit/test_programming_logic_bits.py | 2 +- tests/unit/test_programming_math_signals.py | 2 +- tests/unit/test_programming_text.py | 2 +- tests/unit/test_prompt.py | 2 +- web/extension.js | 21 ++++-- 70 files changed, 290 insertions(+), 98 deletions(-) rename {nodes => mp_nodes}/__init__.py (100%) rename {nodes => mp_nodes}/_base.py (84%) rename {nodes => mp_nodes}/convenience/__init__.py (69%) rename {nodes => mp_nodes}/convenience/operations/__init__.py (100%) rename {nodes => mp_nodes}/convenience/operations/convenience.py (99%) rename {nodes => mp_nodes}/image_pro/__init__.py (76%) rename {nodes => mp_nodes}/image_pro/operations/__init__.py (100%) rename {nodes => mp_nodes}/image_pro/operations/inspect.py (98%) rename {nodes => mp_nodes}/image_pro/operations/load.py (99%) rename {nodes => mp_nodes}/image_pro/operations/spatial_ops.py (98%) rename {nodes => mp_nodes}/image_pro/operations/style_ops.py (98%) rename {nodes => mp_nodes}/image_pro/operations/value_ops.py (98%) rename {nodes => mp_nodes}/io_workflow/__init__.py (75%) rename {nodes => mp_nodes}/io_workflow/operations/__init__.py (100%) rename {nodes => mp_nodes}/io_workflow/operations/filesystem.py (74%) rename {nodes => mp_nodes}/io_workflow/operations/network.py (82%) rename {nodes => mp_nodes}/io_workflow/operations/system.py (100%) rename {nodes => mp_nodes}/io_workflow/operations/workflow.py (99%) rename {nodes => mp_nodes}/mask_latent/__init__.py (64%) rename {nodes => mp_nodes}/mask_latent/operations/__init__.py (100%) rename {nodes => mp_nodes}/mask_latent/operations/latent_ops.py (100%) rename {nodes => mp_nodes}/mask_latent/operations/mask_ops.py (98%) rename {nodes => mp_nodes}/models_sampling/__init__.py (77%) rename {nodes => mp_nodes}/models_sampling/operations/__init__.py (100%) rename {nodes => mp_nodes}/models_sampling/operations/model_intel.py (100%) rename {nodes => mp_nodes}/models_sampling/operations/resolution.py (100%) rename {nodes => mp_nodes}/models_sampling/operations/seed_sampling.py (100%) rename {nodes => mp_nodes}/programming/__init__.py (78%) rename {nodes => mp_nodes}/programming/operations/__init__.py (100%) rename {nodes => mp_nodes}/programming/operations/control_flow.py (100%) rename {nodes => mp_nodes}/programming/operations/crypto.py (68%) rename {nodes => mp_nodes}/programming/operations/data_struct.py (100%) rename {nodes => mp_nodes}/programming/operations/encoding.py (100%) rename {nodes => mp_nodes}/programming/operations/logic_bits.py (100%) rename {nodes => mp_nodes}/programming/operations/math_signals.py (100%) rename {nodes => mp_nodes}/programming/operations/text.py (100%) rename {nodes => mp_nodes}/prompt/__init__.py (70%) rename {nodes => mp_nodes}/prompt/operations/__init__.py (100%) rename {nodes => mp_nodes}/prompt/operations/prompt.py (100%) rename {nodes => mp_nodes}/smoke/__init__.py (79%) rename {nodes => mp_nodes}/smoke/operations/__init__.py (100%) rename {nodes => mp_nodes}/smoke/operations/echo.py (100%) rename {shared => mp_shared}/__init__.py (100%) rename {shared => mp_shared}/conversions.py (100%) create mode 100644 node.zip diff --git a/CHANGELOG.md b/CHANGELOG.md index b86d5d6..649f56c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,28 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.2] — 2026-05-06 + +This release fixes a critical loader bug from 0.1.1 plus 6 issues surfaced by an outside code review. + +### Fixed — Critical loader collision +- **`'nodes' is not a package` at ComfyUI startup.** v0.1.1 attempted to fix the import error by injecting our package directory into `sys.path`, but ComfyUI's core ships its own `nodes.py` module. Since `sys.modules['nodes']` was already populated with ComfyUI's `nodes.py` (a single-file module), our absolute imports like `from nodes.smoke import ...` resolved to that module and failed because it has no `.smoke` attribute. **Fix:** renamed internal packages from `nodes/` → `mp_nodes/` and `shared/` → `mp_shared/` so our path injection cannot shadow ComfyUI core. + +### Fixed — Security +- **`http_post` no longer reads unbounded responses into memory.** Previous version called `requests.post(...)` and checked `len(resp.content)` after the full body buffered. v0.1.2 streams with `iter_content` and enforces the 50 MB cap mid-stream, matching `http_get`. (DoS protection.) +- **AES key derivation switched from HKDF (no work factor) to PBKDF2-HMAC-SHA256 with 600,000 iterations and a random per-encryption salt.** Ciphertext format is now base64(salt[16] ‖ nonce[12] ‖ tag+ct). Old v0.1.x ciphertexts are not decryptable by 0.1.2+. +- **`fs_mkdir`, `fs_copy`, `fs_move`, `fs_delete` now reject paths outside the allow-list** (ComfyUI input/output/temp + user home). Override with `UTILITY_MEGAPACK_ALLOW_ARBITRARY_PATHS=1` if needed. Closes a host-wide read/write/delete vulnerability when ComfyUI is exposed over a network. + +### Fixed — UX / correctness +- **Settings dropdowns** for "Pack default theme" and "Global theme" now declare `options` so ComfyUI renders the actual list instead of an empty combo. +- **LATENT default pad changed from a hardcoded (1, 4, 8, 8) zero tensor to `{}` empty dict.** SD3 / Flux use 16-channel latents; the old default caused cryptic shape mismatches downstream. Now downstream nodes see a clean missing-key error. +- **`@op` decorator now rejects widget names `mode` and `theme` at registration time.** These collided with framework kwargs and surfaced as cryptic runtime "multiple values for argument" errors only when the op was first invoked. +- **`_pad_outputs` docstring** updated to match the relaxed opaque-type behavior introduced in 0.1.1. + ## [0.1.1] — 2026-05-06 ### Fixed -- ComfyUI loader compatibility: inject the package directory into `sys.path` at import time so `from nodes._base import ...` and `from shared.conversions import ...` resolve under ComfyUI's `spec_from_file_location` loader (was failing with `ModuleNotFoundError: No module named 'nodes'` at startup). +- ComfyUI loader compatibility: inject the package directory into `sys.path` at import time so `from nodes._base import ...` and `from shared.conversions import ...` resolve under ComfyUI's `spec_from_file_location` loader. **(Insufficient — superseded by the package rename in 0.1.2; see above.)** ## [0.1.0] — 2026-05-06 diff --git a/__init__.py b/__init__.py index 51568b7..ccf570d 100644 --- a/__init__.py +++ b/__init__.py @@ -4,24 +4,24 @@ ComfyUI imports this module and reads NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS, and WEB_DIRECTORY. """ -# ComfyUI loads custom nodes via spec_from_file_location, which does NOT add the -# package's own directory to sys.path. Without this our absolute imports -# (`from nodes._base import ...`, `from shared.conversions import ...`) fail with -# ModuleNotFoundError. Inject the package directory once at import time. +# ComfyUI loads custom nodes via spec_from_file_location, which does NOT add +# the package's own directory to sys.path. Inject the package directory once at +# import time. We use `mp_*` prefixed package names so this injection cannot +# shadow ComfyUI core's `nodes` module (which is what broke v0.1.1). import os as _os import sys as _sys _HERE = _os.path.dirname(_os.path.abspath(__file__)) if _HERE not in _sys.path: _sys.path.insert(0, _HERE) -from nodes.smoke import SmokeTestNode -from nodes.programming import ProgrammingNode -from nodes.prompt import PromptNode -from nodes.image_pro import ImageProNode -from nodes.mask_latent import MaskLatentNode -from nodes.io_workflow import IOWorkflowNode -from nodes.convenience import ConvenienceNode -from nodes.models_sampling import ModelsSamplingNode +from mp_nodes.smoke import SmokeTestNode +from mp_nodes.programming import ProgrammingNode +from mp_nodes.prompt import PromptNode +from mp_nodes.image_pro import ImageProNode +from mp_nodes.mask_latent import MaskLatentNode +from mp_nodes.io_workflow import IOWorkflowNode +from mp_nodes.convenience import ConvenienceNode +from mp_nodes.models_sampling import ModelsSamplingNode NODE_CLASS_MAPPINGS: dict = { "UtilMegaPack_SmokeTest": SmokeTestNode, diff --git a/nodes/__init__.py b/mp_nodes/__init__.py similarity index 100% rename from nodes/__init__.py rename to mp_nodes/__init__.py diff --git a/nodes/_base.py b/mp_nodes/_base.py similarity index 84% rename from nodes/_base.py rename to mp_nodes/_base.py index 371403c..383492e 100644 --- a/nodes/_base.py +++ b/mp_nodes/_base.py @@ -55,6 +55,18 @@ class OpRegistry: raise ValueError( f"duplicate op_id '{op_id}' (already registered as '{self.ops[op_id].display_name}')" ) + # Reserved widget names — collide with `process(mode=..., theme=...)` + # framework kwargs and surface as cryptic "multiple values for + # argument 'mode'" runtime errors (Gemini review #8). Catch at + # registration time so op authors get a clear failure mode. + for section in ("required", "optional"): + for reserved in ("mode", "theme"): + if reserved in input_schema.get(section, {}): + raise ValueError( + f"op '{op_id}': widget name '{reserved}' is reserved " + f"(collides with the framework's `mode`/`theme` kwargs). " + f"Pick a different widget name (e.g. '{reserved}_kind')." + ) self.ops[op_id] = OpDef( op_id=op_id, display_name=display_name, @@ -126,9 +138,11 @@ def _default_for_type(type_name: str, op_id: str): return torch.zeros((1, 1, 1)) if type_name == "LATENT": - import torch - - return {"samples": torch.zeros((1, 4, 8, 8))} + # Empty dict, NOT a hardcoded 4-channel zero latent. Flux/SD3/etc. + # use 16 channels, so the old default would cause cryptic shape + # mismatches downstream (Gemini review #6). Downstream nodes that + # need samples will raise a clear "missing 'samples'" error instead. + return {} return None @@ -143,7 +157,9 @@ def _pad_outputs( Filled slots come from `op_result` in the order given by `output_indices`. Unfilled slots get type-appropriate defaults from `_default_for_type`. - Opaque types (MODEL/CLIP/VAE) raise OutputNotProducedError if unfilled. + Opaque types (MODEL/CLIP/VAE) pad to None — ComfyUI's runtime then surfaces + a clear error at the wire level if a downstream node tries to use None. + LATENT pads to {} for the same reason (avoid wrong-channel-count crashes). """ for idx in output_indices: if idx >= len(return_types): diff --git a/nodes/convenience/__init__.py b/mp_nodes/convenience/__init__.py similarity index 69% rename from nodes/convenience/__init__.py rename to mp_nodes/convenience/__init__.py index ba5f9d9..f1071ac 100644 --- a/nodes/convenience/__init__.py +++ b/mp_nodes/convenience/__init__.py @@ -1,11 +1,11 @@ """ConvenienceNode — reroute, switch, gate, counter, timer, debug, presets, compare.""" -from nodes._base import ANY, MegaPackNodeBase, OpRegistry +from mp_nodes._base import ANY, MegaPackNodeBase, OpRegistry REGISTRY = OpRegistry() op = REGISTRY.register -from nodes.convenience.operations import convenience # noqa: E402, F401 +from mp_nodes.convenience.operations import convenience # noqa: E402, F401 class ConvenienceNode(MegaPackNodeBase): diff --git a/nodes/convenience/operations/__init__.py b/mp_nodes/convenience/operations/__init__.py similarity index 100% rename from nodes/convenience/operations/__init__.py rename to mp_nodes/convenience/operations/__init__.py diff --git a/nodes/convenience/operations/convenience.py b/mp_nodes/convenience/operations/convenience.py similarity index 99% rename from nodes/convenience/operations/convenience.py rename to mp_nodes/convenience/operations/convenience.py index ee7665c..7ff12ae 100644 --- a/nodes/convenience/operations/convenience.py +++ b/mp_nodes/convenience/operations/convenience.py @@ -7,7 +7,7 @@ import time import torch -from nodes._base import ANY +from mp_nodes._base import ANY from .. import op diff --git a/nodes/image_pro/__init__.py b/mp_nodes/image_pro/__init__.py similarity index 76% rename from nodes/image_pro/__init__.py rename to mp_nodes/image_pro/__init__.py index e5b79de..1ff5c2a 100644 --- a/nodes/image_pro/__init__.py +++ b/mp_nodes/image_pro/__init__.py @@ -1,11 +1,11 @@ """ImageProNode — image loaders + modifications + inspectors.""" -from nodes._base import MegaPackNodeBase, OpRegistry +from mp_nodes._base import MegaPackNodeBase, OpRegistry REGISTRY = OpRegistry() op = REGISTRY.register -from nodes.image_pro.operations import ( # noqa: E402, F401 +from mp_nodes.image_pro.operations import ( # noqa: E402, F401 load, value_ops, spatial_ops, diff --git a/nodes/image_pro/operations/__init__.py b/mp_nodes/image_pro/operations/__init__.py similarity index 100% rename from nodes/image_pro/operations/__init__.py rename to mp_nodes/image_pro/operations/__init__.py diff --git a/nodes/image_pro/operations/inspect.py b/mp_nodes/image_pro/operations/inspect.py similarity index 98% rename from nodes/image_pro/operations/inspect.py rename to mp_nodes/image_pro/operations/inspect.py index 2a0872b..2992977 100644 --- a/nodes/image_pro/operations/inspect.py +++ b/mp_nodes/image_pro/operations/inspect.py @@ -4,7 +4,7 @@ import json import torch -from shared.conversions import pil_to_tensor, tensor_to_pil +from mp_shared.conversions import pil_to_tensor, tensor_to_pil from .. import op diff --git a/nodes/image_pro/operations/load.py b/mp_nodes/image_pro/operations/load.py similarity index 99% rename from nodes/image_pro/operations/load.py rename to mp_nodes/image_pro/operations/load.py index 7858865..b968f18 100644 --- a/nodes/image_pro/operations/load.py +++ b/mp_nodes/image_pro/operations/load.py @@ -7,7 +7,7 @@ import random from PIL import Image, ExifTags -from shared.conversions import pil_to_tensor +from mp_shared.conversions import pil_to_tensor from .. import op diff --git a/nodes/image_pro/operations/spatial_ops.py b/mp_nodes/image_pro/operations/spatial_ops.py similarity index 98% rename from nodes/image_pro/operations/spatial_ops.py rename to mp_nodes/image_pro/operations/spatial_ops.py index 785c699..97a508a 100644 --- a/nodes/image_pro/operations/spatial_ops.py +++ b/mp_nodes/image_pro/operations/spatial_ops.py @@ -2,7 +2,7 @@ from PIL import Image, ImageFilter -from shared.conversions import pil_to_tensor, tensor_to_pil +from mp_shared.conversions import pil_to_tensor, tensor_to_pil from .. import op diff --git a/nodes/image_pro/operations/style_ops.py b/mp_nodes/image_pro/operations/style_ops.py similarity index 98% rename from nodes/image_pro/operations/style_ops.py rename to mp_nodes/image_pro/operations/style_ops.py index d8f688f..67f10bb 100644 --- a/nodes/image_pro/operations/style_ops.py +++ b/mp_nodes/image_pro/operations/style_ops.py @@ -6,7 +6,7 @@ import random as _random import torch from PIL import Image -from shared.conversions import pil_to_tensor, tensor_to_pil +from mp_shared.conversions import pil_to_tensor, tensor_to_pil from .. import op diff --git a/nodes/image_pro/operations/value_ops.py b/mp_nodes/image_pro/operations/value_ops.py similarity index 98% rename from nodes/image_pro/operations/value_ops.py rename to mp_nodes/image_pro/operations/value_ops.py index 4211ada..64b0f3a 100644 --- a/nodes/image_pro/operations/value_ops.py +++ b/mp_nodes/image_pro/operations/value_ops.py @@ -2,7 +2,7 @@ from PIL import Image, ImageOps -from shared.conversions import pil_to_tensor, tensor_to_pil +from mp_shared.conversions import pil_to_tensor, tensor_to_pil from .. import op diff --git a/nodes/io_workflow/__init__.py b/mp_nodes/io_workflow/__init__.py similarity index 75% rename from nodes/io_workflow/__init__.py rename to mp_nodes/io_workflow/__init__.py index 019021a..1c92894 100644 --- a/nodes/io_workflow/__init__.py +++ b/mp_nodes/io_workflow/__init__.py @@ -1,11 +1,11 @@ """IOWorkflowNode — filesystem, HTTP, save-with-manifest, sweeps, monitors.""" -from nodes._base import MegaPackNodeBase, OpRegistry +from mp_nodes._base import MegaPackNodeBase, OpRegistry REGISTRY = OpRegistry() op = REGISTRY.register -from nodes.io_workflow.operations import ( # noqa: E402, F401 +from mp_nodes.io_workflow.operations import ( # noqa: E402, F401 filesystem, network, workflow, diff --git a/nodes/io_workflow/operations/__init__.py b/mp_nodes/io_workflow/operations/__init__.py similarity index 100% rename from nodes/io_workflow/operations/__init__.py rename to mp_nodes/io_workflow/operations/__init__.py diff --git a/nodes/io_workflow/operations/filesystem.py b/mp_nodes/io_workflow/operations/filesystem.py similarity index 74% rename from nodes/io_workflow/operations/filesystem.py rename to mp_nodes/io_workflow/operations/filesystem.py index b7d360c..3051174 100644 --- a/nodes/io_workflow/operations/filesystem.py +++ b/mp_nodes/io_workflow/operations/filesystem.py @@ -8,6 +8,52 @@ import shutil from .. import op +# Path confinement for write/delete ops. Gemini review #7: previous version +# accepted any absolute path → host-wide read/write if ComfyUI was exposed. +# Default allow-list: ComfyUI input/output/temp + user home. Override with +# the env var UTILITY_MEGAPACK_ALLOW_ARBITRARY_PATHS=1 for power users. +def _confined_roots() -> list[str]: + roots: list[str] = [] + try: + import folder_paths # type: ignore + roots.extend([ + folder_paths.get_input_directory(), + folder_paths.get_output_directory(), + folder_paths.get_temp_directory(), + ]) + except Exception: + # Fallback for non-ComfyUI environments (tests, doc gen) + roots.extend([ + os.path.abspath("input"), + os.path.abspath("output"), + os.path.abspath("temp"), + ]) + roots.append(os.path.expanduser("~")) + return [os.path.abspath(r) for r in roots if r] + + +def _path_is_confined(target: str) -> bool: + abs_target = os.path.abspath(target) + for root in _confined_roots(): + try: + if os.path.commonpath([abs_target, root]) == root: + return True + except ValueError: + continue + return False + + +def _require_confined(path: str) -> None: + if os.environ.get("UTILITY_MEGAPACK_ALLOW_ARBITRARY_PATHS") == "1": + return + if not _path_is_confined(path): + raise PermissionError( + f"path {path!r} is outside the allow-listed roots " + f"(ComfyUI input/output/temp, user home). " + f"Set UTILITY_MEGAPACK_ALLOW_ARBITRARY_PATHS=1 to override (use with care)." + ) + + @op( op_id="fs_mkdir", display_name="FS Mkdir", @@ -22,6 +68,7 @@ from .. import op def fs_mkdir(self, path, exist_ok=True): if not path: raise ValueError("path is required") + _require_confined(path) os.makedirs(path, exist_ok=bool(exist_ok)) return (path,) @@ -75,6 +122,8 @@ def fs_glob(self, pattern, recursive=False): def fs_copy(self, src, dst, overwrite=False): if not os.path.isfile(src): raise FileNotFoundError(f"source file not found: {src!r}") + _require_confined(src) + _require_confined(dst) if os.path.exists(dst) and not overwrite: raise FileExistsError(f"destination exists and overwrite=False: {dst!r}") os.makedirs(os.path.dirname(dst) or ".", exist_ok=True) @@ -96,6 +145,8 @@ def fs_copy(self, src, dst, overwrite=False): def fs_move(self, src, dst, overwrite=False): if not os.path.isfile(src): raise FileNotFoundError(f"source file not found: {src!r}") + _require_confined(src) + _require_confined(dst) if os.path.exists(dst) and not overwrite: raise FileExistsError(f"destination exists and overwrite=False: {dst!r}") os.makedirs(os.path.dirname(dst) or ".", exist_ok=True) @@ -117,6 +168,7 @@ def fs_move(self, src, dst, overwrite=False): description="Delete a file (not a directory). Use with care.", ) def fs_delete(self, path, missing_ok=False): + _require_confined(path) if not os.path.exists(path): if missing_ok: return (path,) diff --git a/nodes/io_workflow/operations/network.py b/mp_nodes/io_workflow/operations/network.py similarity index 82% rename from nodes/io_workflow/operations/network.py rename to mp_nodes/io_workflow/operations/network.py index 17db705..a471f6c 100644 --- a/nodes/io_workflow/operations/network.py +++ b/mp_nodes/io_workflow/operations/network.py @@ -60,10 +60,16 @@ def http_post(self, url, body_json="{}", timeout_seconds=_DEFAULT_TIMEOUT, heade raise ValueError("url is required") headers = _json.loads(headers_json) if headers_json else {} body = _json.loads(body_json) if body_json else {} - resp = requests.post(url, json=body, headers=headers, timeout=int(timeout_seconds), allow_redirects=True) - if len(resp.content) > _MAX_RESPONSE_BYTES: - raise ValueError(f"response exceeded {_MAX_RESPONSE_BYTES} bytes") - text = resp.text + # stream=True + chunked accumulation so we enforce the size cap BEFORE the + # full response buffers into memory (Gemini review #2: DoS protection). + resp = requests.post(url, json=body, headers=headers, timeout=int(timeout_seconds), + stream=True, allow_redirects=True) + content = b"" + for chunk in resp.iter_content(chunk_size=64 * 1024): + content += chunk + if len(content) > _MAX_RESPONSE_BYTES: + raise ValueError(f"response exceeded {_MAX_RESPONSE_BYTES} bytes") + text = content.decode(resp.encoding or "utf-8", errors="replace") try: parsed = _json.loads(text) if text else {} if not isinstance(parsed, dict): diff --git a/nodes/io_workflow/operations/system.py b/mp_nodes/io_workflow/operations/system.py similarity index 100% rename from nodes/io_workflow/operations/system.py rename to mp_nodes/io_workflow/operations/system.py diff --git a/nodes/io_workflow/operations/workflow.py b/mp_nodes/io_workflow/operations/workflow.py similarity index 99% rename from nodes/io_workflow/operations/workflow.py rename to mp_nodes/io_workflow/operations/workflow.py index 7febcd0..1bc28ce 100644 --- a/nodes/io_workflow/operations/workflow.py +++ b/mp_nodes/io_workflow/operations/workflow.py @@ -13,7 +13,7 @@ from PIL import Image import requests -from shared.conversions import tensor_to_pil +from mp_shared.conversions import tensor_to_pil from .. import op diff --git a/nodes/mask_latent/__init__.py b/mp_nodes/mask_latent/__init__.py similarity index 64% rename from nodes/mask_latent/__init__.py rename to mp_nodes/mask_latent/__init__.py index d308126..354b611 100644 --- a/nodes/mask_latent/__init__.py +++ b/mp_nodes/mask_latent/__init__.py @@ -1,11 +1,11 @@ """MaskLatentNode — mask + latent operations.""" -from nodes._base import MegaPackNodeBase, OpRegistry +from mp_nodes._base import MegaPackNodeBase, OpRegistry REGISTRY = OpRegistry() op = REGISTRY.register -from nodes.mask_latent.operations import mask_ops, latent_ops # noqa: E402, F401 +from mp_nodes.mask_latent.operations import mask_ops, latent_ops # noqa: E402, F401 class MaskLatentNode(MegaPackNodeBase): diff --git a/nodes/mask_latent/operations/__init__.py b/mp_nodes/mask_latent/operations/__init__.py similarity index 100% rename from nodes/mask_latent/operations/__init__.py rename to mp_nodes/mask_latent/operations/__init__.py diff --git a/nodes/mask_latent/operations/latent_ops.py b/mp_nodes/mask_latent/operations/latent_ops.py similarity index 100% rename from nodes/mask_latent/operations/latent_ops.py rename to mp_nodes/mask_latent/operations/latent_ops.py diff --git a/nodes/mask_latent/operations/mask_ops.py b/mp_nodes/mask_latent/operations/mask_ops.py similarity index 98% rename from nodes/mask_latent/operations/mask_ops.py rename to mp_nodes/mask_latent/operations/mask_ops.py index cb0efae..7387254 100644 --- a/nodes/mask_latent/operations/mask_ops.py +++ b/mp_nodes/mask_latent/operations/mask_ops.py @@ -4,7 +4,7 @@ import torch import torch.nn.functional as F from PIL import ImageFilter -from shared.conversions import pil_to_tensor, tensor_to_pil +from mp_shared.conversions import pil_to_tensor, tensor_to_pil from .. import op diff --git a/nodes/models_sampling/__init__.py b/mp_nodes/models_sampling/__init__.py similarity index 77% rename from nodes/models_sampling/__init__.py rename to mp_nodes/models_sampling/__init__.py index 2c5fb76..e9108af 100644 --- a/nodes/models_sampling/__init__.py +++ b/mp_nodes/models_sampling/__init__.py @@ -1,11 +1,11 @@ """ModelsSamplingNode — model intel, resolution helpers, seed/sampler ops.""" -from nodes._base import MegaPackNodeBase, OpRegistry +from mp_nodes._base import MegaPackNodeBase, OpRegistry REGISTRY = OpRegistry() op = REGISTRY.register -from nodes.models_sampling.operations import ( # noqa: E402, F401 +from mp_nodes.models_sampling.operations import ( # noqa: E402, F401 model_intel, resolution, seed_sampling, diff --git a/nodes/models_sampling/operations/__init__.py b/mp_nodes/models_sampling/operations/__init__.py similarity index 100% rename from nodes/models_sampling/operations/__init__.py rename to mp_nodes/models_sampling/operations/__init__.py diff --git a/nodes/models_sampling/operations/model_intel.py b/mp_nodes/models_sampling/operations/model_intel.py similarity index 100% rename from nodes/models_sampling/operations/model_intel.py rename to mp_nodes/models_sampling/operations/model_intel.py diff --git a/nodes/models_sampling/operations/resolution.py b/mp_nodes/models_sampling/operations/resolution.py similarity index 100% rename from nodes/models_sampling/operations/resolution.py rename to mp_nodes/models_sampling/operations/resolution.py diff --git a/nodes/models_sampling/operations/seed_sampling.py b/mp_nodes/models_sampling/operations/seed_sampling.py similarity index 100% rename from nodes/models_sampling/operations/seed_sampling.py rename to mp_nodes/models_sampling/operations/seed_sampling.py diff --git a/nodes/programming/__init__.py b/mp_nodes/programming/__init__.py similarity index 78% rename from nodes/programming/__init__.py rename to mp_nodes/programming/__init__.py index 93a418b..660d5eb 100644 --- a/nodes/programming/__init__.py +++ b/mp_nodes/programming/__init__.py @@ -1,11 +1,11 @@ """ProgrammingNode — abstract programming primitives organized by category.""" -from nodes._base import ANY, MegaPackNodeBase, OpRegistry +from mp_nodes._base import ANY, MegaPackNodeBase, OpRegistry REGISTRY = OpRegistry() op = REGISTRY.register -from nodes.programming.operations import ( # noqa: E402, F401 +from mp_nodes.programming.operations import ( # noqa: E402, F401 encoding, logic_bits, math_signals, diff --git a/nodes/programming/operations/__init__.py b/mp_nodes/programming/operations/__init__.py similarity index 100% rename from nodes/programming/operations/__init__.py rename to mp_nodes/programming/operations/__init__.py diff --git a/nodes/programming/operations/control_flow.py b/mp_nodes/programming/operations/control_flow.py similarity index 100% rename from nodes/programming/operations/control_flow.py rename to mp_nodes/programming/operations/control_flow.py diff --git a/nodes/programming/operations/crypto.py b/mp_nodes/programming/operations/crypto.py similarity index 68% rename from nodes/programming/operations/crypto.py rename to mp_nodes/programming/operations/crypto.py index a49d44a..288ddbc 100644 --- a/nodes/programming/operations/crypto.py +++ b/mp_nodes/programming/operations/crypto.py @@ -7,22 +7,29 @@ import os from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.ciphers.aead import AESGCM -from cryptography.hazmat.primitives.kdf.hkdf import HKDF +from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from .. import op -_HKDF_SALT = b"utility-megapack/aes-gcm/v1" +# PBKDF2 work factor — 600k iterations is the OWASP 2023 recommendation for +# SHA-256-based password derivation. Increases the cost of a dictionary +# attack against user-entered passwords substantially. +# v0.1.2 (Gemini review #5) replaces the old HKDF-with-static-salt approach, +# which had no work factor and was vulnerable to brute-force on weak keys. +_PBKDF2_ITERATIONS = 600_000 +_SALT_BYTES = 16 -def _derive_aes_key(key_str: str) -> bytes: - """Derive a 32-byte AES key from an arbitrary-length string via HKDF.""" - return HKDF( +def _derive_aes_key(key_str: str, salt: bytes) -> bytes: + """Derive a 32-byte AES key from an arbitrary-length string via PBKDF2-HMAC-SHA256.""" + kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, - salt=_HKDF_SALT, - info=b"aes-gcm key", - ).derive(key_str.encode("utf-8")) + salt=salt, + iterations=_PBKDF2_ITERATIONS, + ) + return kdf.derive(key_str.encode("utf-8")) def _hash_hex(text: str, name: str) -> str: @@ -117,13 +124,18 @@ def hmac_verify(self, text, key, expected_hex): "key": ("STRING", {"default": ""}), }}, output_indices=(0,), - description="AES-256-GCM. Returns base64(nonce[12] || ciphertext_with_tag).", + description=( + "AES-256-GCM with PBKDF2-HMAC-SHA256 key derivation (600k iters). " + "Returns base64(salt[16] || nonce[12] || ciphertext_with_tag). " + "NOTE: ciphertexts from v0.1.0/0.1.1 (HKDF-based) cannot be decrypted by v0.1.2+." + ), ) def aes_encrypt(self, plaintext, key): - aes = AESGCM(_derive_aes_key(key)) + salt = os.urandom(_SALT_BYTES) + aes = AESGCM(_derive_aes_key(key, salt)) nonce = os.urandom(12) ct = aes.encrypt(nonce, plaintext.encode("utf-8"), None) - return (base64.b64encode(nonce + ct).decode("ascii"),) + return (base64.b64encode(salt + nonce + ct).decode("ascii"),) @op( @@ -135,12 +147,17 @@ def aes_encrypt(self, plaintext, key): "key": ("STRING", {"default": ""}), }}, output_indices=(0,), - description="Inverse of aes_encrypt. Raises on tag mismatch.", + description="Inverse of aes_encrypt. Raises on tag mismatch or malformed input.", ) def aes_decrypt(self, ciphertext_b64, key): blob = base64.b64decode(ciphertext_b64) - if len(blob) < 12 + 16: - raise ValueError("ciphertext too short for AES-GCM") - nonce, ct = blob[:12], blob[12:] - aes = AESGCM(_derive_aes_key(key)) + min_len = _SALT_BYTES + 12 + 16 # salt + nonce + GCM tag minimum + if len(blob) < min_len: + raise ValueError( + f"ciphertext too short for AES-GCM with PBKDF2 (need >= {min_len} bytes, got {len(blob)})" + ) + salt = blob[:_SALT_BYTES] + nonce = blob[_SALT_BYTES:_SALT_BYTES + 12] + ct = blob[_SALT_BYTES + 12:] + aes = AESGCM(_derive_aes_key(key, salt)) return (aes.decrypt(nonce, ct, None).decode("utf-8"),) diff --git a/nodes/programming/operations/data_struct.py b/mp_nodes/programming/operations/data_struct.py similarity index 100% rename from nodes/programming/operations/data_struct.py rename to mp_nodes/programming/operations/data_struct.py diff --git a/nodes/programming/operations/encoding.py b/mp_nodes/programming/operations/encoding.py similarity index 100% rename from nodes/programming/operations/encoding.py rename to mp_nodes/programming/operations/encoding.py diff --git a/nodes/programming/operations/logic_bits.py b/mp_nodes/programming/operations/logic_bits.py similarity index 100% rename from nodes/programming/operations/logic_bits.py rename to mp_nodes/programming/operations/logic_bits.py diff --git a/nodes/programming/operations/math_signals.py b/mp_nodes/programming/operations/math_signals.py similarity index 100% rename from nodes/programming/operations/math_signals.py rename to mp_nodes/programming/operations/math_signals.py diff --git a/nodes/programming/operations/text.py b/mp_nodes/programming/operations/text.py similarity index 100% rename from nodes/programming/operations/text.py rename to mp_nodes/programming/operations/text.py diff --git a/nodes/prompt/__init__.py b/mp_nodes/prompt/__init__.py similarity index 70% rename from nodes/prompt/__init__.py rename to mp_nodes/prompt/__init__.py index 47760f7..4e5e653 100644 --- a/nodes/prompt/__init__.py +++ b/mp_nodes/prompt/__init__.py @@ -1,11 +1,11 @@ """PromptNode — prompt-engineering operations.""" -from nodes._base import MegaPackNodeBase, OpRegistry +from mp_nodes._base import MegaPackNodeBase, OpRegistry REGISTRY = OpRegistry() op = REGISTRY.register -from nodes.prompt.operations import prompt # noqa: E402, F401 +from mp_nodes.prompt.operations import prompt # noqa: E402, F401 class PromptNode(MegaPackNodeBase): diff --git a/nodes/prompt/operations/__init__.py b/mp_nodes/prompt/operations/__init__.py similarity index 100% rename from nodes/prompt/operations/__init__.py rename to mp_nodes/prompt/operations/__init__.py diff --git a/nodes/prompt/operations/prompt.py b/mp_nodes/prompt/operations/prompt.py similarity index 100% rename from nodes/prompt/operations/prompt.py rename to mp_nodes/prompt/operations/prompt.py diff --git a/nodes/smoke/__init__.py b/mp_nodes/smoke/__init__.py similarity index 79% rename from nodes/smoke/__init__.py rename to mp_nodes/smoke/__init__.py index 1d5277e..83cdf46 100644 --- a/nodes/smoke/__init__.py +++ b/mp_nodes/smoke/__init__.py @@ -1,6 +1,6 @@ """Smoke node — exists only to verify the registration pipeline end-to-end.""" -from nodes._base import MegaPackNodeBase, OpRegistry +from mp_nodes._base import MegaPackNodeBase, OpRegistry REGISTRY = OpRegistry() op = REGISTRY.register @@ -8,7 +8,7 @@ op = REGISTRY.register # Trigger op registration. `op` must be bound before this import — Python # caches the partially-initialized module so `from nodes.smoke import op` # inside echo.py finds the already-defined name. -from nodes.smoke.operations import echo # noqa: E402, F401 +from mp_nodes.smoke.operations import echo # noqa: E402, F401 class SmokeTestNode(MegaPackNodeBase): diff --git a/nodes/smoke/operations/__init__.py b/mp_nodes/smoke/operations/__init__.py similarity index 100% rename from nodes/smoke/operations/__init__.py rename to mp_nodes/smoke/operations/__init__.py diff --git a/nodes/smoke/operations/echo.py b/mp_nodes/smoke/operations/echo.py similarity index 100% rename from nodes/smoke/operations/echo.py rename to mp_nodes/smoke/operations/echo.py diff --git a/shared/__init__.py b/mp_shared/__init__.py similarity index 100% rename from shared/__init__.py rename to mp_shared/__init__.py diff --git a/shared/conversions.py b/mp_shared/conversions.py similarity index 100% rename from shared/conversions.py rename to mp_shared/conversions.py diff --git a/node.zip b/node.zip new file mode 100644 index 0000000000000000000000000000000000000000..78e3de9c385d2a2853f4fda1c7345fddb1c62c28 GIT binary patch literal 115611 zcma&NbBwP+6Yu#O+qP}nwr$(S8Qb>E8QZpP`;2X#@tK{Qdvo76yPLcFN2QY|om8i~ zp33)A-K{7C0*VR%03ZSA4yC$LM{L0QKmfoSFaUu0^OfGh%GJ`%n8Cxr*~Z+~!NY~Y z#ERa_&NenlK_P?@apcxF3hKTc>|0+_R+iU`#d{l5#c*3)MrVua()_t=3plzHr>HD& z)?vp-WTATqy4Im_`ivT+ejr*E$a>6;WL~5Gs(l&etewIY0i<}jXlN}U?_WTZh04e9 z{B&nNU$i~qMLredh>C-ym-D?@3zkp-1wEQil(L$yDt_5vTynAl59P^50%O+mS-0%q z)Mqs(*DOZD#e}NDV}?1a(51%^?9(e4C_4lBd^MKuW*7%5+OG}S>#BdeTiM+bL5BP8 z;{b`RYt*{eOlW?7zlFb&1C5Ej7YydsbW*<8p6$nd)c|b#Aq~TlKMH0#V$q#q zepnAV!_G;5l3h!r&g<5n7id* zNXr@R)oMejad7;QOd!1)Y#aQ?UtT(3xJ$ObWFOHFvFiEF^WPU?4kofT1@PZoXsWmWQ85G#+qiHXPJ+?GjBWQNznFP01_q4+9gBiA6UyQgT-^=;DlN z_m?3pd+xkTazg2Tw9S)ynn&wrKd2twqV$#3f%gB;%Ai0hb=#Ci)HGlLfFX1M;Aa4c zND9eIh{-BQ(A$~XXDE<~ zF6O-o;TTNcj9kq!P`elrKP*i}&XnrXX45wl=Gafu)v$gz82MEpYHv%|)4wOpy={zr z{}?abBFta&GFAT6>o(@pJ&KkIf0(?y++clhZpyt8|1!#8n*0kVcv{*_#8!Yol@6y) zQ~%+Ce*;R* z{+QPU3nl3XJ$|Yi(ULh?+`E3A$f=2$jllaYi-=jqQMs%F@wRc+#%3Qy_r0cM+TdG5b==x%b?sKEA11x<38K$alJsdfMp*#Ie^%4`H*D^Wh+f&( ztb7%OnJl!z%BjeA(CVT#cb>^yDyCgj%#2f-x9}}c94@G_qTg0}%3Uk5!_{iuYec-D z-XY~U)?GtVDufqRIU=q=#bn=AcUlS53Ub@Fdei(_?1-6Dv9Go?2}ga*aF{yrN*03aEdX3@HB(ln7*`*W^x2)pjR*Sw{3%#}73 z)TZ3SM@$x^I#&%X+TQtU$Bk9pKH<*eyWArloTSEnKJI?G!xQJvD|o&9bF;~OSxN8o z9ZYb-AOYbw^?E}Y@@7MxQO=RrlE$#no%_hQ7yll7{d#$Ox_h|!3`i|Y!`u(WYEi={ ze-+z`vcW&2VF}MngKK5=B8_*gOyUWo5}*kHBuy)&Q4wd57TXBBw?QCOlY8+av`DGx z}bxPQesjr3|t>zkN# zL>++B+q-zV+Q3uZ;G?}F@{Mu)Ct5A;hsT)6J%oQ4}(w659;0neL- z1%ANQU&a@&WNiDKh5a3-s_O+ERw!^aSzZyIkEi>0|8U(EG}O%&s{CAkBnoU-_V>V2 z#!}@oBSl$TTUY@^0c;+0nrja1hvj$^TKAG!ECH7Y9CEF*ASK4E8>_YGPW_R=^kTX|UJr&Org)9t*~Y7v%yP|b*M6M{OAaC+fAZ`RDVQ#(Mk(X>WAw@d z$n&hlXK|HeG_2t|r6P&fCic$U-+ZsQ%oJ`@MAfCg?qCgWbV=(0uH9i+$+;X0l}n~H zJA*ycgn_T|M4^j&cRBv+EA!LA;c5}s97@uTlwVFo%dvq==*3r?$9j_N3Ke$ z$iGKg_EyK10;t$Np>tc~0>$eNfMv-Vv~BXeiv=>;HHd9{MA0S7iqKGp?5#4vHmtOW zEJ<@8V*1TaaU}(-E8&IQ;Wl>D@}DZ2)i%Uv|oyKB>A6l_SF55bxOH(nAme)aiF$EXnBRQNu^~Ae+dclrnQ+pz3jHU*hY@Sy$KxVorDJaOv7Wr8+ zZQ?a`WK+I?RaXiY-&6!*sz9JELk6e{!6&Mb5?UCU(r*W_0TCMO5u}W5);%7DC-Agc z*SzEmM5|C&BnyD$xMnx7+z6Exe-TUCB7gUv8bMJQV)CiZDH52uGo0gr$X27yBJLLn z_Ws7v4l!4{GIRySE@rR!jasEU6a>!xi{_#4o>M)~XxA^(D{czo8y+Wa(kngyhA%;i zV=M|0v#9!DXl-#!v(9i-4Y3t8+sL8%ajuHJM?lEJvEHtc#F#9kNt!2jkW!`HE0FNm z!bNmc#ExY|UWXEXR0}_P`pb*Q!NDiK>@Sb5c7)<*^WE!dG^VEWg!rMaH>hpRx=~KI zM2&tgJCLUWFG72`xcm&C_2&jmbzJ$+q9c!-nq`8iT|8L6UTyLVvbR_Payu2$z^`(H z2sdGykPOF!H7`!?>h2iB9 z`dDb~xg|vP_&ncXzM`IHK>TMkXu9KPhqmyBoo>EPFH(+M%04Z%AhCpys^oMOR_RiZ zj2XK8d)*=qa(Tf=DYKWcxiJ zG@F#rCs;mO+{$sq0yHL)~e(Gc7hN?f2oO{|upQLz!#Ij57un79Y9)rfI7cwGlty*#iBlLeDH5tXm{ zCJdsP)PfgaRSaT|kSR)mj}NxWaEi|xA{*W)5&zrW zL@_oh^1;PuRBVdM;$A*)9B0vR`XpyZDKQFp?-1tGTfoY1$zR8dAI4r&eqi zSc<~FvM)Bk9+UZq5HF*W0nz<0hn!($;C%-EbWv? z{sSN{wxzo7N?c@aKd4Ft3IIU-0En!Vh?u;Jn4XG(!zLr5|4SL?b46;Q{lyUeUvp%Y zYBHyz3}$>0 zv+N))B#O5+U>F!a{&9>Oh!ktc62SJh;Vu&RJDb5go!P(xr$>QA=Uj%s51IW79`*{m z?s0D1x8NA>pxhG6T`t4M`3K#DAx<$opREJo z9pBq8YS7-zF%2yoU_|tE9Uc{OB9BCdhq}xIl7E}`e4FqW{*xB0Ni!d60ceSotVBys%nVB&WDT8&=CKTp4yZ#57IUVE z17W}A=ZwXvOc!ECYi|sjkX4EK;(jknswu~)rivf345Vy!i*PieLpjpfXpbvb5gRqj zi9Pe<9cdFWa4SC$fllqdb!LtEPaVQiv6v68o&6H6{5(m?a zB#GKd51M~y=MBK#+bHKT&a$86{z`e@l$e%TVOR#lSdczE_~rQA^!O6W+cjP6_DSzT z=v~g2rCZaTHg1zc?KI2VrQE-ZN7DCVjH*jFYZ9uj#&(S``2Xu#-xctE(p*>P!fIyI zA79kE9a%G8$xOnZ$ZGre`n9l%hYq>BULzg+5V$KlA|DPX#f|Dmv87ILLuTK6ThC^z zI^;7X4z46kk|u2^SEEMeXpK#lV1F*Zq(RwUd#$|cCO}Dbz`#Rb z+hkbuC@iT01));{Qv{vqS>|_D--GsZ3(yo`~)~i&c%I(eLjQ;fMMAwbFxjcKpNH#?m1yjeL^V z_s7QZNK;w7)aaVj`8)f1OY${1MK12bQu3Iuw{ml5sE}=qWa>C{gL7+vV14?rHk5@O z=9(wGlItQ4GnbV4GomweN|L;5sd5|{ssS-PymiVs;q?xeMdP;UxE<*}*_Ev}NmvUd zHZPrMD5k2rMMxVch21Iu)`2hpfF@`~f^FsP*_#IB*DFD(`Zb&trEp+(dH)ueUlLqS ztNJg+S9&SspTItYW_O=}$5Bn`fRx}A#r-RQpllV?!lpskcNkRSq${>otyaTFMQH*s z6LcvJoQ#nyolaEOg3zG>1QXN==S72O#Xv4!vtl=^G)1MVIWI{%A~K#ZpdeP$$1LeNJzx$F4wkbK#Xx1UrNJCwcz%&z5|Cz@#x7SLaYgQgv zDnyMGHn+G%iwVJ`=sQp%#`cm)*;3~Kj)?goQS5@$T9U%tH7YN(!GaURVnrSGNNm0+ zz?8t}p@!ufVc7NDIk9g*OE76~WEDNkyR9K>7{lne-mwWVs(B9<279=W#BTEI?H;t> z6TH2Os6`>E%%#EnzW{UPIdDiQc7miv)U$Q;A-iX2z0CATzgN9+VZq7(W^{*b^?~6W zt+nI#oQlEo3u4sBW!`G8Po~2x*#@E;H%Z3DMKPJ z+k%qKA(pVJs1i!f^M{!%3(4F038=RV!!PDHpPfoIkFAL1{&sV7XT5eoMWTNKrQPVcaqC{bLkt}wsW48A;F=YkNftvawYV%IQV?`rq|EY z6DfJysc0H!9sdVK}|Ltp~p8RuWf95-ZNxJM0ls=ZPnEn%szE+;w%Er|NCP zyAoTGC`a&ro7%+>g!rUC@SK@oE#@~96Y-~RxmqoB3xJ!CemFpapZ=sOWL zSNUt&d8P6$69WQLL5G!VNSiB2S^>`FkpbbX1l%W_&~IwuWoWZQr4>0ng@y~(7UZ(W zJmCh^L0z7-GF{>b9W8V*m>H*5I@t!~3RKxJkL)qJWH^0x0rO!`9<*d*D_h{eMo#JU zMhl#m2})1d780`yf8t{tciPtlFux<-?)es9Uy)81Rl^TBG7~R6ZeYTsBZE+LIAKEF z?!3CtQJ6gw@YN(IpgTA9j z_*Th=8>&xqgK+k+S6$Hc+1P-kt#f(fqq5G#zVqqA8=%-GnjKOO%1Q1{X$yowTYU0M zMYx`Zx+Td`FK|sD0QN$Ahpo+~%=-cx+j=)@$_t2-r?OlI>Kdy>Y@?{RId=nHcFv~0 zsQBu39};ohJ}S>GHlI7i@%ZTLZG^&irC-k5vo)>VEHI?$^@6nJa57_ zG#s*yMg+Yq+Q`fbmc?sA>)6TY#Qqi>y`*}KIBP241uoExi*ss4u_{4~VyJSU8DTce zlOn`2!50`r0BSN@WTr#@ZCEK#833r6YYICtfETp8bWoVADD4+&05cxXLHTgZGzndK z68!*>3@OI(Sk`a)il*vL<&xME<8F-5X5=%WH`BS<)k^6%sJ)U3)_t(7X~yzySH1R^ ze6h*^D$W4a?8M14*!kl@<6&kxGoCA)k7#Ql_|h2*%($3#YN5lhn0L5$P&brIIx~3| z&rV<#!rCj-4NX{+g)xMI_^ohU|2QlVesmmGs5BuaPbfXyP3~oNtmnLqW!NN=n@ z;%q$eo>z$U)E5(?fLSV z`d=t`YOy3mhZ_dTz{y1B8EtZt3mur|d0W6qSa#vEkd!8nqQ79}g6Dl;7i+%}LdURPgnq9C zBcXqjJ;AIeNO~FybHE&WJxyyS>(7=QTz7pCpO^{c2^7A{gT1nBm;JO7(0|o%>%(pKEcIQDiSTYuV6HU z_0HD+_Ov`!^~^?Dxaf z%mII&G?i#gx_luP;lNd!5v4>mcZM0TmzJ8PzLhBfj~qcoE2bYkH7ZQKjd<6H`oh`C zJeCIR)Z^{%zC9m=rLwd4#5ZF_TpGTYY9*;G63LN6ko~a?7nc}&Z)$wJh^bpf>22kF zZB7WK*W-8?>nSI?!aJS0@4(0-&^Uxn8-51XVdCm-_uutZ$8AaF^WSw-95fiyWu)JPr^9-=rs+el8n!MuYHmG zg`%lH(HvG~b;Cy`@TY&2r(emJZ-Mf|Sd4imJeKMAr|v#Dd3ZAxPxn)rAa~ zaBb<0TN$}cU^1c8E;4v3)DT)}u<8gG1~ptjc%|yq1EE~DF%6;+(4W+Wh+`viNAbAf z+%1Z7y3{~D7HghH(YBUwKt2~PTn|NCoMe~1?k>?=M6VLJtPu+-z0ZW0Y&EtcNTXW3 zkw=ZKTBY2K2F##!bm3XzLiSTYc6Q5UL$t|80Rgv7ei+P`^p0Z z+%TogLY;CIdV+3|7cEm~o;vMUwjnqMJE7v(Dz?u`_O-}Y8jUS{Dne(S*8oi1=B8mi zCX(vw<1pj+!B}lJY0lvkeZpgudSJ&|>NBmI)fg2!t6pUuCC;$nD*45CwW)W84184} zeJWG5a}Ui6VNMCBCD`PNX)qpfW1w?0V4u(@ivAr{(K8pVOOGNj-gJ&dw-E##cY~l; z{sU>^%|>e9NVeJwVK*P~cLi8AnKlrSvw>i}#TI!_;|>p#20Ph* z3Wx#_bJ@e$hcs#mj+dZJEIt2hb?++I#*aCoK2O!F9c6NLA)XSC$($sN_Bn0-|<;~Qkv<< zkbcbG!PLx!;lC1GMOpb_NNZf!QFO8kg|O1jVD9q%4L_VXQKe+na4 zC_bV>aY6*8pwOD}AT3FvR<0-7RqIZ(m#hOAdv!Di@r9xCTH>syiuJ@eNbp^kQ2Uqu^`hdYpL|*_`j%jO5-@dS z5PG>Y0?AfYReQe>2lf!;m$cK@Rv^+^q`T=enHWTh9->wpIrWiWizXACXb9@my|{@C zLFw%2D}!z{$o?>KWGPOtckg_VY-XWYXyDFHRVLCT+HSJLJqpb%$I z+5&z{UvvwfqdIZYNU!2wu6kPewBo4Sgm$TDDS(@>p}l7T8ll3q^y5RfG1PIqfBBkb zcTblz#4(TMq)%spgq!F?{mKGE#Imwu5L!+TCNV|BcW;f`YHgM|-v8ybMpD>nxZ|P+dwyU2fY#*uT(NDw`5W*`2D#p<1wp zUE30&W^U8RNRu1wX6m9$X=bWjg_&0~!A+xTyMVhmFxPCkUR~Q^LGzsqMpaHD(SlQ0 z#&GfBC@rkL98>Be(7WeZ#*lU{T%$YM%ZE^=G=GTdCLY%sER>S`45s?1^6bBy{gf&H zmHtopQdQ-Hv02o{J1g}|YWJh`pNzlN8dlk#wLlTh(;V-)KcRZpnK_xMv(eLvmy+~~ zGl4GTy3-hjGBMX?3vewr)Z zx&v@K2ZewBK(XewKBdPAWH^-I9{Y1yPF+S`2dW%7H!yGIOl4=4$qg#w3AM>lJ?6qw zXpH_SA?1f|Gfk&FudfuePb_PiG`m!oGX^-8JfXp=ON6E=hdBlY``)iNTl8~PV7DYi zNkRk64WmH?t}D6@(p2=lSYG}hTN%-Tl63i=>JRNK2M@v5mbtN1z~g`_ii--|Q7dDV z4iWcGCfJ(Yg-hnqD`!5%IB^;cE#u_di@>*75Q#V9CKQ7V08v}5JvN|Al?%WIZACam z$dal|Mep9VkR`2k7tbE58yH`M2nc<>MKj|nvX&@-W{$N23(|0l(^^bWp@&(uo1|rc z7R7&uq|6&|OnB0wLYVYdn$?%8v=E1GHd5?bk<$v!nWFw%j{~N9*&Oqz?@cH@p%64bBVE=e8 zZz{SA2tE4v?g9bh6r>6W^I1KZXh=}Zn~@j@oX1b38zRqrSLlyQg;21!tDnMtr5Y9B zAzOV9l`&*iptM=#%pS7_YRL5niR~Qp0pVR~ptl}4G+1$e&d`nWoF_Coa<~(KFNU38 zszsbKz)5W!zqiXoP%vaibyUW%$7{2~FhFJW_{a_s-@H@TiJGrW1BA#Zd3=x0Rlf9d z#48-Q09UnQrKo(oJJ(I6+z3we8&VZ8F2G&@jrWf{*z?I#>uM!ZMn8^cLVdXJPclR8 zEdLDeVV1o7h)&&N<-#jXRR`pdKupBH4--_lrC#o?kF8sxFYG^4O&?km`8&m*$dH{N zmb5`q@l(IWkLK<$#5U7^*=5pdFSwdDSF-dDye9A#%nAu#0d6@MJ4$Pqoje9_QB_xz zU23rkD{4zIA)|`)1K?v;=dub3+nPSYx85%PJkef7x2eO%oEwZ zotJu6%Xz)i;Gd5p&#E!E9DGojMle6cBD?uSe(y+_4h*WkA2a>?b>8c*LXiJ=k9e>k zy7)LO(XTB5jM9HBr5<~Ddz{YeJksY(IieZ_*jXzdf??w0AJ#9n*oeWb@|rp?G<+lX z)BQYS!x>;8cAo0>D>j_`0#x^)kt~rlerZ(HBe;B) z2c3#>1Sd3MO4`AakKg7RO)GB-YZ#|ZU6Dg`py2->clwNi{g4^N%gix`!(nH` z<1MfDUX1g?1PBw0gA1IT;5XFyE}q}g`98ASgc+T`bc9Ii8rslMjM~gFM89;Hw_!Pf zpxdQVPQbo^sw~hxUig?~L-83nFNvaDCfxZdu1@g=baXir$Z1uXDeD(Bj#^Ba-Y)Sr zPAWRf!ZSO~1GcDGTlVyV0*G8ac*cQv`*{NC&}!l!US8ETJ-e7zc;GmTx=CIf{mf4Q zgD@k*)?7CoW5fyVM1ny^WwowfCgp{=g+N!E*shBYcXB*d@rQaU+0}csQi_qK=Nbo@ zr!Th)atpHun4Sj^4t_*$N>IPzfUN-^^Vv)-a%ok5`Am<`;3KO^fihZG7% zf#`0(PMMbp`C)VaQ5==fwEQMRSYTqeHQ3tK$^nL*R^iZZ=s|3qJ?xO|iv5cEN%mZX z@)OTP*-Y?ja?Kg5U{>LD+PFL#BziwGJN<8@>nSg`5*4 zMu~rQ_^9{sPf?no#aoC>35xa^fB*3dgjSo+({xY21Oow=uV` z<-xnTjs{$g!WAjmCMd<=G(KQcpkmm8Ba_{-BVD^A!$%lMelXdhH-z{%9j5Ma6Bm&v zuhZ$CCHaI?$9a;VW-@ot=lx|@rR}A;iYHfb{2nbV((_JxR3D;b29AS*C%p!w3yS^V!5CE49so}*lo*!)^_V>L$|N!d zU`jFZ^pLqKHGtOu8w1-yQw|#M6R!g!qE>AbvfrXK@Dk#Z9ACNXuQHAqIw-r+)Jn~2 zh+wk<<^<|}?wC_q7sn%vG67Cw%vBR=0$A|Az{C%s(j@BRq7;LBRM$xKfLq%6Sx`RP zb%^^Z4+8nQ__@+KBBbaC;dIM_gkxCZ_71$^Qwj%EEMCk{t>b@yyRrvO(!^87 zn9Oa@Kq6A%Q>v}uBs1Ozeo3cL?0VE$+Ok9K{6ZQv+rm~4T)FXXYLgz##~3k=*C>DW z!oA2BT>EaCG^q8RC5XX3mgJj2J5PQ=6sR_HG?(It<#uS6TX z9n01_DnT<}s|4PzOzyS}Uw689v%)XY#SYk}f05s&fH(S(Hq~BKU6Y_#Oy_Y^99W(B z4o$}7oan>gGu4UB{rmPu)^lDw_q@32gI01vk%DOPRAQLiMn(LvpdU!!K`4PoWYSA~Bo)`M_m*j1} zgm>z2>GXv9eo@qZw&qvk(_m zyEJQV4xGl;$fdgks#JJzVLrigfb2)5_j=6?Ao@wbB`VzFxC2-4^l-S!_Mw^Js6R2B zpNfWCMgLo8`z%O&FpFc&0s>iNZ}?-fmO08Q(%(h9>s@2^wrVpgq^w7>K8$28U8yY9 za7Wi#yI+kLl!G^Z|AwFAHR&AdxgGoD6n=O)xH$-&rJ}dE7Xw6R`Fk-4rPj%$!O$v7 z^g>3UkefgRGnVSE1X#~#*I2LR5RSrmjzHa3HP`^+yMC5jW-LpV%sE@FMMeA&k?!Jk zqI&yZZMc!C7}#C7Q60|AD)lVPbeld!FEMww!@_snl)>Z)Z_n6LU0mPy-KLLjq;HXW zEp*yhBlEtP5w;N{0~}=s>AG6qMw}oSh7Gw!_X=tY;bm>0xcrf;Yh0_X%k=5Q}QO&Z95K!f86_8w%o=S2JAg-%8BfcP_Elv!&9zmEVeN8rZ2byB4gA9C1s6W z!?Lk3!NS}5*j;(r4M=$z&Y%i&`MHi4V-c>srP)XS7L&<~3XvW0Y?kP}o!F8Bmx$2U zi3XA@9Y65r{O;!B`X=)8`FtPwz+m`B?-St3!~Z>2CqLU=TDq{!M`tx3Q=@i5dRna; z{;4u+s%)kIy3>~fOWdsKr!4%=3O7Wk%NQdUgJ6r-bX|z%6Yj8Epu$(6w9bPrQqKU|%ON5{CpC~I#vSqgVKRY6ibBPCF z%Po||&8^jxq>v0sqwZawi1WYqV+7f!>bA_A|2}gU585di=W>g#o2wVerW>l%orjyc zw*lhRc6QR@Nhz@^GcM|k%X%COHmKVbV0JBXOqK}i){ZVd7Ed0?Xc`E&VXWP7u`1U# zGtgzuvMxA>J8ayjAhKlZpA!0N^b-D0VXn@dwr;oLL&cL1+Cr%IHrcdAQf<|k9X&G1 zYH_8z>39y1sT6Z@;S?k3?nvc!c*CoM_Wt&=<+-u-%$0er!{B4&npY?t+$h=}vpd=W zEyf?0Fh>$#g-d;TBrOjcF}=2~z_b8t9|6x|h9^iySX(Xv&pSG_c_H zt_qu(%xsHz)HBZYt>LJLD&%o;$apF{Y@w!4ujq_xkbFAb7r=$Mi9GjBlR6q!l+Smg ze5=Y?z=pfk*UGCkVjlZzKc(%D&r|lu3k*O>nI&FT8zoiDJq!hJVYN|&J7G8CxpQx; zVMX@Z2^PrOVDapNxNu9EP-;5`{oWX_@%(Etxb`4)y2p>;t~G{Wk;|L&e79dMfnfA& zcvVA69_g|AMrzx><=@}d*u2&$k6utbQPevP6Q9iWse&Hd>+qEkv|oD#4m?j<;k{?L zGv2-1pa0b|{_y;HUzWVjLHQ$+pMMPJe@ibbJ0lA-14n0v|CY&mic>!_8L|6=M*Soy zf|9ztOd2-`wGxs{)r#{8l`b+ni@c!$uAGBEh~Y@4eo2-2wXusOZv&!)6{p?=hYWgw z`StA`{049a)+%e&9wyZ6JBP!n8&i8ylvd3oe4e2!J+2=KBwn@Z^c9q)1wxe)nB&$y zaB>!NONT#@Osh54qC_D)>6&X-?J-zf{SsvM~)S$fiOQVP_c z`t>)AMlwBm3lcvq8VC4qM`$6{NB_Uv72yk)ijtQ89gyjYmshXdh>(zkQi+;@KCL_d zIY+jMZZL-P7>zkIC5-|=Yo5lP7(=i8lmFjZ-k@{x%2`t@@{SP($Kb7=<0{s6+ zM^j46&C$|J(MiiGjnApvP0PtDz)?=V{J+U*yC0WE(Ud&gU;qHZPlulD|LoGr-o??( z#Pvr=_h|fy-)2Sh-|aI-#jls4zdG@5?m-?7uXKjoth{TDa+W`kgO$qJoc(3}?J*kJ zG=XmwC~|=BzIOQbciQ2EcdpCQb&7?=6r^$Gl+aq;5phQTeZDaYXDDzNbHp2;pfQOJ zu8y_|p=4ehJ!*x2S4h#5hCxjM!nF!g!!&3ZD!xFcye;Zy37==(=_T-h&-F7 zmKA$`uU%V-Oq@K7Lz|i)%t2-Vh5{Fqq{&vJYNf4_jHX{;gPb_NdnbKZ{NRm`52LeY z-EOfyOJYat_F}{}muU#5!oG-!B91<*l_hdkTEFRRN99w5V&3W&_XYyzHQE!S&-dj$ z^>pYf@8%^q|0*UGRgRvymIV3T@nT5jqI;iPrLaXSw)o-xdUT_Q*ix!mjwv#W^EVk$OyAQS@z%}&|Pquh4S-!OePPvqpyNATC9_byX-Arol{zo*DkoU*p`Z*+tqC;iaqEO_yW5*g+otz25i(^na*)B5lceYI;fH*9(d*}bFHvH zv=dh!3M&HK2~3qtx?6NYJ1aHh>^dp*sBJQW#9Jc={{~XWw$LMb&9Bpir=Tyh`FmH( z3^Ahp(qb0cfS73ySDWP5O}F6XVM3YnT&TFXQ6ELD5g-}bxJ5`Gj9HJ2xL884m$|#J zX8fxkSN|z5>EZKuLkXL-S?w@~uG%r6ZwJUxK6$!mP30H>chG~e<1o|Ic61(i8K{*0 zbfzP8HVAZQydAtNyl(LVma*c^7utJKyC8TG#qQEY^9UaNC@HG^aXbY!9@G;OQ3>GIH^4m?N}CA2Kawj)j=?<-bdgcz8w!}nZ8T7 zM@y{#;jQoT3iYM8H)eQq_PFf+N8t8;%6&mU7H;tQ zW|R%#B_ZV8ETyY9D_D2e>zhl;B-Ta8L3quD$QU-sO@8mt(LgXT>j)~tN@M8=ZoB1c4gJQ)YLFig2gU{NWL zNE*PZ+er3c%H37fX^&x<7k?@G@x02Ai}UWzQ+=cXXLNkZ23z<%-=DnrSy(W>PjR%Z zecKVSovAsyX+$Sa44mujw;D8W;If83*|FM9--FqfwIike&9uY4-OW!bj&Ycm+{Fz1 zmP6al_)Pta-XZH;&A^e94<}O~V;JPNDMaN!uuW$+<4ymw0sppb`nG8+&mercZ;Gc{ zBcZsP{iv#N`%aBh^d83BkyjEtzpJtnPWj12dYhrK2K2uaQ}!M+U#j%~80Qu!c3j!o zg7XmT420fSNS-Nkp)HLp{bJ|&nPSvi)vH3k=z^Y;cVbEKZN*8l;s_#+jgmC)ZTXwU zaA9dk)f3cEHxRDG7ev3PV+qmig4p!vcnqU&tXogg!k>BnY2s;vwL*XY*W+N=pW++$ z*G%vD=l1(KG5|pQe=feZ4o0RwmG@dh#^I11vG+rx9vE6Nl$O&@0Z7+1+8!OK^_)0c z2NANFwJ}q&Sb9;uJMi1bLUJ;GIJ;PmeWrk06O}9Hlmmf6%6U7As$M)AI)=|Q*Rd%^ z4u@|d1F3THx}N>12#0iBlcX9)yCHyDA<2|6!LBU!5hJ6%I$x~M%c|vKZS!wczZ)ZA z>($b|1o}%4F_w=%$t4R@OJe_Qh>%u^I?S?ywVBVmJbUO;D2Q20qv$Drhb@M-Sk#(jqAnJ#d62v~v*`3+9XP`adLP zZMV2!`uW3R&Fvwx1)knU#q2NKMm67OJ@FopxL#}yJW~!R&Q6QL3_?CS`*+#8sQiv? zD`>-f+6V9z0X$FIU{kQ|9ZI6f*Wwg9(c+q)l{_~SZW`m(Ii4Ys(4PBf6Q4$8c54x4 z1)?yAMqQ#3?4I^=g1I_&kQiE2Yy6$9z*72w48AjQYtJB^k<)^mG-)?ZC5ZF*7xE zX7U(D;Df|EIs@$taeXW`wS))0R@GAb#?0q0TP)&nIKjx}dc`3TN061|?h+L=ud=@o zm(-Yn_(pS*1~vD`-5rT`k??7(_?R^ByCGK-!n4^Gt({$Xn5R~=Agzd}2De6(g+KU0 z;QE$1Iu5Fo)5lSi5%|e0x@Nn)GwPRb^F^gA%2hb?gRt3@A^GBBJ_CaM#(CJpFu_Xa z0Lj@vHZ3ECaAcIQ;9*fl+-WQorLrN71kW2VsKo1l&~f8eK9qV)PS~K}D4)gcrI!?} zuAt-ga76UR)d}={>i}<{n7L`(MY|)hQU#!l#T`tXo2N;zoP~>BLAodb0zF@z{?HpO z#WosY1wT~3edtFxW?e5NO!0Gs)dpJ;`vI1{iTjBSyvco_RF8^P8K>Z>g&8H^JcuKO z#%|$qafXn=gI_{>@Zv`?%|qkx2#nys7O*8N^@At2igZ@Fv@eApevHJcA6Aj;ap4eM z&rG;GbqT4WhGDPUVvL0^e{L~@7LA&{Atm^?#F%aE>U9c(kQ>fXu6<{k)4eK64ti?c zE+AsFN?}PY=db8uTOm@g!}D#L*=B^)(wqYxhtqLk_5C)U9U!A^GF<(bthz|!2r;(c zH!RY$rEgom=O7Pu)|u zs=V~<4V$dQ4Pa^KM8B=o}?;aHf-4O4?;3(rm00|;>; z?_Ej|EWHa)`DkGLH(IEm7PTSyQus7s!;(Daqk(gornrk?JGG%KQ(jcYGzIzo<{@aM zchbqQO|MDJd^g-JH8^G~tM9keTlg0GT5*^nJo7< zKchnb|3T8p{)c5Yu-3D)clwEyOX@au3*zu!IeH9tK6R1jq!iWiaw#>Skm9`#vmNh# zlvYM|`(2#64IQ?K@ZX=NdKatZz@WdJ)ynTYOiK1k9mXIiAaszy1y?X8@Em=EJEB{GGl%YQ~83t zj8q1h)U9mxLcy>Y)l-g=Qj8@GZ)fr{4IKqU%51*$&uM5ZQN~-yrV@W0{(8$dHLrC8 z%Z1ka_K?Z~IqjL956n~og)zv*us4IF>M2kF+8@x-S5nfrY z8{edxoy982>)R}+OJxH?#gw9m@qPdg8i4TEqVPycb5clud1oW=K&&r)&VM_w^;%{} z96Y28l0b}Ck*I5G_AmiCmf1|Fx!{k}_40@Q@JKT-=_=YM6)38i$>F69u?bDWu0`sX?q3Dn^{t zE50rV<#~0Jw%EZ&?SPIo&yZ~JvV~2I*a&g&pnT+)C2?d{4l4g{1BZbFpS6Fx(bcDa zE@DKfnn#;I3mxgxA}8k+PwxYTXdXqGWW8Bu#mDe~i3sUj2~}MAc*z%vK0k?wWNr!a zkb~bxA2zFo%ZSrV-YPL9SJ1pkcuV<;EVl4gL;i?_&k4P8gNWU*xh^D!H1Mu=sDudv zp~E)EN@3C(4%Mj?6dpvhNW@WD_^c?=Qqh}T+=OF+HEc|82XlB;%Y8MfD+8fHqukLV z8kv(Q6}6rF8G(yWm{E~9`ZDB-kqy;4fWPR>!S@2n%egs(JU+TU4JmkFSlKT^VXo}SDxfNg$zGOq7 z-b|C9qFbw|N?iO!@FpT>072#%nHpOy`#$gReAB$N(wr}LmXRE}YV^&H%+Z-a814FI ze%S{oy$85%t13Xu2XW*$*QN|*xVa0j?GZ}E#Lqe{WOcgd0I!dmXmW(Lndc3U9xb|n zh&7}tgA@obW3y~Oi@hR6kr_3*h)HPeif1Y50DuFYjW#*J6$(dc=s!i=_aGE{b%{i$ z0G89pxH}UD-^gB}p1!OO@-l-VVAma|xxm^4OEw_!X4Ccml8OdGZvn^DYG(q}APCX*OUl?B;M(Ry(Gn{-=$UgnuK36_9z!dy4CT-9F; z{#UA~si`9P0{&%b={XS^3l(E;oiQjU6a%%qqrV=*3t*xF!X9Cwej>$-Z}By0xse3}a)Cj_5gbxwz*+Pm88t%h~#az!unhy2J_! z(7?#cX~+6^Nr#@O>7PMk8uC11nPg_4uR5DY2#$BIKu3OcVvBB?UUoHFE9$=x??wkB z78_I9+SedEmo20Rp>#9!BWBQr0^z-r72+xRiAWPK;-IPGB5~B?gDe>@l8cR8O|1>> z&C=Bz{$J6;bs|$%XFC|MS1N_bROW={BH@@-a%!WYEjnLt)j||IT+Ox<@})hwPkdu9 zf8Skq4xq{o%6=Qnk&cw<;^AIb(|6@FJ9?73rY_|lgcH1>{Tl>aen6lMpPGvv3;x0t5HG7nC2lAs5<8FTxt^R#M3MQjqCLe*PW&em(m9K1DOBG(%dc*f-HsN5TxcA=oZi*lFi$jJZXVw&(8C2YKJ>FHh|6l3hN#|*zfb=g=^5P-Tic^s`{L;>VbpnIS8V|&}H6PIm%|vxwk2hIT z)Ep`cQ9A+0s}P}2p>|B|Ec1saX~LNV%{uCgzlziFqxbP9Y_c#Gjfy2KT?7$S)N8ti z+|lAV3J?9AwoWTO7M2s_D}%Tj!9M6|)HM_my+_;eUYLr5S0$ibO|8zM^Id-Pf7PJ4 zaB3+ixO2}bHTDh|k0(Z{X`5o>_T)YGD3p*sYL_;Z)wT`c?rLn5h-PH5@R=86YL4Oj zA=oHF#&}|oA!o*Br-Ll(K989kx(d~W3%qT<9sn$Imld4X+r<4{gVdUz{`2`zx!tmsb3Z6K|C16g&*Wq! z?%Hgf_q(U>#-V!~c(l+Q;4o}N1=$A&e&>$FSXUq~tC(YLISxb{L3$BdH83lYSuJDY z_cg?gbO0)m%hY^kTqlx=7QY~~go?(J_CW-{`W8ed5s#qr6=sRP3-+zTp&x_;@h}aH zh>L}k6BG(*trCE{>jyk{>Rd7(A-pU6;M$(7U^Y%6BWm}C1?Jx_9^d2Fz*CnFP=)+{ zGn_$}+laSNO`$1U4$vF`9|{d5uQUd`2ffjdb~E1smLO4kp~BlqQ$}Db(*sBY@T9Ug z-Id{}pen(O*)c@XY(cZ;(AY~>`DW*&&@0067u?a+CdRq8Rn`W14mRkZ;|f%~e1!&} zj@Gg;gjXKb%Mjzh}fDrqwl8VAkxmWZ3`*Mcv4lmqmB zvQEDepHQ=`aE^dWfMF^qXCpoV!qw8afh=y%!_K?+ESLfdz`_AQ6s;g!tso=}Eg>}j zv=EAY>8KzSc*g^7&#U@2Vp^a$uNOFs%oB7ZgJX^WUk>97d=DgeJzF68mNR^6_jT=P zDnM)QpKcYCUsV)1TtfhQIDbm{!KWJBuk_#&rCB^tXL~qc1z9?4`_9Sj6C!>{(VihG zGl;BLuI{p}!%$QZI~0A`G+?_JQ&seoUs0 zKn+rj;6^qH>l6WRdHYCe-d8H>%_d8GMafJ3Fv+h9F7#l!tpvT6+O@_gj4-iP17Fwr z$Z>G|0ZNgDz+nLI%`_23@N8K?z}B84BSfn02voD@cXPMEllVKo`=1R`W)^=P&$v6T z*>Lts&!EM9n)=?nTM~lR3XQ>2K=hbbESZO}k{M5s1!>iqfJjXfKjes^>!8cHUsM~k z0Jyh5KA^f;tiJ~sf-kJApY!j|`dag!Q%vBhKj|5t>D#PdU(%H6(9}=B6uI9UV0w=| zz}QYVp7%C20`K6)v5=SGLxFpF?Iz~#^P+$7%=!C(KI*g8?X7fJS*f$tR?SIWz!iN1 z;{;y@59`7%Ejpu=>O#G8JeJ6qv>U97fPS|box@4Lvq^gIp;q88^6*^(jZD75RxJxyJ&Ae5?_WTH@n_=X zBK3sHgHU%nZp0>C=#ulr{Ks(3;@do(URN@(0lP#;EP*R2Q9oZkfY zxY`vclpYmTuNX7Ft5n_txglCAP%jZMV32|D=rYMiDLoI)BasU9_$Dt5(HXPMW~KcN z*Q14&xgc1!PVJo1kWk_#FvPn8nt_eoud};3TuPtz{rI$nCjjmCJonk9mef?25v-1i zv0%X-T{w(f(oi9v(iu&1^R;DXZ)3LtPg)Dyg3$L~IF?Y|`;K5atX5~n$?43G!sJ3C z?=P(|lsz~Hfk0~rm5oN#Fv@~wGS~)frV7?y*l=)aHRf>g#5AqAnSH`?Kg^NN7b~~@ zyOt5~P1efRt<)G(NFEg^*A1~Jucfv31Td*&G)!s&ye#TI1Y%_{Co zi`M-q9u}fBh}d39p}6m`s=O^Tivf2ik7*pkxf=*Y%KY*Y(TwWq6FMT_gvX`X&Aj}+ zGPq%abf@+XA4k>j^uCTl9|=s%3@>o>B!o^zA=MH_IZ)m>00SlW+%P%b3_V>PoL`U zl`eVsw66kF8t*(*7Py=~k8=NpdQ2#tu!LsP=Lu`T^Juz$B$)@uC*iZf-#Pjqf7PwC|3HE-k*xQ|_wR zgvU7&ep_iVcO|W;zhWu1_u7nN4p*ZLa_->4=~c4h@>H$Xirfk@d#W;P#SNJdm^a}@ z{|$XAnU(6=1$e{T_xc<7CbbRRH5F6K-#^m3OLA80kSK04~{q71$NDpsP!8bG8%ZTX<=O z-93iUs{mr(QK`rG!0W;^oA}ZI6J5>%F;b>Vqie=UHX@l5E`V>?wTRb9!FXn+LcVx^dZwF}nX&eck(6Y?o?S;rj<+rhZIZDgRy8{Np(Pv6!iawTY95 zle3A-(P=w4rQvrwM|Ik$_V=y$M7l2T2VpAY8x3r%0j-R0}_O*z+ z3k`__g>LMEaO}kxNCyfi&N)B)ifCmUt!&m2p2ZYH3;5Efv5W$q5232S#A(mrBAQoC6F$L=n z{8|;mZQ`Hjj*Oxg+&)NuqS|a~H7+|GC%>>YL<*9K_)X*?gC=N?fNl(*Xdb9#9vHt6 zu&GE-9GDZ0a^kV;8?35>-V?6^(?Ygk9WS(|+gSWfy+u^domAPP<#g1*z{kxll#34F zGGv_PFQpd$3gZ5d8ZGTB@svJX=_PWcLS~{`n9P)0#CTx7+-jRY3|pboNZeLbyF2Ns z_V8$qin&nR+VF%;|GM;y#b}v!sBOWqhr{vgO4mm+Rg|;Hl|S^wO!}C>b6MB^R9jh{ zzM(YzHo~UM653Q?h377loYENcAicN|l2%r^3J8?{j>%*cCB2+Hx?!uS!H&FCxURf$WO4f;T$z10vK0+>_ z98Ia8xlo3eg_1UR*CVL67{bQd1@Z5?_;vR{P3?{fv!j?c4FONN4J}4WC+aT|&sYx9 z1wnq;CtsrFniaorT?a`XjA)DN71qFJ8+WH(w;@TU-qqhEHfQIeU{8JgMm<~SokTZ6 z_k*7Op#AoEo;J%DO~5nCz;8Ocbr<#Ofn2@QK-^f^zL;yiQQ4Tu-)pZVtFJ-VS|4C4 z0x&OFuw0%SQ6D<4rOc(4aXU^#BL1R$ADBkJ`-+T@9v1??+}|?rH$Im^#i@IMWKicP zKj|;NA*mUB*a-q$->OAwnD*JWd0ha(LOQCNy0~;6!Jbd9qteMXQbp*jyJ)Hn{`j)N z9&K*|@4=xmS2jh8I~X6>^;%Bm<)vUQAkzvP=7>RxhSj!F#q`C3_xF{CqYm~ssoI+S zrQ_Vr#X6IskQBXlV$pO`cEMBdViRXSj)$lwOq!&3#i$CC_iX0+gZ~Mn0?!DUrE0E= zm5MXTgEE=^XUewHbUL5k$ljivf{L(wy5HXLsRQ%-t)mVu=lSJU>JQwQ=)x(q(XQkt zJ;;4bznWNYONklaO@z$H?cL4izWMb4p)PLj1>W>YB+)Kr(r^%3^40kdmHfCSs!+YGIeeTXZ}e(Z-^*DKG!ZK+);JS-?KIYig1guK(9%D8C?*cB-L39A z{IF#AW!%1RN*+z>I=a+qk#}n|Lxv(~dNX@Y?u_4el>^|sMjNk8{jb}N4j>OqHsyb{ zH$8V!*iJ<-tC}2bh+2wMuqM5D2Mt=Qibk-ETH4eLeC;ka`f8jvUguR>TH{}3=@Cro zP)lo}o;JSIN#kwf?Z0qa#ukTA_VRrwqj}I7leM zV-=t6|H?hY$(B97hx4;jST@%o{~%hscnTmm&9zJtWkhKn0G|K7<4YlQTFmKR z$8k4yQz5FRv2=%j7+~psY6qrv%2kQu-g!^aIL}#SHb13XLz}|Xl+!fK;j*O;E^SU!m_VR*K0ahpF$d@p=oh@ixklGi6Nj2!4CCWFWX{ zczBar9(H(|bnntn0trpOVR&?XXhHnffgFq}SX;Td0oY)r52JJK?p!Fx38LUQFmaNOvIk(*y2FU&0^I6CuI?KQ|P^XqF3H zzqNtKZ-uq;H|g9isxraU5KfZ??S&% z*nj6qKtFRB-LfoI%Rkl#wBY}xSM9%l=l(NoNmd@S-Jpl>I;R4=5P?K4w(w%q&l6At z;c)iP&WaE;D2kU6M$|}9v_k)ShAkg*+-I()Wi|5{@VF0&syiB1uacFrt^+BTccL=3 zF_jkJ4#k7I~VhwzAj(bAgW$t3)2s!%yx zLaJpnbfQ&qYevL;Yz?M$?S|*1U%u}ua~wxwhsLH?qXDgzV$_#8pO8*sjph>9q4P|I zbzUoy9SJ?f%^myhhb>bQ?6^3!24quc1+2!F+Hxbc%Cc#tTdb<7Te39ZvzhHNF=YR= zat5U8C6tfwelN*#HpUYMWKO!qIhK(XD7FhnLb~XQJiBBdX>DFh&AIJe>GYR8;}IJL z@wj#i^y_6<21Bz?0E10f%?u>54EI2$yj54VGbNho z0^?kqTUS-?XdtPcZFcC?7b%1;^r6(b<;p}H`7NK3B9k3zlk85FCE)(`v<1!%_>44U z{~OvM?+xDI;s#H_oP)8-W*uEIQHU^V`?&DdEo8hu`ua6-S#c((@I`oeL0d`+7gJRO z@sK`}3Uk$o5^F$BylpFkO} z9cu}Js$PUb&lEJt98r3E@R^)+mQ=Gr#vGFum@X6NIV?LR;8L5n!J|Zu*N&c{dPzN! zPw=sWBn?8H5=1!KXu{VPS3g9Zb2OMsD7X=j(YdpCXB?zv)o?L;qwKMzf>Y5;*_X{cTXbzt`XjG+cEEpkQ9+pEJ-`b^P@;8 zZb3WEad)W|FekpuLPIWbODuDjkGR64G;^5~m_;g(iU`os=_0XtD$IJ25v|#=WQj#r zWogQf&6|^sgOj456p075GXJ0ArK$PKzC1AKSjL6O59H<2{S2CdNjb2jyaJ+O+^+DDJ#6|KIGSlPdY{~7up?zZQCKKyT%fyLIWt3 zdFK;604(pb?AZF`L2juAAeT8+daL6^>_Cfz>l1g2MCt%c+!tG5*;Kj`~Y^I(IyGKyHru0=r35qaKm!IjBzQ*QoN< z+#p$e6HBmDb9oFeHp5@*eGJECd8lX6J2Dr$4IF&l&o}^lef2URv~bWn&Gru{6HLUh zkBq_;BhRmbC7>2DDSK<5O>|RTvyXbd>7@Nog;Om(NP`cgj z?q7<~bBlC$QrVlRUI$(dr(O;{@z<<<~*l}WGYDP z7R)83c4i@U=@-?}5~_jenTR4xEK-z}={g(uK$xKIn357_hjv(T5d+H&y6cMxh0R;gV*;s(wlD(quhvv*_5T~^FvOb4v zHL|Gq@LvFOUeXV$#r|k$#^?=7C{sV{nDUpWlCC+5>Oc z#^%t;LOAHg*q2BgrFop2^$W5@IZ+pO6z`W&ArKGgEG1Z9S_QC5zjJ^mdr1@p?;PCC z!_=YCkFiMCuf4miM;HyR4K{nUGarRX;)o;{2fmRbJ_HgOq+tc2Ol z+%`aPf;&RcP-7Br1icPYX10B~K52KwdDpHTp7};@#^eG`qhZVzctQMZxY1m?l0AqAjCwZeqBQ#bxz({kH?wVPyT=*#73w zIS@Ys{6a`nU58$PiH+3`0hV^wb*9RY%g*K;2ZwzQZ|BlfHX;^%RWIJ*$Q7$?Ag0kO zJuhzoz;p9Se)DwH=Wsgn#`x`f<8~DtYh)+qPj8ZB6!>Z4r-xB`N3#lp@yu_<#shQ} zqcY&NhhJef)o#>km|fd~)7QfPYmu>Wb;o~~yKT^;B%Qds=;$NdkyV>?-u2<+&rbGb z@C9SKbMOH2ZxK8i+>-aR-HO0#x-SPKt4Te)n|;+D z!2;Eck88efk|slq?97n+Qoe8LicJ8mUKg9ZOBQ$>+8D-RttlyPZawIeQ}O_!!;UoR z7!)|sva-H3FHzk%i3QlS?Wd>d5BFXy&+@A2VpvEn&Ir7xEGc#{MO%r~3 zucRlvAnMz>3Q{=Q7KOzc9`kx*5^hZKwXy0d@K#yE&GyL6>soY(JU#`7avB0WZ5TXs zGvYz><_lJLGhb?SN^3!z9IMx3IMWSh&pP{O)>&LtMTmcQ79o1nKT%zN#U(x4k%DfaTF)!7;9zwQj?o5Mj0@?#FSvj{Vmr|V1|DD5j)6h%@H}RjX6Wxg@hnUjtL+GUq7R?fbD;%VJE%?WfQURR%FySPSctv3?9806QyIu%-M!0U?654h( z>GhPmX9zTCAH{ioN<4hf^_{x?JL!n{NjmzJ=hvJlRo{~%re z7elQ7IJ!};r-t%_8%wp|Msp_t8UnC zvcmi9{7JhbQ7lk#g()JBnk_pYt_EBIg)H@!%v>Y4)*0tw^cXGvvcXQ%s~_s zC7Nr5G!bOjqrquF_KlUa;rIRLQo)mmwgpaAn3!?hKG_D5-9nRz_KZRfaxD z+&#^Zs%}3(*o8C7DKF9V?gsc%>hFRrsj#~eNbjV7L(1+2JggB=!9SxhLGVW4-vIJk zb~kpluZ30wRM2QWWGo=`H2fYNIFTf7H%1>rP(F6(i_`Ya7Ji3X#V%WL#QOHPnad2H zOZH#F{l~nsCjTrVRjlEI?t@5{LjT5}4ohv(yQ#U5cm8rjgS1 zAq)8}Xjqh>kY*KH&>I5O#v7T^XinSoZ^E2kYIK~s2}bUa`8Q02siiBqH8m1Iax=I0 z4Qwi1bf3T4Yt}Sp-c9 z3KwB@>E=ZqIwS(<@3iK59j8p1=durt7ibO+44Ind&Z+Gt{1;C+_?s&w12!$1JGMcC zQ?ifsQ5BjD=fEZkS&##n05PtnSs54`LwyXDEVdy=(yz^Dm zJLkNOqWMi{M7MvKH7evp_WNirN47F|^kigp7F%D<7 z1WWEKqSXG}fN(V1`z=#EaT`=2hO==y`Vt5qyv7uD5OfxPoAqDB* zx*fy1+saG!DjtQRyGwQv9L?(wr`(vELo@b&Qv1E(6HGSaAmkxy$}7{;p%Pj%YKE}3j*>r4>`LR&O3_^xdBzFxQ;WY~I_hq7Oc>Z( zBVcn^+K!w49>?WuP_xCES)Z>zLhjQc?h&28-$hhK`7ONaM#Bo65CPY)C z(}_~U{U~*!_uz)>S-}}!s1L$8Z$gz0QH%0wN{XY~orqgWtxjy+j)-|;R?FZnz6ltLE$xsJ2s6h)R?T9r6!uQ!gTlBol2?YpyC&eA^G zsUc~AUrL`3UE|`0E0N;ErxQB(v>J zZ+J#cejRLk;^b`Dp8c)%@WAa)Hf(8^!htHX8>XWI)R+c13a=ziOzgubZiY58&zI93 zQj&F@V__|wg@xOxGYdMb{2UtQ1qnti{0QetRO}+ImK*505BL~gJc;k8Ie!PGm;p*Qi+5aZ2{g220(UZNU zCgt=)xptqcS+7B*D<&BiqsgV09}Je4N^Ds0TnZE_;3Fo;CjtP+H6zDew-KF{=YI&MPi-|~TPexJY4>HK`Wki7N?gH<#ksgW&kfY6<0 z?~c9XT3dh233^ocbwDPm{<|(n(KxM^J3lw!;nS$#>iSYix%i3tfI_M);?v<2La_pI z3^KceRuO`@bxU4?0ilICP)pSoJ@h$R;98)ncVtpKv{v{uG_cor7nu4b!j!60$hlF_ z{xEI2DNTI?Pkk~)xW*#9!X{)r>DesC0=FQY&D`BONaP6^Z9-?R>g>5U->ykep?fV& zL;<<~%o$dwlq76a@l_zG`;H;sP&)cllq`sGv+dE}uIK~4o;{VzbGQP`8M_XnEum73cT z3bDw3%umDeZV@k%EestqD(d=0;v(L$PjvP(yfTG`q|pPCq|9v`1Mj;Zf4YKIpM&{o zgcml}pZ51}B>OlUU|6pYTFzy~P2K>Qi3gRwxq)G|fRL;H7~lhvtAOtiWl4tqz;ASd zgrX`%F{{~oT^1_>MvT~8^&rl@rIRbr70OtJF{AkEBco3NISS&;si}2HE|!oOehu#} zXQqZ*Zu5>}yFD7MGgH)(0b?O{uqFyE6gHcS^){@?8028mY(JuCOpaH>jJ#v-;bWYl zvp2yOP*&>~%z&>B~O{=FGy2#2_@<|HlEe>j4H_l?e}W@2N2YmTQdEN;jja7-WHHuHS7kTL3UiJ zy-tStrsoNNnV3RP5AO+$F8JV*bdO=yqC3p{htOtuzLwRQ>351f_OBlg65UW=L(k&n zlvN3rGLi-0aDMC6l+dS?sVadIR?yu{bcstZGyBzCxb<`Z#}Y?H(o9qKvS83>`@+9N zM~zI3?A4bN4@LX14Qzkd%l1(E?Q*`I^)@tUJY(Md4I zpmvzoR(0^uif--Ir`}Q*eIALY2SF|_pVr-$s-*3YureGdMl?ez5iY$uGrX#15`Hduz`Hxd9p$7zF(ei*A=&>_7DO{?W*` z4q}pdcCq-v;e^0DemNQ%zH7Ek$1j^0nBkbUbW<_h)mD2n0fxF?zTsZmhIVe4z76g- zYZx(|T)`W1h05^Q9osHL=ePKF^u)%MXl?!Sys&daA>9Cj=dJJt^Ozj~CF~tsR=C#u zyLH9z7Yty_a9Jn-871Wl%w@(o6SeKSD+d+4c#~Pu0ZyBW4i%XAg$Wj~%YHlb(}-jlH#nt=T`TCliH#w$+d$ zFCrm^&X=eZkyg+9Eh-snvZ=Gz-GUL@atEN@AzHHqBYUSmR+)7gDM>OloglJR%_%mz z*t*?3-u{X%6llarML>^{>~njzA~eDmpXLpl zf%^ds*qp4%OzN|UZt{jpeO;HI=}+ESgfM%?sKe!*Mhsg;IE8kil939WMyH1KbIG5D z#6Z3e@0h1o?h?+rL_SV8!&RP1^vD;PkA)J~X5=swY!mPlwAJjoFYv#s)|8*U?-Bx$ z{nt#_zrNJ}+r9t4F2|ENN(pd20TXxm^BC z68y_+;XgbU{@3IF$CCf!uOHuUXA|oml7344+;)Q%`Rlrr-NaWKQ<>n5mOP+cy@69F zg!C-oA_QOFJUn7;v_N!T7I>f+*HEsNY>lIcSgI}smOMh1k1&{I4xg*H8nffaMiqqs2`gRMbOHq&V z_-^C%@a5v>@CAApzP2WV8VI$NE!hR{(&~6yNu%CA3HzrvJ}0Z2hliV2ZaXLELN4KR zXNGLh%I)NA<)+xT&uBHc&?hM)92?P5d>$cb(q7;+Z{M{UeE2VXPN$!AiK`vROWv4W z8!OO-bZ@K~*~l)O-?+cfN?jW@$fhKh5)@SMKE@luUzrmQL$UW$U0j+@WSaHc;lY3ZHuJb?`{l$*^e)iaQ!{i!&I4 z<-1Tsr6O{dq>Q6a83AqUVH$Rs&NEF|nsVjAI;Zmk93|$>Po8{4Io(PsyXGp%qt=t{ zWtR0(A~A%CU@F~GlDt2+GPEZriTT_mX*pCsY_(3G35}n5%B3vzlagkKbKw0+L-nOD zLd6jB!Z3j319EkZ(YVD-G#P?_^j1*UHnqm7S;>JiOj;d9jHjJscSj9wD(NrMR8_4= zKqetWcXxUDYzw6|5m37=YGRh?4NIC9&}T3w>C;PTi_jlu2&m~4mZ9<2DrQ0&Hdj7H zbQ<;WM-agU)Ijl+$Ip^9*@D~)>rM_o+fm01aG538&N~5c`w7JIk&IUSjmhnU)MStk z#10-sSc{9Tn0(qi+%|<=itk-7nyY>;pTY(jUY0YYZ3u24Ly=g@`~xuU)g01X0^+Va zy@d=9uiCEhm{ppE8j;;yGHp8-%YsUc$@&5&MTKImG7=LK#Y2h_THLc5U8@^gZ5P;O zfTxz3S~_Av>HD>D&X`mB8SWQ&)@AtY?|Vmyr90kILu{#-8!K0HzhBl8jw}0$UVj|! zu~u*;WF*l8R?tf3&@&aF`LN>YASpkvQN+3cw$@zX0@BZMTYqyYiDdI+TCX|olF1~) z6@wdW$pP1;J4DIBe?6O`(NPYTLj2|Yu+!nWNIud>of&+GF2yzqoHdwqal^DgfD%yHy%pW(TGB-D5bav_q|0+g|I{+gLEc!VSuUYRH8CYe z?HdL)=ou@RXZS;}s4*L_h!v6v%yFT;B^%qc0Dk`hGnF2CnRQmbz;E<1Vsc&b;O+`X z=2)tjPgNUb(4PSwpI5HP9Ho`hj6_M5!;P-JF=z2tXry|V}Mi6j@Q)J0|A{SNG zD*A0)JLjnc1qQ6J5{Sxq*>)SJz=9bUu^uNC!#|jYQZM+jNPnkKitpCg%PwVYhv39h z!E*Vzas{r%3juLgxT^4haZOH zDv=dE;YA~aUeUX$<{0af%)phX@+oikmaa5R0K%kW6h(hnCf4 z?cuT!_(!b~W&H4Gb&OdGdA-6^d4!->ur6lmu6r?DN_kZyf$;4X9K=vtYf^-y*2oZuEIz*c! zcy-#bb~1mT?+Hjku;RUR(n@H3EfxiIeMl@wjx|t=_IRR}duWyp#MbQ~uAXt(Kv{yA zFF*f*leIsfHQ`yRRr|vtI{&bUwEs7t|G!+VVN2$@(5(#F73t0iydG33(uh8K{kiJ|hmEJk6AMioFqGMJrlKuaIv(Wx*bE z=a2o7iIs4<{r8b>sn<2M2Cy?cy>w)0pu2aW5lM?qrn_LgAUS_r4%WmYhIyl+%$czy z=SD;G5kM2ylr?0%(=hkwXZsCi5oOK#K(sNGB+}$pWNzCf4!k=HhrNcEkE|ODasKwQ z-g%_j`M#rfe?3mkyt%NA55s^7Md^N3b+roE2bqp2Ih99>3Fj>mhTM^i5K;9K5r(kJ zvY|)Moj)fU5&is>w?q&ldSJkSivoxZsQI>gwWu2b2brDS*z8b>nemX z8jeij4#v=!YjY})x6l9UyPZc!c*;nZdrZ^6NST6?4B?S9i;~XAMdDMYE@32{OX??S zqo61oENwB~YTwc}-%~2L`N)lzlbuE@o-A2J`(E-p_b%ahjq#AWWIq0WFO|w{Ll|v9 zqYj!+dNH1R|CT)sHo9<1^n03$;J{R4l5qUw3c(a9QnAo*yf6XfF8yw-fRYK;3N;c$ zyDYG*TNjCWQ8wBW4^P()QuNPN8VrpAeb79sY%2 z?G(8@!9TKnQ0;eDOwR7D5$5lj0xwF~mY@qS_iFwMBKso#ZuQ!*cx3kXY{zR6nSFfF zvjad;IO=S0m`t{9U;H{qo*DAt8uoS1AkTbkPmGC_G)^S0&>6YB0xO7)%=(N!kSutM zAjul357x1hSP}rB_q$f&7RR>bT^lj&ENvE1aA@5P{TLL2@u5`GWK@dlItCzPU)&pD zUCzfo1Qw5SiI!H4YXBX$BPrniM*BWW_ANNlZJMGqF~*RdEpLf3n2t!$kGmI?LUda1JG2@9Y_?xU&$bX09nOBWNZ1 za4j(R{h}+9!a6UguhRkhv7kWI;t)?rva$!*Jmq_;A~Pi|NAh7}D-*iEU(SqUK|WQz zp21~I$PT{rBXKHvLp{os5ZT#0lO{k{JTbg`?-uWyKjJ7 z<;VZQ5&l0eEBQYr>`o>o#{XHk{e-{j|D4AXxEa7FXiiu?LqwmgWtK?=kkK4mt0kaU z)-158K`aheMk9i##jDe6)vG^<)Fmp`S@sTlapd0SdA_-dV$hYx%n!1!&;NCVh=5BV zkU(AX+6~ddOsOB#%sg#Q%N=!VKA1o~MY4`q7rj=I^+l@~6UOh4nH0%UCWX{x9a1>Q z845Zhq)Ip?TBuOV-t~5K^OpX@u_T}YAL>zsfI2E=ys{zk zo{g{NbKfEdJ&`w_n@q67J+~OU*UVJNIsss@TA;pBfF33OuGfy9}4A8O(d#5*URa5?SJGUlK# z$`eb4efv4RiS;|KWfy{X+i{nA4)>?7w(i{m$DCy#*9SEN+w&Q8VaI#1bog3*<8C`t zi@YNdQ-B~iZyVUO2gqU6hS$~?M~6FzMkn$4--FX_lB$bTcSafKbuXsS+1k<8`d*;N zRtm+&o@Amb?)SS7`L*>tSXzS;QVwL9BouFr_Qquu2lGRs6`jHKF;a3b)gj7?X8<$v z1EMCd@AON~@GAL-ag^cZ7d<2XHU*)fl_gtL+x3D+a|0nQ$N-MXyZ5X@8z~c=uZ6ev zy!>%am(ugo>*E#6c9v;dg#jIV&fJ@&dYO_C`~5VRAK7CSqzpCxQeMDm-yK1!S3L&S zT{%ojXIGDuK+5KM(3YdA!VC0ixqXxOhcxyAi?GF?88mgx`y!miU!Pq@pJzg7?qE-_ zhKy{@g~w^afh#0{C8x)Hi^upGsNjW{hrY_#@7%0{l<2W9uB0@>JeG$=d9_~y6 z%;JUr@abv0OK$Y2Jf?UhUP8J=~2O5f*N=-Oqb7#uAIEa*!uUl1ctbL^m z<(m(pIxMfBZ9>?h|JX01P;%O!7_98BSlD49liZfM1gN{6yHmuMv0Vg@1ryK3g#R2< zr;z?QAS5_wNk1>(RNApzF>D4tdDq4AEM$N*OcJpn=+7KqLwa9XFyc{Js$zKiU5CcI z0wK9Ym;k@>YF)mj?xo{4s}!!Y0z5#>SB#ZB%Bl3&=yhK6tNO<2J3W!9n4vtt=s!OmDa5pd{xJTou5JZWS;tCH4e7T z11I#+l?Fal;Co(HzVFxD=2aMbvW&i#deU4qIP+P31JhfhKlF`+sNH>Rb#RDq?$ z-wujb#44=lgH@!~b=|K~`DRX~jBx)ACZ01YUU2a1gXW#HL%ZE>zH3VMCrp*O%F8`y zQW_G3V+tf^`T2dnf1~-ezliCUA;l`==*+?Ny|de3v)VO z$>vY$@>@?y(CvcC`$hWN!z6xCUAwnHvzF_wD5*_3W91U!2{x|$gN8vx-pbO#(D)3~)u%M6?$_o`qLUQvX4E==nV4K_n z;jw#AuZSxID8)Tf?xx*zQWR=2l%X<4*2xOx`|l4>b$9*wkaJ(@Q!+*Y9?Q66D(2In zwE1WJ26HIww$#PQ2mK1bDM+slx_PVm87bO+7A-RSs%mY5kH}Cub?E~QI+qjn&9Y&S zSEfF#8g~2Ma=0V`nyRiRYmXtW-;&pl93`q;&>>a3%ea8p4tq>F{a4P82?LMkN9q80 zHYmv-pwlZe+T7XDA#}GIsC7FqQA%|kx|Dl7CNYP~6eoW0O5o8Gdy0)H7*jkK zJcU1fgmVO{=QwSb&grL&`tHWYZS>B)ZPv=X8GFoF3c87Al5#zqU@ARqt+(xfv-=Vh zHy=jlq+$M2RD>SAAV@2)FFz8+u{02jlfEAY?$lxIRKDAg1eLNx+Ou0W;mwR0{6lZ~ zCC4{Ki@HxIhGsW-xPHEV{$ch@pEEruaU8@2F-5S9 zq*tF7R7mK-9!}JcPrF4!Qi3HgcI1X=%dw+g9FKO5EM!H30#Owp+NdN6W7&j&f0v&A zX$q{*{fHutp12Qm&1FsCfr?xW+J|YPu^=v3C<;5SA9=amR z9#2~vX_Zs4?Zeg~W?NVPJCItxxuz+?(qY1GaNjV=C!yVFm=Vqc5k8A@s%R?QObn^! zPZV#^h!(%>+_nZ;6a)WigZnSdfh>FjO9C{Hav_x6IuaPwB7NVKE`10NKb20e0%jU! zSdJs-FMszs!Z1u4CMH&mPTkUR;RYeQ4K?MA<4$HCd@0%tHcz`9Xss!|L*wq6oQ5S+ zU~POE`m7l^-Rd~}L8u%nVRC#s$s7{YyC^D{R97L~wNa`@WVrH6FTcrMrd&1NkkRHX zYr<>tl;Y-a73N!NxDpmDt1WCMe8giqemUMDK5-qyO9MK9lKweUiNN*@0|#~ zTJA98)TohgXztvtd-xyroCG{~@QHC&s%6>ga-_icH{#%WVCNJwcCtM%YEED6Ksrfp zC;g$ZU2nouMjDVyHFbjzzmx}yB!D4l9jX^I0Me$+TiCYydIJkh{nEz+SwZ~Qt*8Cd zPTWTvHc{VHK?Qc}U0T=aGjw#<;f}EE6a6V+^QBrRh0^kZmVVSYAh|}~ubx879|ZyP zv)8|54;Q(`P&vC&t+11z72`_@CXum@_AMaa=`V})`h(dr~2si^E!x9WAJ-^lOu|Rqv*CTL1 zgK2{##{$k@>d#5h=V2=A_F%#P4lQ^DIKNOeiE|$1SzlF((FDuN%EsfNwDRfImC&lT zcH9N&`<}v03QX^s#f27`<@6lq?o507J=(;{%CbnGfJmjL%mn#LUlj4C2yi>6PCnCRmrvT@1rl@`{ zC68c8rV^yFW&N$!atB$rzbST~hDO;e#}`drGtHEO9eh|E}M3Fn3mg~pT=Ti`jnM7iw4qe zf2ET$J0t&-J5mh4 z0_=Z)t|7R8tBU?x?r`+5ceeYdDWj(FKZJbGwd{8i1H>k-8T#}k1{w)0B8j}rI(iX; zdBhupgyEPH9QPX^&o~ql35o1dD>mtXSd1QBowuEGrAP!v>NQw2DG98gv;54`iqj(1 zC-U+t^eBDn!}AURVx_73mxqyI@Qmqq6$pw#k|%il~-Byw!NR2hIOacPl03T5NQ z1R|j8@UA#0e`6yq%Hq2YGHTNT;-yM*90@1fmFUX}C(L*7Mza`>$#8ln&=manKIa=- z$$C4K3(NG7r;QmUrc@n0oWB4K#U&~|#veJW))H8mJ2wiYgD0ytv}EGTBxX?Kj-(Ca zK-?0jTAuDQ$!Sjg_;`c(1Ugovr^)>Aik^owfrCFa%w3_O`HJv+mRD43nI8&3nv>3*ZcEN!Lbr&Y`XZcQjlp} zxcTu|-Mir(B(W_M$(i3DBKt$EnTlMn0hzEH%Ln}G{~NXqSwd?qmmy;ls1?+KBzNdu;+=nW5Z>{P>?ws z#2srg5^9i;Xh_Z4;KnrA(T&m3C3Hq_Y#k@NY&EmXs`OE!d*R1@?Anbv(g)ED7lxo> zwSC;bPqwWP_b?j-HW#Ro94~iwTy=#jqp@^Pxs52Fdf@65dx~w7DoWMcfAzIJo*K37 zu=WDNhfc%c3i;A!^ThbjyIcHC8-JL~T*4F3I|lE}*UQ58Q#PT{8=?Tev9z!25wXoccI;c0Ui9+6I+FHNT6tTtdX>AD#aj zX|XO>{~h_uvyA8u&;!3c^Z{y%9T)7{Ni>%mmW$ijm*wt5=gP62%bkM*{m>0%)ty6= z`fbhN=>6Asw6E2qEoO>E3fAjysre^7Yr;|XM~f@){1cQwX=RvX=%E2vzx+(Zkb(ip ze)fC?aE!Gcs$l)gFeI>PXQ@eFEv#%Oa4H8C9&!SvE(7Zy<}%6Ai4psD!z(IA zzLW0v>GK~R4Rn3+Dx9zVZW5*T@!i2unGs$Yv)uhlwKG!tM0>~R2a|&H1w3JfgV}Jy zK)WsCIq2Xyn42Boz{bpDYkg*9N=uSzCk2m1jn-rf?NKBJyy=aYf$h{eE`>&$dgqsd zzjw(@H$Xn2JJZ^m*`<_+-eS9K6ouP*6URZHN{$%wu@=2mraMdS?^K)IqAp^u@-=mR z1z^bF%G&!{>Z;a}L-i-Z4bhfWNlnP*9>AN*6Uip;!fL8L^ta4890!B3^w|a^rJ)Zn zPzy((coypoNBOHX}0?4P>bC#7$fadGx5KD9W@z zReqjKVG{#+r^qKhv|uuY|K%NU5Fo5@OkCz|{@zGm?c!#*R<|07_z-kX*<{RQcso0$ z=wA>qQ!uaxm#c?Se$XMY-K6?`6#yR?#r7yc%*kU&@czxtzz`id%ZCUM_wZ^bIX3WrY&RJ&1F@nC!Hov{l zL;q0J2f8T})HsK9fV$Xwd)1i<>^Z4Z!Q^Ta;M~o=-|yz{=<|K_u1?u8-hSQGY4uz^ z&x)=(Pd;e#`0d&QO+p=V1)QXUkQfvnahBq=Y7KR&H6hSUMj%aiWQos?D4bGTc2MUc zNCPG|;4h4=8K+Ta|84KlJtAjw-5lSIzI@N@_>|c=HRZNZV$6JQon?!O@-wU6JN<3H zMYBE_b-T+nJTm*Kh5K^>Uc%4T-6b3H84oye8#Sr5oy9WtLTtNUAb6Rk(G-ybt?oc> zSvO{+S1;Ww%z)zK$gV{zMAIbaSuB0e1emAQzKhr*GxAv6v?4TveNKC@E?X$BvpKs> zro5)lWA`fP7mYXZhp(9{Q+kn3P|{PkB3zkq^5v7Ic(52&Ecce@`McHVJwZg?9kk7Y{S{q!pmSMP zCduoh%HFT43y2@vr}v*Tqxk39oV^iiE#(I+S^H5>|6iZYe`XNc8UKJN|5Q3j)3TfV z@T0dr5%W3L2DJ;2`OFE$6_g_4zf=ga;G0>Wh&9G{be;ke3bfw=xD9iQI$MMgA@jHH zEL=@p|LpEchrl~hOKLbUfS1cNO%~G=r7;GIg;Hq8%qr41XDSJbBj1Zk*u0;;6*<^& z=&X86qAY?HnwOXWCkf&uQAQNZDh?yO$W;WWahy}4pflr3Y|0NuSFAU{SM_IkTQKdp za}^q+o+{Ekh~5A8gF3c0m;hy^?j^&t!(LU7N7zD&bX&nD4?RvDNX zr_!M5qEghD4~>RGH-B`GFXL%qE;+5DpOiQ`-fI2J*N5*fJ3eblc*Hp+=x_yOWss+a z#E~zE>kbB1l^F$yz<3!=)jTH+{>{SK-?O?|XpW}V@Ww`K@q{4NHI6h24l;?1F6kDj zZLue+9=3+=I=_}c>#8S7@+b^8bACv=)0E||3**ng_R|NvwD+mFL_w_u;4!j|{Se34 zPmKwmgJ+o9@At03@z0nJ;U8lH_C1=5VlyN8T|?0001H*kC3q-^km)*?^lFQpt~ZmB z%}&1^c8k>{q2a8n{dO~B;Mr%kTC1FF`!Ed~V(Jgtvd=nP049B8s(XmO)evX!JS3U{ z!R47IY%cHN;q_w)2Hub@y~1K^#y^7wcEnwrVPG zt|Yu}ncAchG*;z8V8))$I*@J;hyaiT1^}yKB&0^K+#}d<=@!Xzl zAAutLqA~SqZ!(?0jMNexiO6EjygQo3u5&ojtgK&MtUR!BtsR*IClasrb8MS0Gu4^m z(U~K}L^{0HfpKeg?{%NbzkaYPgBsDyh*f2~g%9%&(Esf1_Mg4Y^{F2H=LZY~`f=m< zkEzjr$%ocaW^h~VrbI8B z&oqRt$P;F~TU#F*jb5NI8%abVqdg{-+iX9B{Hs%V7>O~!<+>Kk;AxpGasBFRb2~|1 zJu#@XYAP!GdxmkE4RBjb>~|qs<_U$A5)P0^0K;m&uZRu*-nvR%@`Y7SF*15EIr?%W zGFvg?o*Du#6lPg64k)vEAgKyj<9cmufON&-P!T7rwD_WgHK*5yobhk*M->~n&g9>D z>z;k*wrHK4inwxkrM#-YA&->Sbns=MtZs#ChO>K+KGS${`O$jX&Vuf3Y;D`*+`0NEF7Y~@AQ`4{;&(@c6%HYF1nnd=k0K$-9t^qbX@Xq zc7!v*6S#wvc2LFqj>sz=UF|j2Ee%i8E_6}NvNcA*B9E2G3Au`e{b;b$Z24dX1c&AM z_%8v=vTT+1^~}JIY0(>wDygR*!^QtAa%f}VY_8{IVPjlpBL;fb|n!l1J=JaK`lMHsCTP6i&dh zj0EK^wIY^S#ng@hsWCr$^xZG0>kH8pb-;$0(Ho)xIpIu6FGXWq8cI@S?c$60vPdfg z#O#f1ZU@)>j+!4oKRv7^55^KU567~#zNE*RNo;z^`g@->yXm`*&c==ugra3~eOiVIfgMFA?KSCL&V^GDMQPHpK3aHn$_2z`Q;p$%a&DZ_omaRqe%XU>Vu| z#Lsr-U(*m?m!W4T=GJm^A2d&MvH`sPfP3~UlR>aG(5u(dmwFA>)9}HmphLcjRXPGY z+o+^G%NuT_20uY)+Ri@Qwcaqot{c7B{!|GIMSwS zYa+lDB%?*q`Rt*Fg-}=lHzJ}dTyrYeSkO$V^Ibovdat-e(+7l>VOGYV8T=ZPZTl-r z=|8&&I~1OJDdQY<0s10UgHf*~&@((lFhb-u1k~pwD*7ereFgwqC_^OiULt^45sM`H z?!cj5=gL;#?ya)dZWCt`U8!k5G;W6WM$@bvKKS%4$(u9s7zA}k^6nAsjOx^rI9h$mjyBd2N7nT5BNQ#5Rz=5`8%IptXCl4n zmn;0N2x?!YejSef5j7`)N@5LRsvI8jiNlZ$9dB_K_cWDc>G0wz9iK%@cAQrXh3om&t zt)A#-UiZ6Eis^#*(9B4wove=`12y_OM)D#Ync-6@3A$9&G5AvI%~oDTlB zCYqp6{C@ndcwu!@khqayoYerB7YtqiJ$`a95(&c&4YUAD?i#K|3>&vjTM&ots+|u z8U_wnI#fq{Cvgo%E@DWND<>Wcb5%iLeVhMl@IDrM(b;5%okIN4mVCm5xE1HC<>8Eq zDzL~wso2+d9a$H%BDc)Xo9xe3@OA|V(MIwT>%F8|h6<~rza^&BvNn&;ll5@PFco$wX~diisAT={g&;+?|ByD4Xr#>HGPjxegG?Iy_!o8I~7Cls{y+@teWJUycR ze&dojTW>~%J*_wm>0)cLxyUHV5kZ~&ywMfoeg0wP)@rf^rPrzLv4ZXDck{fz8q-{% zwnn!-nnO?fi`{4oXka3L5Q?EO&!ikhn2}o638hwM+W9SM^c%KI9@G>;5$>wfQs^*8 z4ildbwK%b%G2g4@sJT})FU)7~l2aOuzlTr2R~0w{TpVDQkJy>My1b{cpDGE# ze!(Q}JvEw5Gjtf$<)q0b70F*j^)bcz-EM}B{^II!>uF!ZvZ(VeTI%hW3d6-66oGCU z4Xdx#rZ<7=(T=ldZ&PpJEvanSa##kQ$J6xto_WB<`0b1Ok*@XXFzLaTw3N1kKe=4B z`{+i~hvcxG`-;12C0%>@xA*M9yQ_r{VRSqE_I{IK630jD5u*47X)%2v{hZbY5U95q z|2y(|n`a0DjZN94rp$<*#Km^c%A<0~y3HL=tw-tXWa6_Tu0xNYBlCW@*Xw&`@$OFc zLpO%=@`HAx=cZ^H?>V>k>q&*q|FEFx;Z52f0r+gx@?AZd&ataM>!Q?K;jY2?*?%X) zG$ZGHy86%X_ufeIWFX7C)xr%UUW5bJk6R!IP0IfCI9-CmxSJGdh#Z593*Ux&JXBt` zozt{skPQpqV^b#%e;UeEX7lg+m^&@5J+8|0;UDe`aI;Rp(3j`*Vt>{uI9G|2|D^?EhEk zz1%EKfvk$&YEb#@>IpcQ{1 zRg{dG@E3Xf{mMP_zyZWu+-rxC1qw`QOx~ z=I@+skyN6dg$IpvJ;V44+GD^kUehhNjs0=HqAJP)KHA}5rK^nQOwCRZHNwemn0z7qufyx}xOwX3FX*b-u6 zZ)&SxEP|bcM;`I7M(;KvkHoo2C?O*S>~-_;Sk{8Qni@7?zgETd`L6^*^H`c1L4%ai zSA&Dn4OL<(!n>jo*$>YW6}2RC2C2|jMGVZy%!|#zS%O*kf-=w!*L=l*)WXkh z?pB=@Z(il*?G?Wx>I}n@yfF`xe=OFMzlet5NJXKHV#Ba1VY?R09-uOS)De!1|2vU>E{-xNILQtR*Zq|N|QUfHx@UCyL`jrL;!Khp1+%%qv>}LF>19z zzHx$%lyPWm3+q}AX2yAF;UOOE)2xs9Y)BKk93wLqha{*4rf$Pd?R&RxsoGcm?z@)f zBctS^zLNH=aJ-rHF-32!{AaYmXa8j1+ysAGNvn5GiC&@Q+S_|n!u(mSlBGurJ`cAK z>ZNZ6EGuy!ni+O=mct%2RRdcE6x<1i$>DDC!g024!JP__PzFUKv=?|3KD7-dbEaWz z{HFaV{$xD{)ya4ysYn(3mf0^yp{|CZzIAI3EoDViO=s0O~y&x?TOthq{YehcM z%zZh@z*U6hgY4Wsy}g*-of5q;yFFel_wS?{H{har0+I7hP41R&_B0mo4c#MP2tkD> zCdi<7xw}50zquo>58%=m(qQaq&pa&Fey5^vw!R1T(PO!bb7wZu6_Ucp(-b>yr6{b- zufdX56kG+YwB1}-(mO1 z21G;lDAHPFf~UW6I(x_lK4#0>9tT=o2aVV ztJ)qe_6PKRH#$vt=&Hr|+wJ!9;ojs{@qZXpRFRhK@uPxP&QUd58MM_{Ph%KOkC{Y+ zT^aOpnw&{k9BU{{^;-bD7L{^{(r^3nyHum+IIYOCuS`gFM*^eAE#zSD4uJ7og}K@^ z(uDVmeMVS=`qM>D6ATz(7i_G=tSNIm{^bd;|1k5<6C%X8ek|}e!C_F}I_XU7qb(`oSaWyo9;6X_YNp8aarSdWEfGrUwX~1nE?W9?|T-zER{ud+Nj*{KBC-XUsS}FRnLoYBi9fht4vD$a<4m@CY`+ z-~{&&u%0@GX5ys`Aj2cQf_W}uO<`0@kF4MGNQGi$uWY}&d#TrRsHyJmf^#R%PS zVS~BGZZpo!`Equ&-xs0`OSJpC|J;d;6`+fvtt~sS$xDcD?UH7Sa%+qiL}QMjfE*! z)CV4dY6kZ8=cIF+?YAV5lLl3vM(f({H0Lbsnk~UNou2x9>LV2vwfIc}1j~K7OO)0C zrqKE0hf3nH4w{r_g3_f$o7HP*sl`$Ru{p-`iFdD~+@Msa1`f;S_6m|bq-3}cR`?xJ z3~OtN-Yf3c%bE8hvZF_{-`AD(W?50fz^p|S6NZcD?-m8yb@#)%Lu#1&wMXUzFLzURpuc2 z6lL><(mcoHM z_pxn64%d-%1|M7jU%|UNvtqz`IgSxD^;Rp1=p^lnhiH^b#-3fqskY~-?HgNi$dWuWt(-Hm)DP z?==qwI=iceV+pvozgr7k-QN@V6@N0}RklkffLCGK`ONbC^90)T)*Y?JrC&P%Zz_XA%By!TA4C*3L}N%ECy{ z(EcAQTQU0|s!V7Cy53LlxqpI>``_0yF*5&$A`_g@Yk5QuKjOtROfO@7m%aojlmXu| z-cLHM(m8-xJ3N6Yl8~sC66_V`^;6AaQOPP6g<*#8e!TKV2ZgzPj@{kP>V+kpo)N@? ztb}q1P0YUmPxat6^Tk>0I~)*wjwLbLnjxBBGTSktqtCN*8=i+^6a9{o2UwYs^2e9^aHgK)=ucA+bd=f3A>&7`Vd5 zAV*9f?|frtn2RF<>s~VZH7chelrxOaGA_k#E%@D$$*sr3zQ)kDcSpL zaXM_RM%n`AnSI`OzqFqSZ4zwa*|`+Ue14-n*FeAUU`>S4!HxJvzDW%ro(1ck-sE{m4bF*|9<@*@vRw7ORZLm z*q-k{tod81qZ+SBQ(md^`#&9!gP+huRSxe~YyZeq5&;1aenMk!U}Ru&MrFOYY8?q=x3?Dc_j;S|-o3M~n zS87Iz&Z4U+SxR==7n5UZsBi7(0_{Y6`iiTMrH%TbS;e=XPXM1k8D?pOfI;#7Bf=X} z+i%z6`>XCyvG2=2-@ZClUo;Xl@88i%l@V8lf8BUy8n|Js8?ldFtIX1K+h%)feGcY! z^K$PRpJclz7s^_yABbwH8xMM;Wr?+W$scaa5{3otnWnc_wa{UFb>QfneN%4k(QNoA zY}okj@1?u$oRNb<==W}=46kv)rP8;HTlDZ;DPjg-0YHyzjq;bd)6Mp+q3-Odl8}D0 zgj{nwy9sxx!MBOD%=AmARCDe#qNU7{1YfyM_kO>_2+sQTZbf)6%0@s93_-}w@_uUW zy<*t`VIK7yZ$#sW5Ut9F^s2wN`T~@~iRJ=7cR8kCmV9ct{PF;LD0kp;_&hS{G;^M{ z`+W8tr9GGz1A*kJiSa}?CTb)PF~ux?#UiwSBgjScSJPws|wjt(SbFa`BcY{Pmc@ozTT|M<@t-yRIhhE>a5F}*Uc zOh$T}5!5t_4M6)BE^*qFbpid1>u-4#!*XOQ6R*FlMYAdXO>(KyT(assyI|n9S|}5^ z$lr+izn5%CXFad7+qhkl^mGn|uZ?W6>NHFZ6Jb^og-gg})z| znUn`gm>P=I-%jW~n&A}a>jB}Um2GD(Bo%G8C-PMVyS&W6(oB46q50^b%IQRUV zt}J)YR~1vY%!eA-uNCPYCVURev+9|+pcQp?DzlsxZ2yZLO4yf0!`6+3i%2f8XrL_7 zF}1lgr+Y>vW38^b*2*(-aA8TiWqCyMW`c2{ayl7mBH}`~g!c5*~}?OjaUUaY4NI<&(IgTjx<>rwVz9b(VWF zGU2|YDm7EVvy;31to8Nk&HuZUDfba6n<&uRvTJkK za#6gmw&B(G=DP!5KrrW(26oSz{me4?_1Mn%G6nq|G{+O?n?;gM8;X{}b23iFy&MP# zuPn&~gyFtA#N0hcu0|@5szg76jX?~_zkEav2wv7Q!d^m?7Jhbw&E;Y?&-?WB1=z?$ zBj|XthXb|pImY@%r|=$?$J3p#?)nwuRCd~RyHa}B-v!sMw`E6GR{IYA71;44!o(&U z5DFLiZm~5)-aZoOQl(?FZrzkjX%k71Jw*aG2FA#c?OB(bON~W(1SGgdbNo7yW_<6O zoaD8&n-JxJIf2jV0#L8r$%IPm^4iO6`)xZn$@jNea8x> zcL&gac4~9i?lK?k%hlK2;`*>g{IaP!Xd?%zs{QoF*|a&XHPM=DM@D?D51u$LWmtGz z+TE)5gtkQ)B0P@403TK~DGnBY6$gVm!wW+$X$D;$4%XsxHSBLHMtqN8U+$v2RNZ$W z*s>B99MGq7&o4)~MN6w_(nXqUwKdOfax$EK1CI_pqs3p=>01@+*W71g0r3D9t5xW3 z962;AIFl7&LRF}h9IRBG-AZB$oSF4wId~$;3ck!VZ*&4*hFCkaQ4_IM@i(o5)JzOV z*BIepyH&?yE?jj+^?)`(UkG-w7>#MR;pVJ_?sdj1 zlLG>5j+k_#L1&06o%7QF4xh%{`q00yixqfddjB`3|D(nsNn0qnpi+uI8 znQv?7XM5|Hj(ce(p`aQi%qs0=UCcSCKn;g1no+(tf(Vhmh(5*z&o6!jGk0i+=NDv6 zc5VtXM#IGy^Hg6VIWqU<>t!vwPi<;4&#pn!mL0t_o1ad%txia9{%oPGL%=-FVh#wi1ur22sP{F@())K0k7?u z@qU5b7jnB}O$!4=zfc!IoH7(az{IuMhPtB9hNnCJ3=RdR-1vg^o8mt6FGQW!Sb(de z78~SUF4o;9hk)0TEi4OX14$dI`3b}F{YWJ?8|j3LJ?joh^3){K5lAg$F5xg9F$#^5 zkY`HxOBe=Rab|-*lUZiZ9l(8+;(wbVcy7hFgm{?;zu;Hks9%Njef8XYRtA{zZqMf; zAuwyk0JuZIYLll@{7J#m-wjXyY!GZm1hf%>U6lZc%7aWMDxl-bZ}2;~);Wb+pNk-d zLdu*Ma5rQnmsl2l=6LFveV4<2n(@bc6j}B<2tEpbgWN5;*tthH`y)>EFkdZm=e1q? z6imTAkPeqh2B%E|hscqU`wwU@AzD$WcId#&uR)g~qYAO~ra?)~5v`h2pIR8UodeLD zo`j-4;rTxgMDc;%v_9^c(Qt#m#Nb|GY2&zc5~;t}8D$zJ{i1kZ4V{t@{&(O_gmIQ0l}*Kc}xGN#xdtPXgDe5h2_@PRkf4$ie2dfV|+se!;D-*uO+tUrzow?;8FDu79h|`B?veJRe5yxHo#@4-NQf*~43dkQ-04Qbo|k zi&x zZ3*7Ug4s^yq>F0yp*e#(#wtwRcEO$#2qfv=xL8#qFvLx@Pwrks$*# z%Wq(Z>jdhkufc|WBhEo=@m6aq*7|i8)D=+9?@24ar|AylZ}Ae=iIGa4h!=Mp6lP0v zOL-v5T6n&(+0cYdY2p})@>!kq0rs941QEDg*yyt0)ax1^yVMGJe;Fw z#kXf}9&Cw%pQ;6k2g5I>26y6lkeEa{Byd$c1yYZWO}P1}H%Rk`tWW0Zj~9+!FD85c zd*vLK9?K(vE|(=>EwxOd&5No+$Jk1astZg45_U| znj*)hq%5#={!O(>H*;({Ow!~J>yIz^HW_Od`pyG<@|p0XDi~>KR_&!LT3aOQ&xj_1|00W?!@ zrnczZzPZ*8*4$=$pcjpDwjr-R6l1F>TdV6ioDm5GnLVRIuM%pAoBWzzPGVHAd8n3M zhF!}ci9xdI`ub-n$S@m7Njz2U(PIpU2}zOU>wP5t-zra`+US48_`gI~Pinz(@Ovhk z+ZCGEp^H-Dnsn~pTF~Q&QCNx_cbA&9XvGR&%fhe*gcoVp6jxA~Ye`8erEaKqrcJeR zJMT(}0%UljvH73S^rN{tpAo){YNq+X@1D!g$lm1cb_xrm<}v+S8p~p=AScT;n5t8Y z&to^1r;?rx5Uqwma<9$JcttWEgCa~fA#$9xeYEX2S#C9)5wTNqik-Vcs-WW4!C~H4 z(fxaow0q}V8qw3^^=1OG>9%D*s-a6`=bkg352y}J3X2M-iLL;Zs>#KAtg9kDrVwPF@a5vT$pAva7o{W z`I)jQ=hA0{rk#$oO5T2Kkhq?pRq8IGXY`Y39uF0MKYe`de*13`yLhy5?8 zX#?x|ekGhe-u!&h?Oi7T8KQwfb#ALxqvo(y*!HFvtN799sGoh7uaAi508&ao~ z5(rrJgCK-0QhjCc6T3F3PWG(N;)n`$_eERWYU5Ty)_Aj>;x6bZuV^_a|CuYCoEqp z4ZOUO;9iEC2loz9`AQKDwOd*;1Ngl%LXx4uKx!}K)1ir>@oC%+c2dGZQmgH^@3r;CY8m3qc$}o_b(I z-5ojeGz53L)lHK5`_sXHR%F*_Fnx0p|K)&Qrt`1e?hj=Hw`E8Z87s{7&h|2BARUpT zA3LG0VQ7w&$k^`&T`ZE&9Pt+vNjd?Lx?2|wvO364eNIhAgTtiE@;0(}_9}y!525@g z1@-g>vqzlH*I@Y~TIZF!y^*X_Y=??Y){aN$RM%s+@d5RqLW85@SH;%%IK5ofHEaXF0+|iH%dNF{*s#lEAhXmcOEkk+80o+%2W~f- zcz>0>+%MxX?gsO_yW?#%&F?pR!^km9mx5I;Qg$l5@n&3-q{W^V7C;-glVdj4E4W}| z$S6kToU|#(&*ED0Myv6H^MP`r*bqPgRyHj>A6J#ia0h1c*9dt1G>E@qSKOpJn~k3H ztzf>=-Y-yJdDvguZZT((w$q)u5NdtT7HK%<@?8X!>)O>(VGngR$E;SnE*wF5$}bNh zVa&aJj88>$=P_C0Ex82^Z$Ncc6+{>-)fWA#`~-%1`=)!j_1n8&yNBo#G7wiq^hbr@ zSA(xf!LO&i#UK{BbU_^JN+__|K;CzTC?In|-tbB_t^w6*ry+!dxj3qnR!i4%$pn?I zRO@)=m88id9FQgjp5#+_(Rp%3%}ROnRg;;QVsQSxzn&DFH0r7Q{TTtcw)G13*9W~} zSG^#B4ynJu27<8Q=UFN5^rOD017Q&le2&R9kq6iC@;DF5@yrPKNQD?n z6P7uR?kz$uSSRwGCNX06>$W8Pi}e2!(=Q_Vr=UMK_B!_GC%wT(cJhkh4)erC2c5q! z;n*FIlenuWCtf)k;;iFN(KFIy!vSeEnfYX^i_jyWX{H|o|f#$mcWvJq-O=INJaXw*)Y^EDQ6F?b+ z%F3EnbMJWgy5w{J3cdfz^gq=1e>L_x_ILIBDX_#f(AuxSk(7A@bi1BmS^&+*i$%>7 z)K)9?SYEJnzGV?)2Xlit?ut4;CG^@jP;*%aF*Z4|c$;gO&0|@-7>p0^EC2rp_``y{)BR$kGWynG6kj6BSSIWgrcJwE72BG@W1~*3VBd{tROD*P zb_93~ZhTJ4+LSh_mW0=`Bro+&CBr(VMNT%gQSttC@4m3Ue}&%v$oN0h|DQMamJHmb z18C3aym2s@bp{XbblKIJcuOoB0w+SHk=Lf#QQ~+a&nLt}DR}YVu0Wce?pHZ75EZBK z)p|)+v5MAKb4+bzHjr2C`vbs#ObGshTHqRH`Db*%Q(G_5e|pdxdUIic8mf5=6GdgH zci9nCOm%re-b`5^)K#yjb(Jnc8cdIn%~4&J9BNs_yO3Y8fV7F@@hX(9&0({l$44G$ zb&P%A?7Ju4^IxI&w|`9cx3BB{_D-Eo6MLFuL}w1AV&(0|v5B$p=s+S%1#(0xkm}Z; z8*8+%YGdMb*&1ON$RRE(ELsJ2fsrT-5-e`Ipw3Z{j6j$TgSO$W?%yhB0MAx#R(0>zm%ijB(S z^3>~V)u1vLR3v^xXqL)q-v#<#O22;`6Z$oP-$sW1ezP~!qOO{(E47EIC0gF%pd$3b zEDHu>s5nJyrriNy&TKR!;7j4)9I1hIpyw6C<71;j7<@t6zM;0`V0IjM*a)HUhCu%s zLEbL|MDGI8)AKk@x<9aZ{#$FefZjan4J)B*&8Aj-SY+2_o)Ss}mT1S!+3vtiwT3|v zRY}cR8tuJoPUqFaEO2wsu2-~_Aet{0n=wCal3l^5Pz3PglIBD8ud<`%8C&iL_jwFv zT-0yvfc_e6UpDp&+LurI``*wc!SN%5Q0W7=n%WT)nM@r++ric`a0+8rvsTn3hN|`L z3d|QylOwCzS|hXWsOZas>gd#5*DVKA3^~Tzg}rm8e-&hJBg*c?^b&OWhai1w>m{O3 z5BfV*raa_!9yvq#S;As9&siF{B+_DvL1Sr79@%uxTgxb2??WwDRsa%D~$zuEP8!!$0rD^H12yH!Z!y@?KkcicSrb8PQp*ry4UDh+z%k zlBq0=luxtRq7`CEn#Y>qow2uKTb`=LuB$Aas`F})OeXLi=sw7Y z^Un$O`~;AHs9&~kDBAX3HS4X*k>M^;b2<>S!AM>+5f3`xKd(6 z41r(}3sZ4B8Y~YLE=nc6g{R>J6|tE;W96*!B217A;$IBQ>nXwZiSPDXXVIQx74Z9^ zdrtWT)%9X{A@(semdYF%9a0ZdjU5T#bRq(YNiVJbH1asDI5e@sgBWP>CJblmT>0sR&e}i}DgX$*t^X_;0 zm!oH&N|)0>Cp{8NK&FrbVZj3cpG}=^JH*!k>9>F-1U>2a3jX<(b>i5<^_5YOio-^TyE za)9zVR`L09$(Oc_>Pi7QQ5Zje^ca$Rnm8M`RNxa8=*_CRouzsCQ1L~8^G(HTSkgIK z>+w)S;92PK^V(OEkH-jtx4$#iUEh2-+WO%m;Zw8FZ*}yde0+X;5MfbTLp!M-$m6$+ zJPX*+Zq2B=hU)wt=w13rm+O9Fq|M*1p;gdpVc5S;D0_zJ#|MF3Mf`Vi9~?dR&@+rY zZ6410zU^Y??suj9^ytGTc)Qd;YUbC&-?i}5qsODn-V1elplr9aAcu}n& ziaY3fo`-gET(qF+QfN8fe?zq3!&eqr_bU32&#TgpidSoxp6)+%%oiCG{;0%%EF*Dh zhHoB;_#cyzE9sMrTrc{{NV#2N)xu2`%g1~(Yhx=<(A_G9U6^j`0n04N9GGj2KDA`C zfoB7A%_W64^CBNhgn=`{HZ-?prW~lO2H`*!YhdbqHX`x!GV)-HG!U$M(uqg1IRjcT_sU*ycWpSH%y7% z(1X7t-e;ch*O7|Li2Mx#k#%QT$hy8_Fa=2%oi#^IlZ(lWS#hViZ*eTfaoJfbALUs` zQ8UwC;F{qWpiAAbulmo*X8 z(+|J@nD$>}&*!c_6^0jT9}5DX5QCkJJB_ngyJQQ;7{IR%dKkliCWIZEGGK>!)oHFl z0qbTzABR4^(#<7l=4)e>rg%NwcjHA?h2ufM3j<0?Yx%Q+@If||AHC9{TvFkk7-he8 zEC6=nsOrtpL`kAl5z%PS zfRUR<(@c4pN_?2niwxT?0qq#6I>pFZ9?ler-^DagEDxVGk^1NUyQ_D&e(6=l^`Lt1 zG&_98rvIzR(xgc9e+T;_Sd?94zojv;8;|8(5MJp%Vt6fGKg1MVtQ0VrZ+N|Iu<8^Bxx$$m%81Ji`bmDWgXp=@J%?jH3fw#UbM z!PNf}AYS=&ABsg`=jWO1(9ZMp?fJ@cziDqIZ)u;=yuCBL(Cu-w($U8y(R7>xWv{mG z%y+rS2a6(%SR-Q+YG-0Zm}v7|N@8xlICQqrje{D57K-S`GLf)!VwfRVwc%;XVjWlT zS|vZ5obW4Ba_U8wn4$mH!~r;s3HRsYdL(+`^YwCgq0}QEtnE((1}P;E*c}vj-g?@2 zV)3K6*}Ao{U+9~aE`hcd1kDj?KgaSzeZ6?ogJ*e!o?)v0)jLbq>`EL*Rn|_Mvj&1Oi5310?R|^0x>m zi2wMX|MLd~od5p!e~0=At8q!pXSdl|$6T2G8voBex%*QszMy>u;P%e&Lib|?w)`%h!^1!;awJ&J~gpIAdZ%c5@R*->_4>lrS{4jI%ee)}yCpKapF#mkPM~fVbR6pOK3vx)%zfMA{+W5o4du%wq-W*6 za}-IMQtKiZ`_-s5(3IPm#f;fx*=&^*!3c$ftt@c{a$KWnqBYwYeB8#doUTm4nNtHw zqBDmZDJYIOybI%dhSH}I_x1Yp`fZ6GUtaqEz>7EQ-mWq3nXvn&vDe4M&p6Y5k$nQ| zTo{7=oN1QO4Pc^DVI68##-3p-exJ#v8_cVvGOEbss4~p`Y$9j|o#Kvz2za;6P|^Wx zXM!iN9|llJ5IGse5?Pz|Y>JX3$m}_-+{}m#?OD-raujw= z>){b?C3VaT_Go~bRywu5ekswiBfWdh%Co?KW-Rz|=5`Nx&(oq97Mb3>HA@=b?33$_ z+b_Cj%zl-I7kWL;BHu!ypst}rJr*6L^BJy7zTx?k6CN{^aXoay=;TE3<@vOuWq+~g zTA=DsP}HnWHA;e6RXUdg0lIp13E)9LM^D zwRaubpH*r+|6yKr9SiP8x);txadEZ1>59NU;l6n!dPex+a(JQC~9<((pp>W31gOjY@Ia4kkM9KrX!p3JjW>TRbW>X#h0%1Vm*D zW@RgZc9mVN0*lyrqsXo^Ws=PFl=8fZq-@Z=CGt7vY^SS9`DMGtExpu-RsQB8Dtgmj z`!el`>WO7f*B8%F4~Fe`YjjI8rxH#0+kG1t($<_UGIM~mb5{lo4z7qoA*c$&Y$ zcjJM5ZZl(1WtXk>PkQvL_9t4l>BoBXGkDXL1$=6R`$hL61&@W{mE6a&u*+Q0XJr*$ zX!E2zT1wn061c@dh)Hl6+-)_Fss@b!3)u#-l}Uu`EE+xpozGcYEUvfE7jzkq{jmV@)DgJ>k$1WZ)>0V z|8K_pTl1pN2Kf zjg~Cw@o6%Yt=zI=Sx6^BcdU=a4FU%3db0<|8M3wJ3D1P^r(LQaaQwQ&*n{iUNde`< zEKkey9iHFsba_tr%<*T%3%`4~TsU?$O6UbCN~YET{BrB&-H_eg7ULEKZME>of&1<9ru-B3ng&0Y{^EH-PkM#Uq3dyy-aZSR=;p*e&FwR z)9lgB{p$9puW@tRdQs|yuP2WuZ@zB-LZ9erx1`SjJU2In7pgsW1mL*5+pFe8^LLCS zA|u#D^B{2CFz}Y03PshN_3ZgnGFBoc?8cqko#u|4jMnx#-i^1)da{dYbEgmN?tqy3 z*k2yhFZ1EpaT>h8yJ_;N-A&Kvo;mlcG@SI?-?RjAT3-~}MhT$-0%bKGFAFqS$dGrK zg|jIVKwu-puAv-?(>SmkL#skj#yILuB+X6X0@$zIZG=Fw&t&kv6zb8o`?3+#PD;P@ z-`P&|89nnz^vtk_%OTcJzv!#Uc%`W`j8I8g_nfW3=HzI#@xr5zGUM4K2a8Ee!zLXd zT#7o-!SlB1j`t01Hl9bz+BQZsp@-0riuCGK?yuOtU{8@_ zpy(63?%Gdu;;v;oryqD$e?JLKh-x*%$e$4k^DyCEu;$CM!#Qx~B!qTG;S&R;#ra>!qibbI? z!U9vEs=^7erN=Xwra9zNllSAD)s;YiY*R(7v1-2B8J0Q-@7*@{E(=rR91cI!%QSvl zT*1$&iRZMhEPQ_FvR2eXLCoeZ19|w7Tlw)M3WBkvC*f>uvJ}frs0=mYfeE?dW@oRx zOrN6zyU3RU?R#vRCxf+(&E|`v>SyN zir=F4VlWz%0+G0qfSUr%uT*0zS=%U{@5Lpw+rXWK8?5qY@Oy6cpFaNdb*FDH{Kq3; zq1${5o?g7bsHwHD4Ut*`w;mG+!P-*r)EtRG&_+>07TS#Ysp{L55&bpwGs?fxVqMF_3(b#}_E=IlM{_qkA0A6=Uup3{ z&x9&p&ev851UjfoO%q0!mf3O?x)%3V0@3`zPYDrPrK*|BTpVNH zu%`VYx!85o?c-T}$XxTV*!P7~wO@39<;OXf`cBuvr0#6J+IfdS*wrG2@yBGt7dYI+ zOip#?YP!ORbN~_dZtAoO8&>|dwWd0Fi00EZH&9QrYCvXu+Vppuf-+roM5lMyOhl%e zo;|$rE`B(;^<=t}o;a4LRbmZI?`;$1yb<^QS;tG_C-%R*HM~&nvC7YC0e2{8l{$5g zGigCgR7$iri{+jU+=U|W6n|{XdUhDWV=fJNketmc=TJ}`kRykU81I(Fe#hILbU3oA zz*iXn-!o-qH&idz_RWT;YksX8v$XKtlYKF?TmPEB3;EQh9PpJbig$Xn9(-8E$S-u= zEc%H`JUs6o>%Ho6$UbcHT;HQGCDF=3g^{KbrFNS(|hUyS9_^?bHFE zi4AxpdX|)j%OTV+MgVP7+R+Bia_s1Yoak<>t}|m+Hr_}YkJXsx4+mzx!B7gWYZBNo zxjP$>;4a?72m+RTfF>9m0uTskt8~#EBhPWf@0#&^^8E0A0R99231UAlfl~jt?lZW? zOo4re>6Z~EZ#k*%U(LCpd`Qo7Jv7$59m{rS+589-iqcPjGp;yIn+KzZuTA6}N z=dD`sC6p041glJj@wpVO#xrs=%cv|Lq+0=)jS<9GTnUGg1@FwE&1W4PpVi4x;OV*1 z1B{oBkgv0vZYZC?xGDFwoF>jD*y!m=Xe4&aW`lOKh^AQNG~RSGUam?`r$|ITeiDc01nwSAfY;(J{z%99 z`aK*^R3AFbUAs35dE<~ls*xE!S=21Y+RmXFD-cE5Mq!ST(BENt0Jph2z7sv^I2dy9!_6m*uvpm)D;!MAnaoFD-tg&X4nt z{h{afk0dRBJnV(Et$+KWC)N)$O-kKQel)&iN8OLCGETtzN89>Q*k*P9kRNpwC3dbG zeso#n{-*A60g;-|e42 zHrMuloJ{&3>GK~N>)nam=@;bYNx_^5{_lGE!*0{u@JszfFh9Ls{2xDz?)sZg>MwDB z_<#40#n0I8E6P1*Yp+axUkS4abTce6TMwTezp$zk|5xz(Btq8!B56yYY#~0+q)cr@eciATyO}P$or12vEWqFuj`m7V@wwH=kJD51 zT!)lbU`Xu=*K!+s;i+ooa=TpA`4wZ*%906B3}cJTDuXSuL-i_`tbG;`hXm_1c(y*s z#3XSU%0#?H%M-jicWN6)zkcTaN_vXE^Q7N_d9Ep5-vbAHBB9p7A0-!SO#s2VPC`*Y(_Iwve6) zo{g2$zaiLfyXaowBo7;f$}A;(nJDGmdcT0ZX40(Mc0jVE;0H-GpRAL7QBdY4D8Q!c zXsljj`mS`7Bb{?QX>)L3LBJ$OpTKP){fT{Ee{P0wo*y~Uo4Yw~Ly`JI^+@yt*W=~I z*VzNIX%{Z!kqB~uteRz(xh&y28v!V}ZM9bd&*gJ)93JE<<|B-7YIw?SB3l_B_R2BU z=$T9~GEE^XA)Sx*tpZQ$h+qE`lf~T5Ki~2qeU8NsbRW36O(ipoN}# zu>lt@BE+1|lTAe-c(F@-lu;Upt_uoZ26K7IO}v3^fEp%smB84DCj-ioQZd|5V=qur zPB}#1#pr)x-}rdL)90+V7qlNRe7@DGr`@(X5HyKzgY|65Lt9{5Gm70bP0;{*mDE|w zO$WZslpK7bzdRxbo05W^d2mRK5OdOXB`(Xj8NNiJ(e_|%3jrC^(bt<8zx!c;PlU|< z^8_J%GqxZ4)MELSdn`CPyTRzCa;X6;W_#5 zSqz3n;yccpdmBDEqP^C_8NJu;_Z{nY5R}RXRza$2?N063IgVHK;)n@0=YY_%s0Jc6 zC}47%SvrP9vd{0RZr-?P=p*&44f#g2>^&Vpy0uRaifhd(+5LLI@k8|S<5xcU2R^Z& z+iS$Uu(UAs?45HIKBaP>5q^U1TD)|>9?F)1e7>{*7U4_O-uZeaSu7o71MO%>nh2D$ zDYDp%ce7?@*P6!1LNxQkYsADDw^kVOSy%M|034Zlz-FT{Q4`RDN zol?Jy*%wH9qI!Rdnct{Cz*_uXo3>#V6p9#iZ!>Tg``eh=0XlL ztCEMhmYNs|Z%nNWZUIp6#8na)IJ?;SFuslCDIAkpV>)%$RGV+ur|>(j(5tw97o`3< z$Jj8sJzeN_t2Firlj0ZI6NtYG!wa<@2?iZ198a3U!={rEqBz@J)u0e*u0TwVTYCR;_(HDL8L7TUus_22A|@TcsIH`+J@`uda0zRDi9 z#<7cIbWjZns@SLHozH>mke%)AGw~ z5+xinXQafPh%-;3cw!+~3t|dMb5dLZl7bd=Pe+c&r5*KhQD`tZnP`4wTUrC&k z#Cb`nUd07%et?=mkR8b-s_Z%0hnDtMO;jOPrFM4u=!LL~%|Wvy<~1kor-Gh@DX^T~ zlQ8KV_6*+}_S{cZymr*>TRQtQ+Tl0)FR{4Zz3A#5I$%yYQ!p+IW-d&Zl!Du%{dlJy zv$!+zb~Layva`(V9e$8veTN-2nNt%?71=;1W(|N%d^5)8TYeR~6|g$8h`-(|t2@N| zbtK*%lHWUa_{HjOtcUi{^799?`Nh-UQX4&5-FHtk`;F&so^1S9CZGEa?6(?uDIw1u zT{8XjFtw&^7qZ zq13NP-1o}Mud}V+Qt6)=2XBc#myd^A7e(Gq8vaJfx4&B$~cFY#tPZ4~0n-tW@IVr;N`ax)=KJyz5SrWc1%lC9OseD?FKj(`iw6w^g1sh! zL1Dd_9uz`9WkXy@o~7dae|RO*BQV4tFOU0ROwRBsi?qSM6C(DAV_QrnicK*{GAste zOr2D~LiLg32rLJ)qsf7~9S%A}P6!I&JaUOt8nu#v##C6F`uZvRv%I-iU4H9EdzL%& z@r>u2QQ%ot!oQt2^CP#%Sw9chdBvu9Yi;1ws=xV9udz+g%UUlTa@T6;t-D&lE=G^fuCB4V)L%&yD2-0&%s@jTAl?CvYm$~`+B z($KeGN2!6$VH-zhuMp{tzQW--AQ>%wYBFSiJ^3#&7=lQ8&XN-h=vW18= z+DvwMtJEM1C?kGFsJur;9GiC4)J+#aIWmlfG44I$_8lhW)zCZ#v%cko0zPFuJdr(f z>+yPcCH67P7AxYoH?^E95okMKR=Xo`bciGE5cNV;F~ZqJG%0rU7Nx0(g<`ZIVs0R? z!ni;uno~%H%47f(5Q|*Wt+h0lOKfuo%ii~yyg2Fkb*DId4sySL=ioEa=gou9io*w* zJ(e~sXol>RZmMx}bIGKKfjsS~psnnU%_Ot7>w;B=1Dy+KTclRJgtqfhRM*jgAL%)X z8k)F5IcNl@e$r7=*Wxnt4Iw_yr&!k+?6@f z5KfoF3#A?j3<32-n2rVzDHNSiPlY8nc2rm)06_#;5wfs-1hpd_tG7}C8^jKnQo@E| z>?In|gN-3%0;NoSpaW@_nI;o40f}#5zo8p;bTum<-4`cY?q)1~sPij8_4g{jd;!Bp ztLG-|=XnZ% z9&Fcu^EuDk3x6}M(00irJBQ4rEg~_{K%h2)FhSX3G;Tq4ff(Pgrew$U6JY2k_9cOz zJhJ+=Sxw<5))|@HtNQG-{XItQ>oys_pnVa5n>&3s8;)45G@5CMGV;2cHXWy7-aOoC zEk8+&07J-uA8i*2HIgtA$QX2vY+(b)w#ITylL1nO9!X@GAu5rB%`h#RESU=Y=O$dd zgqd4!24ww2KjQ^@AbQ#3ez_W63H1o8TPsMl9b3DoF4aZbG%Z5|>&o}GD}OqcJHimr zjoQ)_If<2BEx36q3e8Rw1#Z2-Db|<{z^S~Agp>mn3Q82%-F?^T$3M=F)SeuE)RS)o zcfg+*ho0y@^7&G@9SWs-1!;m4M6^rnJ7#zETA;Du?sY?{_j=HV+U$4}D3FxCV-mBGm1iz8G`4*OyTcY&AQiH_FC}A^>E$$V0-h7*spfCy)O_C z!GN#tRQk-(h>t`cq~Uxy9QQjEv7D03flkJt|ep~y_Buw|B&ITq*z zO18!twGb29qvqT#H6pCg2?PLCJGT6ak%gVJA!$6p;uycjA^y57d^U#X9mc<_Deq_b zh1o^lvnR1xbgT?{xu3%eNFS}X6cO1|FE}31v~`(Ir<6&z(`s{B)ah1d7Kc%Rwi=6# zG;NKQ$FVeN387??jW8;;at5oC9DP~d*+frL|J}q-_pifWX`YyLy|`w1>aonH(14rKJ!Eg%d!CvY2_Hx$Yk2k`rOsZXRz6R= z2kVT8l5K9(7G_u{Z);-AXakUOD$JwOua+}!-Tn<+U6qy!&n3D&zee00ugNzre?T?cdn(0yzP}pjVJopZLvrNA`BV zf-jSnjs4~zbMH5Yi6iP^}QxQezFh^-T7 z+gqtMoKxdbCs&Lf6Y*`Jqh?TQd4<_ZiBVMX;e-+do&L6Mue(*=-**t}_9J$Ry!}Sg zLsv4jq{TLW*3*YGVMnj>xyfxMNQ6iUXVYHY7VDVhWoZH#;v zk?m~>pwiZd=|C(dHr4qPi!HCQBkt^jJgwuS;Io>KcP~eQU#nWt5Uzl*fk<>CVK62# zf8vZqB#eg}A6wRi92c5z425hY)?}61gdQ@aRvD^E8cQI zMt?j8z29{%lNg7z>pNhjE5wgOQtU9l?mO9W%Eg{blwC)?H_~Q5zI@r6_x}0eXCU7W z5>Jj}HHCO9?1{Nx)d?&WbPKlw99(X@dOWxMtTo{@jvFr(tj%|}8g2Ham6D~&#Ae-% zNpVVRYbOGc(T*P?peFFX;)Y=W2jn+S_Nkp~_gL?qBV<3xJSl2-)s%!C1cSu(Nfb4Z z&LA5UaHpJu<)Ewe(9wWUOfAAj<6bX_8MAA4z+p-DG_DKbSc}I9fXJ+M2Qrouac%&- zHt<$HudDN)s%750b(3wC$T7mhImqwj$aoXG*YoR5Q)Z0Putsna*r|@Pgy(F@#HR|h z<5_L2<4a4IQos$RgR_zwRMwiyJj~(}udhsr0l5JuM%GerhN@V@n!-UEX``5=<`0Zx zpQjO{FiDNO8xQzz^f&Z4@BB&VUR}qlm!qJsl_iGa1Oa%FN@oNy84Z?#GoH8-O7zhl zr1?W0;%cebV3GuF`|?`mJ9f$Ewacp|3FK_7^w_pCrkOf)LuzJjg)O-BzuJUqyw2N$ zobQb9Sw`yQ*2UQVaXh{L=wVzvYvp3m_m9t?m+n!yoK^bilg=MH@=+as%4s+UcP4qY zaO0=`TUWF9r_;A(?Y|DY1Xm|nuDbdSfW+Gg!AI+Fub0GE@XDX`s4S?Aqkwx5w@-{g zC!zZ;b-Fx2+%`$Xt0^XvkW4IgV|E))>`6M|bF-TPIGOZ8#~Y;7d}NG(qB0FJ;wZS) zU5UJztvakb8))_f9jKTBg)$Z}^W`92ZSlbm)! z7cWOKU+XsN3JfvQJPJny(MP6XoAMX*a%#2*!NwKsx$CfA4LP2(Ua0Ouz>1M-Lel{# zVZsuX<|5h2L@5l=b#Km!EHYm8=08BJ`2l4Ky zjBKa<(>mpL*xTSt>d9;;qh0W%yjYs<-0+7_8y#sOzHITHIHhTWvf;-+kf=0n(ew$g zL@6)pAlqxoUOm{(&l2yMh8<-8$cNkPANN3`6JN`Nk2<${@L>lImj@qpZ1doQP82W? zKI+`+!G|3fDr64@py*ERC{9$Mr`Q91CR}p zZvzi`S66>8i}?8F$EDz%;x2CwnZ*WMA?h-0GdL`g*vzS%76nnlG~7&T%#Exl5Cxi` ztEoO3tszBGtXxOUF^#2B#lc;&92$Z?8e+Lo%|>p^(2K1h#EzVPqvpy_$%`M~ls`y3 z=&C%?7B|?EFS~0@gdMka;*bQXolkLx(OUa>KJNS2#jQH!x zzS|iS?Dyl_0Y{O0>l`j#4pJH{Tn0=Zrm0j2=5{1uxDBZhUerar!uu@1)(kruj+d)m zTH8ymGG>E7Nk(pmLZy;+T{c;BBIcV)yF2V*B`mP8=^&3}$FI!uAvAtF0lANgk9WTx zPP#TMq)co}*MjxSZo%zqciAt&>-rXO?AmfXa#0xqF= z2Dw9=EP=HfG0+gP6hBvz!Z#?$mKME*D%W1DsRaa9%c!Gfq>5o`n(JK4^vUmE44r@7 znRWgriz44Y>-%6&t|VXB?8tK85aI*_0CQlcM%iwo?X0J5agv-NTvqn1v{(1BrNG<1 zB+$W#GCR5$E2cwLbw3}ruqA@XsyPwugDEokPZ*&!xp&<@PZs5Qg<4cF^6wtS#ky*>AF6%SO*Z> z>96Q6)E2Yhme&X-jGrZR`%k1>d%v}L4=?)hc>PN9-g=k!2L$ORCqtA4%h(gXm@_&C z9#~GLR)^rKfVtfOs~f@V>z2vCT(CUX#6I(-LfPg0X^u<#a-ey&*mqaDXO- zGFl0LxR(CA5Ur9_GhBW5cXy%m3{v2$)VZl-Q=|M5& zdz4F4frJQyHf*9Z2$(CTl3Q$x?y=((uUA^BK-0Eh&x=^K02J#08@J1)q6quFM=M4? zQ<(W;1^L~%4|-}$J_u~jQ6x4w4Z74$Uz~q;l=dh^X<;%frjnQ!{TGTjeMylWG zs-5}N9Y%T=Th_i_b4G70w1(3>uNG@V#e}ZiU#|$c!U?+7UTKr}Tr7V?S%(_C17g`7 zjiJx2C@%ybwDxxUf;L-Gj)Do_pSAOiq2js~a+B29EKx8h16W)6EYv{j&c&A64&Q3s}CacK^d4Le>WT7-r(&ap>!))`{sIS&#Wy4R5_E1v+V zlJVO(SmPVeD0f~~E#x8>rPy(nHTeBtr|-S!LxSTz`r!?jr*|&bdCKJ)#mMh`tvmwXxv%Zp$jVqo{7NWuy-BV%@1W6~qnOWom`nx`QC|ehAK{>mY`} z;V=apqwS2PTEMBSEtxEYh*}ZMWq(L@zO7v0bERJOuY3EBPlU{`Vo!tJ@#e4t*}$>Y zHa?$tS!10!yEn8pvA9b83n5>(Odt##(K! zxoU|007~<Dqx$h*A;zB+V!Q_i z{l?MsO7y2&4=n15+}oSn zy`|pn4!}_jvT%gJxhPYR1E81Jz_j9Z*B8|djk?KPR@2P_>&h@3!yIF*CSya1h>q=& z3%Ly_fypPb4s-=Vuvhh}FTp&}=N1wwecSd@Y9;B5RT^G)_rT?I3dBzIe4b+KYTfTPMHF-}ceqxI~0t#N8w!NmhJygCt z5qUJ}CaDut`tDcU+a7~|6Hkn2uTuAxK3~5aWPNSs7C_4RcEQdyZ{pip$BPk{#GNk7 zK%;uz0yQS+rwg6Pi4Fou!4zZ%IyN<6y$nlXBPm@fjw@xpmibi4LSuQ2s5JMuo(*QVxciDMF=-A92KKb^499smlI>E zhHk)zi~eG@0a38qlh~QlAqFucqpQ_)Zli)RaNKAyrokx`8jnL;%^)U&6ZuTJ6n)(uhr&9-WHe_&`LDZ77ZXElchz4 z_oj#nR-neu*suc)gjP=L=mtS@F+``2ZdW(!zyCTO@I7H@v>I~Z?)4FCPN0XyU6nr* zqFl&5Y5jE9ln1r2D3aP@OVpA^Q_@Dtje_9>uo|rfz!cX*XV3#$xxT0)J28S~( zv>h;GnR1QKmaGzZ5S?}%kd{XaHrbGK%>AKo5JDc%b`ssMrG09iJjy(2d3SXfNVM+40HMUZJE%;_iaBEp)r&rlasiPTi;+YEXd2<>+LquF%FIl#rb}tL zHrrAbt$>o~mE(;yb};Nu*RW^aMLF;6Iz0Y39R5M%UcLMOFGnF?Yj?pqATPjLIhR&o zM&?Ay=%IGiTR7TJ-={bUZ@~Z{2Mi^VCEsKMSK12>54qu)1d^me$c+Ta)t1ql`e%g1N=pgsiPWzk2 z#u{6l2#Z{NyEy$H}&$(Ig)Ea9GYPs8gO1GqnD(jn#%lKNjLE=lmibT8cZvu{0k1us(o0 zglK13hglWI6|p)PoIEQvw~lnd5pNc-{C|2AjD(oz zJ;^UEhj^uuvjG>C-hiAdZUE$7Iz`X{1LIP{Wn99wM|eQBwsZRqs`zh+dR<+KR>CyE zy2OjTd@3erD`_Uh;;lbwNENg>bo>LRMrUGGdPDStrQW@#A!7BiXQcJNX9V0--7c6s zNuFz5Oqa1*RNTk3PLaOyUq)rB^saV0aQ1u%I#9F$6V-p|ORX`jwZ)J=byX5L?tRCU z%T7Z7pq81Sa+5*uVf)?$-4pW+>+_u=R>7O!T&U8!^!D;EXzvv!AH*;*k<>el8zvdNWiO?I`+l=hms5?;f=@uzxV#;57GMMlEv{Ha|s!R z_=D^pLt03_#PrZjRP_VXJ&R5{N1tz6r+6p90>1rq>_-b~m-Rf`$GMRFzq%4ebVG{2 z)*3PN>s9bZPPGX~ye=QNTU7?$3Z1Vk%^6&Pd=^m%w*@Xd3Au0cp00A-Jso~R>s?9Y zXn|cK(p_zN;R^{^w%1i+QgVO4^2y9y1R*4X%8XNpAn@yWohC@n2v1IF7bqij^M#%G zK)hKqaXMOEegH5%MxMoZ{fmM177Ve)@%YN~*UBvH3HMq7R5t(jV#Hy#0H*8rvg}eZ z(<#I-5RX#Uznl-I}r3T|2ob9$z~hFY(vO z+%kBzu4}8Y2MeTD42-!Yd@=|JJH9x49#1MZYPQK@T1FlnG8DY}kc zju-^Pg%Ly8@;gEhNgvU(;kpZRWqdE1%1??Xxz*0h)WWt}bUAG-KDIEkNh#;rBvPGF$n?`gs1s+CaR!V=vM z-&%7OP7?Q`%eb+R5Z#E55*y{G$<7f`5FcF-EV6LO&wNlj=)Hb6St__7+IU@9udDM$ zf?A5HQmvb=s9@G~n$3UHSYnm|S%iqW5F4MQYN#5{sDR^K0D)iD{_RND{I<^Z`b1jH zHEO@>p{>Eb{=`i;G9@i=OWF17whSPz*LYl=yu)8KV!?9)`2uMZ;Q0_pxEdNSza=Lh zlfz9J$D+|HWY!wV1DobO#K`fg?A!*!>9a6|&OBZs**BZaT(X~&dn!hOopU~e4tY;2 zMik$E75*H3cSC4cS+%;2&uMvz7w1d5Bv9?&mm6&AncVRd za`ync1^1oUiOERF!#aJl03cTa+1?`mk-;9}i<44`O{C2ZR-fO^QnvtkSYzA9kk4qi zs$E^QtGndIN$po}>DGD8)3=QE`vWNMXqOV7s&RYnjPR6}QMz(^5b;XjCRg#^hc4HP zeL-ZA)#31$3g6wK(O289fdWI4{j0`+Sv(Kn9PY~EdlXf&ScFVUW}QaOZxU$jWFze; z2O?ym-k(8B^S01p1ijT`?OO6GE=g}2S(rT0WeD*#S?bhWj!Y=t8H)Ms0e(b-pF0MG zn(Gd#&jQG7?>AkytuM<4X8ed9%N|om9&p#E1+B|k(_mhSKHekqtwoLP^#t5vDeK=k42dETw`)hM_z*FJw^ zi6b1m%DUc*k#mVt6)<(Q-k`9vYnrnlAis&Ji4^DSW3U>XYd~hLaqTK=it9o#rFA@E zpEyKS)+RxrsPVaT!n=~8u_-@6hkvYFzALk96@9R@~k$!RA zI8!);mU6NDdm?=mbo*QZxk0na> z>i!34wScnHml+=o);37aB5_sPyudlp9^#`ky5g(l>n*qEgYci(=97Tg=Bf#CTLplb z=FouA^Kk#nHvfC9xm?^hLO&6b*wY70|JstZv2lmfBaB&aDkuq^=&?wq-U6@7BC*`- zq;4&cA6n5YJ&~{-kv4OF*tg3m^JtT1lHa;Q!0EraKzH`3$tDlCbF*IrTwa7J!FID< z3^~Z`j>v%xdzbCGX-qv;w7&xX^IO0`D|Bn&g$ot|RFZcK8^@YSQG3Pe8tSSKhP&dhqRafYlpAG?l)q?FEC1*LLhZvxgn8HcgF^q7phEN=4cVD zEWAsG<8IlbBdRsUuLq8dbp-NgL>6>s9=x!Dfx;30Qk9y*wNU9tLbRM#Z*O#YLM!

U9685@_bbb}# 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 literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml index a8f5a32..364b47f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "comfyui-utility-megapack" -version = "0.1.1" +version = "0.1.2" description = "Comprehensive ComfyUI utility node pack with 156 operations across 7 nodes and 11 swappable visual themes." readme = "README.md" requires-python = ">=3.10" @@ -40,7 +40,7 @@ Changelog = "https://github.com/IxMxAMAR/ComfyUI-Utility-MegaPack/blob/main/CHAN [tool.setuptools.packages.find] where = ["."] -include = ["nodes*", "shared*"] +include = ["mp_nodes*", "mp_shared*"] [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/tests/unit/test_base_anytype.py b/tests/unit/test_base_anytype.py index d652dc1..c700cb7 100644 --- a/tests/unit/test_base_anytype.py +++ b/tests/unit/test_base_anytype.py @@ -1,6 +1,6 @@ """Tests for the AnyType wildcard used to mark wildcard ComfyUI sockets.""" -from nodes._base import ANY, AnyType +from mp_nodes._base import ANY, AnyType def test_anytype_is_string_subclass(): diff --git a/tests/unit/test_base_errors.py b/tests/unit/test_base_errors.py index 8022174..14165f7 100644 --- a/tests/unit/test_base_errors.py +++ b/tests/unit/test_base_errors.py @@ -1,6 +1,6 @@ """Tests for OutputNotProducedError — raised when opaque-type slots are wired but unfilled.""" -from nodes._base import OutputNotProducedError +from mp_nodes._base import OutputNotProducedError def test_error_is_runtime_error_subclass(): diff --git a/tests/unit/test_base_input_types.py b/tests/unit/test_base_input_types.py index 5e64ccf..fad788d 100644 --- a/tests/unit/test_base_input_types.py +++ b/tests/unit/test_base_input_types.py @@ -1,6 +1,6 @@ """Tests for MegaPackNodeBase.INPUT_TYPES — the union builder.""" -from nodes._base import MegaPackNodeBase, OpRegistry, THEME_CHOICES +from mp_nodes._base import MegaPackNodeBase, OpRegistry, THEME_CHOICES def _make_two_op_registry(): diff --git a/tests/unit/test_base_opdef.py b/tests/unit/test_base_opdef.py index dc406f4..c3a4d98 100644 --- a/tests/unit/test_base_opdef.py +++ b/tests/unit/test_base_opdef.py @@ -1,6 +1,6 @@ """Tests for OpDef — the dataclass that describes one registered operation.""" -from nodes._base import OpDef +from mp_nodes._base import OpDef def _noop(self): diff --git a/tests/unit/test_base_pad_outputs.py b/tests/unit/test_base_pad_outputs.py index d165827..72a5680 100644 --- a/tests/unit/test_base_pad_outputs.py +++ b/tests/unit/test_base_pad_outputs.py @@ -3,7 +3,7 @@ import pytest import torch -from nodes._base import ANY, _pad_outputs +from mp_nodes._base import ANY, _pad_outputs def test_filled_indices_use_provided_values(): @@ -100,16 +100,18 @@ def test_unfilled_mask_defaults_to_1x1_zero_tensor(): assert torch.all(result[0] == 0) -def test_unfilled_latent_defaults_to_empty_4_8_8(): +def test_unfilled_latent_defaults_to_empty_dict(): + """LATENT pads to {} (no 'samples') so downstream sees a clean 'missing samples' error. + + The old hardcoded (1, 4, 8, 8) default broke 16-channel models (Flux/SD3). + """ result = _pad_outputs( op_result=(), output_indices=(), return_types=("LATENT",), op_id="x", ) - assert isinstance(result[0], dict) - assert "samples" in result[0] - assert result[0]["samples"].shape == (1, 4, 8, 8) + assert result[0] == {} def test_unfilled_wildcard_defaults_to_none(): diff --git a/tests/unit/test_base_process.py b/tests/unit/test_base_process.py index 5d2c894..e23dfe8 100644 --- a/tests/unit/test_base_process.py +++ b/tests/unit/test_base_process.py @@ -2,7 +2,7 @@ import pytest -from nodes._base import MegaPackNodeBase, OpRegistry +from mp_nodes._base import MegaPackNodeBase, OpRegistry def _make_one_op_node(): diff --git a/tests/unit/test_base_registry.py b/tests/unit/test_base_registry.py index 68ce6e3..adbec3e 100644 --- a/tests/unit/test_base_registry.py +++ b/tests/unit/test_base_registry.py @@ -2,7 +2,7 @@ import pytest -from nodes._base import OpRegistry +from mp_nodes._base import OpRegistry def test_register_stores_op_by_id(): diff --git a/tests/unit/test_base_theme_choices.py b/tests/unit/test_base_theme_choices.py index 58a050f..0aa75ff 100644 --- a/tests/unit/test_base_theme_choices.py +++ b/tests/unit/test_base_theme_choices.py @@ -1,6 +1,6 @@ """Tests for THEME_CHOICES — the dropdown values for the per-node theme widget.""" -from nodes._base import THEME_CHOICES +from mp_nodes._base import THEME_CHOICES def test_theme_choices_is_list_of_strings(): diff --git a/tests/unit/test_convenience.py b/tests/unit/test_convenience.py index e88cbc5..298e561 100644 --- a/tests/unit/test_convenience.py +++ b/tests/unit/test_convenience.py @@ -5,7 +5,7 @@ import json import pytest import torch -from nodes.convenience import ConvenienceNode +from mp_nodes.convenience import ConvenienceNode def run(mode, **kwargs): diff --git a/tests/unit/test_image_pro.py b/tests/unit/test_image_pro.py index 5b09108..8f0581e 100644 --- a/tests/unit/test_image_pro.py +++ b/tests/unit/test_image_pro.py @@ -7,7 +7,7 @@ import pytest import torch from PIL import Image -from nodes.image_pro import ImageProNode +from mp_nodes.image_pro import ImageProNode def run(mode, **kwargs): diff --git a/tests/unit/test_io_workflow.py b/tests/unit/test_io_workflow.py index 5e48ee4..0a30f67 100644 --- a/tests/unit/test_io_workflow.py +++ b/tests/unit/test_io_workflow.py @@ -8,13 +8,21 @@ import pytest import torch from PIL import Image -from nodes.io_workflow import IOWorkflowNode +from mp_nodes.io_workflow import IOWorkflowNode def run(mode, **kwargs): return IOWorkflowNode().process(mode=mode, theme="(use pack default)", **kwargs) +@pytest.fixture(autouse=True) +def _allow_tmp_paths(monkeypatch): + """Tests use pytest's tmp_path which is outside the allow-listed roots. + Opt out of fs confinement for tests; one targeted test below verifies + the confinement actually blocks paths when the env var is unset.""" + monkeypatch.setenv("UTILITY_MEGAPACK_ALLOW_ARBITRARY_PATHS", "1") + + # ----------------------- # Filesystem # ----------------------- @@ -85,6 +93,46 @@ class TestFilesystem: assert run("path_sanitize_filename", filename='bad:name?.txt', replacement="_")[0] == "bad_name__file_.txt" +class TestFilesystemConfinement: + """When UTILITY_MEGAPACK_ALLOW_ARBITRARY_PATHS is unset, fs ops reject + paths outside the allow-list (ComfyUI input/output/temp + user home). + This blocks the v0.1.0/0.1.1 vulnerability where a network-exposed + ComfyUI could touch arbitrary files (Gemini review #7). + + These tests pin the allow-list to a known-narrow directory via monkeypatch + so we can test against paths that are definitely outside it, independent + of where the test runner's tmp_path or HOME actually live. + """ + + @pytest.fixture(autouse=True) + def _isolated_confinement(self, monkeypatch, tmp_path): + from mp_nodes.io_workflow.operations import filesystem as fs_mod + # Allow only this temp dir — everything else is outside + allowed = str(tmp_path / "allowed") + os.makedirs(allowed, exist_ok=True) + monkeypatch.setattr(fs_mod, "_confined_roots", lambda: [allowed]) + monkeypatch.delenv("UTILITY_MEGAPACK_ALLOW_ARBITRARY_PATHS", raising=False) + self._allowed = allowed + self._outside = str(tmp_path / "outside" / "should_fail") + + def test_mkdir_outside_allow_list_rejected(self): + with pytest.raises(RuntimeError, match="outside the allow-listed roots"): + run("fs_mkdir", path=self._outside) + + def test_delete_outside_allow_list_rejected(self): + with pytest.raises(RuntimeError, match="outside the allow-listed roots"): + run("fs_delete", path=self._outside, missing_ok=True) + + def test_inside_allowed_root_succeeds(self): + out = run("fs_mkdir", path=os.path.join(self._allowed, "subdir")) + assert os.path.isdir(out[0]) + + def test_env_var_override_disables_confinement(self, monkeypatch): + monkeypatch.setenv("UTILITY_MEGAPACK_ALLOW_ARBITRARY_PATHS", "1") + out = run("fs_mkdir", path=self._outside) + assert os.path.isdir(out[0]) + + # ----------------------- # Network (mocked) # ----------------------- @@ -94,7 +142,7 @@ class TestNetwork: mock_resp.iter_content = lambda chunk_size: [b'{"hello": "world"}'] mock_resp.encoding = "utf-8" mock_resp.status_code = 200 - with patch("nodes.io_workflow.operations.network.requests.get", return_value=mock_resp): + with patch("mp_nodes.io_workflow.operations.network.requests.get", return_value=mock_resp): out = run("http_get", url="http://example.com") # RETURN_TYPES is (STRING, DICT, IMAGE, INT) assert out[0] == '{"hello": "world"}' @@ -103,15 +151,27 @@ class TestNetwork: def test_http_post_with_body(self): mock_resp = MagicMock() - mock_resp.text = '{"ok": true}' - mock_resp.content = b'{"ok": true}' + mock_resp.iter_content = lambda chunk_size: [b'{"ok": true}'] + mock_resp.encoding = "utf-8" mock_resp.status_code = 201 - with patch("nodes.io_workflow.operations.network.requests.post", return_value=mock_resp): + with patch("mp_nodes.io_workflow.operations.network.requests.post", return_value=mock_resp): out = run("http_post", url="http://example.com", body_json='{"a": 1}', timeout_seconds=5) assert out[3] == 201 assert out[1] == {"ok": True} + def test_http_post_enforces_size_cap(self): + """Verify streaming enforcement: a giant response must error before fully buffering.""" + from mp_nodes.io_workflow.operations.network import _MAX_RESPONSE_BYTES + big_chunk = b"x" * (_MAX_RESPONSE_BYTES + 1) + mock_resp = MagicMock() + mock_resp.iter_content = lambda chunk_size: [big_chunk] + mock_resp.encoding = "utf-8" + mock_resp.status_code = 200 + with patch("mp_nodes.io_workflow.operations.network.requests.post", return_value=mock_resp): + with pytest.raises(RuntimeError, match="exceeded"): + run("http_post", url="http://example.com", body_json="{}", timeout_seconds=5) + # ----------------------- # Workflow diff --git a/tests/unit/test_mask_latent.py b/tests/unit/test_mask_latent.py index fcfbc18..46e0a90 100644 --- a/tests/unit/test_mask_latent.py +++ b/tests/unit/test_mask_latent.py @@ -3,7 +3,7 @@ import pytest import torch -from nodes.mask_latent import MaskLatentNode +from mp_nodes.mask_latent import MaskLatentNode def run(mode, **kwargs): diff --git a/tests/unit/test_models_sampling.py b/tests/unit/test_models_sampling.py index b9092dc..880c8ed 100644 --- a/tests/unit/test_models_sampling.py +++ b/tests/unit/test_models_sampling.py @@ -5,7 +5,7 @@ import struct import pytest -from nodes.models_sampling import ModelsSamplingNode +from mp_nodes.models_sampling import ModelsSamplingNode def run(mode, **kwargs): diff --git a/tests/unit/test_programming_control_flow.py b/tests/unit/test_programming_control_flow.py index 17f9f2b..e249593 100644 --- a/tests/unit/test_programming_control_flow.py +++ b/tests/unit/test_programming_control_flow.py @@ -2,7 +2,7 @@ import pytest -from nodes.programming import ProgrammingNode +from mp_nodes.programming import ProgrammingNode def run(mode, **kwargs): diff --git a/tests/unit/test_programming_crypto.py b/tests/unit/test_programming_crypto.py index 39197bc..b408b44 100644 --- a/tests/unit/test_programming_crypto.py +++ b/tests/unit/test_programming_crypto.py @@ -5,7 +5,7 @@ import hmac as _hmac import pytest -from nodes.programming import ProgrammingNode +from mp_nodes.programming import ProgrammingNode def run(mode, **kwargs): @@ -79,7 +79,17 @@ class TestAES: with pytest.raises(RuntimeError, match="too short"): run("aes_decrypt", ciphertext_b64="dGlueQ==", key="anything") - def test_aes_two_encrypts_differ_due_to_random_nonce(self): + def test_aes_two_encrypts_differ_due_to_random_salt_and_nonce(self): a = run("aes_encrypt", plaintext="same input", key="same key")[0] b = run("aes_encrypt", plaintext="same input", key="same key")[0] assert a != b + + def test_aes_v0_1_x_format_rejected_with_clear_error(self): + """Old HKDF-format ciphertexts (no salt prefix) are too short for the new layout.""" + # 12-byte nonce + 16-byte tag minimum from the old format = 28 bytes; + # the new format expects salt[16] + nonce[12] + tag[16] = 44 bytes minimum. + # An old 28-byte ciphertext must fail with the clearer length error. + old_format = b"x" * 28 + import base64 + with pytest.raises(RuntimeError, match="too short"): + run("aes_decrypt", ciphertext_b64=base64.b64encode(old_format).decode(), key="any") diff --git a/tests/unit/test_programming_data_struct.py b/tests/unit/test_programming_data_struct.py index 0657ecb..eb5ccc2 100644 --- a/tests/unit/test_programming_data_struct.py +++ b/tests/unit/test_programming_data_struct.py @@ -2,7 +2,7 @@ import pytest -from nodes.programming import ProgrammingNode +from mp_nodes.programming import ProgrammingNode def run(mode, **kwargs): diff --git a/tests/unit/test_programming_encoding.py b/tests/unit/test_programming_encoding.py index 0430d85..ebe6a96 100644 --- a/tests/unit/test_programming_encoding.py +++ b/tests/unit/test_programming_encoding.py @@ -2,7 +2,7 @@ import pytest -from nodes.programming import ProgrammingNode +from mp_nodes.programming import ProgrammingNode def run(mode, **kwargs): diff --git a/tests/unit/test_programming_logic_bits.py b/tests/unit/test_programming_logic_bits.py index 5d04379..35b1f89 100644 --- a/tests/unit/test_programming_logic_bits.py +++ b/tests/unit/test_programming_logic_bits.py @@ -1,6 +1,6 @@ """Tests for the Logic & Bits category.""" -from nodes.programming import ProgrammingNode +from mp_nodes.programming import ProgrammingNode def run(mode, **kwargs): diff --git a/tests/unit/test_programming_math_signals.py b/tests/unit/test_programming_math_signals.py index c6d44b7..ed21723 100644 --- a/tests/unit/test_programming_math_signals.py +++ b/tests/unit/test_programming_math_signals.py @@ -2,7 +2,7 @@ import pytest -from nodes.programming import ProgrammingNode +from mp_nodes.programming import ProgrammingNode def run(mode, **kwargs): diff --git a/tests/unit/test_programming_text.py b/tests/unit/test_programming_text.py index d916dc4..e5a88c2 100644 --- a/tests/unit/test_programming_text.py +++ b/tests/unit/test_programming_text.py @@ -2,7 +2,7 @@ import pytest -from nodes.programming import ProgrammingNode +from mp_nodes.programming import ProgrammingNode def run(mode, **kwargs): diff --git a/tests/unit/test_prompt.py b/tests/unit/test_prompt.py index 24c2e0d..a3ecc54 100644 --- a/tests/unit/test_prompt.py +++ b/tests/unit/test_prompt.py @@ -5,7 +5,7 @@ import tempfile import pytest -from nodes.prompt import PromptNode +from mp_nodes.prompt import PromptNode def run(mode, **kwargs): diff --git a/web/extension.js b/web/extension.js index 9632170..8f95290 100644 --- a/web/extension.js +++ b/web/extension.js @@ -59,14 +59,23 @@ function reapplyAll() { for (const node of app.graph._nodes) applyResolved(node); } -function defineSetting(id, name, type, defaultValue, onChange) { - app.ui.settings.addSetting({ +// Theme IDs available as combo options. Kept in sync with registered themes. +const THEME_OPTIONS = registry.ids(); + +function defineSetting(id, name, type, defaultValue, onChange, options) { + const setting = { id: `megapack.${id}`, name, type, defaultValue, onChange, - }); + }; + // ComfyUI's settings API requires `options` for combo-type settings; + // without it the dropdown renders empty (Gemini review #4). + if (type === "combo" && Array.isArray(options)) { + setting.options = options; + } + app.ui.settings.addSetting(setting); } app.registerExtension({ @@ -75,13 +84,15 @@ app.registerExtension({ async setup() { defineSetting("packDefault", "Utility-MegaPack — Pack default theme", "combo", "default", - (v) => { settings.packDefault = v; reapplyAll(); }); + (v) => { settings.packDefault = v; reapplyAll(); }, + THEME_OPTIONS); defineSetting("globalOverrideEnabled", "Utility-MegaPack — Theme ALL ComfyUI nodes", "boolean", false, (v) => { settings.globalOverrideEnabled = v; reapplyAll(); }); defineSetting("globalTheme", "Utility-MegaPack — Global theme", "combo", "default", - (v) => { settings.globalTheme = v; reapplyAll(); }); + (v) => { settings.globalTheme = v; reapplyAll(); }, + THEME_OPTIONS); defineSetting("respectExistingCustomDrawing", "Utility-MegaPack — Respect existing custom drawing", "boolean", true, (v) => { settings.respectExistingCustomDrawing = v; reapplyAll(); });