From 696b21db555447a9f39c98a03e657eb26a9121d5 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 1 Mar 2026 09:44:37 -0800 Subject: [PATCH] feat(files): add JIT file sessions and batch sync API (#701) * feat(files): add JIT file sessions and batch sync API * feat(app): sync mirrored remote files in desktop sessions * fix(app): harden base64 encoding for mirror sync * chore(pr): add remote file sync test evidence --- packages/app/src/app/lib/openwork-server.ts | 177 ++++++ packages/app/src/app/lib/tauri.ts | 25 + packages/app/src/app/pages/session.tsx | 387 +++++++++++- .../desktop/src-tauri/src/commands/misc.rs | 67 +++ packages/desktop/src-tauri/src/lib.rs | 4 +- packages/orchestrator/README.md | 40 ++ packages/orchestrator/package.json | 1 + .../orchestrator/scripts/files-session.mjs | 312 ++++++++++ packages/orchestrator/src/cli.ts | 252 ++++++++ packages/server/README.md | 8 + packages/server/src/file-sessions.test.ts | 45 ++ packages/server/src/file-sessions.ts | 152 +++++ packages/server/src/server.ts | 567 +++++++++++++++++- pr/worker-files-jit-sync/cli-test-files.log | 14 + .../docker-web-remote-artifact.png | Bin 0 -> 161782 bytes 15 files changed, 2021 insertions(+), 30 deletions(-) create mode 100644 packages/orchestrator/scripts/files-session.mjs create mode 100644 packages/server/src/file-sessions.test.ts create mode 100644 packages/server/src/file-sessions.ts create mode 100644 pr/worker-files-jit-sync/cli-test-files.log create mode 100644 pr/worker-files-jit-sync/docker-web-remote-artifact.png diff --git a/packages/app/src/app/lib/openwork-server.ts b/packages/app/src/app/lib/openwork-server.ts index 1834d9b0..a7b018de 100644 --- a/packages/app/src/app/lib/openwork-server.ts +++ b/packages/app/src/app/lib/openwork-server.ts @@ -120,6 +120,86 @@ export type OpenworkWorkspaceFileWriteResult = { path: string; bytes: number; updatedAt: number; + revision?: string; +}; + +export type OpenworkFileSession = { + id: string; + workspaceId: string; + createdAt: number; + expiresAt: number; + ttlMs: number; + canWrite: boolean; +}; + +export type OpenworkFileCatalogEntry = { + path: string; + kind: "file" | "dir"; + size: number; + mtimeMs: number; + revision: string; +}; + +export type OpenworkFileSessionEvent = { + id: string; + seq: number; + workspaceId: string; + type: "write" | "delete" | "rename" | "mkdir"; + path: string; + toPath?: string; + revision?: string; + timestamp: number; +}; + +export type OpenworkFileReadBatchResult = { + items: Array< + | { + ok: true; + path: string; + kind: "file"; + bytes: number; + updatedAt: number; + revision: string; + contentBase64: string; + } + | { + ok: false; + path: string; + code: string; + message: string; + maxBytes?: number; + size?: number; + } + >; +}; + +export type OpenworkFileWriteBatchResult = { + items: Array< + | { + ok: true; + path: string; + bytes: number; + updatedAt: number; + revision: string; + previousRevision?: string | null; + } + | { + ok: false; + path: string; + code: string; + message: string; + expectedRevision?: string; + currentRevision?: string | null; + maxBytes?: number; + size?: number; + } + >; + cursor: number; +}; + +export type OpenworkFileOpsBatchResult = { + items: Array>; + cursor: number; }; export type OpenworkCommandItem = { @@ -1588,6 +1668,103 @@ export function createOpenworkServerClient(options: { baseUrl: string; token?: s { token, hostToken, timeoutMs: timeouts.binary }, ), + createFileSession: (workspaceId: string, options?: { ttlSeconds?: number; write?: boolean }) => + requestJson<{ session: OpenworkFileSession }>(baseUrl, `/workspace/${encodeURIComponent(workspaceId)}/files/sessions`, { + token, + hostToken, + method: "POST", + body: { + ...(typeof options?.ttlSeconds === "number" ? { ttlSeconds: options.ttlSeconds } : {}), + ...(typeof options?.write === "boolean" ? { write: options.write } : {}), + }, + }), + + renewFileSession: (sessionId: string, options?: { ttlSeconds?: number }) => + requestJson<{ session: OpenworkFileSession }>(baseUrl, `/files/sessions/${encodeURIComponent(sessionId)}/renew`, { + token, + hostToken, + method: "POST", + body: { + ...(typeof options?.ttlSeconds === "number" ? { ttlSeconds: options.ttlSeconds } : {}), + }, + }), + + closeFileSession: (sessionId: string) => + requestJson<{ ok: boolean }>(baseUrl, `/files/sessions/${encodeURIComponent(sessionId)}`, { + token, + hostToken, + method: "DELETE", + }), + + getFileCatalogSnapshot: ( + sessionId: string, + options?: { prefix?: string; after?: string; includeDirs?: boolean; limit?: number }, + ) => { + const params = new URLSearchParams(); + if (options?.prefix?.trim()) params.set("prefix", options.prefix.trim()); + if (options?.after?.trim()) params.set("after", options.after.trim()); + if (typeof options?.includeDirs === "boolean") params.set("includeDirs", options.includeDirs ? "true" : "false"); + if (typeof options?.limit === "number") params.set("limit", String(options.limit)); + const query = params.toString(); + return requestJson<{ + sessionId: string; + workspaceId: string; + generatedAt: number; + cursor: number; + total: number; + truncated: boolean; + nextAfter?: string; + items: OpenworkFileCatalogEntry[]; + }>( + baseUrl, + `/files/sessions/${encodeURIComponent(sessionId)}/catalog/snapshot${query ? `?${query}` : ""}`, + { token, hostToken }, + ); + }, + + listFileSessionEvents: (sessionId: string, options?: { since?: number }) => { + const query = typeof options?.since === "number" ? `?since=${encodeURIComponent(String(options.since))}` : ""; + return requestJson<{ items: OpenworkFileSessionEvent[]; cursor: number }>( + baseUrl, + `/files/sessions/${encodeURIComponent(sessionId)}/catalog/events${query}`, + { token, hostToken }, + ); + }, + + readFileBatch: (sessionId: string, paths: string[]) => + requestJson(baseUrl, `/files/sessions/${encodeURIComponent(sessionId)}/read-batch`, { + token, + hostToken, + method: "POST", + body: { paths }, + }), + + writeFileBatch: ( + sessionId: string, + writes: Array<{ path: string; contentBase64: string; ifMatchRevision?: string; force?: boolean }>, + ) => + requestJson(baseUrl, `/files/sessions/${encodeURIComponent(sessionId)}/write-batch`, { + token, + hostToken, + method: "POST", + body: { writes }, + }), + + runFileBatchOps: ( + sessionId: string, + operations: Array< + | { type: "mkdir"; path: string } + | { type: "delete"; path: string; recursive?: boolean } + | { type: "rename"; from: string; to: string } + >, + ) => + requestJson(baseUrl, `/files/sessions/${encodeURIComponent(sessionId)}/ops`, { + token, + hostToken, + method: "POST", + body: { operations }, + }), + readWorkspaceFile: (workspaceId: string, path: string) => requestJson( baseUrl, diff --git a/packages/app/src/app/lib/tauri.ts b/packages/app/src/app/lib/tauri.ts index af7fded6..a9c5f1fa 100644 --- a/packages/app/src/app/lib/tauri.ts +++ b/packages/app/src/app/lib/tauri.ts @@ -703,6 +703,31 @@ export async function writeObsidianMirrorFile( }); } +export type ObsidianMirrorFileContent = { + exists: boolean; + path: string; + content: string | null; + updatedAtMs: number | null; +}; + +export async function readObsidianMirrorFile( + workspaceId: string, + filePath: string, +): Promise { + const safeWorkspaceId = workspaceId.trim(); + const safePath = filePath.trim(); + if (!safeWorkspaceId) { + throw new Error("workspaceId is required"); + } + if (!safePath) { + throw new Error("filePath is required"); + } + return invoke("read_obsidian_mirror_file", { + workspaceId: safeWorkspaceId, + filePath: safePath, + }); +} + export async function schedulerListJobs(scopeRoot?: string): Promise { return invoke("scheduler_list_jobs", { scopeRoot }); } diff --git a/packages/app/src/app/pages/session.tsx b/packages/app/src/app/pages/session.tsx index 5bf06b7d..223a1813 100644 --- a/packages/app/src/app/pages/session.tsx +++ b/packages/app/src/app/pages/session.tsx @@ -23,6 +23,7 @@ import type { import { obsidianIsAvailable, openInObsidian, + readObsidianMirrorFile, writeObsidianMirrorFile, type EngineInfo, type OpenworkServerInfo, @@ -67,11 +68,11 @@ import { parseOpenworkWorkspaceIdFromUrl, } from "../lib/openwork-server"; import type { + OpenworkFileSession, OpenworkServerClient, OpenworkServerSettings, OpenworkServerStatus, OpenworkSoulStatus, - OpenworkWorkspaceFileContent, OpenworkWorkspaceExport, } from "../lib/openwork-server"; import { DEFAULT_OPENWORK_PUBLISHER_BASE_URL, publishOpenworkBundleJson } from "../lib/publisher"; @@ -770,6 +771,89 @@ export default function SessionView(props: SessionViewProps) { return !isAbsolutePath(trimmed) && root ? await join(root, trimmed) : trimmed; }; + type RemoteMirrorTrackedFile = { + path: string; + localPath: string; + remoteRevision: string; + localFingerprint: string; + syncingLocal: boolean; + }; + + type RemoteFileSyncSession = OpenworkFileSession & { cursor: number }; + + const remoteMirrorTrackedFiles = new Map(); + const [remoteFileSyncSession, setRemoteFileSyncSession] = createSignal(null); + const remoteMirrorWorkspaceKey = createMemo( + () => props.openworkServerWorkspaceId?.trim() || props.activeWorkspaceDisplay.id?.trim() || "remote-worker", + ); + let remoteMirrorSyncTimer: number | undefined; + let remoteMirrorSyncInFlight = false; + let remoteMirrorLastErrorAt = 0; + + const textFingerprint = (value: string) => { + let hash = 2166136261; + for (let idx = 0; idx < value.length; idx += 1) { + hash ^= value.charCodeAt(idx); + hash = Math.imul(hash, 16777619); + } + return `${value.length}:${hash >>> 0}`; + }; + + const utf8ToBase64 = (value: string) => { + const bytes = new TextEncoder().encode(value); + let binary = ""; + for (const byte of bytes) { + binary += String.fromCharCode(byte); + } + const fallbackBuffer = (globalThis as { Buffer?: { from: (input: string, encoding: string) => { toString: (encoding: string) => string } } }).Buffer; + if (typeof btoa !== "function") { + if (!fallbackBuffer) { + throw new Error("Base64 encoder is unavailable"); + } + return fallbackBuffer.from(value, "utf8").toString("base64"); + } + return btoa(binary); + }; + + const base64ToUtf8 = (value: string) => { + const fallbackBuffer = (globalThis as { Buffer?: { from: (input: string, encoding: string) => { toString: (encoding: string) => string } } }).Buffer; + if (typeof atob !== "function") { + if (!fallbackBuffer) { + throw new Error("Base64 decoder is unavailable"); + } + return fallbackBuffer.from(value, "base64").toString("utf8"); + } + const binary = atob(value); + const bytes = Uint8Array.from(binary, (ch) => ch.charCodeAt(0)); + return new TextDecoder().decode(bytes); + }; + + const stopRemoteMirrorSyncLoop = () => { + if (remoteMirrorSyncTimer !== undefined) { + window.clearInterval(remoteMirrorSyncTimer); + remoteMirrorSyncTimer = undefined; + } + }; + + const closeRemoteFileSyncSession = async (session: RemoteFileSyncSession | null) => { + const client = props.openworkServerClient; + if (!client || !session) return; + try { + await client.closeFileSession(session.id); + } catch { + // best effort + } + }; + + const resetRemoteFileSync = async () => { + stopRemoteMirrorSyncLoop(); + remoteMirrorSyncInFlight = false; + remoteMirrorTrackedFiles.clear(); + const existing = remoteFileSyncSession(); + setRemoteFileSyncSession(null); + await closeRemoteFileSyncSession(existing); + }; + const toWorkerRelativeArtifactPath = (file: string) => { const normalized = file.trim().replace(/^file:\/\//i, "").replace(/[\\/]+/g, "/"); if (!normalized) return ""; @@ -808,44 +892,295 @@ export default function SessionView(props: SessionViewProps) { return relative; }; - const readRemoteArtifactForObsidian = async (file: string): Promise => { + const toRemoteArtifactCandidates = (file: string) => { + const target = toWorkerRelativeArtifactPath(file); + if (!target) return [] as string[]; + const outboxPath = `.opencode/openwork/outbox/${target}`.replace(/\/+/g, "/"); + if ( + target.startsWith(".opencode/openwork/outbox/") || + target.startsWith("./.opencode/openwork/outbox/") || + outboxPath === target + ) { + return [target]; + } + return [target, outboxPath]; + }; + + const ensureRemoteFileSyncSession = async (): Promise => { const client = props.openworkServerClient; const workspaceId = props.openworkServerWorkspaceId?.trim() ?? ""; if (!client || !workspaceId) { - throw new Error("Connect to OpenWork server to open remote files in Obsidian."); + throw new Error("Connect to OpenWork server to sync remote files."); } - const target = toWorkerRelativeArtifactPath(file); - if (!target) { - throw new Error("Only worker-relative files can be opened in Obsidian."); - } - - try { - const result = (await client.readWorkspaceFile(workspaceId, target)) as OpenworkWorkspaceFileContent; - return { ...result, path: result.path?.trim() || target }; - } catch (error) { - const candidateOutbox = `.opencode/openwork/outbox/${target}`.replace(/\/+/g, "/"); - const shouldTryOutbox = - !(target.startsWith(".opencode/openwork/outbox/") || target.startsWith("./.opencode/openwork/outbox/")) && - error instanceof OpenworkServerError && - error.status === 404; - - if (!shouldTryOutbox) { - throw error; + const existing = remoteFileSyncSession(); + if (existing && existing.workspaceId === workspaceId) { + if (Date.now() + 45_000 < existing.expiresAt) { + return existing; } - const result = (await client.readWorkspaceFile(workspaceId, candidateOutbox)) as OpenworkWorkspaceFileContent; - return { ...result, path: result.path?.trim() || candidateOutbox }; + try { + const renewed = await client.renewFileSession(existing.id, { ttlSeconds: 15 * 60 }); + const next: RemoteFileSyncSession = { + ...renewed.session, + cursor: existing.cursor, + }; + setRemoteFileSyncSession(next); + return next; + } catch (error) { + if (!(error instanceof OpenworkServerError) || error.code !== "file_session_not_found") { + throw error; + } + } } + + if (existing) { + await closeRemoteFileSyncSession(existing); + setRemoteFileSyncSession(null); + } + + const created = await client.createFileSession(workspaceId, { + ttlSeconds: 15 * 60, + write: true, + }); + const next: RemoteFileSyncSession = { + ...created.session, + cursor: 0, + }; + setRemoteFileSyncSession(next); + return next; + }; + + const refreshTrackedRemoteMirrorFile = async (session: RemoteFileSyncSession, path: string) => { + const client = props.openworkServerClient; + if (!client) throw new Error("OpenWork server client unavailable"); + + const result = await client.readFileBatch(session.id, [path]); + const item = result.items[0]; + if (!item?.ok) { + if (item?.code === "file_not_found") { + remoteMirrorTrackedFiles.delete(path); + return null; + } + throw new Error(item?.message ?? `Unable to read ${path}`); + } + + const content = base64ToUtf8(item.contentBase64); + const localPath = await writeObsidianMirrorFile(remoteMirrorWorkspaceKey(), path, content); + const local = await readObsidianMirrorFile(remoteMirrorWorkspaceKey(), path); + const fingerprint = textFingerprint(local.content ?? content); + + const previous = remoteMirrorTrackedFiles.get(path); + remoteMirrorTrackedFiles.set(path, { + path, + localPath, + remoteRevision: item.revision, + localFingerprint: fingerprint, + syncingLocal: previous?.syncingLocal ?? false, + }); + + return localPath; + }; + + const createConflictPath = (path: string) => { + const stamp = new Date().toISOString().replace(/[:.]/g, "-"); + const marker = `.openwork-conflict-${stamp}`; + const dot = path.lastIndexOf("."); + if (dot <= 0) { + return `${path}${marker}`; + } + return `${path.slice(0, dot)}${marker}${path.slice(dot)}`; + }; + + const runRemoteMirrorSyncTick = async () => { + if (remoteMirrorSyncInFlight) return; + if (remoteMirrorTrackedFiles.size === 0) { + stopRemoteMirrorSyncLoop(); + return; + } + + const client = props.openworkServerClient; + if (!client) { + stopRemoteMirrorSyncLoop(); + return; + } + + remoteMirrorSyncInFlight = true; + try { + let session = await ensureRemoteFileSyncSession(); + + const events = await client.listFileSessionEvents(session.id, { since: session.cursor }); + if (events.cursor !== session.cursor) { + session = { ...session, cursor: events.cursor }; + setRemoteFileSyncSession(session); + } + + const refreshPaths = new Set(); + for (const event of events.items) { + if (event.type === "write" && remoteMirrorTrackedFiles.has(event.path)) { + const tracked = remoteMirrorTrackedFiles.get(event.path); + if (!tracked?.syncingLocal) { + refreshPaths.add(event.path); + } + continue; + } + + if (event.type === "rename") { + const tracked = remoteMirrorTrackedFiles.get(event.path); + if (!tracked) continue; + remoteMirrorTrackedFiles.delete(event.path); + if (event.toPath?.trim()) { + const nextPath = event.toPath.trim(); + remoteMirrorTrackedFiles.set(nextPath, { + ...tracked, + path: nextPath, + }); + refreshPaths.add(nextPath); + } + continue; + } + + if (event.type === "delete") { + remoteMirrorTrackedFiles.delete(event.path); + } + } + + for (const path of refreshPaths) { + await refreshTrackedRemoteMirrorFile(session, path); + } + + for (const [path, tracked] of remoteMirrorTrackedFiles) { + if (tracked.syncingLocal) continue; + + const local = await readObsidianMirrorFile(remoteMirrorWorkspaceKey(), path); + if (!local.exists || local.content === null) continue; + const nextFingerprint = textFingerprint(local.content); + if (nextFingerprint === tracked.localFingerprint) continue; + + tracked.syncingLocal = true; + try { + const write = await client.writeFileBatch(session.id, [ + { + path, + contentBase64: utf8ToBase64(local.content), + ifMatchRevision: tracked.remoteRevision, + }, + ]); + const item = write.items[0]; + if (item?.ok) { + tracked.remoteRevision = item.revision; + tracked.localFingerprint = nextFingerprint; + continue; + } + + if (!item?.ok && item?.code === "conflict") { + const conflictPath = createConflictPath(path); + await writeObsidianMirrorFile(remoteMirrorWorkspaceKey(), conflictPath, local.content); + await refreshTrackedRemoteMirrorFile(session, path); + setToastMessage(`Conflict syncing ${path}. Saved local changes to ${conflictPath}.`); + continue; + } + + throw new Error(item?.message ?? `Unable to sync ${path}`); + } finally { + tracked.syncingLocal = false; + } + } + } catch (error) { + if (Date.now() - remoteMirrorLastErrorAt > 6_000) { + remoteMirrorLastErrorAt = Date.now(); + const message = error instanceof Error ? error.message : "Remote file sync failed"; + setToastMessage(message); + } + } finally { + remoteMirrorSyncInFlight = false; + } + }; + + const ensureRemoteMirrorSyncLoop = () => { + if (!isTauriRuntime()) return; + if (remoteMirrorTrackedFiles.size === 0) return; + if (remoteMirrorSyncTimer !== undefined) return; + remoteMirrorSyncTimer = window.setInterval(() => { + void runRemoteMirrorSyncTick(); + }, 2500); + void runRemoteMirrorSyncTick(); }; const mirrorRemoteArtifactForObsidian = async (file: string) => { - const remote = await readRemoteArtifactForObsidian(file); - const workspaceKey = - props.openworkServerWorkspaceId?.trim() || props.activeWorkspaceDisplay.id?.trim() || "remote-worker"; - return await writeObsidianMirrorFile(workspaceKey, remote.path, remote.content ?? ""); + const session = await ensureRemoteFileSyncSession(); + const client = props.openworkServerClient; + if (!client) { + throw new Error("Connect to OpenWork server to sync remote files."); + } + + const candidates = toRemoteArtifactCandidates(file); + if (candidates.length === 0) { + throw new Error("Only worker-relative files can be opened in Obsidian."); + } + + let lastError: Error | null = null; + for (const candidate of candidates) { + try { + const result = await client.readFileBatch(session.id, [candidate]); + const item = result.items[0]; + if (!item?.ok) { + if (item?.code === "file_not_found") continue; + throw new Error(item?.message ?? `Unable to read ${candidate}`); + } + + const content = base64ToUtf8(item.contentBase64); + const localPath = await writeObsidianMirrorFile(remoteMirrorWorkspaceKey(), candidate, content); + const local = await readObsidianMirrorFile(remoteMirrorWorkspaceKey(), candidate); + const fingerprint = textFingerprint(local.content ?? content); + + remoteMirrorTrackedFiles.set(candidate, { + path: candidate, + localPath, + remoteRevision: item.revision, + localFingerprint: fingerprint, + syncingLocal: false, + }); + ensureRemoteMirrorSyncLoop(); + return localPath; + } catch (error) { + lastError = error instanceof Error ? error : new Error(String(error)); + } + } + + throw lastError ?? new Error("Unable to open file in Obsidian"); }; + createEffect( + on( + () => + [ + isTauriRuntime(), + props.activeWorkspaceDisplay.workspaceType, + props.openworkServerWorkspaceId?.trim() ?? "", + Boolean(props.openworkServerClient), + ] as const, + ([desktopRuntime, workspaceType, workspaceId, hasClient], previous) => { + const previousWorkspaceId = previous?.[2] ?? ""; + const hasRemoteContext = desktopRuntime && workspaceType === "remote" && workspaceId.length > 0 && hasClient; + if (!hasRemoteContext) { + if (remoteFileSyncSession() || remoteMirrorTrackedFiles.size > 0 || remoteMirrorSyncTimer !== undefined) { + void resetRemoteFileSync(); + } + return; + } + + if (previousWorkspaceId && previousWorkspaceId !== workspaceId) { + void resetRemoteFileSync(); + } + }, + ), + ); + + onCleanup(() => { + void resetRemoteFileSync(); + }); + const revealArtifact = async (file: string) => { if (props.activeWorkspaceDisplay.workspaceType === "remote") { setToastMessage("Reveal is unavailable for remote workers."); diff --git a/packages/desktop/src-tauri/src/commands/misc.rs b/packages/desktop/src-tauri/src/commands/misc.rs index 77065538..7cf8df15 100644 --- a/packages/desktop/src-tauri/src/commands/misc.rs +++ b/packages/desktop/src-tauri/src/commands/misc.rs @@ -1,6 +1,7 @@ use std::collections::HashSet; use std::fs; use std::path::{Path, PathBuf}; +use std::time::UNIX_EPOCH; use crate::engine::doctor::resolve_engine_path; use crate::paths::home_dir; @@ -447,6 +448,72 @@ pub fn write_obsidian_mirror_file( Ok(target.to_string_lossy().to_string()) } +#[derive(Debug, Clone, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ObsidianMirrorFileContent { + pub exists: bool, + pub path: String, + pub content: Option, + pub updated_at_ms: Option, +} + +#[tauri::command] +pub fn read_obsidian_mirror_file( + app: AppHandle, + workspace_id: String, + file_path: String, +) -> Result { + let workspace_trimmed = workspace_id.trim(); + if workspace_trimmed.is_empty() { + return Err("workspace_id is required".to_string()); + } + + let workspace_key = sanitize_obsidian_workspace_id(workspace_trimmed); + if workspace_key.is_empty() { + return Err("workspace_id must contain at least one alphanumeric character".to_string()); + } + + let relative_path = normalize_obsidian_mirror_relative_path(&file_path)?; + let app_data_dir = app + .path() + .app_data_dir() + .map_err(|e| format!("Failed to resolve app data dir: {e}"))?; + + let mirror_root = app_data_dir.join("obsidian-mirror").join(workspace_key); + let target = mirror_root.join(relative_path); + let path_string = target.to_string_lossy().to_string(); + + if !target.exists() { + return Ok(ObsidianMirrorFileContent { + exists: false, + path: path_string, + content: None, + updated_at_ms: None, + }); + } + + let metadata = + fs::metadata(&target).map_err(|e| format!("Failed to stat {}: {e}", target.display()))?; + if !metadata.is_file() { + return Err(format!("Mirror path is not a file: {}", target.display())); + } + + let content = fs::read_to_string(&target) + .map_err(|e| format!("Failed to read {}: {e}", target.display()))?; + let updated_at_ms = metadata + .modified() + .ok() + .and_then(|value| value.duration_since(UNIX_EPOCH).ok()) + .map(|duration| duration.as_millis() as u64); + + Ok(ObsidianMirrorFileContent { + exists: true, + path: path_string, + content: Some(content), + updated_at_ms, + }) +} + #[cfg(test)] mod tests { use super::{normalize_obsidian_mirror_relative_path, sanitize_obsidian_workspace_id}; diff --git a/packages/desktop/src-tauri/src/lib.rs b/packages/desktop/src-tauri/src/lib.rs index 49ea1bf3..65cf8964 100644 --- a/packages/desktop/src-tauri/src/lib.rs +++ b/packages/desktop/src-tauri/src/lib.rs @@ -23,7 +23,8 @@ use commands::config::{read_opencode_config, write_opencode_config}; use commands::engine::{engine_doctor, engine_info, engine_install, engine_start, engine_stop}; use commands::misc::{ app_build_info, obsidian_is_available, open_in_obsidian, opencode_db_migrate, - opencode_mcp_auth, reset_opencode_cache, reset_openwork_state, write_obsidian_mirror_file, + opencode_mcp_auth, read_obsidian_mirror_file, reset_opencode_cache, reset_openwork_state, + write_obsidian_mirror_file, }; use commands::opencode_router::{ opencodeRouter_config_set, opencodeRouter_info, opencodeRouter_start, opencodeRouter_status, @@ -137,6 +138,7 @@ pub fn run() { obsidian_is_available, open_in_obsidian, write_obsidian_mirror_file, + read_obsidian_mirror_file, reset_openwork_state, reset_opencode_cache, opencode_db_migrate, diff --git a/packages/orchestrator/README.md b/packages/orchestrator/README.md index e6927bcf..053a3b9f 100644 --- a/packages/orchestrator/README.md +++ b/packages/orchestrator/README.md @@ -161,6 +161,46 @@ openwork status \ --opencode-url http://:4096 ``` +## File sessions (JIT catalog + batch read/write) + +Create a short-lived workspace file session and sync files in batches: + +```bash +# Create writable session +openwork files session create \ + --openwork-url http://:8787 \ + --token \ + --workspace-id \ + --write \ + --json + +# Fetch catalog snapshot +openwork files catalog \ + --openwork-url http://:8787 \ + --token \ + --limit 200 \ + --json + +# Read one or more files +openwork files read \ + --openwork-url http://:8787 \ + --token \ + --paths "README.md,notes/todo.md" \ + --json + +# Write a file (inline content or --file) +openwork files write \ + --openwork-url http://:8787 \ + --token \ + --path notes/todo.md \ + --content "hello from openwork" \ + --json + +# Watch change events and close session +openwork files events --openwork-url http://:8787 --token --since 0 --json +openwork files session close --openwork-url http://:8787 --token --json +``` + ## Smoke checks ```bash diff --git a/packages/orchestrator/package.json b/packages/orchestrator/package.json index fec8ae99..75718e6c 100644 --- a/packages/orchestrator/package.json +++ b/packages/orchestrator/package.json @@ -14,6 +14,7 @@ "build:sidecars": "node scripts/build-sidecars.mjs", "typecheck": "tsc -p tsconfig.typecheck.json", "test:router": "pnpm build && node scripts/router.mjs", + "test:files": "pnpm build && node scripts/files-session.mjs", "prepublishOnly": "node -e \"console.error('openwork-orchestrator is published via packages/orchestrator/scripts/publish-npm.mjs (meta + platform packages)'); process.exit(1);\"" }, "files": [ diff --git a/packages/orchestrator/scripts/files-session.mjs b/packages/orchestrator/scripts/files-session.mjs new file mode 100644 index 00000000..bd5b66e7 --- /dev/null +++ b/packages/orchestrator/scripts/files-session.mjs @@ -0,0 +1,312 @@ +import assert from "node:assert/strict"; +import { spawn } from "node:child_process"; +import { once } from "node:events"; +import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import { createServer } from "node:net"; +import { tmpdir } from "node:os"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const cliPath = resolve(__dirname, "..", "dist", "cli.js"); +const serverCliPath = resolve(__dirname, "..", "..", "server", "src", "cli.ts"); + +async function findFreePort() { + return new Promise((resolvePort, reject) => { + const server = createServer(); + server.unref(); + server.once("error", (error) => reject(error)); + server.listen(0, "127.0.0.1", () => { + const address = server.address(); + if (!address || typeof address === "string") { + server.close(); + reject(new Error("Failed to allocate free port")); + return; + } + server.close(() => resolvePort(address.port)); + }); + }); +} + +async function waitFor(url, timeoutMs = 10_000, pollMs = 200) { + const start = Date.now(); + let lastError; + while (Date.now() - start < timeoutMs) { + try { + const response = await fetch(url); + if (response.ok) return; + lastError = new Error(`HTTP ${response.status}`); + } catch (error) { + lastError = error; + } + await new Promise((resolveDelay) => setTimeout(resolveDelay, pollMs)); + } + throw lastError ?? new Error("Timed out waiting for endpoint"); +} + +async function fetchJson(url, init) { + const response = await fetch(url, init); + let payload = null; + try { + payload = await response.json(); + } catch { + payload = null; + } + if (!response.ok) { + throw new Error(`HTTP ${response.status}${payload?.message ? ` ${payload.message}` : ""}`); + } + return payload; +} + +async function runCli(args) { + const child = spawn("node", [cliPath, ...args], { stdio: ["ignore", "pipe", "pipe"] }); + let stdout = ""; + let stderr = ""; + child.stdout.setEncoding("utf8"); + child.stderr.setEncoding("utf8"); + child.stdout.on("data", (chunk) => { + stdout += chunk; + }); + child.stderr.on("data", (chunk) => { + stderr += chunk; + }); + + const [code] = await once(child, "exit"); + if (code !== 0) { + throw new Error(stderr.trim() || `openwork CLI failed with code ${code}`); + } + + const trimmed = stdout.trim(); + return trimmed ? JSON.parse(trimmed) : null; +} + +const root = await mkdtemp(join(tmpdir(), "openwork-file-session-")); +const workspace = join(root, "workspace"); +await mkdir(join(workspace, "notes"), { recursive: true }); +await writeFile(join(workspace, "notes", "remote.md"), "hello from remote\n", "utf8"); + +const port = await findFreePort(); +const token = "test-client-token"; +const hostToken = "test-host-token"; +const openworkUrl = `http://127.0.0.1:${port}`; + +const server = spawn( + "bun", + [ + serverCliPath, + "--host", + "127.0.0.1", + "--port", + String(port), + "--workspace", + workspace, + "--approval", + "auto", + "--token", + token, + "--host-token", + hostToken, + ], + { stdio: ["ignore", "pipe", "pipe"] }, +); + +let serverStderr = ""; +server.stderr.setEncoding("utf8"); +server.stderr.on("data", (chunk) => { + serverStderr += chunk; +}); + +try { + await waitFor(`${openworkUrl}/health`); + + const workspaces = await fetchJson(`${openworkUrl}/workspaces`, { + headers: { Authorization: `Bearer ${token}` }, + }); + const workspaceId = workspaces?.items?.[0]?.id; + assert.ok(workspaceId, "workspace id should be available"); + + const created = await runCli([ + "files", + "session", + "create", + "--openwork-url", + openworkUrl, + "--token", + token, + "--workspace-id", + workspaceId, + "--write", + "--json", + ]); + const sessionId = created?.session?.id; + assert.ok(sessionId, "session id should be created"); + assert.equal(created.session.workspaceId, workspaceId); + assert.equal(created.session.canWrite, true); + + const snapshot = await runCli([ + "files", + "catalog", + sessionId, + "--openwork-url", + openworkUrl, + "--token", + token, + "--json", + ]); + const catalogItem = snapshot.items.find((item) => item.path === "notes/remote.md"); + assert.ok(catalogItem, "catalog should include notes/remote.md"); + + const firstRead = await runCli([ + "files", + "read", + sessionId, + "--openwork-url", + openworkUrl, + "--token", + token, + "--path", + "notes/remote.md", + "--json", + ]); + assert.equal(firstRead.items[0].ok, true); + const firstRevision = firstRead.items[0].revision; + const firstContent = Buffer.from(firstRead.items[0].contentBase64, "base64").toString("utf8"); + assert.equal(firstContent, "hello from remote\n"); + + const wrote = await runCli([ + "files", + "write", + sessionId, + "--openwork-url", + openworkUrl, + "--token", + token, + "--path", + "notes/remote.md", + "--content", + "updated by openwork cli\n", + "--if-match", + firstRevision, + "--json", + ]); + assert.equal(wrote.items[0].ok, true); + const updatedRevision = wrote.items[0].revision; + + const diskContent = await readFile(join(workspace, "notes", "remote.md"), "utf8"); + assert.equal(diskContent, "updated by openwork cli\n"); + + await writeFile(join(workspace, "notes", "remote.md"), "changed outside session\n", "utf8"); + const staleWrite = await runCli([ + "files", + "write", + sessionId, + "--openwork-url", + openworkUrl, + "--token", + token, + "--path", + "notes/remote.md", + "--content", + "should conflict\n", + "--if-match", + updatedRevision, + "--json", + ]); + assert.equal(staleWrite.items[0].ok, false); + assert.equal(staleWrite.items[0].code, "conflict"); + + const mkdirResult = await runCli([ + "files", + "mkdir", + sessionId, + "--openwork-url", + openworkUrl, + "--token", + token, + "--path", + "notes/archive", + "--json", + ]); + assert.equal(mkdirResult.items[0].ok, true); + + const renameResult = await runCli([ + "files", + "rename", + sessionId, + "--openwork-url", + openworkUrl, + "--token", + token, + "--from", + "notes/remote.md", + "--to", + "notes/archive/remote.md", + "--json", + ]); + assert.equal(renameResult.items[0].ok, true); + + const deleteResult = await runCli([ + "files", + "delete", + sessionId, + "--openwork-url", + openworkUrl, + "--token", + token, + "--path", + "notes/archive/remote.md", + "--json", + ]); + assert.equal(deleteResult.items[0].ok, true); + + const events = await runCli([ + "files", + "events", + sessionId, + "--openwork-url", + openworkUrl, + "--token", + token, + "--since", + "0", + "--json", + ]); + const eventTypes = new Set(events.items.map((item) => item.type)); + assert.ok(eventTypes.has("write"), "events should include write"); + assert.ok(eventTypes.has("rename"), "events should include rename"); + assert.ok(eventTypes.has("delete"), "events should include delete"); + + const closed = await runCli([ + "files", + "session", + "close", + sessionId, + "--openwork-url", + openworkUrl, + "--token", + token, + "--json", + ]); + assert.equal(closed.ok, true); + + console.log(JSON.stringify({ ok: true, openworkUrl, workspaceId, sessionId }, null, 2)); +} catch (error) { + console.error( + JSON.stringify( + { + ok: false, + error: error instanceof Error ? error.message : String(error), + stderr: serverStderr.trim() || undefined, + }, + null, + 2, + ), + ); + process.exitCode = 1; +} finally { + if (server.exitCode === null) { + server.kill("SIGTERM"); + await Promise.race([once(server, "exit"), new Promise((resolveDelay) => setTimeout(resolveDelay, 3000))]); + } + await rm(root, { recursive: true, force: true }); +} diff --git a/packages/orchestrator/src/cli.ts b/packages/orchestrator/src/cli.ts index 6f427abe..c9f8f03f 100644 --- a/packages/orchestrator/src/cli.ts +++ b/packages/orchestrator/src/cli.ts @@ -2420,6 +2420,7 @@ function printHelp(): void { " openwork instance dispose [options]", " openwork approvals list --openwork-url --host-token ", " openwork approvals reply --allow|--deny --openwork-url --host-token ", + " openwork files [options]", " openwork status [--openwork-url ] [--opencode-url ]", "", "Commands:", @@ -2430,6 +2431,7 @@ function printHelp(): void { " instance Manage workspace instances (dispose)", " approvals list List pending approval requests", " approvals reply Approve or deny a request", + " files Manage file sessions and batch file sync", " status Check OpenCode/OpenWork health", "", "Options:", @@ -2452,6 +2454,20 @@ function printHelp(): void { " --openwork-port Port for openwork-server (default: 8787)", " --openwork-token Client token for openwork-server", " --openwork-host-token Host token for approvals", + " --workspace-id Workspace id for file session commands", + " --session-id File session id for file session commands", + " --path Workspace-relative file path", + " --paths Comma-separated list of workspace-relative file paths", + " --ttl-seconds File session TTL in seconds", + " --content Inline content for file writes", + " --content-base64 Base64 content for file writes", + " --file Local file path for file writes", + " --if-match Revision precondition for file writes", + " --from Source path for rename", + " --to Destination path for rename", + " --write Request writable file session", + " --force Force write despite revision mismatch", + " --recursive Recursive delete for files delete", " --approval manual | auto (default: manual)", " --approval-timeout Approval timeout in ms", " --read-only Start OpenWork server in read-only mode", @@ -4431,6 +4447,238 @@ async function runRouterDaemon(args: ParsedArgs) { await new Promise(() => undefined); } +function readOpenworkClientAuth(args: ParsedArgs): { openworkUrl: string; token: string } { + const openworkUrl = + readFlag(args.flags, "openwork-url") ?? + process.env.OPENWORK_URL ?? + process.env.OPENWORK_SERVER_URL ?? + ""; + const token = + readFlag(args.flags, "token") ?? + readFlag(args.flags, "openwork-token") ?? + process.env.OPENWORK_TOKEN ?? + ""; + + if (!openworkUrl || !token) { + throw new Error("openwork-url and token are required"); + } + + return { openworkUrl, token }; +} + +function readSessionId(args: ParsedArgs, fallbackIndex: number): string { + const sessionId = readFlag(args.flags, "session-id") ?? args.positionals[fallbackIndex] ?? ""; + const trimmed = sessionId.trim(); + if (!trimmed) { + throw new Error("session-id is required"); + } + return trimmed; +} + +async function runFiles(args: ParsedArgs) { + const outputJson = readBool(args.flags, "json", false); + const subcommand = args.positionals[1] ?? ""; + const { openworkUrl, token } = readOpenworkClientAuth(args); + const baseUrl = openworkUrl.replace(/\/$/, ""); + const headers = { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }; + + try { + if (subcommand === "session") { + const action = args.positionals[2] ?? "create"; + if (action === "create") { + const workspaceId = readFlag(args.flags, "workspace-id") ?? args.positionals[3] ?? ""; + if (!workspaceId.trim()) { + throw new Error("workspace-id is required for files session create"); + } + const ttlSeconds = readNumber(args.flags, "ttl-seconds", undefined); + const writeRequested = readBool(args.flags, "write", true); + const result = await fetchJson( + `${baseUrl}/workspace/${encodeURIComponent(workspaceId.trim())}/files/sessions`, + { + method: "POST", + headers, + body: JSON.stringify({ + ...(typeof ttlSeconds === "number" ? { ttlSeconds } : {}), + write: writeRequested, + }), + }, + ); + outputResult(result, outputJson); + return; + } + if (action === "renew") { + const sessionId = readSessionId(args, 3); + const ttlSeconds = readNumber(args.flags, "ttl-seconds", undefined); + const result = await fetchJson(`${baseUrl}/files/sessions/${encodeURIComponent(sessionId)}/renew`, { + method: "POST", + headers, + body: JSON.stringify({ + ...(typeof ttlSeconds === "number" ? { ttlSeconds } : {}), + }), + }); + outputResult(result, outputJson); + return; + } + if (action === "close" || action === "delete") { + const sessionId = readSessionId(args, 3); + const result = await fetchJson(`${baseUrl}/files/sessions/${encodeURIComponent(sessionId)}`, { + method: "DELETE", + headers, + }); + outputResult(result, outputJson); + return; + } + throw new Error("files session requires create|renew|close"); + } + + if (subcommand === "catalog") { + const sessionId = readSessionId(args, 2); + const params = new URLSearchParams(); + const prefix = readFlag(args.flags, "prefix"); + const after = readFlag(args.flags, "after"); + const limit = readNumber(args.flags, "limit", undefined); + const includeDirs = readBool(args.flags, "include-dirs", true); + if (prefix?.trim()) params.set("prefix", prefix.trim()); + if (after?.trim()) params.set("after", after.trim()); + if (typeof limit === "number") params.set("limit", String(limit)); + if (!includeDirs) params.set("includeDirs", "false"); + const query = params.toString(); + const result = await fetchJson( + `${baseUrl}/files/sessions/${encodeURIComponent(sessionId)}/catalog/snapshot${query ? `?${query}` : ""}`, + { + headers, + }, + ); + outputResult(result, outputJson); + return; + } + + if (subcommand === "events") { + const sessionId = readSessionId(args, 2); + const since = readNumber(args.flags, "since", undefined); + const query = typeof since === "number" ? `?since=${encodeURIComponent(String(since))}` : ""; + const result = await fetchJson(`${baseUrl}/files/sessions/${encodeURIComponent(sessionId)}/catalog/events${query}`, { + headers, + }); + outputResult(result, outputJson); + return; + } + + if (subcommand === "read") { + const sessionId = readSessionId(args, 2); + const pathsRaw = readFlag(args.flags, "paths"); + const singlePath = readFlag(args.flags, "path") ?? args.positionals[3]; + const paths = pathsRaw ? parseList(pathsRaw) : singlePath ? [singlePath] : []; + if (!paths.length) { + throw new Error("path or paths is required for files read"); + } + const result = await fetchJson(`${baseUrl}/files/sessions/${encodeURIComponent(sessionId)}/read-batch`, { + method: "POST", + headers, + body: JSON.stringify({ paths }), + }); + outputResult(result, outputJson); + return; + } + + if (subcommand === "write") { + const sessionId = readSessionId(args, 2); + const path = readFlag(args.flags, "path") ?? args.positionals[3] ?? ""; + if (!path.trim()) { + throw new Error("path is required for files write"); + } + + let contentBase64 = readFlag(args.flags, "content-base64") ?? ""; + if (!contentBase64) { + const inlineContent = readFlag(args.flags, "content"); + if (inlineContent !== undefined) { + contentBase64 = Buffer.from(inlineContent, "utf8").toString("base64"); + } + } + if (!contentBase64) { + const filePath = readFlag(args.flags, "file"); + if (filePath?.trim()) { + const fileBytes = await readFile(resolve(filePath.trim())); + contentBase64 = Buffer.from(fileBytes).toString("base64"); + } + } + if (!contentBase64) { + throw new Error("provide one of --content, --content-base64, or --file"); + } + + const ifMatchRevision = readFlag(args.flags, "if-match"); + const force = readBool(args.flags, "force", false); + const result = await fetchJson(`${baseUrl}/files/sessions/${encodeURIComponent(sessionId)}/write-batch`, { + method: "POST", + headers, + body: JSON.stringify({ + writes: [ + { + path: path.trim(), + contentBase64, + ...(ifMatchRevision?.trim() ? { ifMatchRevision: ifMatchRevision.trim() } : {}), + ...(force ? { force: true } : {}), + }, + ], + }), + }); + outputResult(result, outputJson); + return; + } + + if (subcommand === "mkdir") { + const sessionId = readSessionId(args, 2); + const path = readFlag(args.flags, "path") ?? args.positionals[3] ?? ""; + if (!path.trim()) throw new Error("path is required for files mkdir"); + const result = await fetchJson(`${baseUrl}/files/sessions/${encodeURIComponent(sessionId)}/ops`, { + method: "POST", + headers, + body: JSON.stringify({ operations: [{ type: "mkdir", path: path.trim() }] }), + }); + outputResult(result, outputJson); + return; + } + + if (subcommand === "delete") { + const sessionId = readSessionId(args, 2); + const path = readFlag(args.flags, "path") ?? args.positionals[3] ?? ""; + if (!path.trim()) throw new Error("path is required for files delete"); + const recursive = readBool(args.flags, "recursive", false); + const result = await fetchJson(`${baseUrl}/files/sessions/${encodeURIComponent(sessionId)}/ops`, { + method: "POST", + headers, + body: JSON.stringify({ operations: [{ type: "delete", path: path.trim(), ...(recursive ? { recursive: true } : {}) }] }), + }); + outputResult(result, outputJson); + return; + } + + if (subcommand === "rename") { + const sessionId = readSessionId(args, 2); + const from = readFlag(args.flags, "from") ?? args.positionals[3] ?? ""; + const to = readFlag(args.flags, "to") ?? args.positionals[4] ?? ""; + if (!from.trim() || !to.trim()) { + throw new Error("from and to are required for files rename"); + } + const result = await fetchJson(`${baseUrl}/files/sessions/${encodeURIComponent(sessionId)}/ops`, { + method: "POST", + headers, + body: JSON.stringify({ operations: [{ type: "rename", from: from.trim(), to: to.trim() }] }), + }); + outputResult(result, outputJson); + return; + } + + throw new Error("files requires session|catalog|events|read|write|mkdir|delete|rename"); + } catch (error) { + outputError(error, outputJson); + process.exitCode = 1; + } +} + async function runApprovals(args: ParsedArgs) { const subcommand = args.positionals[1]; if (!subcommand || (subcommand !== "list" && subcommand !== "reply")) { @@ -5591,6 +5839,10 @@ async function main() { await runApprovals(args); return; } + if (command === "files") { + await runFiles(args); + return; + } if (command === "status") { await runStatus(args); return; diff --git a/packages/server/README.md b/packages/server/README.md index c29c832f..771f7c9d 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -112,6 +112,14 @@ Inbox/outbox: - `POST /workspace/:id/inbox` (multipart upload into `.opencode/openwork/inbox/`) - `GET /workspace/:id/artifacts` - `GET /workspace/:id/artifacts/:artifactId` +- `POST /workspace/:id/files/sessions` +- `POST /files/sessions/:sessionId/renew` +- `DELETE /files/sessions/:sessionId` +- `GET /files/sessions/:sessionId/catalog/snapshot` +- `GET /files/sessions/:sessionId/catalog/events` +- `POST /files/sessions/:sessionId/read-batch` +- `POST /files/sessions/:sessionId/write-batch` +- `POST /files/sessions/:sessionId/ops` Toy UI (static assets served by the server): diff --git a/packages/server/src/file-sessions.test.ts b/packages/server/src/file-sessions.test.ts new file mode 100644 index 00000000..7d8db105 --- /dev/null +++ b/packages/server/src/file-sessions.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, test } from "bun:test"; +import { FileSessionStore } from "./file-sessions.js"; + +describe("FileSessionStore", () => { + test("creates, renews, and closes sessions", () => { + const store = new FileSessionStore({ maxSessions: 10 }); + const created = store.create({ + workspaceId: "ws_1", + workspaceRoot: "/tmp/ws", + actorTokenHash: "tok_1", + actorScope: "collaborator", + canWrite: true, + ttlMs: 60_000, + }); + + const fetched = store.get(created.id); + expect(fetched?.workspaceId).toBe("ws_1"); + expect(fetched?.canWrite).toBe(true); + + const createdExpiry = created.expiresAt; + const renewed = store.renew(created.id, 120_000); + expect(renewed).not.toBeNull(); + expect((renewed?.expiresAt ?? 0) > createdExpiry).toBe(true); + + const closed = store.close(created.id); + expect(closed).toBe(true); + expect(store.get(created.id)).toBeNull(); + }); + + test("records and paginates workspace events", () => { + const store = new FileSessionStore({ maxEventsPerWorkspace: 5 }); + + store.recordWorkspaceEvent({ workspaceId: "ws_1", type: "write", path: "notes/a.md", revision: "1" }); + store.recordWorkspaceEvent({ workspaceId: "ws_1", type: "mkdir", path: "notes" }); + store.recordWorkspaceEvent({ workspaceId: "ws_1", type: "rename", path: "notes/a.md", toPath: "notes/b.md" }); + + const fromStart = store.listWorkspaceEvents("ws_1", 0); + expect(fromStart.items.length).toBe(3); + expect(fromStart.cursor).toBe(3); + + const fromSecond = store.listWorkspaceEvents("ws_1", 2); + expect(fromSecond.items.length).toBe(1); + expect(fromSecond.items[0]?.type).toBe("rename"); + }); +}); diff --git a/packages/server/src/file-sessions.ts b/packages/server/src/file-sessions.ts new file mode 100644 index 00000000..ad846fea --- /dev/null +++ b/packages/server/src/file-sessions.ts @@ -0,0 +1,152 @@ +import type { TokenScope } from "./types.js"; +import { shortId } from "./utils.js"; + +export type FileSessionEventType = "write" | "delete" | "rename" | "mkdir"; + +export type FileSessionEvent = { + id: string; + seq: number; + workspaceId: string; + type: FileSessionEventType; + path: string; + toPath?: string; + revision?: string; + timestamp: number; +}; + +export type FileSessionRecord = { + id: string; + workspaceId: string; + workspaceRoot: string; + actorTokenHash: string; + actorScope: TokenScope; + canWrite: boolean; + createdAt: number; + expiresAt: number; +}; + +type WorkspaceEventState = { + seq: number; + events: FileSessionEvent[]; +}; + +export class FileSessionStore { + private sessions = new Map(); + + private workspaceEvents = new Map(); + + private maxSessions: number; + + private maxEventsPerWorkspace: number; + + constructor(options?: { maxSessions?: number; maxEventsPerWorkspace?: number }) { + this.maxSessions = options?.maxSessions ?? 256; + this.maxEventsPerWorkspace = options?.maxEventsPerWorkspace ?? 500; + } + + create(input: { + workspaceId: string; + workspaceRoot: string; + actorTokenHash: string; + actorScope: TokenScope; + canWrite: boolean; + ttlMs: number; + }): FileSessionRecord { + this.pruneExpired(); + this.evictIfNeeded(); + + const now = Date.now(); + const record: FileSessionRecord = { + id: shortId(), + workspaceId: input.workspaceId, + workspaceRoot: input.workspaceRoot, + actorTokenHash: input.actorTokenHash, + actorScope: input.actorScope, + canWrite: input.canWrite, + createdAt: now, + expiresAt: now + input.ttlMs, + }; + this.sessions.set(record.id, record); + return record; + } + + get(sessionId: string): FileSessionRecord | null { + this.pruneExpired(); + const value = this.sessions.get(sessionId); + return value ?? null; + } + + renew(sessionId: string, ttlMs: number): FileSessionRecord | null { + this.pruneExpired(); + const value = this.sessions.get(sessionId); + if (!value) return null; + value.expiresAt = Date.now() + ttlMs; + this.sessions.set(sessionId, value); + return value; + } + + close(sessionId: string): boolean { + return this.sessions.delete(sessionId); + } + + recordWorkspaceEvent(input: { + workspaceId: string; + type: FileSessionEventType; + path: string; + toPath?: string; + revision?: string; + }): FileSessionEvent { + const state = this.workspaceEvents.get(input.workspaceId) ?? { seq: 0, events: [] }; + const event: FileSessionEvent = { + id: shortId(), + seq: state.seq + 1, + workspaceId: input.workspaceId, + type: input.type, + path: input.path, + toPath: input.toPath, + revision: input.revision, + timestamp: Date.now(), + }; + state.seq = event.seq; + state.events.push(event); + if (state.events.length > this.maxEventsPerWorkspace) { + state.events.splice(0, state.events.length - this.maxEventsPerWorkspace); + } + this.workspaceEvents.set(input.workspaceId, state); + return event; + } + + listWorkspaceEvents(workspaceId: string, since = 0): { items: FileSessionEvent[]; cursor: number } { + const state = this.workspaceEvents.get(workspaceId); + if (!state) { + return { items: [], cursor: 0 }; + } + const cursor = Number.isFinite(since) && since > 0 ? since : 0; + const items = state.events.filter((item) => item.seq > cursor); + return { items, cursor: state.seq }; + } + + private pruneExpired(): void { + const now = Date.now(); + for (const [id, session] of this.sessions) { + if (session.expiresAt <= now) { + this.sessions.delete(id); + } + } + } + + private evictIfNeeded(): void { + if (this.sessions.size < this.maxSessions) return; + let oldestId: string | null = null; + let oldestExpiry = Number.POSITIVE_INFINITY; + for (const [id, session] of this.sessions) { + if (session.expiresAt < oldestExpiry) { + oldestExpiry = session.expiresAt; + oldestId = id; + } + } + if (oldestId) { + this.sessions.delete(oldestId); + } + } +} diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index 1cbf6ea0..0a49e2c3 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -21,10 +21,19 @@ import { workspaceIdForPath } from "./workspaces.js"; import { sanitizeCommandName, validateMcpName } from "./validators.js"; import { TokenService } from "./tokens.js"; import { TOY_UI_CSS, TOY_UI_HTML, TOY_UI_JS, cssResponse, htmlResponse, jsResponse } from "./toy-ui.js"; +import { FileSessionStore } from "./file-sessions.js"; import pkg from "../package.json" with { type: "json" }; const SERVER_VERSION = pkg.version; +const FILE_SESSION_DEFAULT_TTL_MS = 15 * 60 * 1000; +const FILE_SESSION_MIN_TTL_MS = 30 * 1000; +const FILE_SESSION_MAX_TTL_MS = 24 * 60 * 60 * 1000; +const FILE_SESSION_MAX_BATCH_ITEMS = 64; +const FILE_SESSION_MAX_FILE_BYTES = 5_000_000; +const FILE_SESSION_CATALOG_DEFAULT_LIMIT = 2000; +const FILE_SESSION_CATALOG_MAX_LIMIT = 10000; + type LogLevel = "info" | "warn" | "error"; type LogAttributes = Record; @@ -1050,6 +1059,159 @@ async function listInbox(inboxRoot: string): Promise FILE_SESSION_MAX_TTL_MS) return FILE_SESSION_MAX_TTL_MS; + return ttlMs; +} + +function parseCatalogLimit(input: string | null): number { + if (!input) return FILE_SESSION_CATALOG_DEFAULT_LIMIT; + const parsed = Number(input); + if (!Number.isFinite(parsed) || parsed <= 0) return FILE_SESSION_CATALOG_DEFAULT_LIMIT; + return Math.min(Math.floor(parsed), FILE_SESSION_CATALOG_MAX_LIMIT); +} + +function parseSessionCursor(input: string | null): number { + if (!input) return 0; + const parsed = Number(input); + if (!Number.isFinite(parsed) || parsed < 0) return 0; + return Math.floor(parsed); +} + +function parseCatalogPathFilter(input: string | null): string | null { + if (!input) return null; + const trimmed = input.trim(); + if (!trimmed) return null; + return normalizeWorkspaceRelativePath(trimmed, { allowSubdirs: true }); +} + +function matchesCatalogFilter(path: string, filter: string | null): boolean { + if (!filter) return true; + return path === filter || path.startsWith(`${filter}/`); +} + +function normalizeResolvedRelativePath(input: string): string { + const normalized = input.replace(/\\/g, "/"); + const parts = normalized.split("/").filter(Boolean); + if (!parts.length) { + throw new ApiError(400, "invalid_path", "Path is required"); + } + for (const part of parts) { + if (part === "." || part === "..") { + throw new ApiError(400, "invalid_path", "Path traversal is not allowed"); + } + } + return parts.join("/"); +} + +async function listWorkspaceCatalogEntries(workspaceRoot: string): Promise { + const rootResolved = resolve(workspaceRoot); + const items: FileSessionCatalogEntry[] = []; + + const walk = async (dirPath: string) => { + const entries = await readdir(dirPath, { withFileTypes: true }); + entries.sort((a, b) => a.name.localeCompare(b.name)); + + for (const entry of entries) { + const absPath = join(dirPath, entry.name); + const relRaw = relative(rootResolved, absPath).replace(/\\/g, "/"); + const rel = normalizeResolvedRelativePath(relRaw); + + if (entry.isDirectory()) { + const info = await stat(absPath); + items.push({ + path: rel, + kind: "dir", + size: 0, + mtimeMs: info.mtimeMs, + revision: fileRevision({ mtimeMs: info.mtimeMs, size: 0 }), + }); + await walk(absPath); + continue; + } + + if (!entry.isFile()) continue; + const info = await stat(absPath); + items.push({ + path: rel, + kind: "file", + size: info.size, + mtimeMs: info.mtimeMs, + revision: fileRevision(info), + }); + } + }; + + if (await exists(rootResolved)) { + await walk(rootResolved); + } + + items.sort((a, b) => a.path.localeCompare(b.path)); + return items; +} + +function parseBatchPathList(input: unknown): string[] { + if (!Array.isArray(input)) { + throw new ApiError(400, "invalid_payload", "paths must be an array"); + } + if (!input.length) { + throw new ApiError(400, "invalid_payload", "paths must not be empty"); + } + if (input.length > FILE_SESSION_MAX_BATCH_ITEMS) { + throw new ApiError(400, "invalid_payload", `paths must include <= ${FILE_SESSION_MAX_BATCH_ITEMS} items`); + } + return input.map((raw) => normalizeWorkspaceRelativePath(String(raw ?? ""), { allowSubdirs: true })); +} + +function parseBatchWriteList(input: unknown): Array<{ path: string; contentBase64: string; ifMatchRevision?: string; force?: boolean }> { + if (!Array.isArray(input)) { + throw new ApiError(400, "invalid_payload", "writes must be an array"); + } + if (!input.length) { + throw new ApiError(400, "invalid_payload", "writes must not be empty"); + } + if (input.length > FILE_SESSION_MAX_BATCH_ITEMS) { + throw new ApiError(400, "invalid_payload", `writes must include <= ${FILE_SESSION_MAX_BATCH_ITEMS} items`); + } + + return input.map((raw) => { + if (!raw || typeof raw !== "object") { + throw new ApiError(400, "invalid_payload", "write entries must be objects"); + } + const record = raw as Record; + const contentBase64 = typeof record.contentBase64 === "string" ? record.contentBase64.trim() : ""; + if (!contentBase64) { + throw new ApiError(400, "invalid_payload", "contentBase64 is required"); + } + const ifMatchRevision = + typeof record.ifMatchRevision === "string" && record.ifMatchRevision.trim().length + ? record.ifMatchRevision.trim() + : undefined; + return { + path: normalizeWorkspaceRelativePath(String(record.path ?? ""), { allowSubdirs: true }), + contentBase64, + ...(ifMatchRevision ? { ifMatchRevision } : {}), + ...(record.force === true ? { force: true } : {}), + }; + }); +} + function emitReloadEvent( reloadEvents: ReloadEventStore, workspace: WorkspaceInfo, @@ -1092,6 +1254,44 @@ function serializeWorkspace(workspace: ServerConfig["workspaces"][number]) { function createRoutes(config: ServerConfig, approvals: ApprovalService, tokens: TokenService): Route[] { const routes: Route[] = []; + const fileSessions = new FileSessionStore(); + + const serializeFileSession = (session: { + id: string; + workspaceId: string; + createdAt: number; + expiresAt: number; + canWrite: boolean; + }) => ({ + id: session.id, + workspaceId: session.workspaceId, + createdAt: session.createdAt, + expiresAt: session.expiresAt, + ttlMs: Math.max(0, session.expiresAt - Date.now()), + canWrite: session.canWrite, + }); + + const resolveFileSession = (ctx: RequestContext, sessionId: string) => { + const session = fileSessions.get(sessionId); + if (!session) { + throw new ApiError(404, "file_session_not_found", "File session not found"); + } + + if (!ctx.actor?.tokenHash || session.actorTokenHash !== ctx.actor.tokenHash) { + throw new ApiError(403, "forbidden", "File session does not belong to this token"); + } + + const workspace = config.workspaces.find((item) => item.id === session.workspaceId); + if (!workspace) { + throw new ApiError(404, "workspace_not_found", "Workspace not found for this file session"); + } + + return { session, workspace }; + }; + + const recordWorkspaceFileEvent = (workspaceId: string, input: { type: "write" | "delete" | "rename" | "mkdir"; path: string; toPath?: string; revision?: string }) => { + return fileSessions.recordWorkspaceEvent({ workspaceId, ...input }); + }; addRoute(routes, "GET", "/health", "none", async () => { return jsonResponse({ ok: true, version: SERVER_VERSION, uptimeMs: Date.now() - config.startedAt }); @@ -2375,6 +2575,360 @@ function createRoutes(config: ServerConfig, approvals: ApprovalService, tokens: return new Response((Bun as any).file(absPath), { status: 200, headers }); }); + addRoute(routes, "POST", "/workspace/:id/files/sessions", "client", async (ctx) => { + const workspace = await resolveWorkspace(config, ctx.params.id); + const body = await readJsonBody(ctx.request); + const ttlMs = parseFileSessionTtlMs((body as Record).ttlSeconds); + const requestWrite = (body as Record).write !== false; + const canWrite = + requestWrite && + !config.readOnly && + scopeRank(ctx.actor?.scope ?? "viewer") >= scopeRank("collaborator"); + + const session = fileSessions.create({ + workspaceId: workspace.id, + workspaceRoot: workspace.path, + actorTokenHash: ctx.actor?.tokenHash ?? "", + actorScope: ctx.actor?.scope ?? "viewer", + canWrite, + ttlMs, + }); + + return jsonResponse({ session: serializeFileSession(session) }); + }); + + addRoute(routes, "POST", "/files/sessions/:sessionId/renew", "client", async (ctx) => { + const body = await readJsonBody(ctx.request); + const ttlMs = parseFileSessionTtlMs((body as Record).ttlSeconds); + const { session } = resolveFileSession(ctx, ctx.params.sessionId); + const renewed = fileSessions.renew(session.id, ttlMs); + if (!renewed) { + throw new ApiError(404, "file_session_not_found", "File session not found"); + } + return jsonResponse({ session: serializeFileSession(renewed) }); + }); + + addRoute(routes, "DELETE", "/files/sessions/:sessionId", "client", async (ctx) => { + const { session } = resolveFileSession(ctx, ctx.params.sessionId); + fileSessions.close(session.id); + return jsonResponse({ ok: true }); + }); + + addRoute(routes, "GET", "/files/sessions/:sessionId/catalog/snapshot", "client", async (ctx) => { + const { workspace } = resolveFileSession(ctx, ctx.params.sessionId); + const prefix = parseCatalogPathFilter(ctx.url.searchParams.get("prefix")); + const after = parseCatalogPathFilter(ctx.url.searchParams.get("after")); + const includeDirs = ctx.url.searchParams.get("includeDirs") !== "false"; + const limit = parseCatalogLimit(ctx.url.searchParams.get("limit")); + + const entries = await listWorkspaceCatalogEntries(workspace.path); + const filtered = entries.filter((entry) => { + if (!includeDirs && entry.kind === "dir") return false; + if (!matchesCatalogFilter(entry.path, prefix)) return false; + if (after && entry.path <= after) return false; + return true; + }); + + const items = filtered.slice(0, limit); + const truncated = filtered.length > items.length; + const nextAfter = truncated ? items[items.length - 1]?.path : undefined; + const events = fileSessions.listWorkspaceEvents(workspace.id, Number.MAX_SAFE_INTEGER); + + return jsonResponse({ + sessionId: ctx.params.sessionId, + workspaceId: workspace.id, + generatedAt: Date.now(), + cursor: events.cursor, + total: filtered.length, + truncated, + nextAfter, + items, + }); + }); + + addRoute(routes, "GET", "/files/sessions/:sessionId/catalog/events", "client", async (ctx) => { + const { workspace } = resolveFileSession(ctx, ctx.params.sessionId); + const since = parseSessionCursor(ctx.url.searchParams.get("since")); + const events = fileSessions.listWorkspaceEvents(workspace.id, since); + return jsonResponse(events); + }); + + addRoute(routes, "POST", "/files/sessions/:sessionId/read-batch", "client", async (ctx) => { + const { workspace } = resolveFileSession(ctx, ctx.params.sessionId); + const body = await readJsonBody(ctx.request); + const paths = parseBatchPathList((body as Record).paths); + const items: Array> = []; + + for (const relativePath of paths) { + try { + const absPath = resolveSafeChildPath(workspace.path, relativePath); + if (!(await exists(absPath))) { + items.push({ ok: false, path: relativePath, code: "file_not_found", message: "File not found" }); + continue; + } + const info = await stat(absPath); + if (!info.isFile()) { + items.push({ ok: false, path: relativePath, code: "file_not_found", message: "File not found" }); + continue; + } + if (info.size > FILE_SESSION_MAX_FILE_BYTES) { + items.push({ + ok: false, + path: relativePath, + code: "file_too_large", + message: "File exceeds size limit", + maxBytes: FILE_SESSION_MAX_FILE_BYTES, + size: info.size, + }); + continue; + } + + const content = await readFile(absPath); + items.push({ + ok: true, + path: relativePath, + kind: "file", + bytes: info.size, + updatedAt: info.mtimeMs, + revision: fileRevision(info), + contentBase64: content.toString("base64"), + }); + } catch (error) { + const message = error instanceof ApiError ? error.message : "Unable to read file"; + const code = error instanceof ApiError ? error.code : "read_failed"; + items.push({ ok: false, path: relativePath, code, message }); + } + } + + return jsonResponse({ items }); + }); + + addRoute(routes, "POST", "/files/sessions/:sessionId/write-batch", "client", async (ctx) => { + ensureWritable(config); + requireClientScope(ctx, "collaborator"); + const { session, workspace } = resolveFileSession(ctx, ctx.params.sessionId); + if (!session.canWrite) { + throw new ApiError(403, "forbidden", "File session is read-only"); + } + + const body = await readJsonBody(ctx.request); + const writes = parseBatchWriteList((body as Record).writes); + const items: Array> = []; + + const plan: Array<{ + path: string; + absPath: string; + bytes: Buffer; + ifMatchRevision?: string; + force?: boolean; + beforeRevision: string | null; + }> = []; + + for (const write of writes) { + try { + const absPath = resolveSafeChildPath(workspace.path, write.path); + const bytes = Buffer.from(write.contentBase64, "base64"); + if (bytes.byteLength > FILE_SESSION_MAX_FILE_BYTES) { + items.push({ + ok: false, + path: write.path, + code: "file_too_large", + message: "File exceeds size limit", + maxBytes: FILE_SESSION_MAX_FILE_BYTES, + size: bytes.byteLength, + }); + continue; + } + + const before = (await exists(absPath)) ? await stat(absPath) : null; + if (before && !before.isFile()) { + items.push({ ok: false, path: write.path, code: "invalid_path", message: "Path must point to a file" }); + continue; + } + const beforeRevision = before ? fileRevision(before) : null; + if (!write.force && write.ifMatchRevision && write.ifMatchRevision !== beforeRevision) { + items.push({ + ok: false, + path: write.path, + code: "conflict", + message: "File changed since it was loaded", + expectedRevision: write.ifMatchRevision, + currentRevision: beforeRevision, + }); + continue; + } + + plan.push({ + path: write.path, + absPath, + bytes, + beforeRevision, + ...(write.ifMatchRevision ? { ifMatchRevision: write.ifMatchRevision } : {}), + ...(write.force ? { force: true } : {}), + }); + } catch (error) { + const message = error instanceof ApiError ? error.message : "Invalid write request"; + const code = error instanceof ApiError ? error.code : "invalid_payload"; + items.push({ ok: false, path: write.path, code, message }); + } + } + + if (plan.length) { + await requireApproval(ctx, { + workspaceId: workspace.id, + action: "workspace.files.session.write", + summary: `Write ${plan.length} file(s) via file session`, + paths: plan.map((item) => item.absPath), + }); + } + + for (const entry of plan) { + try { + const before = (await exists(entry.absPath)) ? await stat(entry.absPath) : null; + const currentRevision = before ? fileRevision(before) : null; + if (!entry.force && entry.ifMatchRevision && currentRevision !== entry.ifMatchRevision) { + items.push({ + ok: false, + path: entry.path, + code: "conflict", + message: "File changed before write could be applied", + expectedRevision: entry.ifMatchRevision, + currentRevision, + }); + continue; + } + + await ensureDir(dirname(entry.absPath)); + const tmp = `${entry.absPath}.tmp-${shortId()}`; + await writeFile(tmp, entry.bytes); + await rename(tmp, entry.absPath); + const after = await stat(entry.absPath); + const revision = fileRevision(after); + + recordWorkspaceFileEvent(workspace.id, { type: "write", path: entry.path, revision }); + + await recordAudit(workspace.path, { + id: shortId(), + workspaceId: workspace.id, + actor: ctx.actor ?? { type: "remote" }, + action: "workspace.files.session.write", + target: entry.absPath, + summary: `Wrote ${entry.path} via file session`, + timestamp: Date.now(), + }); + + items.push({ + ok: true, + path: entry.path, + bytes: entry.bytes.byteLength, + updatedAt: after.mtimeMs, + revision, + previousRevision: entry.beforeRevision, + }); + } catch (error) { + const message = error instanceof Error ? error.message : "Failed to write file"; + items.push({ ok: false, path: entry.path, code: "write_failed", message }); + } + } + + const events = fileSessions.listWorkspaceEvents(workspace.id, Number.MAX_SAFE_INTEGER); + return jsonResponse({ items, cursor: events.cursor }); + }); + + addRoute(routes, "POST", "/files/sessions/:sessionId/ops", "client", async (ctx) => { + ensureWritable(config); + requireClientScope(ctx, "collaborator"); + const { session, workspace } = resolveFileSession(ctx, ctx.params.sessionId); + if (!session.canWrite) { + throw new ApiError(403, "forbidden", "File session is read-only"); + } + + const body = await readJsonBody(ctx.request); + const operations = Array.isArray((body as Record).operations) + ? ((body as Record).operations as Array>) + : null; + if (!operations || !operations.length) { + throw new ApiError(400, "invalid_payload", "operations must be a non-empty array"); + } + if (operations.length > FILE_SESSION_MAX_BATCH_ITEMS) { + throw new ApiError(400, "invalid_payload", `operations must include <= ${FILE_SESSION_MAX_BATCH_ITEMS} items`); + } + + const items: Array> = []; + const approvalPaths: string[] = []; + for (const op of operations) { + if (typeof op?.path === "string" && op.path.trim()) { + approvalPaths.push(resolveSafeChildPath(workspace.path, normalizeWorkspaceRelativePath(op.path, { allowSubdirs: true }))); + } + if (typeof op?.from === "string" && op.from.trim()) { + approvalPaths.push(resolveSafeChildPath(workspace.path, normalizeWorkspaceRelativePath(op.from, { allowSubdirs: true }))); + } + if (typeof op?.to === "string" && op.to.trim()) { + approvalPaths.push(resolveSafeChildPath(workspace.path, normalizeWorkspaceRelativePath(op.to, { allowSubdirs: true }))); + } + } + + if (approvalPaths.length) { + await requireApproval(ctx, { + workspaceId: workspace.id, + action: "workspace.files.session.ops", + summary: `Apply ${operations.length} file operation(s) via file session`, + paths: approvalPaths, + }); + } + + for (const op of operations) { + const type = String(op.type ?? "").trim(); + try { + if (type === "mkdir") { + const path = normalizeWorkspaceRelativePath(String(op.path ?? ""), { allowSubdirs: true }); + const absPath = resolveSafeChildPath(workspace.path, path); + await ensureDir(absPath); + recordWorkspaceFileEvent(workspace.id, { type: "mkdir", path }); + items.push({ ok: true, type, path }); + continue; + } + + if (type === "delete") { + const path = normalizeWorkspaceRelativePath(String(op.path ?? ""), { allowSubdirs: true }); + const absPath = resolveSafeChildPath(workspace.path, path); + if (!(await exists(absPath))) { + items.push({ ok: false, type, path, code: "file_not_found", message: "Path not found" }); + continue; + } + await rm(absPath, { recursive: op.recursive === true, force: false }); + recordWorkspaceFileEvent(workspace.id, { type: "delete", path }); + items.push({ ok: true, type, path }); + continue; + } + + if (type === "rename") { + const from = normalizeWorkspaceRelativePath(String(op.from ?? ""), { allowSubdirs: true }); + const to = normalizeWorkspaceRelativePath(String(op.to ?? ""), { allowSubdirs: true }); + const fromAbs = resolveSafeChildPath(workspace.path, from); + const toAbs = resolveSafeChildPath(workspace.path, to); + if (!(await exists(fromAbs))) { + items.push({ ok: false, type, from, to, code: "file_not_found", message: "Source path not found" }); + continue; + } + await ensureDir(dirname(toAbs)); + await rename(fromAbs, toAbs); + recordWorkspaceFileEvent(workspace.id, { type: "rename", path: from, toPath: to }); + items.push({ ok: true, type, from, to }); + continue; + } + + items.push({ ok: false, type, code: "invalid_operation", message: `Unsupported operation type: ${type}` }); + } catch (error) { + const message = error instanceof Error ? error.message : "Operation failed"; + items.push({ ok: false, type, code: "operation_failed", message }); + } + } + + const events = fileSessions.listWorkspaceEvents(workspace.id, Number.MAX_SAFE_INTEGER); + return jsonResponse({ items, cursor: events.cursor }); + }); + addRoute(routes, "GET", "/workspace/:id/files/content", "client", async (ctx) => { const workspace = await resolveWorkspace(config, ctx.params.id); const requested = (ctx.url.searchParams.get("path") ?? "").trim(); @@ -2394,7 +2948,7 @@ function createRoutes(config: ServerConfig, approvals: ApprovalService, tokens: throw new ApiError(404, "file_not_found", "File not found"); } - const maxBytes = 5_000_000; + const maxBytes = FILE_SESSION_MAX_FILE_BYTES; if (info.size > maxBytes) { throw new ApiError(413, "file_too_large", "File exceeds size limit", { maxBytes, size: info.size }); } @@ -2422,7 +2976,7 @@ function createRoutes(config: ServerConfig, approvals: ApprovalService, tokens: } const content = body.content; const bytes = Buffer.byteLength(content, "utf8"); - const maxBytes = 5_000_000; + const maxBytes = FILE_SESSION_MAX_FILE_BYTES; if (bytes > maxBytes) { throw new ApiError(413, "file_too_large", "File exceeds size limit", { maxBytes, size: bytes }); } @@ -2458,6 +3012,13 @@ function createRoutes(config: ServerConfig, approvals: ApprovalService, tokens: await writeFile(tmp, content, "utf8"); await rename(tmp, absPath); const after = await stat(absPath); + const revision = fileRevision(after); + + recordWorkspaceFileEvent(workspace.id, { + type: "write", + path: relativePath, + revision, + }); await recordAudit(workspace.path, { id: shortId(), @@ -2469,7 +3030,7 @@ function createRoutes(config: ServerConfig, approvals: ApprovalService, tokens: timestamp: Date.now(), }); - return jsonResponse({ ok: true, path: relativePath, bytes, updatedAt: after.mtimeMs }); + return jsonResponse({ ok: true, path: relativePath, bytes, updatedAt: after.mtimeMs, revision }); }); addRoute(routes, "GET", "/workspace/:id/plugins", "client", async (ctx) => { diff --git a/pr/worker-files-jit-sync/cli-test-files.log b/pr/worker-files-jit-sync/cli-test-files.log new file mode 100644 index 00000000..4e9870b7 --- /dev/null +++ b/pr/worker-files-jit-sync/cli-test-files.log @@ -0,0 +1,14 @@ + +> openwork-orchestrator@0.11.127 test:files /Users/benjaminshafii/openwork-enterprise/_worktrees/openwork-worker-files-jit-sync/packages/orchestrator +> pnpm build && node scripts/files-session.mjs + + +> openwork-orchestrator@0.11.127 build /Users/benjaminshafii/openwork-enterprise/_worktrees/openwork-worker-files-jit-sync/packages/orchestrator +> tsc -p tsconfig.json + +{ + "ok": true, + "openworkUrl": "http://127.0.0.1:52876", + "workspaceId": "ws_9c77bb26ca53", + "sessionId": "27effa8d-4958-48e6-a520-9b2fe6f9e1a0" +} diff --git a/pr/worker-files-jit-sync/docker-web-remote-artifact.png b/pr/worker-files-jit-sync/docker-web-remote-artifact.png new file mode 100644 index 0000000000000000000000000000000000000000..9c74141742fee4f0498b5d3feeb8b44b96ad70b2 GIT binary patch literal 161782 zcmeEug;$i@_cvY@Eby8L5+W(7q?Cv>NOy~rGy+41UKB*SyMzITmhKSg9=b((C>grn zJ@;4NKjNLWbS-DW<2>g%XYWt#@l%kKAiPF;4G#~GP)br%2@mi3A|4*TfC49&#QLWy14D^U-;z3BGp0)`XM0I^cyGOu!@8V@ju<%(@2`y| z9ww3Ox3x)L#`~aTU|@jd;pNr508b3}>T_>yY3Z0LDdD_~*RV2||6lL^_urh4UpfBg z8z02Q+KB%1jlQfaWB+rdf8Qs)N5%i2Kkzc`u@S+4zVS}w)7pRj8!zx{2>E}$k)n93 z=;D9A!O8glKg55n$NygmScjLzxBvdTuL@xyFW*~KR8&(_)791W_3O3EDeQ`hihHq* zjg6X`n!YzF)!>ISZ~WhOk^is_FYfAY7-UsE5?wJ|=3wS9J$;rtl3-_k{9M(pkF)%RD z)3=;pXK`rTqAKO*9;E+S>(uk^D_nP}GL=4LZ9v+P5( zZe|;+%-~NIlsWb$a=diNsl)M2C3$u^Wz%DCV@IM)n|{Bg9;0u7B~11 zKCJMJW2!t`u+h^?`U~&ZPHHqc-iPh0SFU{V^9#i{v$3&3DTh?iyA-IgbS?L%6;yBV zNxARzOKdOoj>MKv+I9A&iXe5*+v$@h>%HoKJek|rV7;gP{{8#d*jOdP@f#TH`M6N& zDpo1!J$*VU;JZ z&3iHo^^J{FZ1fvUsU{=l9G~bdF9jNC}nzGl5Au~Mo-K$!`>vp z4zt6fqoc#a$f&4nhi8G*krLhfRYXceTUHV;iH1a+{fQ8~r?!2;yrl zo~%qv#hVRE@{BwTrgJMRF0kUOJb$4cXcoQ^R;4X2D)LO`+58hqO)VY};zdA6N|Tv^ z{zZ{#a;KmvOJhhB{N>BTQTud|awIPQ_bCzF-- z_n7${*A9Mp-(;N2>WSl$;bPrrg1Zn4H9bB%Jzh14DWR4KIyqcQUR1X5xP*`2^7AK| zfV097`qR}rKYskU&#F`5aqMW}nJJgp54bf^9x(O&2032|Vs$t>!Qb1)xrCU{B#yIJ-?g%?d|OTZJ~i5a9hb94w7%l^6K8o z;NYOX=b`cW*{M?MePUwzd)F@F->|i}uimIyg>`swIDcn|Q*u3>v#&!uTlvwGCo5;i zo9g0&Ut*si>}8^uzZY2WBel_XC1wMQn_lN`Icj+kw`iz_cuV6y6!n<8G2U#CVxi4*z&pPmd#wp2S3{g}SY6p-N_(f6R#$_j?vtC_+X4 zjx1-g7w#2uGM{^g^RyT20CnPJQsC-3{jY@uL=z#;vNJhK!ue<=vwPMJJ^@YlIc(hu zSg6UVsb0Z-9aLy&75&g(D&sp737!u}1_LMrwOUtC4(5XOy-rJeA{eJ`3Z3+|wwktu zP}%uOH}8BC^S_(jtg5BGC-EAyu*>otJ(0-axOfu}@55rMa;6+sOGQWL1U6P?Mn>Er zIiCX*a|PMg4h|(M)NN_Iu)A)OJx@sp;eBdt)ugPJr!|C>A-1vE+?cB73GTZOPyUw3 zE){XlgH%;h6Prd=UX#9Ntv7=~9yLLLq~9^)nodGPmSP(80UD}%?gN?fJadTG{`XnF ze)&@I>!0g1f{tq%#e~&{twB{)2AxqXN`?~ z^T!rt9#L$+v#iML4ZBR$N(y#CLP5x{U+5xsUQMXLyXVLZYWi2#mOptY^mIfr`Q9LT z>?bYaXpZ=(T4godso^N}?3p5~2})0=-m})_8f@SBkdDaM@bF@{eQRER)n=GY2N?-c~$c>B5WsdwB(zN}V z@|3)yRn$(#I+9g-szG51M91ClhO$-R(PRdZ4{)%Dg@xsI;{}DtD6-DQ#5~Zfs0*;l zr=+Azww|hzdiCPhQMu37)>hG&-O+qU^w&=Fe4bb+$8|;5#@-*k=`c+_YGU#qB2LnI z(2l>_uPom1Ex}r<%`B2hf@G)CV>?XBvEAwcAGaKp4+OZfVqrUwn)Dy(e7p_H60~kWtE-1efJN$ zpYs)B;^G*uZg09wJLo*zGLG}szYf-O3^t(v;>qgQd!Ej3Vi!8CC2a_Mf`RH?T$ zG`w`lMa$=5(`h#EVt=WxXn%-^pl{gO*&)@BU09Jx+EXvk(=}}PD9|i*`JWoh8MMIR zZhKbp-@hZai~tTzYjrcU>Q7&Gkl@E&Q|{)kC|-t69vkKB#o?am+Fi{-fqET6j3k{y zthMW4u{m1!a7w~xWxk96(=`jkNsF>WUbmc(>?zYeXrVBw)KTDc$hZ25uCpD+dceM zgcYtzskTscB!4&E6pFxzCAw@>F5wE_?O?&Z(Nqy%vdP;Lq7o7__Pv6xk0`;F>}WKrU6e%V1LrS3bPl)zY!8AY!>YGr;%Xcl9X7g zrbgMHSzk)a`;SfcS{~Yvaa+c8k4h;$Jl?3@?C?`4T{`g7`2P0uTCR@P9&sw~ zgQ(LKxzefzM&sH5i6Al@c8Uw1UzUaK0F6Y%=SHl6OHsmGdo*+=qkG)7#ge55R^?L} zY5pZplg0e+L$j9XxS)h&QkQ%A@`GjNF8cO}o}1OHSO3sVyRNeEDwx!{8#%NW)E!Ct zu8ry-!$M?avRAq_^8x%N;wtcZ)0262T>nQ9a4aU(onSkIepa<_g-(_0nRQf*8clc} zuk-NU<;w&t?i!EGmZNKDo~m{#k2*hEE$}*AAU<9QCgs9>Wy7eWPHX=#g^Jey~LF7k@YSY)D{v-Ouh}+#hQlv=N^S=uX4aMG0 zZRL)n7ug=FX2fbR^wwfe)I8dP?v}4#hrae-TEIgd>x5iIy)OynnR>hdc>yCYU9#0n zqr<|!Q5wffw8>5F@NjlEBx-oSDxD`BCoPzh3?u^x*n}h8eS=aDv&X9aO1jlm*ABMk zc&42VRh6OlT5{;MN@pX}Sv1vB1KQ#NWh^hh3o_jYz_mXh3F9Q?XSp842n8>qyfc78y}m8mgruJo5Cp}W#t-&h|-TA)vu zRYTBaEIG~qERnxo55-e!|GvU|7X0EU%d$8f^oa1#(A4Ac(2hn43KyfJot3JqUMpxM zpOFMnglt>T7{831T#!oV`0J{@aSLRW0!9xhuA1$ntD{4+9VWxd>$;OQIH7f2=K6lD zX%cDxs8bPj5n&j`(>EUyq|ICltcV|#^4%H=a5na#9(6$K3W;DJUp9vJh2sLHrqViRQXLjH7i;0!Kni%gu){ zxy3zUGR>ryl<)D1YL{C>%YSEUYpbB3;OJP^6CH^$%nG=2+oCOqUEYWIqc17uP zzkeGIsq48Q(}m6s4C6J84(&*mb;I+}w-lAm;8cVJpTl9u{k%|A5BO%sAVM%qMsfch z4Y8?|R6Fn}izxx#w{{m|A~S&m!`{}@)3dw1K-MnyR(V?5`bCjDubOKNw}&9ZoMkrL zmU)DGWnSzSU(tPD@}jYDdimkZcHz=-<@RKDg?$4YP)p$-jL2Ld zC1{Gwly-R&C6NK}vBNO6=FP@Lxje-L+>B!jiD?WK|Lp86_BPGUPgQIpE_YU-H{D3l54$+h{){0+$p?)0$A%Gohs75Y* zF}sAeQ{PM^k=H&$WeCn&2}v+HzvJ(peoF7{p^wzUelwYDXude;tvR8+Ija-*9&X3x8CZ`g8! zH?k-T8rtiar@}`L{mkwG@ZP zaB?v{mPxJ{RGLJvnwFCJ5c1kC;PKwJwvYiH=Rd#VZR*CRrfQ*#DRrt1>BNx{dT_+@ zJ8ghOkX;~x`sInMO-E)wzudCBFuKzqWc%yo;qjEy42y0RClD8NyMw7^x`n zq8_?W(c{WDxaw@2;6UZJvtNI%OLA|E`(6Q}`r&iCWAm)zr$Aqn`wTjW&e&(mXFS6* zWnBzH)gQRL85R^R<7yMBBYGbt;oax%=fIn z0MoI2OL<67Q8BdJE@2#KEzouX^3pqIS#*~qt})FGz1%NK3&MHmf`M?ceNAyn0O-^*wCS z)=Vs_q(ok(xJz}^Xp=Lf+#cb1D)nD${M{X>mU0O9EP0^dFJI6GVda)zcC!#EnpRw-XIbLCkjTo}b%NKJp?G+tsRB_(C|(I ztRJrwtIX6bS53{>y909<+WQL~|M7s6fcY7%@A;&3R*9FDz!ngm9bV36>2wDffow2S z`pVP9?s)F@zmOEIWG}nByJaQbwCM~veVltVHeNpM%0x{qa*C*pJ>>R%&)ljvkI|K3vGM~x6#REx=?^gqoUeJp4#;J1c zjf0&+phLo;L)qvoCD;dz1&6xxIoK*e5dc9hZ_ZO%TAJHxdDSEQuk^sjdq z2i3T~^&JGYZ1&!%-OUG2U((MnOqzsonyre0c%jo=R4AphI;=QMQd|JE>It9$# zbU_A(*oCT$E*%@?6ef#*{`sdXg+SJvbG{+2`@G|J=s+(viETFz3aF}hUu1WMVqEvK zx|o<4?5Cj>sJ4kA(yHV^#dZtQ@t-BQROoBPWaLuPQNPM06@Dp zmLn6rz;E}F_XjCEZC6@ET%7s#%F~;nE_e&fB^D!cB&W!U9p%SvfaQ}JTO~cJHrVLT z3T82b`c(X0tU6iM1w8!Yp^QT0rF{zCjRpGk(}7}A2)2T-ncR7z26@C{iQsoz)fg|*w>+V}fWt4mACUTQ(8p`6@66IH?Dl@{Uac2L)Zxk=$SEHLU6#@HpZetw7Oz*Isn<&^Q~ohBHI3Kx z#gHsq?W^22wv;^p}A4{O_+2`? zd2_@}a55~Cgnp2Ub>n6=vRwt$^KPmN!Q`!S8RH%=-Hbi(L4*RZOz$+R`7tiRcuDS>(ab)Vv;JK)d`&P4Q zvPT*Fj~99_3->HIr94;*vq#s`G*MPU4$-Fta!o&e>@?=?;7Mnwxbr(-uo){t!cJU& z`_|pv{o1u_>0#4Rl5k>&2Jd=ASYF)>-kB_q^KD4*+N-_H>#E?X+&5iLvjUS1rMZ8afAGrNVFYE|(hf~v`l z7&enJnxQ^(H?FvT$;g&bRgRX}K&^F@W(AsPh((}8-F7y>>VdHcRrHZwQ$t%*%+r)TDP#{9f#ij~_F(k!M^^-)o|4J=&})wK&#%J2-s0zxi{`)M@@J>`27R zHaUCSPL}fJ#wU6nyF-i5##!++VhJ&P=IPdJ6I0z=Wj|HbbybG~o<-&Jr+3)=_;eLp zXU^?85_rg8cAd2%C7V4pHIags-kkMYr_qWa0j_`!9aJ|Ds5C!Oa4?F=q~^{Y)cM&Z?jVQ*ghvl_7U1N$s43 zha>O!8?W?kje*8L0Hg z*H1milCsijYt_gFQwaLpq|6`K^XTK|uzLk+MYOOt{^iS;jZIC#VL&h5E#Uq(u;Z+O zBNC@GjplpB6X!;k0l{2(XsKaOKO-5OGl1HP$nFJb)7sqZ;_U3~>Pp0pbLNJH-PLk6-Vdp&S5{T+ zt&Yh1uC?t)u>VDkvES&@AS7V&G9FUR>sd?4ctQVmzAcn*N0^a0$!&x8*xY|y?@eYL ziRmMCJLKi;j`HY;2o)8T`Gtjhl$7<*xu^UCUW`CHioDk@Q7Ym~1czyuil1<(S?;vG zxe~-=R6aTI^Fi6wY2zx=^zjYA+NDWc3k~1V`3vvbhM0`^)+aLUyZklj#%_)} zF%IsT=bo9rKT*Qf`;U=D?#H9d_Q<`hXu@wRlTjcGCd6%AP}z5ScdnqW&4<@kC(`Y6 z?JDl6mOpf{A`x`m0R_)$o9`Dh6dPn3>-^9$sBcd0EuTJJ5{$|r`_$s{%*=?-oSvPX zosqF)i*MWH@6x#g1~U+DK!S+E5)q{?FwY09zGJDx%gvl7EimM$)3cAX%*trMa9OCS zPvxlu7GK2sfG-TGh!j+~k5chE2+{BRzllZOw|8=?-)ea%C?EjR8a#AO!zK(Itd7o3 zU0q$I8csY^;aZuhE)NmD{B20WV#c*LvwlzFw_XP$nx~WOh7{O_Y@B&N{!va4v{Cx>q$;vNVD? zyssL41oJ2R`w1|39m80@+irN_cE+Y4RV_x2i)j=m{@V(7MSsqZTuqW#OQ2d>@@@b8My3NF|vALO+j?QE- zOA%C-@Wo4lDp9gt2ayy^v5GgpMMOp-tJT;oxYcttX4@*Gu&!6_o=rF?A-=-nEzx7y zGraVCdR8-UPvzQb$CmW}@+eAd+=yD-Kohni9 zcqVO@lm2K#jfltrIU_-u$1$iZF>Os2JZ{E&fn*pwq&r#Xgph4YNv&W6?k`Z>eSpa+ z$j`Rkz^Ok+86PhaL`6pOqHp~Z(VwVS@@!C}&a^j)Zwr*<(Hl-va4yYF9PI6j+n@0l z`uY1S2ex_2U@}Td9KhZzsKIt*$i_Pa5oNKjEGGW0=rd-@P9Dnc4-Xc(RE^7{eLDg!R zfvgg1Y*yq{Sy^c!u~Su@`17ZzhpJ@rQNC`qdGXpgX~gwK@gd&5Xf-g2 zMP9wKWaoESeng1haUhl76Z&c6sJZcad(EG<5r&Z==+msV^I=+I7lyNdu2xz^dQOtn zYnhj3qR}aLBBvD>Ox_lE9TnEMkgk_a{;T$U$dF1B7|GM7_iOYfnAlN!9PEPqkY`Q} z?h%FT!fdxYrFzTUJCWU@w6m`(>MaEhq)%^Wh30`dOD`VqKyihmCh)_D51p*luY+l8 z&4;q1o|1*l#mQ3c$)>Y@^9$j-W>F&-eq?+L(Eh*n-ycAOn4s6-d2iMoTi?8SGyX5^ zhM*amCK0Fl8p^0H$}d}2tG^kMdB|^T%{_i&vW#qV=sg+R0oxGU8)#ittAS zF#b%5=J7%U5ztJteL?(L{&EMEEO@+DoEM(%2P(Kt{qi^eDyPkS0!BLpaye0GAt2ka zR%*#5324gJ1gYg*e@!BM!>+{UtZbMsyAlE9y4N(*-+sMZ)!M`b^Lot~5)g|i8wIuGiOHwNwvWMs1%u0!Ge?&H+f zAMP)k*-c5^F;w?{PTY8stOb$*#h2Rqm&R}KJ!7l6&Q_za|HdY4RXDqct!DXgE5D$f zLSB9g2rHQ2LifoYp>ubOS-o1G?F9M}<~Q(p})6Xd`r)fKYa?&e|BsZhWWmRnC{2MC*jy&_|l zPjvOF3pDqo?)Y->cs0M2EO(;+@%Bb`K?)*^R8AEqz9$t);lfX7r2k|xl0w1!qIRh{ zmr>i_Q-Nj7lVsu&;d5msyXL$);IItwc1E+Q?^5%emrWQSg2Uhi*V@Wi<|Ep{PN|BpmiKV zX}9Yj^Zi^)ySOy6atUNYSwgbDW2S0{Zsf#Kilc zdwGC6t5j#d)MJ+mK@B4`SaS2C@w6ZWH37irY!xMNw}g1twYGZnw-hhPCy^Etlf>?f znI`MHEJWI|2>xlf2&!CbOW?x=&&TTiDg{h)neAaHQkA@X@v#rF9H!Ht;)F%bQI~6u zQCDUg>mB~;GzcY-lu*+G?Lxa7%93aEJ@qyOE?&Z_QTURTLNhd1g*b{Z=7oo=N0Mi^ z#p|AaFaauEcg-tx5DePXlToYoxDbWyV{8Lm;BOKV5^PalPs;YfY@u3Yh?cAC5eQkf zAo;XB?C9v2pVgtzKi*kU9%AOdk!@>Yl8FNtMrI({+crBlw zPMHs_D}+fzs;+4Mf(%>%Jx+A9^Nlc z_xKi;zABX+>*JpJ$j+#hEA++=Ce}o$s+4vo3pt|uu6Fs2d~i80F1Hn1ojkdpqu&j? z$)nlc78}w(v@1ch$>Gi`NhrO5fKE`^#$x&fBobMBxEQaT@fz}aE7nixrRv;otu7G~ zNC`#@Fvggzrgz8l#QWRT@V%x8$HkxM)nB~DzInteFLdhQ9q>{Fu-A!5QuP{}oF;e* z^_GELp`EI%0+rwXx_FW8Sxor+Hf^siciQHVJ}|z54SB8^#mC<|v(5=LAuc9PbiaO#n_fNh80fU^9qRppES z@~Mgk9&K)7o!R7HKp!Zq`vs~9u?Zg+m#W@Qm)AL`1~Fo1xgRV{Ns7BjId)vO=|&_n zxJ3(A$%e5zn%>=oQ&r9y!f)8O?CuA)yb4NF=I5g}${KrG=M66jI7_+|e6q5#pmoQS z8*qC&le0hwJK5v3_~ZSDrPM>ri-oQ~R~uY3MeH4JIuiBJrvG6os8O1Dwt4}Ven`{` zA^riyd4-Y*M7o}F|E4duOfe?hW^WFl;90G!h-($^(5&|Ir~jiT(G&yu7a-aKW~i{J zHgNSd>7|N=U%w8ZnO_jr8ti)ZE(bFx9PW6_Ok^m>a|IdQ<)mwt8|D)Q)iItH*{{f> z)h06z-nq4SdmG)#7_#A>y3W?mJMtRkYNVOSlSp+vvpWpb@r*2#5QQ`es(HM}*|wTo zasZZIua=(HB3M!&1SzckARwK8H|esJ$Zc~MRwn+Lp-<^UJhO+@0E&7b2L`I-_WT7- z8?-ws%kR0Cz;JwZ3!5ROQ`j1*farS04g9>*yEUe@as7}GZ9+L!)Br9ML5lTDR_H|ZM!oO^ z$P~P7I!EpJhAHvJ19Ne*f?i^mt0JON zZk_1v-+qCIfO^369NZI*>I(&V%C3*e>KlIk_1jG!q3w~nUBH4F$M^$&% zWcz$nb~=sS$Bd(;KIaP1t>E3iO!vQ9z!$P5$Ss4J|Jteat^D|HNYEPPXq8*%+0Pe6 zQ=15Vjbc8pTuKTzWV)BO*rr?r!G@)CI8b|ue|3Z{6jRIdqkRnSN3~Ln#VD&JYJn0D z_Fgqa6XlR~b5GK4>%U+dx+(IeK+VluUxQ@U6&k1aoFKQV;c%`dWOwZ@&3n`Jq61yD z^OycbdnEVn-2;fG!S!hl|Fb!SsrLc&VWrWvZcZ*P1tkFjx}8iP1oj!{5@lLfOFn@& zbSL2J*P`~4Agu|@--rlBLuwmsK0W&36pr=IER#TIa!wAYJl|I|C9GGfD9NWdGg_O_ zp?oGJX8s9=3V0tfI+~AEzLLhKa?1;BeJUY~IYjpucq;389xg0yC&$Ofmnci8eQ|#u z$6|1Y?scEwmL$`cnybvZRf8iVP6vNxK^X{%y66&FyOI%~zR;yp!MVL0LT;vHK2uj) zvur(H@HNy#bN6Et$1efQJvj%mXC z7Ldy(A=O)(1(D;`Ff;>e;6TR}ZVZG0H8saDLpjDx5SfKIZTienI%3_%>o}hIr_LG3 zZE0SIzzG8meEp~=wk68OQ7LB z0-H74UBuXmisxq1q1+r}F>uhOR&zO>+gc>UQ<PU@i3ynd%y1%tlC9~ zSAXSP?yyqh*4Pl+4ZhOv;nqejj$uw0E~g1 z>J0w!CB!4z-hlD}jl48YnuvJTb{as!;gF%TXLkfDjX7DUGR4XKGx0NNgLp%l1V70ns!`7@km<2DCkI<5OzA;`s59a923o^pKtHFw|3aY z(Ysv2Y~J|KWj-8*gsAMg)3i%wbo2-qY%YzLiAPV~=#osW3jnU*^33ZXH8$hZTLPF= z+OpclQx!B4N|yzJ0u|Ga;WPOCV$cvjIeT0s2{mwyrZnwK34cm7#mHD`Kkld}h$5S}TA0f5W&EXxBKvI8P!c<-ju zGf-vJ10#~8KEb49Pb_B|#dp)Cp0lwPWJ_t{tIFtBIrRH!5vC}2!vk^hKI zkzZCC7!90o0t+`o@Vr!{4SqM(j;m8dS zu+9qPV$u*e8^$&F6D}%mQWLZX`92V6ZGJU;%+oG^0hTI`>&2JKd?c+`07)@JJ~^Lf z6Cm9=FpOMf1X*yAKW+3rkD$YQWV)^}#g*JmXDp}J`RTqFJOT*jLi&(<8>y%!bf)&8 z>*V+Ew=P6=kgkf{ipSqpH9)6J@ofp6Zwn2}hPi+lKL*rM!_gEc0UJr^ks>k`aDa-W zAgw!r*B+X4k

gDs19z4PCsxt~T;xP^p%Oa-!Merpa#-y}JZKIPfR12x@&Jz9kSr zz{Q7Gid2#KI;XnQ_Q|le4eCB>EZFmM2?Ho&(#XWs*Rkwk*!72hBZ5dusbv#}_Pdq6 zV4g>bm+axgYLjl#$BNngAfUNzR7#BdBL(*%>|&D;RDOX|6V`bg?0B}Slg zh4-!b-t|3#={8Oq05WxTUe_wac~F#M?d79j{}`VgL!t#YM*>necpMqh=B@~#TnE8_ zOx^&%fP5E>Iz>7rb%+ryMFPRI={t|Rz~|bZ**Vw>-^0=23DeKhK6h!RKcxD&ruD){oa6br8=Ky4h zF0Kz`_9lCsJ>m8Cy`d;B#0S$CEUc_i@iq_?j$+l#KEoKBLK5pp`{&RQ7bm9*TP9lZ zRo835m@kB@Avuw*kiK;|kFwk6aXb1x4$)`8Be*2uSQ`{6oPIJ1)e**xc3L`hB3aey z7MZ%E*M#ofL)%P>n|I;0>(@ETv$IvT#Y9Dye}a^5 zN$#y2ZkT^(@fQAvMGQQxComDQvV!HJDStl{v3z_8>TEi`D0cz+kxYtTcq{kmp8=y* zcHC&<2OlxAi*eyi_?J1^Km|4~nH>8$(>5qgZ3lqDc~<=eM!%jpgVl^m@&liwmI zC%<=Z=uxt$*}$M~qaYA8x&8jSS)YgVgLAfr&Iw3HFxz4Yz(b2bF~U^=|sg z7^-xkGg@6m1+s3~U1}%o%H(`dK1rPcRdF}~8S*B;g^%dy#Batk7B9PiA)P1D%m=Xw zfXVB`);jbb$sy}%)V6*DH6^C{tt~3MttQTCou|;dd$^SMXPdl3Owj; zc412j-iMGxzP9%Eu}XVWXl#g^d?Ep?j@&lWxgxevEZQcY)=q3PDDKIaVm?Lm&%S{2 zwzswQgn^+@vtq2W2OJWWL}rjog6VhLsDmxRBZunAeHla#2;DZLlrxhM!1T4Let06zlwCkdF`7Rc`sBbq&w<=x>~#|L<#e3+ll{@bgqv zK72KimDF^)-vSQps3KZRxLk<@u+FHP1!`gA+6_NfzcV! z1GA-f=u!xrAzXYmR_}GrR)6yCzt;ihv%Uk-E&F4P$DRt8rvK zSRurCJA=siEu|qlmk0|UMfU?Nw;2<(uG7@Q<|dm}1f~0*k0&@mvH9oE<>s#teL_`K zXpjV^!rgH!h2f7Wrinw+LFP7koMF8X2G`Dkh<0t@mjF4{2YD_mL<)k@WRQ*K+!dFQ zFf}oerrw0$5(HgYyX(*qPae*cS!tU^L@Z80%^Is~R^!J0bDcHhGW6l9%1YoV2au^M z8ig8O++`Rnm~lob^s}{9;@k~JLCji)E>pNJQ#m>9{};pRs|MJ*)8uV|gfEic)JJ>F%wKL3@o-Gczkkz|c*13}jb4h+nY#lV!mONOOtBk}25| zc%tnl6Z>I!DWR2&gGoevTYvs6j}>PUk#dPR!o4{`rm1qZy<5e1Ehg=)88~+6t5br( zgd79RYE0MfGl$flpY7N0s;9WC3V{5@mt8^%gE2U`02@2=99UPm?1e+|h)u2duB&c4uFloeBCT=<&!rh7^9!4d}gJ<>hP=wp+&(6+{ z^xK0uB#(1>q4~6>1;5}k>52gk&cnl_u08@%45S5ROdvUucRjjHQqT>i5+H?L1w$3` z*V>q2F#QflFEF5xnYjcHh8rfF+CvxG^v!nMe|;oy7!5>0CJgkY9hi%P(LILFh)N>} zfV*Vmeur6ClV__L9q660`St+ z)eP(_cG!)t;)3C4N2^E;SaC3tXt&{9C2^XCl@^v%O*?JYM(aApT6(>V=PsHOHc#g(F}bHyS+{OzLnhR;OK}#$kQFrz@iFyoqaoPbeo`}<~JA4Z5hSI z$cb+V+~nYn)%W!DdC(7(5vnRG4gk~%zEh?pT_85 zb+LfYJCovgv4T|aCp|#8v5VnyZRP)H0oG$M0=@I?Xn)g2_n*m~b&&{0Mn>SJ^ogFJ zIb6JWF;97+TDFdDfnT0YxhtNhps-NkcC`Nc&)q!Xxm%?pdfFC+!GsV=`R`Xk+mmYr zX0{s}8%NQHPv8doqS4cW?qcpYu0hDU`}f>**OmCx46@v9f6ZO4VEUPDj9R0 zRtr7gFnKL1`bl#QYQyq)UOWxVa~`Y}_r3s~tJtI)#x}zv<>ELY=KlJ^bB_lPa#-hk z0EWihq849nh=^oT*YdrsQ)U@EIkiJ`7#4yWb|Gfd6EJKE(6+7=vtG5!iQJ4w%0$CR zFVREp*TD~Ii*alXHFL4(9GPC7u=1|w1Ws?Z2+q+ zh5H}jL%I+VDt!|>hWz@oBk9&!*SSC*upwUn+&pAIfDHNkyEbBND7g?xWXw?z-$8>q zoOj*S)Kmz}k0NDY41mkO&dbb#4Jhq$$c=$g#s1s3%ZB=YwhDY(L;w$j&~R0RC`w&5 z7-@`OxBMl#_-$17!D^M5oH&=F`jsB3p#X zBXIM%I3br$IgZHK6gf4}+q((V+($TFvsgp0!6*F${_&Bg{l1K`gKC^Iy^)N$$3B}wf}S;P0p9==s(UY)Q} z_(@2;=f@`@HPyyLk=^QXk;YCLuVcUQM?RIZD6kADnZumrbw9E2@A99<{FVyF4DQ@s&f}F^D-4bqQr8n2(|@I6u-x$)@G$*{kh`{eQ8i; z7R+4%VqJ#j3%SRdjyH)%llHwsnf~Mh&_iJM*`v~>fpN#qs zZwl^&bTivoc;ZT*4MGj)9|~510g=xYa|n7HxIu-yj8P$s|o=px|6S0SEd7 z41+z32pbd>xqSe}Ymml*vunc>qb#5BoN7kUgpI%BiZdkVGm|{_G{HT9u|xEH{34X% z|Ev8U2GCT`xx`1|`D&WpvcHjii;$F4l0?=M9vqriyUg+lBO{P+(&x6nP{+txP6@^| z;*0U8z}(G1H86Z}tzo65K?7k#IPm@I7_SR9edABJ zXV?n#j95NIHpxU*`v?Z7a{KTM&%l^8;Baa`2r1VFSi-OG%y^8fqjNCsQfg!Uk?|9l7LJ} zNkP*t0Y88of03h9Oe|Kc!g=dAxE$_qY`GV3b01I%+xE0MU;tmwZJle(Ggk3m;`i@? zlj{sndGcJiU_LZq;of_l<&V4r;`@8c0c8CdL|M6kxHfUnFA%AG@ z@egmLJV}X8q z7DY900a{&ouf9>Az6uajtZ`I)e2)3}FPj@hjUXn#?Unj$YP5g*x4ivj0a9&s^)bM3 z2g^QnA8FIj%^FCt?m^@D5)cr<#vLmb{I_Q0%ndB!_N`k~`r%41=;zEt?agoK|m0QF)b|-$1+S87Zxt# zBen9L7Oi#V#>Hp*H&Eu(D z+xOv>w&qm3QK3PSxy+VCsU(rigvvZd#*(RRr%2{`h|Cs|sY20Y9x`i*l&K6+hBV5iTjY^I$}NLs*Ap5iQb9?LTH0p^|@rYf?U&WIl9 zZ>~t=p&eACUVn8ViU&wf+o7v{(3k2u(T&?(Jv`>G^A}g?q~7gs)U$ktf<#0b{OQ*C zzy4xid_8skuRvCH>sF?Z)U*baucD0~u-|`Jv;2H2Hx4=%B(;F|Iq7InF|xblmGdx{ zsHi9sus?sEH+cdHNlrW0u&`M9$EWmJJyR6xZbdSW$Vy5>;MqjH;&q%}RaMpKgVyjB zZ2QEi)XwhN`HH|VNdFv;Hi~rU+CxKuQVb-Cc-S%RQJ@JQY4Z}kdw`1DxS zBDq6{@Wra!y8&H?IZ2%D{)|xl06_?tWE<~1@KJF93_BrP+dj}I>CCib$tK8KdCPF* z2hSs;dE4Kg-YMxaWrpJrN1Y3#I|@%wf(Sl*_|TZIw6ru>AB-Y{?N{-~kF=uE^*USj zY~*l2_((4N3)wDn@U_JSi;zHLyka2}6*{r){G+S}1Iuh6je#J*dyXPH>UKLCgA zMxG$(82p&pm!+MLyOO%Ob?f9?g{K9e8_Z-6AHD+T8r&YhOy_|lyo@-VYj8MzzuV3r zGhjSuPmrVOTyS@vLsrGl2-T4sbCxPH;qS;i(y2%iz5>WLS`Ne-QiZ$77*OIBd>K)$ zJ*k7%OY-&Q1SF#sPfPyKmk%e}67M0cJ{y+3#x?8mAMhrhl`999LL|h3N6I_%j1|<& z$;B?#BXN6-6*@jMAX|dh$F|AMu2-JGjztMV#3aNMHt733vN_>L!gya*Zq(9D*NpEkD~YLd*)kO z1%N~x&xH_-4NY`@?5iY7AylPmZBG%l;vtrnP=@)5-59#Q*sp$sfVanW@&=3kq6!8j zfe47r2h^-8Dt72Erfl{ddzN?3>(=k;>oqS)?QL{)qAez@{&F1LMB|DN3{3boc>d0J z^EbT{Wj9)Z2|*duz9h`z!lg?Qn@2g}8MJ8e;*#Rx!Tx?0X?NW?12i!M;YDUIiG+uJ zz}VCcO&ZIWTl#f2!72;cfKH(WM`oauoh|?3dx(CA!dd=gu++9RkhT1R7G$@i zmFefpY-#LnBRTcHYot_ov3XUq-8<{SoKq&71B3jUe#<_tpA$U|eVJD*9d| zjiZ3Rp4Zl@DjvrfTI?_s92QmxJCgtYzGusgz_{Yw0PG-GRl;*bXo9OsAxwKvn0`hN z7SIKC)!va2nB#QJew$hQ&x0YK*_hChHU1k5ShqS)b>qOm09MFg8GKZrIM^g%XOwjo z$1YUO|Gv`UL$z`j0CI%a9Xp-?^b;3Hq@)T9PeB-j8dXU_frp1jDD5ubE5Oje(ecaJ zJ^sk7>Hu{Ovz>=Z74;;)0=!`YnT=79tZ%VQY*G>*29v8^N!)^Q4N(pRBBLk&%(%;Y3Fp z77;PR;L^$suLAsxWG2_}?)YkuF~g*W(4sg=YaayDu|Yq4_<)|!Q%O!nUzRRi>hcAf zA~>AmI~qc;F+z?toSq>paxV!N+F} zYbwC92;igzsx_kphFp-}S;gH0z=v)V;@O0@dq5{REBJ&eDk{d%7z6POnk<7QAlv7< zi^u`)TS^mgXzibW;)I1iL|9bR(7*r(<02-O3s5POBJPVfIb^uGkJ$x>gly#3HPg%J zi}|mWqUWN1nz9Y91lKm+k`|Ec41XUXR8*j)AcBxY?|5u1OLcW^EpEFu%%1Sw7cwwp zDeUIqAE%K{nU{E9sao?XF*jvl>svmDtJYH2)30zYQp5eL7* zw+0C}^+~O)zOZxOze@@|F56t_8Oz~uBpG?w&^}Px?2r%uXEnWtF4Mso zG@bv~=N)t!CTuEaS65eP6MF+H;%y%F48@WpKE<$EqPm=J#Y2jU!Z-Moxtou#aPE}8 z{<&Gr#RKn{?6R;*(Uf}dpaqzjw*P(j3)WyK;jd8GwVPwXDexC1t7mGUDrpa7#aBG` zR_c83p_6BC723oUH`V4iNCb1eWZz=)z9_Akexmz--;5hDp(ajHUDjRqPsTBT%A2@{%t4eaR6I~Du(oF-!cYE>H_Bc%eHSe@h?Q6W{u^9Q6H2JM z&@vQ9!>x_AtMX5k>L)S1%WYnGL8SBl_|vJp77CC-^d%uDc?ZgZ9qKb@8`4vcLlU|) z>~!m%QoRWDFKj{3z#%^mXrJT?OR%MvhYzEKm9po(^$AveFN$xEnS8~6WRd8znV{x<^Efx^>uYe`}%~S z^XIyY^Bu?9kr)577S((M;}!wMizcmdD9%xh;%Nql#K%ixk3h-}zF=|%z(C+j(wgz+?OV>}9fo%5hc~anvc~#UOTvext=_O9^#afU@%$FHJobPt zf`Y?{!e+2;&u(G%+d75FULtm)Ir$5qy<-M=swiSYpFvxc56TM%j^{3)K-Ndhal?Ue zznorW1tf3uqn;X8tEsQQoqN3KRh$}2 zJ=uQ8Uj48zIDveKsgYjdoYR03tz80aNYL+0rt(+%urdkHoB9!mx;q$%Wr|)oIpYN0 z@*#c@(xmw!Ju&eK_VB7}S$^BSt^fSBaFp3Ke;kr#OJJNu%{8k2^U9#Y)xpRG)WOwH z|2fHRS$=wZHtqp#@3;eInn4o?=zuQ6lz@k*6DjCEH?I-hfCIIcS%Qa0jeWrAsDu8m~NC^ z6?btkiu8qJcWpoA2sleS-(>c~(^Z0PEiIGLnK?IYKbEgqvj*ual9$hhbKg%$f2(t! zOit!Ki~-oSq_Gd;bkt}a&EV~(iY0gB|m0IUx1km@`$e`#{%a&&NAx~o)c60=t< z5j_X+vKheM;9P3#=UxacKS`B8b-2Hu`klxm%>A?w_H@C+hAnB~AZbJAbl7%%2@_Mh zF?I)%>Z1K_U*EY}rimkDW%IM+ztV4RNugWNaF{nya+NhT&CVu>o2ls z$*;9fll$qg+**sa1&4k0csG>1ygqnT$nE3}5Q0Rni~|Qo7KC_Wjcthy;KS#*||o=9tmBi=vSYFb(^WLHTMwu+}xt?)5QO~MX9bAWRV9&PZz9U2@I zMLfp=5Bw0?4fVU)GgF4^H*fw5xRh1g_6$7FaQ!OxvHF7rt|0M&R7wn#BASuuSMXJb z<;A*epZ>+TJWFl@ew8Ku9R#smr(fb^t*hIGEURA}x#a^qGvZ|hz5luwcg`opM#DSh z1bV6F!je8h5;SrJyzRTc0|El9ZlkXf!jfhVh34j#=i+^SAeO8B_krI*f}FQuME_kQ zi_35GYUQo`c*4k%`yyt3*<-(sh3dS`d>U~UcSZY8s95w(Oi~@2=f6LnC)PM=pjJjA z^j$I@Jm`w;1grw(o6^;9uJA7a1Wc32K&Pb5!|8AY7l11uzzZS#iD5(wAz>v=j?2Ta zBjv%^!Cjo3ZxJFz%es(y&%sV(=sVC0wGti?t2-#dN9p;Fk+ozE0ySxmb-;tHcH(CL z@D3I;O;I@fi8M~HI4PQ-bgpfky;L0GuolYiDG-Ro*=I}}eB31&AzM4(33U?2@ zMV~}lURlkxj-p{&W*w7b|LTWmR)xiA;1m+Pa}Pcqu@5nld|MaAPJ;Dmho-}#mk4_? zl(g4*OqXsc(Ln7PmEA2Z?E$}|_O;QGkuPA$tX;VOE)kU!r#GK0z=Cc(RAqRF;kA{6 zW)_l9)s<=3u){ggw%_8T@gE~~e`E>?xmkK81h~E9UACJ&@<`=~6^aHdNYMf8xD3C` zv2~j^XOi&PxwsDY%5|IK==o)L7xnRL=N+1nTDxhJ8S5W3rY-v*Ie+*!kawLv$7ff! zy5B$7O_g_e->I(Y)k#bx>e*?9E-Rz~N)xjL1 z%b!%$I9v2?>vORqkg?KevU2jV*ias(l|Gp*plq1=yy^ z39Q>vNZxr;HRH*XgE1`MZi}o^L(ZgFDcuP)(J?@LaD&EkUMFzSG0VlGWN*{Qf5*4h zwZBg73t6%PQFzKpYk`mD#p~$kATw=(bV09q7jmpj2d{dqaFj+-3#gwM7#P&NXmc_? zC_d?-SvQN=YyLDh%6aVk$FHyV>j-HXMMgtivw3r|>2tQ2jO^@e69EB;3X~N+nMDcS z6V+!Mg~SSKqMheIe>mHgP&Zw3nO5M5WY24HjqYnC=_b!_TA!8~zwYL7_}7~&pZJ*u zJTebQC9@Wly>6*%QGF|}6%2_yB93rSAgjG_HUK0nKU#$W8?ny6 z#M+z%ZuBxu2B_{SFX0!}>z;hFM zReJ`yU(9fR?F4do$8khMsE5Uw7y57iG{U*a zr6=oz0R5r5fTh8YmohUDG5jyj7C7S>qImlNZ>_<>K_}3bQ2JRTcB2e}Rhk6EuSoDx z>PR#Hk<3^pR8F{(cW95Qk}9vDfZI{h>&rt#xOL=QIDAhAG1Ccf0t6C( z-oHR>2z32K`;HMQToB>D8BtHnJzBU*%e1<_KFo`G)27`+Bt^o9Nde%k)6&u5mVoBt z8ZYP0o&7j=!C##&b-k#f63naX{~ei4nrQ^98q@M6OCrH>K)Ql&1EO^=Hu>*Z;PiWS z+JNdiv4xlti)p1b#7oa38CFrOh5~GUI>%$SZ|gkLyQ1bZanZrS25ZGylmUt$X?Tnb z&H?BHKmXVUFNwm0gx5r~rKEH>Y6Ov~CGOp`ii%fAJcG3g%z~KcP`*_m(`{qnDU!-I4(-;~%fcO`G78f^PS}iLj$dZfo~l>lhKVW7-3N_L%?H zQ(IfBzIGBc>HfLT0J_HtOiWBoL9)yOQ4jx{DuS4SbB}-=$TFg$fD-U%WZj3R=eDdt z6dqF&PDm~ej>A`nj-DN9Mac*(#|>et{X2L=@^qj*hI4^NAu(%*Wr^wrN63~W4*f=~ zxcmNOX3n5cl)1qK%yrIlV_1agExG*`i-2XK#z$S*($WG>sq+snN2_-$o$T!;;r|9y zL(MQqcIU{-rAv`W7@^@5G44h_Hs$AU-!`sWhh6*P#PsxZn6nX=Ekc3BXVD<3!X2kT zAgjl7;Ep7m9q8}hO=&n1@Sy9CO*9xR6_2*R(^68nP}6PwwRlrGUM9p=4t2in?d@H- zsM0heJzX5ofV%4B#Do)c2%8Vyu4w+XMH?Bw=lr8Q)su#XhDD&qKNx`7-ij@>D4O3jmM@aVHKE6zy3}1+eII=h zszO7?m9QmiNuVTX+oIeNdM9}*_f}Tc@!#N}=U~GweNNDHbHkUj>s-G@vRUr6Op`2L zbrY^<+PLoE!PR+v@87>47)YTcd}%)E14}Fv`oEVBMfh~PRxFkr02g>9D9+t3SjZNW z?cmVK%e-}KPRYxY^+7o~HfPQx?1YL6PDbk};!EE$(LsbyqRc8=M$w@N2oR&{i|xrJ z{|qUK=CBl3QeUVeZ=#~oOh~u+!c`-=;c6a^b?hD~Mk(dP?0{N!?MlUhMtTM$7KIvI z^OY18)o-K&Q6nv)JBdINE&@Ch+dq|9^7;_@Mn*<@dlM-M<&F1BA3WHX_|G#jtDyOI zG=URwgk_NUpI6wI?_FFSJh5_9Uf-KH=b=XdwpvvO!oAf8?A;G%!<3X|6lD?3fmqO< zjRw`XPn`2~*h5-g$V+C@@V!A=#S?YMY{S#XIUQard7%yF0_^u4A1GbC_ys5kRN4p> zaQWLVK|DP&nqzNKDC+j@`c43Tf4=8R<#S#DZvMI12|56cfLJttqylqY+1%8CLIV0t z0Uw+U$lFE`7^%|Y;-9^@oI?YQJ>;p#>IWO*GV#u~HTq5n@h#rXXX6B1;aksV~4Y|qYapl(}1 z5nlIp#8^%znH^m?1Vz)-sI&8{tn8N*wcvNN?<_5*$NGroJAnCrnxi*x#}IC zYcGpng_#lzCFB7|-$BJ|VsZBD8)&*=g|(BfJelt)wGh=kGUEV$|L`jSkCIbU`2n*M zEysIS9D%0D3K5rx$R}o?2a+|MpjZh+{mMWw9dz9;iTk^ZawhOpB9q!bs4ia!UF-zz zuzH_<6zhk`lLw~HH@_Yq?*VN0g~vRG;na#xLQ1jXA0bK+nxFpoap+7SAL#(WdaINF zX+TWfQ+4E9WTdC4bys&>PRbUFmAJNzA?Al5hw+p@=VNC`23P!eJ^B*WBnokO*s(nj zFYOd^t@JK>^wl{x={-GPjydifQLJImg~Luy=s5SHK~G3%5*fiIl+Y*)>{0tyGeZD` zLySgAxN<)4h3sD(3m$oQZL%&7{hjJilwffpf3?GJt8{5|Zf?bvogZs>aw4DVMRDJl z-rIL!bMkdoYY1pjQUZ1%1ZKCRCTtqPUm?-F2LJ&#`DFxdbNc}aWOA6`u@Of${}A9( zB2-5MAc)$}pl|sU(hGo=M>d@GhZ?E9#lJ=il#Arr)N;k(6sMcY(p7H zaJrx6u%z*d^KBir5N)ym{sioGeWO4_eZ5Gn^Kh#?Cwf(d&}W3h4SfzHxs_}o4yb3A z(ib!)iqjuyAqe78KE6SWNyy97#1L#5)2AzIk-h~6o^i^Y#FuWp-+!#+eMY{YpC3`G z5E+t~4Jj#!DnJ4V3#nP~wMsL%SX6z2|aL!wuT@)Xi3T~T5;tPR>0Kvh5Bt`kv$AR`WM zL~jSIlW|Y)XdnE!>p7bCi?;KwqVQb~xgk_8wV7gNYxw&?CP*|r78^|0`t-05dnWQR zb&&TH$8A|ldH03>zUmoJDQ1h+#YxWGy2>^&J-rgIRjW=YDu!iXolo4uvD|oYovG%Gh)&TrP^#)(eBp9yEX}Ke7pgxW^Jfy2>O6Uqc zzzHs7fJE$j_wLQ|0;`&sftnk@Lc1$e#^0W>9hq815nlKYY=?K2X;E=&^;cZtuegeu zM%6ccap$bSCYPBpJ@W;c9rKpn?OsMc=$?r)!w?t6H)!2~(qURtD;lvNPAG%KYy+$GcW^3?^E=X_Gk67ve>efr@G~2x}z_ z{*dAPG~Sc57Ux!xBc^#o5SQ1q+uJ2!4$^vE@Y_%mJT-BK71>VC%xI>`qBiUwM+##h zqM0(Ayg+6-MBK@n|HAkn+H4#%T7hKe*?y#Lqwq54rZnt=7DHs}t=R6}M`zX7W`8>m zD@$Fag9rcm(&5(%H?_@zM%Q){S+M*F(c8W|?#NyFG}zk*j|DyY=92(u5?HE1epjQl z)Y`zH7n@=OP^WS*fTUcNQUKr*+XyTS{yf;13TLBufHpl@E-(t>X&oYaCE#uwCO-~^ zz{B#1z<7{u0!9@4Cb4MaQ}lFzBFkD{3B~`MJFusi=x7?{k^KE16)D*;cuFD3PtQ42 zN=%5s$QVvL8b{YHwd(eUZ{hw8fDG)uBZZIzyZ>MQFMiU7mI7-WVu) z;rZL?>5-_*aiaI0&bp$iqC#@w=4KTkQ*Y^8nf-|yEaXJutU3FHpf1JR^ELL(x` zQ~-zo<)*Zn`WSV7yFj>rg-V{<*J2cLJKL44wyLe;|xqc4l{HDp~&&Ntj_dq2755Asdxb@ z0Xy!#mDIS<(ByBOO`9e3E4|CgJRBSxFklF7A?PLozn3X6J~+5x-MXE8WyBA%p@APv zeCnO+wexq)BErIqjf|>JGg9iW#dOKQN3`NyR@U9qxvyTmT1NTMW0lxCQU_h7$W$$g zg&WoAk@x|C8(UFrD6iLn8&#L}aQA|1(P@!42qZNb5Q|p#fPDM1g#ealcB<`x4r5Lj zYM!LDSa4N%2so|Pw|XQ&``-WdE#$_vT$nAAllvX8wY|N)F+Z;6ddaVZnLHEWi&Pf5 znL-3oX()PPGHz@|TTZ&rPkx}Dp{$Vnt}>VY28uAJdielp+EIcY;Jd)-(>?YL5~ko8 z5uHs;TwT8dVcN824e}Ti5Hfs(F!DV1F^`fE1u=R9-@Pt_MepzpwC5Qq;>#CRe3izD zjSLPakTtd=&K&ngf#KnURquT22@paW{(VrwAcs@fv2ELHOlNR#bW}Wk+!X?&3-RV! zIL`2hafYp0wu}l#2xKgFc7n28C;?hS_C2g!n?)|&bH4o`?dyU9VTa6xlm`Dm)`79H z{WuHY->5Aa+4PYI)Z0%zx~ih5l|D{}oggm#*`#QjLlMgr4{E#xCbX+A5D7~e_OiT@ zi3z$8;&S_L{Kf)wZ+*gA#m#sdIZsZAIX0la5M$bW5E^MkvVoSOux^p9Y;0pABd6MQ z?RE}?ASIKzekeiW_dUXO?y4d~%;K2P!pUdsRiBF$2Rs!|Zz&2HYz;rs`Jc=F_ zRaI1s(40)Qf@SO@&@?miumjfOC3KB{n;zjzvqH}a?Euk{c6Yns?xJ37EE9#0pz7!e z0WL16j85Cx+0_N3^{WIs+(>BWBtWWVg>QO$?O@6gkN&Tkca3JC&+YmLXyGG@bBIXJ z0MH6&m4yxA@9P8mpcN~K6Px#_1LoHtJG!$BM;_lEBRPJ~w$=UnlPo+;7f}+1Y{h=a zDL>o5??k2hHbp!Jr1Y1Ucb=~MgOM?#%;HIkvGM}+f)z{+kAl|*=yr66nAi;3u<2M8 z{)IEs)2u=!br^JpER#s>p>xGokQX^Up*sPnk#2OZtH9h)wsKLSEwW%1gkeF0OXRb2_X*H?M8V| z^z@_D+hGB3_evt+M~;SjNje$u@hOQL*l-y#DHpWKKi0M;UZOK2WmY0Ei`=3YT424v zK_FO=K~Od>F2zP@+5)~(9;^keI0`7z%;P-9%Luq3A4H#266T)qen&qs*dJ2MBQ3|{ z-N>h7#Co=7-TL*8#NbOEsM=QG960-daP7JDC4>R!k}$gjF5dx>R)NqYc>{xX7KJKX zfM&JG86wAoJcBw(?Dhv>#Cg>Dkq`(AaJ%T+8P3S7$HEDE2DR^Y9u+40^XHl2*TMF( z>UoEl6uWHu?!_%ECOo2|8kI+@4UFH2=GPuG{xq*Q{{72K=TYS&n(|-|?A{SuC}76) z24t@oXU++8XDoVGAHfmSmV`P zA(r9-(xKiwFH-_*jkA+6+z&7*~Khgv2I5i79rZ-`ad4CovagRcZUeh0b^r4 zf%F1D;Y^GYDuD>5rrGj(LCHvN0h+Dc2EsI>XHy}}iSZ%EM$c2^EoZS?pd^rht#`~O zf7F`sLMDDAr{9PR+HlGu`y(Y2>+UnzCsBhp&6}8*L`M~(iu3fOJUlMJYhr>b(OCNQN@uWzIh(2Bk z1yw{nPMi~8c%E?rP_hHlhxlg$lR!YZBf$fI5Zj~nE6{!(&BeNlk!bS1yl|20445y? zY_-Dv&yPJoKhkurUEJj8ESY*vuSDN@c6xq(V)CPpP?dh|8rAxs*h2KWp=_zWW&dF~ z*q)nHEqg)Hg}UPs?1%xOs>tF5Hr_2&0`RXpTX`Uyy(5jqmh~&9#hsGqBnTTny{)Yf z6QaI;72xArxs1u**SGWj`6S)Lf)OH!V4tyO-^F;TEdw8R^q|vijg#kKxp#h|O`xZ! z&EXUQ+7lRqJN|G#gu2pmSNeeW=poE|a+;d*^Rwt3nc2b}Cv5O+3FNw{ms8P=HaIxE z!OeWAY&5UdVX6W>x?rTki;B*AV zU;OfB+3b58M8eNKe8*4B_g)vkxkCC&jSm&Tc12CwK+>YHnTly2nOS>b6$;yniE$8< zuo2t2y=46JLAU`~vF7BX7Xk+g$}B{f8=FOrIejOsn|zq+0QpWgWFwvn7V0ei1H+YS z*NB=%cs1r4%E=Qgw}8O&Vxv{70uY(C{li!m(3K=e;TWOX1v)}IG)rM4SlWk!(UuMm z{H==CH8eDg!3&SY30VT-diAZ4LY}K4RgYJ}o?JoP_46HiNwA=SPj>NF3+-|Uz`&dF zMHrZw5jF1sZ`A=Dpf7ya^QcYkc#FXs2l!XGIZ7N=N{<~u=Wev#XcawF;qd-QyIVEQ z{hPe6TmeHXyFp)3EnN4E-B|xJs4J3K?7PF1$c}M(9~~YJp-JNP>N2~UenXxu7ojNx z>{JwA^(5s%VZhevh>(Ks%0!juy2bkC6w>R7r!t z_f0?QnF9I>IcOw?FHWC1RkLJYum|`B6=R3%i2+``7hwz+jGW*q*0Fyil-Gg`-d5qf zU!X7q?0?h!8~C7*I&t8&4^@PWOc50GR?9=k;EV)@`p5vOG|F=K*K^R|ndM$i0#)pQqwUoET&?=2zl6LC0%yG2ZNy)s#riP?CJ35FM z%JSE8*?#BKVTv0wsqS3kKlc2{{_fx$zpY&g81MQ5Qh;4|D!j9(&lCK%_Ea%Og@F+~Xl2 zH;1WFgN-`=>GO{XJvw&L44_E+O-+owLty9g`GUR}ufqip4ztqH{9?dL%VGwK2vtl? zU0qgNno2{4E@n^{+*EcOMt544tR-A$YJ;+N>8AmzTlT`?b#i4|OGLKdk!K-g6Hy|P z-Azne_h*ed$uV~uXbTMtx5;)64IqFmGM!{yNp9K$IAB3FWl{8SG%}afac<=pL>}*j zAdF4(^Y-Z0u}P7WgWz!T!-w>#m5+fezkKt+(%PYFenphl#G!p2Gpl6ITF3 zYN@J|s9KO|B2)oEBK$n&CscRbk`P70*q(D*8hoU(7n9a`XJ=@Wm5=cH&z4s4e%iY> z?*nMbFLUdyZp~3OuuV`}6>vRhK2tA&Np48s49hO&?T`|UqRl*xPzSxI%-sFf6%GUK z@-Edou(S8m2vRXlrEIFugA*;cL_c)|WR<6Outc346KW4?DCp|aeHeR8 zz#X;Gi3vx6(~n(N=#qOFxbg4JmW+rOo-K(38b)vD+Ij)WWDg&5?mJ6(MOJI1o}aOg zG^r5~{=?!MtnOR~Y_m{E0bm3}neVi?_dYdiN-gk65ULb)>JZXb*u=O(GEdM!%*28j z1FX+>NO1aZ?I|^^gu6A6tSkYgU?DU_7*A#8V~mPCn(jyDY)0kE|14h=Q#Zf7&hC8E ze9fBn?cz-S-tE}4$l|}b0*qnJQ+@6wZSd?Gq2Za?uaFsaq6@Z~R5f5gS1RpHxcQ>~ zV6-;%K#RuYXs3)C33@z`j@Dlrf-F&rD;c=!Qq0RK2it&WLZ&#YRxf0?&%NA^vr{8q+Cdlwr*TP8Z&pv?_s^yH-d9;Fe& z;FQY5t#ZfNj``Eceis0jUaTyw+-23 zN!6WOTc5h|!uOo?1K>Aa_t!#>Hq^p|o8>FH3_WGWGAwtZ~x zaWHLy=Exqvh$~?zx7{P8X9G6aq&GN#!(avS_~6vQ(5%+Z^wVxKK33{+nw_5X`6A}u zSOB+_L7|G4)_oXuKpSL%nUWE_H6Jaj$<-TDW zY)O3K+nF-^ecRl2skc15!B)wQ09dydvvUE?6**QlyWnI2adqd;9pI7Jx3zrbg9Fkq zBAJ5_puSTxWF6Y82(6l8Xo8%_+o0#lc5<^eT+<|cs<1Jin2{VW3_VdA9; z4Iuz24OHi0Yj*X%UUG<9cQCpib7P^k!36=XhBPYE@z6C$#TxGUxP6?QZ0W9kfi$zt z$Em5wQQv%acD_En{+*rXaYqE+R+9&0}brrgRBFeV6DPi1+nh6S}-SdapgfmmNn{DD=bJdz#dNEvMgF? z=mY!{ciA7aKbqlp02~%m^ib-WK&uKoEICYXl@G`BDPX{kwo$~?@a`8p*`;fD0aS(5 zgB^suxcCK#Pn&ge{N8Py4YoyWkxErTZi)*ofrYjCbO4}Q=GocV_Yu0-PNK1>1sr97D5FPb`u!qc%Zvjavzk91&hHIUU$}iuey;I6Zv;z5@Gzcf#U*P70PO zjz@h*L+g1#%!RCX6is&l(S`FH$O}E{H<(CxI^P{EM>Bz02GFu4aTf5!NkQ>PsSAsYV^> z8rmUuqQPGf%%lQ82S-YDIUCH-LEre_85=rgLWdI}B9Ee+WUuFe{WLpz)wIp0E<+)U zQ#iL{QMY~(4dC|lHQYd* zMH(ZWB4bxzQ_(5l3gCs1$Ite7eJ5iRJ1kPBIj{Dj{D%WIz$I$u5a40N?LziRa3W*? z>_$C@AeKpB!T3&ZZ|{@)RN|@Tno6fox!yryFllD zl0}ln;?t?oQe%Fb_9iwnVmiC>)zNF{vHtq)TRgx|>=nYh7e?wz1AaP8`u1CH$u5pL zB=W_C%Fmx&!4Z1|iCirDu-qV|hAZ5pWvpgHs%t6o!~RYo$!oM%+r3Kr>vA)9R_av1 zG|k^bUcOr1D{5$L>|75=0Mpoc2!R<^cr#LD44a$Yd}3WqfI73K$#YRZ8q|c(x1Q!P z{~QlB0uop23EsTfbJCZ*xuM|4Q&l>}f^PCe$&`<|$)ku%;FSc;N|+|zn@%g{Bq zc1)r{Cb5}=__=~~xXOEarvI#7X&=vRPVG7Psi8}ODis=5Z_;W8E*f*gPbet?^bZ;g z!N`d`0Tfz!q>s7OO-9z-dT`%b(vMe zf2(WEfku|SHm~;4_jJbv7Zn1gh=o-G`9J0(H#s$RoFvwPr&IE)3J*NM33wx5oXm1d zDDQQ|KPZav3PwiBR8};VgzdHPquuZt7y`PR*Mu39Gwl3dkuH)MJbhkJz_J)PUZs%ho ztwc=s0|HJ!h&b)#qz8x+;Pp$7^3{WnX)8 zgr%-&S&!F|18hR%_EnA_!Pf5RN(=QeKgQR(P_YfzczsWN35P*K`P}d8vw}Z7cfr)< z+^oEnv^M}lB|a<0+bA=I%-o>;#m#a$Lmdy&@kwWl!j0jzgUB||zSmFZh9DQZUge4w zty#J9yp#6~+ai|>SHGd2OE1;Rc>MVLv7AH;e!fT?Pny}fT+gSF)*&d`Sq*rxcjWKu zc`4U~L$^iYrZphKb$BSA;eQ`f!av0s4?5`-Blu2@(uH>II>LNF0UJn7(G!IOrXl=9 zOS-$~XSnt5oH?MbS4+7eASUJv*gM!3wrFiH5PlMZh;!k_jh9?pez0A9UJn?AkLC>H z?H^?vsZvttxMy=(N<8|(Q!;^6WF;~n9NnVxddA8!EwW_AD4quI1}5Z}6tb}k)p27v zNv2|9&cfG?d?J8FA~-6l2>n32MDE)_35G)xPZ{7dfKu{}lsUm5<542R_m6SH*sp{)zQz#p{ zHZ7o}FU)8+{hKfik20#+r9M*0?^uxE(a=c!{W~!bzoFp#3I+>b!GB*x?bCgIXOS>G zqDyF3DVdvRFi^CJk|3c0TN3C|bp$kY=<@AfpwHGfa8@6`N>Kv$MX^2i<3j_W)5#c1 zlXSz+A8}PBmF#=e@?IGK61|vnIUNUjLXHAuPc>iA8?bokWmuQESEcHB8_!(|Y#jr) z^w_$I0IgB?K94E;`<9}^yijd|7efl8hWC}uLvlt3r~ZYh@W6CTugq(=uXyP(V<4Tn;O`4AQK@8(}DG8!Vr>c~Y4q2F~=;<3Mlb`*5$*8s+ z|ECA!=MPEL`2C;BKV_2ae%!`iKYJS*7`Vs%`vpddt>`~3Oys{QnLjwdd|8s1kuhMG z|IfR=HLMu9hV&QQIR1|9zv>jvklcz9GdzuDl-K)yX>oskRIHEN1OG@NQ-AVy{}pIH zGAAr|{lfoV=WCX%k5uaM%Kfg3!PflL8~?gu&NFLs*dgFLJC_-~2fc|OKG@0w@&^f= z_4NthOr&U}w;ZWJW>j}ahZvZjKS4TUF)pLTuKe$ZG`7+!M;y*kf0Z~E za%}*DL0Vp~=gkY?TasvWR3~o4WV)A#lhC~YRh;bqsB6pt z!L(-pA6L22@bqaPx?PwtvpXsT<0aRkxR`}m8(iAd-$S)t73|pA9XDu2sb`)M|Mj); z53^$26f8UVDT`c_6Ul~l1$Kj(AoYX!k))s}rlA4DbIjqU4$v54#Kfc|pdHb0;IYVn z`W=Qhk{Fnc?sD^VoO z6WYKC1uZJ-1lQ#P#bS<{2&x5m+dQtUB(es_<#cnIJkplEj1s-`zY8RxI%AimvDolT*8sI9R zD$mIEU>#xD!fo_6`EgHA8OZGk&1O-?yPGU@{p&8F#S9?`MnorKsOps?OR6zQE%Vg4 zI1YZ)QTvd-)y1xs%|X>L_3k4^UTJJS_y^?%np^+#*biFxbu{7ou&c5T8OY7f$mmSp zLna2O4YVE+H4^?AJOH|GEerpEL=WCuCn`sd$M2UipE=jInsDiTj{Q~N(HaR977~uF zdkqeuI0YSFhGz)lvE3B2&HvqHo~b*wZ+{$w(GL-~q8VUf)8vCRKiyjhOy`RiFHjMJ zkR^*RA>j;iG=N-yyWs4 zMw-k>QC#sqVL&ZXZxp0k?Qu;qzApO0M#~4p9AvYo;1Sq>5Nl^ZCRkbUJV&2j;v_pe zI~-{Cfd57^J&g|(wTF(N8z6~ZiDSqVJ{zq~N^J@Kkq+apy4%{OKUT7Rvw!mhZLU~T z_Y)HjEKn%bKB6*t`f@%|o=3NVPt!_bk&jzu6gfk=V!u>{+MqZ-L<-b&KS zVEx1xem!SX(cPdfi=G30Y<4W8HI1qDf5<}Li>(V_zYvX)0H*M*E6kVEw+FAG&~P9n z{=VHZ*M)&dF5c?e`1lDr@$ZdWrZx3CW)V*8)gCkoUS)FuA1x*>T@9o7WyNZj!kD^L3i1>fjr&mzsD%`4WeRaNp$opBEi zc@;3E*wVI1q;0c^#VG3~x!lWI$~E64^KaHk&Oclrw&dF2x1tC4z3G;n=&G}samx?i zuljQz6N}tr3367G*N0WP=%a3|Zc^}hM?WD~o|ER~dPwG1%~Ef^SsUN(e@>({QO>p%R8*@>iwN1LL& zH}qnS63BxvZuQoMNO$7Fj4gh7e7?wIeh%8PWL=qSqR#HoB`qytS~kK2*agdVU0_2d3Wn79wYFSJcMU7)My!RDAAl2jF`B{DjNc;9$VRGXm@a+vww1^a9E`xIw^w z9Rh!>FGS8YcYz*3ExpJ3A2Fe=ihb;Mry6V5I7AFx$2N7MDEm644cpMlcoq~}ML3E~ zE`p$7dl~)2cQf$-M4O$AvT`H3wQd<;iQe@;1uI>`a~!R~@+1<@nFDB>-Nedz`wHR$ zuGBm482x0A&TSo$@|eO0&g1&!ktleO3A%>F#9XRMol|u)$Lyc|a5-;^KUKirY<>QUC^8w! z&NL-;y-cc>WB#z!f3Jp3ISt6-p#|4TpF0HHFdzGH!5n7=piyS1y97Mk_raxAh(iOn zmNZk3+`o-(SF|d}i`l$_AS=`zT1GN+)D>;NwZ z64Px8)DRy<##~kGRLZe10z1cm!pJ{%0`U2?J<_;;79OV#V1{XXoFf`T=8XxLr)R|8 zK(cTLJt+SE)eT(puB@w&`2o7cZ3C3k1!cv@=Qip(NX=l}>7cR8Z83#Ax;b_e4+i1r z>BEp0M^$yl0^puZ8dBEy_|^fC(`gR*PQRbbqv|K}?YT<2rT(KmWgc+J(I^i&n@>qd z(Qg#ca#;8YR`plTyDS)KGiQ#<@+tJIYfEFg96_GR@3+!vwS{wI?xf-&uSe34A#9AzVx4fL zyiVR=78ZOgjC;rwx+z@y#$h{jfway>cm0!dd^qs|c`nX$F%c0*K)W08SL*l*pxVA( zgxfDZga3GhWihE`e!MXa=JT zQ1EFXOsQm`gf4Fxh#uSc?=RT?dDiMb5_*(6xo`R4~U>$ynz^XTI;><=iZ0H46S60z1$eIFtoq!CHa+q1bu`w2bG3f;fd2dZcpZN^l-#S(Xec5u=YIXk*U*5#V<1mSg7jKyo5mT_ zH;MxY&Fn=Qga&uLv@3Y$hlq#mOOd)np!@OgasYkxH8sr#kz8Oo;=VX~qmSMhqgStZ z+y%veMr|PZZwCi;IF$x!!ZOt`{Vd$X9eX76WKsnv79_7mZ$ek21#a_Z+Lq|JIO|^z zoRi_lmrl!Tv9h<;po26#4Kl-bcVJY|`4Q`T+MJx_y$YNLz1v8$o* zg-EISAh2@CDY^vI1BIqi?LSRU3N1&Z=M+^)T0*VHS60i`y@-ej?6qC`2pUKrmw@(b zv}y1LmKEB3(I0RIKi1%ackV;B0z}<6e=sdhc?tF=zOk{9+)da7S;FbA2T-7tHUe0o z@-+m|`$KUTYvF?5k|E6ZlKtmLlDibGTu7ZPf@^zdL&dj^pW zlja-s%joXJ{Q3r3;9PWo>8+x!c9&y!2t*@2u;#!Pz_tJ6|ZlUhoqPj<})P~02z8|ACka>;$>jgn$ z%{QeTN4RdvV$K6{Rk+T5Lj!G){$xOKeEhgl{M)TOLD|x&E5KzLWZoo?`+GHD~g1N zi#8-C9ndf3H&8S2<7F#Mjf}G3m&k}<`p`N136caV22g+zR>uKujiVAAVC+iA6Bzl3U!xO4C9~n~* z4$@(qTU7Kel*f>pXXq5tdfQP*Y3CVL6MO}hM$WmQeK1bx=yJ3S=bux?Q5S=W>}Bq= zU_;Y=N$V4yoYs|0)wBk<+;3UE`i#4Eb8keSb%sULmlDOH4^5=5Hc8DAV1t*YR)7}uFE0`So=^;<#oc#KxhnQ5wY}8(rRYP4 zy6;(f4>?kEoO+L2p3Ea7SjhI-s` z%DE5mA%e>#t8`AyO4`XJZ<>y=gSgcGa@G%Zqlea~+iQ5K<+r+bF=9VXU2t)*q^eI} zaIpn2(>)MSbdMq9rlWv^Tx)u(N$b7LMaR7ZozlOhxY1s~&qgi&l~ll3!rkTr)4~}m z126xq7Ce1D$=T!S@wabyVUp(*<3`6Z_^agjF4#%={FB^^UPY=F$QWKK!Nn z=1S8^@Kjw~>p0SHAh9HUQ4P1%#LI#dO((FlPjP@iqxB9xMjsM3eEtm#gLA~Yc=u#YmrP=%!S8B{y9Bl{8iQhr5!(mr-%sLI8}B>nOETc&5{cZ}D7HHGrCEAN3 z_!579Vu04)i7{9GQ_=+Y@g844(7@ihwi{6d`-Y}|pGSLrW~CfYTeid9&gX<0g&*R~ z8#D;sc5ll&6-$Y-lpMTWx^UVLx-W`&`QV<2USf8E)}_hR!CMNi z$~?+wN1r2D$hAIyH-We~?O=9w7{esmZc_`$`P!Y5zI1w&KI!p1E?W6q_PO{PINfxU zG|Bc@qetUZ?&*0=L67bBk5cDZF3ziiPFcV5M`kj=Zauf9+~rRo+x8)7`rgsEuNU#{ z;O?}b(?xZXs=j*2`Pm-XAOW=UcxeMdqe(+-5yIr*R8reAy1{4R6DFlp#C=N)-gVj( zCoa?ngn3^!5|BE$C4j4qK@s;U^f`Wh1?; zPlVAePh0N{Y0ASVFK{)_5#NSb`=o)*O)-Y_&LDMKwsh$b9^a0y$b+CSZUgAFT)_i9 zGcR!hoOY8S)15~&)vxVnPyI8?@Z=SY5bws333TwJu%4XSo>M%;5Cc+hYn)BRuTWWo zse_+_-T)vtY1Gg_4WsHE@1ott2CF;nWwLyTnh5cc-cVvnmwQG~0L! zd87h{m$mL!H-Xd(IX5wjBkjBpT+9q|XN4zBQ+>HL?#XV!R2!vfX(S^d3se~nv*el` z7F)GmtHkNHi_f!XhYBy6^2}P$Sraa4T70tjfTXd$uFlWL$GrJ|ZnW>$Vzjz6eJvTD zs-wlXdPApka<8KtHPAxFvA0k5R-6MG#Uz`)M7~Kt>@B&{Le`IJ=EJJq zhECFM74;>Z7gJNx9x(^uTOf53(?z`I@aSR$en$Tz?T3BzoPRQ#;>}dI_{lbVJ2_Kci+ET>{OZt5+^X9KEJR%~~ zfWm}(&Y}TaQ9VuIuG(a=1z*f533NTSokH5nEBXGBJe^P=%*BQnBZwzr$1em-Dl5MM z&etu_t037rk$K29W2GC9Gn$}a61w|4e)c@`?5R&`s#yDOuSN7}S-bfRh zZPgRa;It6M&hG%ex>8LB3gv{2!`I>C8*wk!>w&|eU?3w`t@`1n{S19q%x}KM2FuL zNK4v%97!?5_gVE6lVJ|^th?wGw?-O-;$?ZPcFu9u{EO+APV!}R7ppq8c1~X?wqMHu zvL?(bx(4ZKtC~e?7AZj1tuy+x0VW<#Q_|8#9t$T4XDGBj-L!sv3JnH5&a2#2H%UP_ z)PYod5~WbxW~pPe=O_V(nKlF&B^?8*l%({M{^7zWlk|(}rb5pru))B9Ig<>2*-~aq z7aK|~u!Cpa@Wr~_`;QB01WoUTi;<~-uk}kb8irmHwdb&63zge%~c>v@U zUI^~Jdw=Q}be`mCoes&GsR=(AQ?<{vW(=|lF*&;=VIq>=K=V)1?MFR0Dh~_Q6CRuK zNwT?DcM3TGfa_G_4^SPs8kW!l!W_D~!fS=V4-2h8Hji(S-bWq1y(8j&;9(ZSv*K}8 z`I@}MA)XA(b~wyB3a(*7dN^8^&+@{lFTytNcrYVtg;%*iriHKaN>$x$U;(GZOlcQQ zlZ8TPQZwZeSWmUrG`je!uXLxcPl_>-X2)$Ej(jLB`8jp=`RA8nxauhW-!`5QJJ)}{ z<-)bu#0w@>jCt*ex+GIyk%pwew?d|0ftr4veSk6Su>7{_S48Ubh>FWtF=$0DJ|~n` zyi1?Sp5llZK4WswWWsFDKXxm;^9cGe8q=q?7*}3p+!w7C)jGB9U^!z$PoO|4vB?-(OV z>DZ$pmQ*xG)rsc5i+{%VBik zM=AZ}i>`J9F74U1d(%uVQgie8GHMG1>>{OWGc>gel`az4OZaXGPo(;m_Y@buqg1X0 z-evSWwO>{6T~l&Y2C6A19I z0$eh`?)A54ia@9%h&;skNuCU}tvhEEbxb6Me2>56%J{O4Yh2Y+s0gPXL9zfCCdQdC z&qnVn$`4FhR^|Gy@kp4FEiTAeSwMY>L|;^Oo2`x;RWBrF>h{V#HSht3R0B5pa}x5gH{TE=)?xcK(05pH3`#3W1^ zI#ed_(jfg&;pUE5j3TkyKh3_Vj{N!(M-tU@pEq+TGG~am|KyB!IAf((xfA1#&{i_)GfHHyn->Yc-BwrE|)&ZeQtNv?l-w{Cv{ zdk{GslS-~GFxsgBPsLO*G4B<%N=-u(20hg(!u5=rq>k$v3BJ^|(!ZNE;o9Yy=^X-w z3bbZS_H38dfxUQ|KD9AAYmSkxctTIts_ztd!?8!GJQv9=XW zPl-qD&G~#Wca}L2$XE$}(WnHHyB}wmZ8i%dZEcz56*I~5iAn#M&W}VLD(^$)j}n&5 zKYnyE9*Z!-@IF@N&1hP}F`GXU&CA0Rn`+ixaJ!Q$xMGAVkg9~q36=RK-`Ub{xKsUy zE`EGc{KI(rsX3yNfvIP>nbv~&wh#n7G7eZmyt)|kx@BKH2?-hHQu&a6kA32RnO2}g zF?}1hx9NE`{W>cb%N+1Zt$C*xb^n&}A9j^>q?(1Nw_@U^nzim(0o8%F!leH^L~xv7 zfZ=H?eDqX9=LM~5IIlJRqfJv;iom5GlChp{W?YD*S2Y~1d<@Z76xHz7(c_11UuC^| zu+9E;_^}%6wZY@*%A77L!&LrH3ys5XM-Yq#-#%ihurGOh!rW%o@mKxHRjXDqu}q)j z$e68Y0Zl$}yd<$p|gKzcHqTCe_t_1m+o(dZgZ@^?_ z6W@W^)sh`Tnx>B1#@qbLUkC`$C3Y1|1m4;mOt&7=i;^{zXDT*L0s%|qf+NyV(BXqBjhZieG|hl zIqKzKoeO4sK>#!~PoJtaYbE603zOX9^GT!mh6uGlV#+8alx==r`V*ruLpUM< zwmNKwo5Ozd3QEkIw-!hInZE!bK__DLf*~uDjbyszT%Ui{yS)ST@(Su-fH$J!4lL9? z@Rm7Bq3#vLwQ@W>RRN^f``Qk%nLbEnOV}>B13uzHILL4ZzWei~Ty&MXM$4{TTmVWM zvHaL2J#qTwKUhFzj@Eer;&{7~&fBQ^zBKqK11_w`bI#|?qzgVT4=RC|O1^XlzfmqH zSKah?5J#}#!JKc;pay;&nS%=d@bP15v{*1#T&)z^1~-*8wT92gYNvI_p8Fiv^)g72 z8aJ6!#0A{D>i~-R1RfI-HL`;vo)_9H}xP}q1F0PenPdz^zc|>4gSK^{Tp~Nbsk;k7Tr5cT$ zYlu^Om)`GVyRnU;Bj~I-2u`D6W84Ye32~LuZ#M6Z_#&K3B@eNNl*XNN9odw zde8Yk8Q%#-j2Mts{JB4CsJubPy1W8#S#r0PeNZ zq8SzR8xoqhE1PYWH+@ElKy*dwFTwDU!3tH<^F^e=1eh)BCQ03njo^#q<8iY;VmR-o zbAbfFg1muM!tnHgyb)nJb=J6R03|@`ZHAzPSmf8~rL2Cx@czWU7jNaCt7U%aiAIt( z+BYawTGrP|l8UZ+XPr-9P2W?Y-w##-fSX0jXPa^DkQXM;vD?pjj5wL+wsf!>SOrTS zx!K>PS~rSNExjyPoj{=IP%Gfc2)Vae+z>~tp?6=A9&Lm94e0G_ zhRf~%#)5g|oZ7w#zBoT06%o${!c73q>e#On^2Cx9TuPMInNYJPzq-0~PY2+Od@+Wu z(*s}A1g3(uQViO4BYn((@|{kDhLDmX`Q+orGqdyf%VR*usa z$q*^8yXj}^jguX13~P`)w8_&)KmwY zg2<&319aG2J#*L!4<(UG?amwmS<%6Zh+7hej55(&R zK~+*;Vz?hux`nacCF=AxPm!QCYd$9#e|Ez5ymp};Q1qNUp=hyyp;&td|m!;Lj9Q^Q2ITMmb9? z>|FN~-1X2EP(JgY5;WA?hm7rzpd@q5rRH>B#USpgA^xNW)2ozQ<-@CTJmDleQ{&+{ z#!`2E_4Fn^hq#(D8Z)Qn2Te1*@8WUQ$~^Hk{jEVu%rAxY;<;begxlUB~i!XE#p#4$@YTO@SQtmwJ_#7jQdI-^K$Kb z|4eO1v{bTJHZ=U^cjmpl&7Md`W7N~f`+}c04^EUbmEV?c0j~GB&?f{OLgBdeqPt_}R^m0%cb~8<>~GM1GbXMI!lu$CBwxxT)}+FhS|Bw*tVPc`C~LSw zEY&OwsiesZ74{jHpyz=4o>#w~Xk69y()t~1eELZEeb-!i_w~s)qNjL%N?zI%wEH=i zY}RHD7CKcMf9GT6M-5|9wbqbxkfr(*OUDCyK^G7rsdSPy=(%-*1DyFNqVzY17u_)> zQqMm?>?wiZj3?nupA zH${~D02*bcKb=7J$OYfQ%4dl9*8_$-*4m4Yo(-cQuhkf*do}AOY+ZclG8Nq>V01exGCI^CNiL_eW$u}n8pjF2Y}%ojF4{!atSvqA$mp{KK=*?# z-2m1@FJ+AUO2x_why7!7G{D3ztFf)M7KcBMb`2s)$UGD0p8dGOLeuA&viuljN;bHW z7t>rt3d^5Ay@Jvov!6zV*m@Dk2+0lYOqBRJ!@FL2K>MV<*DNg_OJlkEK4Px+2qTY4 zX~=r*vqM~j2h>z{mV7weRQ6t75?^D!X6}J@zJ^doqqQStA3v(D$?bn>SF*t~)j8}` z+7FOa23c%Kixkv}mi0^Y)j(i9X^WqgZwF^vz=gkGjP7xgKfRayx6_ zayJfbcr~xaj+{jpdtcBrR8Dw5k>q~L@Om~jKw%56?UH$$^3zp5BN24X08v@Z>ij;; zuO$>yGmDo81d?ADe;Xdig8-W4qB(RsrN|jSd5XUh_|A=&sg|rVLc0o3^d|_UBXGBA}c+`LP;@{la;}X=f%@xqBymKLr)*Zf5=;FlxDSXc~?Qw8l=-pB@w# z-@PPhsHQ@RH(7R`K)l9al+a*I8y0p-TDt2eZM%Cwb@t4?x=^rE2?LQva{FX>>B5h< z0D=ZhY578mr=soz_0(OTbmN%!wZ%wKH}4o=RbPZ8Y^-Q`iFm23UVjDHR*}7kg})R= zopIVZi!sh2!9f&m$JimlWk=b)TOBYH)oyE}{&zsJA%?6o+V!s>>6C1P_$YXW`jOc? ze?>*I(x~isJy78JC+EF-&w7cmUPJy2z?D$&VCSstiX;(x!ugTk60cz?5+f~Jj8}Nd zbmRlt*3#7jcL)IQ~D+SHbw!@wzENtJj-!*fy^d`Be7Xzy@wx`NC({8dp zmDJ*&zVOA(zKIoqQUg~cYa;4yyGaxPhY0lkWPU_XlypKF*jwapR!bWqv(^_w8as-z zCV>}B@y0~{0vivqTr!SS>}%q*pW9R|XgFqI@W;Nivc01c`y7}Gccfh^6xk@`7w_HK zkrk`b)4{U`uL>p*YMnf7YeUw;(!C8<`j%84c&8?pQd(B(n~e%eNuewI?3=KZ)#YAB zvm1Y5ex^epD@Wo)Lyq~IBU-t!9c6Ir5I@J9cboLr$DZ+@y}{d%pgpT|rOqj)ddJfp zMIg-rJ)nHm`H>wxUy=`*Bk;_}US5vN?H6JEsoFU-&p!w8li2l#@!Px$lijgmm-X&M zb4Fp-4uoUrUIS*2T6Qpl*xXL6NoXzVT{Vh828B&oDDv(I2c!0)MPhZ!1<|$N)%?+J zb;oJeDM-nxKFL%o2qsjsMyrmv4La z0&H~MATG@AwrGEqd$=WMBQ50>B_Q`Aa|3UsS!_tm`B)Qg*JY%-iFZ~HS4;rq)lZXm z4_O4B8dNlZ0K$0M{L|B1pVEDl#6DQ$6w%Jy|Xc^1=276=YWQ zY{9OyMD=O(8UovsE;p?cEEwD|#i-~ZbA})_b1T8)i3>o4b_B9~TlNn5ux&GMmBK>c zwX52GZUK8U3Kz$u+>Cn?%Mm^9DsAf(nbz^=HDPGZ^)m+;n>~NQErsaTVeI) zZ@rr8g`WXWtzD}i1-H!7>HB^ z>`KT-MTlPf;wn$gD+D(SWf&UVBQl4y|786&?e-bDMDF~Jq~zt3N=TRGH=MUV1A+p_ zbWC6yU_n?Tev2l5uG0wqkj|t0L#Go?_XP2_o@aqY;=-MGwqEW&Pe-S~895T%T~0c# zvD4jGUA1c7RcrsA!-pFJ*egQPVf?tXCBKvhM2knj_40!B`;#C8JCUWdP}AZbbw~vy zCI6S{!v!YmzIk@w=Iw*FEKRijUOur3@cQ*z^)3z9p;?(ukZ*y|;ibvOsY0{?8J3{_ zDsN?!q#?`%XwHy;YwROq9My8M)q8n(I-yec)EMG8GU?E^jv^>={jL=+t^fJNnO%~7 zvMcC#*STnXxgao8oM+)%pK6nkTzk_7U-Fq# z&kyqd9Gu&Y{QVo2mRRuyk~8vgOr$R!|OLSQ5Bm zlnC#DB1204q^A={0t-)x@a@8nXx^2JJ*No$sAUjoH5ojb$!VAT<>|ArQBzpv8si$x zcMt>JgiNN^d}unj#{>}l(6$-&_dMNZuQV@ec;NvyC4M{g7|dZ^5sL#w z8AzD`J~QqbJzQ^}6UOPLnK`rjE`J&Q@@gOVz6Sh3H8nLM?hch-+kQ`V;h!RBk7$w0 zy+}#^Vx0BH8;ptU6wTEbfpAq{@zdz9xfJ?ijk`lP()TW*pRbhrTetO#`ANFnkbvYI z6^|gg>a6lpxR-GFUiKx3>y;G*8EGgVWI_s6zHmKbOj^-{w4{8KS?8yCf*ho?2(cpY z@Tr7Cj!v|a=vblcJTGkF#fSAYVExdMU%KcgwqBC^peedRG@o7>d@dJC^vM{Aqem9z+_(0JRBAzpDu(FuI@koSW2#T<*B@PWx+T+0!@|M>_wdAt6CrGYj4&o(`Kq%;!Xy>S z&|T8f?L7?uH}z!@D^^g}1YLLw@LA}>o$JZZpW^TngZ9HOWWR2D6f_n1rGO6w8F5tQ1Oh_Ceb&y7h+{`wm?jBKbMuD{hSnjdvpBm*}zOF&Sts^to5D|MHU&}ZW?d~_0@44}gdPcsZ%1dalnW54UpFsZTE zkx&D7tZQib%^LsxLBrdgrr%D-lF)WsXu(8WT8vf_287j3K{7y^Oiw7khRH(f85xNZ zU9j=%qjVVJcX$gqBc8%;}8KhzBtpoI3&8$?t!3vgryhh_p*^x*^D+L#Rz~zA-*V-askk`&Yg!) z-#278+LI;`NYHog+_`Jl1uUb389deV-M4Q0Y-aa1X9+_8jTk*-PT;W+OnDvxWFnw` zU&vgDQ`<{n=!oFgz~g5LZrwzs>9Fe$i-ahdpIxhb6p3&%6rrFO5VaASN|UB32y-1& zLkuvIwq{!|W3S49Bha=Iwg>-uH}Kt`=N@C=Wv7k=L*$7hku5sM4pxOqFJP!4G-wb2 zBWNE{vK?+yRXkyC>A0~QEXiMqOhH5A8f$YO@{@eIU8`SQ~3}+CLkRMaHip;s)#qTuqUWqKX zrC4FbJ?`(}J>Ach936k)ZYJ|n_8HoIV8t`34@9F;B~XMS7LG<2S}lX~`Y=xMI^REfoM*G5lyHcSZ4nby1$vY zgi3M^0-hRUcYv!&-TtDMff*PCR}GI_7dwqK!+CzC`;buMQEd<9Mja5ry z?Sf@rNsTriYif>GOLN4ch=zzJa17FBoByfg#646s_`jCK|8sxoj^*q&`S0UQ$EzJR z^6x(-{!6dtZ@v4!|KI)nX-E9@`u~yf^I%WsH)t0`&Feq@qV=LX z278;VmsP%!F8IiQKkE&jcX3RK=Y&=&#<_XqnE22 zXGUT%*S-~Wtm}S@)&Bn4s$Yqnh#`r_E%6c_k3)V&-Xm;dtq#NPVL2)HB5cfIQRax?T(Y&u3r(Y>fuV?wa5g z?!*XZ@7Ix>h10c)(cEG7Q4*r@J4Zq7R|1pn{S;yWwC_G8&>1bTMj0>wMWCC5jjGVWKPe6bKg1r-d zn%Bo&cy<$I7$mACWiMuPz%5jXLr*>n(Q%}x51&Rg*bdtP=$pUXAV2DR^&Dp}|8wfz z(C{FMc$vhG6U6bOKx5WY+{gC#qdZR`c_CzP3|MpV9#tsGbMMik%Y-i(!t1{bWR47U zUMlg?BhWQKPQO<~f_V#B|2%1w)5(jB3xJ!Yc3#r!X>jlne0!&x z_4W0T&6e>i0zY4%kC@S7o0>gnkou*jCZ%3+OWbRozJ*QRQ}5|CMWRK^t5Ke2Nc;Ei z7Z>;6wF4Tj_za5Q<;C_zf>i=#+_nDnwbq`kc8FX=l4Fh`@~ z=?l)bXqg0plVd0ejhUq5NSp^T9}-6d(v+aQM1EFP0v1Z%ejr5@RiIOLuK*_+ppH7}NuO7ynKs~&@9nE|IojhP#VGt<%rJKJji{e9{3mFII?*$s zNv68zLn2H?hm?H{AIWDl^zI?ypK>+K};AQ zVVK(BPDe4l+`{><6AbTxlvu>CS=CJ}{;zwa<+VH%8ih2MA%&bU_Lj!R3j$3zXjs@5 zRdg16clF?)T)@hS!PrLlm%pO99e^YOa))c2pumW`|0E#5%n@n^5xgB}?+HcmHw?xo z2%j-qW2z6q?pppMur_%|HlaVm!$NFChh`3RkT2|qDKjWkFirG`2gE|Nbztou60zm2&+6agrx7s`g!}%KEm%Wkavt@ugl3Ex5E1o~+Rt_pDEybfd zN{or6N{4fQpmE*<9U! zc8FNVmW~Ci_b3k`Kl}#^aKq~%NXL^@s|3F(ktjAJGsK2*(s~75`Q2RIMNPlqZ}tS; zP5j)@2zBcA>N#0;rvVl|L@~Rj7-GHwE?jAg^%PjZLEtFb(0rqoLI?zTlo`^SrfV?) z;pH~+6vSOfuQOCCuZE`#Ye!QmclD4Tn&j5rUhdqU&IU0YsCz5#f)%M|4j&qJ_1k06d;S_ltFk z6!DN|P?ejp9WE?v8EIVR0vXq^pAM=#cOa!OV5Kbda@E6MGlOk|U>=*468lkfT0v_O z!meJFXemA^1Za!wnSF$cj8RChIm6U@%mH2;+-R1-CYuLaR`8_Y8m5gf?M6ft1{<`n zwoc)6H)>(7Y`4IJk$X1g{Xg}vE3D*EWY5LXk&XgFXmDWpB4;ED*_z~|NNV;;-vhSM z3fL}TG6G$L6$F=Y74&j28X|H}r!XFW?M2P!CZ==Z@uCEo1>A55X>K#Kvnkps9Z-$7 z>l02WHd09e6{8|?rCMHQ2=CrqobF={WGq-dk>#N?v2xis9vgHa^v^lPV_5o=z(GM? zYqN`7>MHmO(N}wmsN@u;k~GlEYBhKn+gUf9bE zGHD5zZxCM^%jEZoV<_W745&9L^VbRbEgzPMZ)pu6_p1{1z2I34TH|AC92FCz_v543 zMw#!&!ercW9J_fJi}SpKL(AJQJtITRx;4rJx@nS**NLr#W!K6U+LV@_d~IvNiG_i_ z7ws8V#HG^(^D8(%=d1|jQapDBu02*wCMG7bqM}{y8MY3rkBc5)dO?e`n6zH!w)=OGiFG|4DOT;TlH}`R&~%_KSF@b=l@?dU z=^|7^oN#zOKF`6$5o(6rt_|fmqTczubUYzANRWAnkSei?p|kY`B_BTUWhQc&L+XL~ zQ7snB&OHwz^Bdw-Ny*l9sbQFfiS_Y)R;taONdGak}s&ka2Iq69L;=_h6$Bhq3Vl$TzZO zqrn$|&j*OS!4iY&RWxlXU37|cUWR5~>39t;$$tHY_e&Gn8zj)QI^to+JcYwNK4Eg3 z8zWtpY*;oNOF&!@Kr}N^k=TaQ8w&MB3+jeB$-in7gu792feK&xw6nkI;ep&s6Wt#$ z1y9>d5fT^ddS)QF^tx2wwzZf_WITC9yi3*h*e3id77^CDJhc z!*k`W4JgLUvCw&nnh@340=>o*XlxM}BcZjE^c|WqT%K;N|Y%Y)FKEr>9jo|&n9#eMqARnJ8Y>XyiMggwi5ph9Y zdq~dNBd?8j`-ZR?Z$Wlk#cq?*mn00V#2|@wFETQ67&aF81dD}@gP9XRiJ-8y9prls zW6=4QcMP1%Q6jXs(O00oh!sm1wKveyn*%5f!AAJeX&ua1a`L(PZLjqDK& zfEjZ-%-K)tDYSQ*+40pH=i(&QXD{k(`9H69a0Et;s^d5{9cY04K0`kA(j)BJycr-i ze6}V+4pOBcw&rBzaUhtki^N-d_5e=OAKUmNF#7|#p8`;gbky^#hS0o1S3-i94v~(f zJxT<2wIz5iDIfb{KS-ou`Npgw(cJ({64cgHu>o43!sGaXReSZib$LxG=kgl&b09x+ z<&|3gUNuKX(?rhU@vn~4m$kGSVTDBZZ{uIj;eFB;#_zv>#Crj_TLf3}$Nx2j1@`u| zsBD5t*A}h(_zW>y3mMo7oB3#;NI+Hu(kocuJ~{jR=idEVl=j8jr~Ok57iGG637Cwi2Dy#;TT zmW~0_e{N|pK8ct`^Pvoj%~lFeERCNz;6eYE$QRk>1(YH6%?!qRK*8X_81Q9;Zx8s3 z_yNJLQ1YI^I(P}$ZypR=&BK5a(Xh50X2`5Qtc??PK7xbnAyNVy058w_>RZ|DAO~Fs zbAk*qtp|@dr9{yX9yyX9W{D{TFCvPBiy6F3rHRYvj1uz6c?bZ><(O53FU<{yGgiJB zzqo%O2amF3w&*L=iSt1w-YJpudw((o*O&8~l=2^uy?35O_ndK&=b!r>n~$Cw8<7>q z5;^%AuSijuXSa{4=4FxZK!{ITXuyVg-~GDXiG<$+_g6(~G!uU-9Hquk)KC}kG8CAk*;lOdt7d z1LGADjPg@>t{$E?dm|KxL`Xb!BkK&19rC;AYsrsx7A<20D(Je!fPv&scp~7bMI;p5 z>pSpVL70qYjr2Q3V&rMtI=a$Gw`UHRfvh-7?P>pf+vgE@-Siz>@08AC&9u%3A#|#y zIZ*pIc6Q!DHqbj-b3Vzs?SWKv!kT1zoS=9vA2yaDn?r%Bu-|*eGYEcu++=5k2M_GMknW4acZCc2xOZt>mI(Rc1p`@8h)K20lpt z?$W>Y`8>oXAR_R)w&_rW6F-HjACCs+JSQHyq+_-*=+4sM2hd4JW(aW_~ zz^RdphpB);pY8^|_kRJLxPg3Trz3xVd1O26g_`4^3y8o&4!jJ0fq|`YYI~fznMfB_ zyeYf?-+x`)EHNFocdSw0T3Jz1Ki8hG4A~FD{{teCWvmNkkS-x^_CL@Mu+smx2@d~$ z!2i$TDqPzCr!f-$Km8D0+ki}C@Lyb_t`KMKT^57^>F7T=8n5DCXrBi{O+d(C@jy0R z@DRWd=@}4bh#lHH_V?H3mHO*9*I=pRM#gWeNZ(byS;P8*B9QU-uY@yx?=QxGKehsz zk&jI)mcG`nj}u7!_n$ub{h(f*oSo$%Z+w%UG3&n_%lL?&W4c}AchOy5DaTHz(`eLz z2cS8hn**SXO|*#vN%#ue4Al3D#Q51516 z-9iq=(jpX%gJ?H#P*5Lt+=Ub!D1<{XkM8Bx%t6ecjc`-pL*vA(;cX`3c`z`{gvK2PO3{O*IH6CUUiiuq zp%v>J<3Z$-VZ+gq;on(8z|Z1qQ0fSlo}Qx`5_pt*C=zv$ zNkAwMKAqUem+lJs!5tlI`D85pVc?y6C=#Tgisprj!toU|_2; zCvD&m|KvA`Vwu1A<4cWr52NYH4Z&A%Vl~ttfPz~&@X||&sAGElPX;jeLdlfZi*pg$ z7vh7Wx6so-&j=)4S3Ha0S@w&MR*8JXs;t9*bE}$1ej~eAXoIa;!;t*NskR|1-O#!B z1&9~8o`^6+h#bD+TZy5mLJPsmZ?R~MVbc||6e1ktphsB~pM&|Gv^0UX8`b|{0crTn zU?^yW7zAF&X$6LbT}R8I8+VlkQ9K?Eh{#d#XvS$|D*B*BTUVk z;jP!qpehS;*d00~7Se!B=K3=II68ag;f4g$Ez=q*p1MV7q=#3VwWRlc*lL&$Fa-mV zVi9iS3j*1@b`G43hsHxQhp{zTZ_xP--+6V6cr)l!SO1j*!s*G2jD=K9p z01Y$nh$7sm>T+E1$J>Ou@xKw-ALp&#HJ2UzKMuAa$APXJh}{>7QA+?7)q!-uW+O zPTbX>>_!BXQ;Go`Fo(;B7oDIZ2?Zn9EQ+A&xa%wMszYha-+_Y-$DcnnWKS z5j}Eb35!vHtRcOON;!gCMkwVG7H)I=_-ED}Dp`6*{_+ygPo+Yy<3+BsBWWY+e~tm$ zCJ>4p!sgl%_Dn#8h~Tyk+(iOH%8zRG%GO=pi&E1^+e6b#Bj4Gqxd831|G(LxvF*7^km=<1|lvqp8`VFaO&fe&m^RVQ8tD1vU?s(!8P z!Pj_^BhPXRzyG}*&L_oMrC)?v)xooaOwVRW^H;1|3k4Rbu;$R3;9@{+7Z#;69}xS) z@XrVWPSOfxH99m8NIk~rkEP0h0W7!V-?J*_lNVGSuO>SsWt$1+vG4tTk>gZ5u}0;{EMer22pkJ41u;GTuEU>)Jw5vY>1;C&LhMxq0K&Q*(x&o z3Q#hE!vgw1si7T;+I@n^4**P4;2mLv3w{j~F|v2ia^|7x06KQVuaa*PejSTuRR;d> zFvOnuowa@7%qJzafV;%oVpAnIxET;6wjaGl!Nx=FJO5r9L=MPrJsl(>U`@9<#z#lo zkGikK()utDOTup4YzbK-?vTSS&j`$Wb&*PdG=t5G`R{oQngZB^Zgr4Fb0$ZhrUxwa*k5Y?p>*B7JIK?#OfXq}l@t3Wc4I{zvMcqj0&!*F*mSyS z;3;#AepqOzK{FE`i*c^vlM83z^LI+?i4QQEh^C^(Lr z3dlCeh8LpaMyP3%2Jv5}QLPdpGMPu;N+;fr)Y(wG6_u|D!p2KEP~pUm`sSG>1jLSaVaR@m!?q1a=m5DvT{1Jz@XhY%C(A9n!fX6Z>9Py%|ACwdWcXS5T z??uenhwiio#Z5Y#qb!LM8B0bLV_zC(%w)wrKF`P85pq6%sOXJv`sXG40TsusZTmp)76~;9 z;Ag{REz1^p!9AxQT`7{C9%@i5h@Q$%C`5TS2M<`PIhCF>u)dfp zTTPK-6OD+aLSIXh&eAVfPg{|&F4ax3aQ()OO-Y|`Sv}URgM_YMc?t^Y+LRA2vrX@! zdB|eNm3Jc=u|XUd9&CJh6_-vFC`&DXJ_y);o2ECL4Ee}Vasv%e`c~6MEcs7mw7#FgL^kkKel=)alf>#d9H`U z<`;!=yDyJUI6(m6_hjK1#qWyaq{fPZC?kupk^ zxCftqRG6oN9v%5Oy?DUfgUPAM?P6x#tM4*IsoZcvn+0)he0aTGjWl7(=RIX&pq`J~ zc`zSI9;`jy68CqO**{=JXMe>H3hDu5e*}r z*F3X2Zcgn@Z?PKQXH$ftMqDj_aKN!|{)o@1^k_rQuIFKyiTBLpk_XjTalAwGeY9n??rBRv{?Fa6)-e=iag*CE-g3hV3=939b z8$FOjX}oVQFj#S};gyl6*3+@qJzhjR2@*4R)7Zy2^t?+~j@OsqT6_mN#ro=moMXFb zMCV&x9y*VYwRUHr>*fXfL1$i6{%U)Vv_hb?+k|cZ26V#uF}j2n*%VS!Z4Pe7rxUooFhFjYXdE*4GE4{UBaC3K$$@ znzG*&1c41~AX4gR2D9(FCr(AyHSW?}H6%FmKESl9?X;$c}>odQ4}tw`Xo=pllcoS}$x(H5?d z(Q;isaEfue2ybPzc;-msB+)1$+o?}`#u~l!IrNvx9&Ru&prX#>urtWEI#)p+KO~_9 zZCeo_$cHkjP`PTZ_b8w-5V|*ax7q9^uJ+Rn(dA8}R#5B&1~W}S=oFvEDSqV*iw3(E zXJFgO3a%-YnZ%7eH1n(-JWh=w*1x?I$l43OOLJm&$dD^KU4efo$tMMr@Eql|rjLf) z;JN0J(>nx%-S#-haqK8)QN@Zfw<> ziPWH!COff>z9jmHU$8mVhZc0wyT^;1TY|8kAy0iqFL#03;unK{<_b-Cxyi17A3L?x zsWWHYdeP-gqMdhwiq@~d_K00**}gAhLJqFCDxB*B9ABDVbV+q@p3WEk)p38uJP;Fb z2?-5N<_%n#hJh+;WX`+NZ@XUP)fc-}JYNTWdfu%=Z_n2k8%|7vW54DKR~qAhr(BO{ zex@54UJoLY^*q|RhYEqy&oNRluh)lV>9`?4RVV7a>I$Cu-US=hx3wPcKYXYPJ8Jk< ziRXTte0gf5@J<7Eo3@&il@&-X`|ve40SW@*%q{aiy_`E9dAlhbtX~`QxqMVohh#-6-=+`d9|deASx<Ac=&9nmKl=4ZBTqAv>wG6{v zV(7^Q%>}icXQfr}yaOc#m?~!Kb2&NZ)K}*Uu5!78fKZzdd8kMo0`L+`FFog6&%gj5 z&v>O%;aEKffyGJ^ZZv!Du^DblwNVA-<3&RTZm7@HX-*!K(@iF)PA-gJUrrb!GYkb} zq^DPM1xq@qJL2=lE)G;zW7!O6jMt@FReQgzoP#Gg2HmFbraKI4wdG$xQywP?CrJE^8=ACcZgN9vp;u?iEVfApMQ0ZC$S%S;M1sXV35O1 zybbIs1hf4o`!;}p+0t5cjWRxgkMbq|TfS^uk{25(2Iz6?ZRc%r=X`QavW`*m7OFdN z%-~S;GZ9-wdCGMvqyub4!$v~d(yEY0EH|s-)j;Jai}TaCEMz?T+D9>%L98F9rR(1B z!!a)e@l(cap#9~TI>vokxj!Z0Q!%F0$krT zgTGZ+9vR#Kbu5xgU3&XajD72nN`3!8%y7HdQDy8F{gY1qq^zb!w#v+PNCs&A+C1@w zDY--ZhuJI0r|R?ig)Zx4MR8v^bzI%9b|H_fE+taRiA-b{as_!Gv^`Ur#*Q6Nm6;oE z$=HfNxC@$4(^Zu6GNG}B#t;1gI~&^^@|^;gPFlC+a#%(PVBJIAiACJxK-ow2?*@$T zE-<>Mh}Z>be(96gj$Mo-nr>(nKUD*hocIiZoQY=%31O6d6aR*d=bd!geQ%Zyt^CqT ziFC~1*y&@qD*`+O|0T>xIS4hYs3xoaVfc9#$8xKDjM@#26@4I?Vli-e-sBrHYq`0P zSx&>kKuyIbBr-Lw`<7U{y^j|EAu74HN2QihE2YrCU?V6+joBGTUcxisH0iM4b+sS* z;5I)x)AhVVKi)H|h}Q*T_rO;Ak9=H-^Y$BS!&Ac!bf)ucQ*UlwT@9qfA!G_w)?ICR zDa>u&3|?ivBOO%Vpu?8iIhiBRFJDJE$Zm2Ex(xVWa&Lto#cep2sV||y zfB=IpQD?n*jE*OT zheFi=t$b3ZKt&7+xAS~LqBbe?WEt=GU(LqeW*bpk*nvQh9zQ+a4l1ZtEEo~WqBv7i7k`kJtNKe+CyLb6l zTeWz1a5}YJsGds7?oj_!fR|s3Pwigh9|dLShhb2WGC6%(NQ2X1tX_V@iKo~4_fbAW zDquz23OzAOVd))Ra_KS%^AG={*BA<;{);l?;Z+p8cXF_UOMH`N4$GRuG zd_!p6`9WA6>TjcAzYD}Vb+HE;A=o5NcY&?49si0BRbuK9Dc5m0hUXHr6V-8eB6ehS!0Da2J$)+SG6u%;E?PUQS0fT;i0= z$(S68jP+Puq&q z?3=Wz`~YT0vcA6a*;@z4muT-`3_Z>uX*zr6ybFVB)5`4-uimRt&le9dv~|>9$rG3_C`YX7a}WV`|V6fPREP8?e4STMe77=dsRY@%z@`?Nas?YIbUs3 zh^~a&mSPVvJu$~Q$Tn0V||Sp9$AIS>q+AOkX~y_6#TLHu|$&2t)T6Xr0k9t1u~MnMFBHh zDlRb}b>WiUd>7?e`@GB(@A?kNudpL@Jg1)v3Y+vW7Urv5m`;S4lZX)j!^rTrsx}?3 zZqbjMbza%W4;m=l@-Xr4zt9rd<-aE|ZB;L1vJX5x)xToqtzLg;IN0#&*EN7rI=?`I zWTK*RN$=^)lJmJHN3UNe`96AiTK>W{T>k3A`vX!6ms*XlMxo!W+7A&ahusuV5uN_{ zx+|L>?3a2`B~tV&{|L$}V5i(xdB&msf;xs0760=sdtMU*(AZ4%1BrKcwheVux#_!j z1W5;9PSWXj#156GdvYOpe$PGdAMx!mKd++2M??{ zUFUve(2C1zZfqGSElo^g)%Va*o42>tP#cf7Um_X%ea$~ic*`Qc*yp|n$nRR&D}p`o z8C^Z+uB>fdfVieI*?vc!!*EOx7QNV4aJe567FOq(LMQ%OtUk*>ttHofbV%uLuZ$&$ zBk+WDs&l5`!sD97XRYdm=gheJ&)!lHIBpa47X>Mv^P70N!hhwI@OPn=-yT9>AJoo& z*SybK54g+e07R9LL~M-IpJf1wNEt!?9;Aqa2iUf3p>9FbyuXuW_%;2mAdi(kd&{d) zfyUBG9wk_COWPnuM#s^^6KXBjMUSlpabZV)eLr#Y#1`jF zwiiP0e6MB(Ajge)t*B2ci=c!=i=TkQ{JJ_S%U8!-rc%Q2|B#-ENtC&er4LaUxs6=m z-JQ!z*`-|uA1t5jOI;r4^9<(L$}{nC^<>DklIdNNKG58}o(=aeC=Kk1QXtjX!VVOm z{V}~X7R-AMpmd_qHiXh){P{S_uz^D@i^4-E;d`WY0VM5n7wr3hK4p(o3OaRo)9jHl zY@WSQ`d6y+so=r1Bd6{QVd~wO@d?W?E5xeBDTPI{aVx97c+RrFhqScvIFxw@^Z4I> z_V&@Mfc;xAFUXd9M`U{KJb@g}@$%f68cQL|FWxob2^62Yl~d&}^e#Om0%L69NDG@% z@_oO0rle=B$mH~Sd0B=v(Xgn4?M=xAz58(fu&qeA)S92F)YxuhI`z6Hr(Ph5_N&0g3Wh>}oG_NZDJ0#dOe>*dCWEkR>2ygwZTcl={ixV@ z0gg=9aa--2S<5PIon@!hTSKOx^@8R&8D$yGsY-yb96PlC{8CbR1FLYl5ab!6`g}_1 z*0ZVt_w@A7t+4zQE=x_k)!q20I2r>e7B!e614lXcu1WuWe<}55aW%3Qy6WRFqb(NUEH2( zqM~L>(F8}=fJI_4eKzpyN@j2lhD1P+1u*0_MTo^cQqYx&FC)B!1O-EdX-AGpb)Y)i zi8DJGD@%7}F_R|4f_w^Nw%At6zxJe7H@a6x8<_F960IVB=F3`CEhy>b%dO9A4Cws=`m!9e8SX%$IBf!2KYO?51H+Qbo3XrgCK@(Az=@vua#nLWnfG1#2e zLvWsF<{;WZepo6t-X{^>_o{?K?{tmC9{%GNOXY1R2l)dD*7=E@oh<`>@X4~R5Ot-E zypMrue&KZ)ATkzq+zSUt*WpP`@IYINf>!{c7w)A)U)BU7_WoStIC{-ZqF?SsCFm$< zsEt`QY%ZU=jNr2RA4teSWaGHUojZuHE#-H+&)FI9W&3%lK8-pA1Sj~r`htWU1w{VJ z^Mv+1n){QdPaibM|LG+)#(QX0%IXd2BmO=%p~BfKwYfGnlzz%x{5v7TT#$OL$(5Yq;zdDX|h zILCc6qaTm+{A3A23eaOAfFX1X;g3YsfQ{=M9?RD5xdhB(NI%cy>VxPS^D<_G&6=%{z4HE|rs*-)(I#d6|JGp}zGC*ouy5 z83um1dh9+Idw5cU+&C+cF1i1wOgTBtYxvP6bp#G2bPGjtYbIz_E$qt>mXi81SFz9y zk@fAFq7}ZhEvAvny<{v08wY}4i1Qan-CW6V%5Wo8u3LPmgz_G`IKoc{r9vZLlFs4C zVj!OdTZrzug6sa73$3sSBZ%v>q)gwdd0Yw`BLy`Wg4hOmUYSp{!d6j<{wHpV*p~)F z5wFE_* zBzB9)f89``6t5*KE|!~GSnRB!kFJCAbOfARR5%Ee4$>*&C0P0UB{1P>X`Hn#l~{k6 zuImn5rN7UtPvb?n;TN(&8p95?uzJ)+OJ zw&*h5sQ<4nVEDS7X}#dYA1A=Lxh{k_Ro)Q5kYhiY&}MHc=sNJcxX+c}w6MN%Vk(*n z3R@Xjz~eY?JnI6bw@NKz@Yv#Spj-oJnUC(;@01Msltj^z$R z`$#XK+r4%UEz4|E5kcZ>nH}15{P3kuZ;T77lc0FlGJ`hVm#u?P%Hzytw~QU!W_@O2}CNA;ajA=cJRnL8Qo0kMX2snf;+e$cVdeo^?x73ob z3?a3m80XU2^FA`65?#|>1O4cPhA(QfBta?&Ikgqu|2m{NZsf{G;oME{r*(Tj>;J*t zdq!oIZQG(~89_xQ7=gk75(E?kB$@!pNEAt80tEz=BvDHNK@brHktC8NNX`ZL#C zGyF0W*%x&i88JPpGdl&skUbWnuO+mn)mzjP1H47rlaD^ZbY25YZS!f>(t!k{Z9NiX z^n{FWb^y{5wVg|#{mf%RkM_o2q(BpGPzM%u2Ir_v>j#u8wx2-Da!Rf=Y3RX6_^aFt zP&({mo=U`ZH&t^;9GL2(fZAS+IdH*4uV)TZgPdlC9)b>fLbwgvC4joXwG*S{j#D6k zPMwWGcNhX|9wLFUjTb+fSrg+@|whC-H zBX^}u5*3i{Wo2m`r@MJ!mk6gYY8s66GE-$8A`A%A^=tCAl5o{ypEl$J*3T|kJa%^I zbGkasL(UUQnup)+DOjgZ_&$z~!iU9Lk0Y!zuYjYNvsP+B7?BSVand6(3&B{US;S zgC}o5M@06?_j3XZA2wsNjE>EBG z@-2TG&Aq#!bDW=Q)XS_=XF0@X8J+wJjr&2e9_f*gW`iM7#7+RU^j~doR#o)`p~YvG zOnIE1ng8@QT}-ow4(5uL2E(?T;MF||Vyp0TioF=+DyICtyWoA^ZEIXi*YhZ>-1Z>s zsKwlcx!in}!%Ao8j=<=d+wNl7%CFzVmnZ2(aThcpztHQe4xx_+5iM|FP=TLjPf=QiERL{ZWildSwLBhN2hN{E-y6a(Ng z$Y*Cuu!)Zxb99D}P7BdWM>0r0#&$~tN{5rk40RGt>DGaDC(W6od@K(mA~QBxz17Lf z_1b#%2Oq&;dAtlBSwVK#hb5d&5`qjtokK4TIbU@m^&;WKB`fI|Bm3GCMGN_rD2y){ zP9ewxKRlfoW+rDF?FoCs$MM`-s=Qg0K4;SqMgvz2@l&qu?r6`;Lsj<^cg>5$L3JI{ zMspYJ=W0KF%Hig;oYy*{+-?tAAc_O?Q2Eb$pb4p-RQywQR!mpOE`0)d9Bdx>1sBg_ zbKqJAOS0Xn(>*06Wi6lbea#b5?;}nH#^ql-*DvmWXs7d!ds!aQ1BfW?mMx|wzzNO# z%njR4>%ZG``O9e)v1T~Nz{(;?g4Y^nM!0P7u-D`i5r?j2(`qNIg!xfC+yWlt|3Nk6xc>xK?Vd&ML|FgVyqIt>sd6&O-SJ4W`8tLvL)$ z*ZdSy76H-Lzli>W(8TRxzxcXbS;7=rOIXg?cswv&sv88Qhm)0c0559RsvoYQ7c^8# z-g1w(DfsgjxM*PxTeC-2l#mU_Q9p1z@>Zrfj|!oRKp1MuFui*2Tp`XzxKqfsFMF!{ z(2=T^O5L9v(zYppQ?LHmt>CO0$Lm4r795pC7u;SYT~fXQVRRZrK=hgb#27@OT-*}@ z;~(l62YIHRhTiS~_|L0EHQ+9;mCn%x#h_a1``ID;&7SD+Ncm)T6BztM@&+WDLR5H1E`i z;AkI(K{7)sssfUD?4`7kVLT8A*JTUvjOlCF%2L8S~TDUcm4g z5#bk!wnp{=y8y1yo?A3XYwncUsHF~+bv=k02=Ka4USpFYSIkofPNQBSlTqAWj;xUA z@RLR>a7FxL5B0$nar-;^cT-bv$E(rxprH*kug=Zi2)c+w+fYKr3#P0t6kvVoUqm1g=|o#( z!@6~E!%Bmua-tUB0%gC!c=)i`Mpo4|`ZCfvY7tA-6-cG0gLYrR z6-tyVv$v5NEW-wb<R z{#PE5`>U@9emgPeKbP;yBCh{cS8jb|D~TfF$Z?BGt?*OND?Y9ZZ2Zf(X!*Sa(6dx~0i}vf zGG7S-Gm@1Py~fLQ*g#Hc8@@UUgJ?SuCHpgmyKT5(*X_!DQHTxkt*@;}R! z*Ii(e4M1jWw}p7YMP z%?Ojeq${=7J~@NsEy(*_vhhpcf*pDp$_`P=6z_0h-bBa<=z<|xex z%MD+TXWoYb{wvYFeUbwYKUj}-QS9Ga@(oWIb7>e10NisW(&#?@KmdvtAM7IEO^mid zQ=|LB#ndUFzOSxNza?)IJZRF@+g%ZGVi0Gt;er{JFfZ{z>i?VTdb=Yb-AnaCAZlYFyvk7dWt?7KuwNk=Q|nI00HLTPY%A?CjeNP4 zn86A?a~l$69KOcRa45I8fWpnbOLh20ZC33BK_A9o--O&jPxI&Gs{ zz};X=YPCG7kvd@&MexMVnz4_{Qy^NK&>JzZRoE5kTDA}KTouz%-aN9u`YNw^`&KYx zdTDoWA1n4Z9(X{QN3ilPTV50ek#aCG7bZlirZRy7_3FIIS^mcP6ib}8j&G`-Rel$6 zJUnPM_$@ttrF^JOfy9Xal7}5dX|fm<$V3y(IDvDQF)ln zJGPqDM;>^L%-q#+kVm;zrmG_a&0T;5Hb}g2IbfZ#Z^lf#z+-45}+rW#PcP_d{6o)c}X`dgK7~N6%>Fn+3i-Bj6 zBABfmxMU|2H*rB-%?9W^eZks&GLCPULZswmWHbRSyV9^>I@;wxA+zR%xsY$^D4Qm> zlv2mk*4?kK($mw6i4s&Vr=&(7Xa3-G?%5*o^@grXq79~Jlq^uhwuyT zeIL9wFg4W1w;hdX6*QHNTdrQUr5(FT+ghe3nIs1C8EZbq_oKb^l; ze0+T+%ARW6x-|fJBNK0L=OMPYOcm*8qU$>Y@}mxXxlVA!QJDdM_>#^=D|cc2wvnH| z|I{9q7s~sRL%zPSA6VR@(^Efj0qD+W_7tR&tR@>SrXy{~*y>x17=aA1uOTW9N@SwY zyY6v$_?QC^S;jAA))I*rX8e+KFf&o^0O0|dSSBGMLD&TPET^A?)?EJl`RWPaK-hD! zN|gYtQ4^&?7FdjB?al^V<(7XZS}|Q=U{d&}I9y>jntcm`!x4ok_wL-egC_as+kbn^ z5aeStL3RAnYBGO9iywAnKMYL-ccD)N^J(@fOFtL4o)8C&qb(yi7_;$_4v(Fj)8V z%VV^1=9rnr`13=NdHX`E8IyAIk751Z>z+xV)(!$hBIZu!QPb16fs#8YP_Pg%nxd9PesZXxxrtuEPZ`_sF<+1(5PudT57c`4y^+; zQVGdC09#(C)A1*;VTdpUM25lRC>j9-ehjqh zEdjhl$3d@sPp=kpX9+5{z&9uzF|7VFxa3H*h$)9K!Hc011j!YWp&L@SjL{^j&4*Zs z@X1rv##DNNNvMmFKV%|+VdqQB$;By#mq7|+WdV%$H zJ3mgS2HA?_X9bO=7yF3Cc^z=_>=0qG5MJiDPuls&i}^G++)S7^@D?B(e(xe~^K25E z(TyUo)fMsR7=xTWYf>Y&D-c*|WjvtCoA@!L6GXj#SlTB;8b-@2nBfLgfHzP4+&P}K zs3kC%li_8btgX{MJ=&g!tGLoBk#{&5Oe);&iMiw4gJ;j2iOZP*RR2xja7;7SupS~4 zO3|fi78ZbMVAtt^ANe^RGSwT-@Rzher=~&g`t_5JslC`Jw?5i!#%ro818{>kvtD9)*nso*a34uJkGtF@0B)81FQu+k1gsq+m#cgzO~ zkTNkwTTN3_6WJ5uc`N5lS?IP>xnNm=ex8jnL1^%bPln_cfV(l?FUVh@A-cP`WFl!M zh5;G2WYlh_Kw9AX6li z2Gwk4hf)uK&q69{YXLUP8&${FZ`?>Y^9VG+IRsnKIlpbI0Yn$CInY2{ke6Z`UX2 zp7nAhI8=VNbg=9h#&PTiR|q==4K(7~{z`SjPYu%kuC`LJc7_u*0v}%YP=qSxilW82 zMF)VFLQ#K1ViFn0cxuGY1e8rO4cCmHh|~=9FIC z4lB095^4i-g)FUjZsg6^esJ-Db3ZNI+1`0MOA2h>w5boB{ba@ROQF*^y=*3I z+=E^)Q^*(~b%!-$kT;yBuD$-3SqiI3dc-!!Xc=t3KL9BM%#u0&FLov-CYT8hAx*_K z?U7~wFYx2jL3^V_6*rJs?>;&uZEb{Te<9V8ww!MZf?wa(5o>w%Uy^EAPM(reRbVMA zE%iHjmk6ggx`dt9OV691V=ofJ5j~D{_Jj=y0bq0-R=@7UsT1djI@Wy;cMntqTefTw z`r?B*v@h-&-8e+wNw9VSKw)0ffzUS`Da0VFB3Bw>NNToKqFj9r34%Mw4eBrj1={vQ zhr$xRyv3-X>&y*Sy=8q@kX}U#U(7vur|a>oTz1Uc|N8djg!I_`pkPuDFZ&vD_i^nB z01aqVKmmHj@4!LL;@Fs(dr_y|g1Hxr%YaPLQd5U%rKSy|1xi#=w`C=1GB5z*v%Ub) z`t$SKXw>$Vt2xZI!jHB5C>&l`jk+Dqkuk6P8P@*Z1PY9Z=dY$-0pF{Yitk5)pL{pQ zFbv0*d$i%*Hzbreym2p33$>xj)%7=oSqM@x=WTS>KU|ozlNHHRDv5alu=@(dEhQj@ zY2?VX^H!$g39`1I9NWL_RmAjYAX!3EhRSIk-2){*3|zu~GSEeUf&vEqxPIKqRVXBp z8w`t#fHwsXP?)`>Zq$leJUpDnzEi)9^cd)b!`~r?_#6srh$-*v{P%dn-?}+99gC0) zj1C6Tkhp~r9Rf}`enn&PI^|m`wDV{r?Cn<&gfIhQ3T9Gd`O^Mlnfx9jQODS0wEl$oT;;y>+*e-4Zoi0rzG|7 zg?)X63L^DK#8xw7mlhN>PgVu9=voh*EaL(Bv9^j2hs|%O$=c;VcfO^*5R~BJ)AxW zkvHJ`(OW+iG3CTF>N#hsM6QT!*adE4@Q)Fask4PQUiA45V*;XcS zT_Qq0xQ+0y%k0a?+XN5e0tEJ65we2KE%8XPh=T`*W$rvVf8KXM*f0mp$Piv*A9>5^ zb@UMDoBdf%qMJkmWPel-R&P2`=))~pyBV}brOnvGS{Wv~$N<5QK*E!L>n?X~75XMA z0neZRFij&GsQ9-Vw(Z}IF{e6+q8I=+0UY_JGcuF(pReiE{J(A()^&P2*|-fqn~p@= zMikxw@O~8=5`jfv9*Fi;pDLKbq9= z*Y6@yPyqn}At6mRN?533OG6PvC>&X^-~NkhNo^UC>uF_8;p`rp)Z0U#CimC;0aZS{ z7HLF9^H3#}dNC6XOXyFv9_YnLe+j~6xC3TJFJ6RG@Yrx?ipW`9U|}U4Tzf>%o{5E$ z>^{&^jkruWQT_Vje}J)tx4)Ls(hs7Iq)StRGZ@&7O?}D1fr^;g9rMFgOPNr48O3c!oH{^AL-U5@7;Sv9{xWMSM-%HYTH(ogEDjM>uRyRSDV!vV)S z+bvCYZuxfX$#7fF`@Xe^7WdHLN=>*@bekaurd+D)rOSjL1KWT~Kl<_oJl%rLDY_i* zfB>0@N|&ThH(rDiuVCWbKfPC!Hz7&m_R>~&i0rq@zIEKH#Y;DnxfCt+bH7s1Upwc% zYrb#SJ|!m}Ix_yK|20_owJ84wQ(|BP|ChVG{8|S5^Z(YB;J-;wsFu8eZztbDU$L#I z(8g~4zs!r!%UO#%0{$*haNTWqx&9sB*OPKjpDI})!%;25Jh#6 zFmI3$6C+wB6lf{`v_4ctTY{LPLkj^NgnPUm?=W1zqUm!PU<%`h!G6BRz)_}S# zcK=>$enE|mtqHozC>qefZ7o8^h77o;w>PpI5rt03s`{#^z_Gv}TChf$)<}7J+jYeY*(~TvXg~8=_EZ26LeMAtueR}F ziu!CEAfQ%gYs!N{ZeWNUEtxkjx9wAvYq?L)uCJpz>92fkg=o(I{q;KkF)DscSX8my zXN2sb0z>BFfnotOfw{v|i9|d$Xa-fKjaWqz?#@}*pV_-(b`^4ubLSq}cmbw*_0b#< zEGR!M?~l(5z^n-P#O&^%jbu`edpJvcZ3F4kTt#!Ae*D)xmxxR73Kj#|TCVYNzyt*H ze%--=g#3{g32>5^A?~K{9A81egm&yGibBHo!s59(t6H2b7!*at#TzzmgwVtY_(CNU z*d>0TXKB~pL9Idz_(Fkm6sE7ok!(=Fd>Cne0@>@7lkw0!h@lBfGHsI`ioAzcR#A0DqU4mh$uS z1>kRXy^K2M9NG)Z%Ot+P9+r&lBnV&x|073Y96t6Rpl5|`;p=YX!I&Odusnd)LT6_u z%J@1^v5eZHl$eKISrW;->GbOMGkm$tYG@(%;HMMG@0ZT{jDVPt@cRVvd;v%h^Ab)^ zc!Px9Q8%E&qna7j%g6*&D)N!hQ?#_Gk9wP7{jGpg_y~_Ti0#r+4i(>ev?^FMnD|4U zYnihIu5gJz(tYg>f;OpiK{&W6ai^wN2e;{1%ey|!;L*8 z1{+Iyt$OpOCnQbD^Jfn3JVWK>XZYbVSK6^{yq5*#hO&%W!oI}IzT%CyGpc2w)7J5y zUT98lj!V%R9+dXtv9mL3o_@Zqw5&SJPv~kwqRU5d-sBzJXVx;VdDYuqDLieLzH^i0 z`CV?J!J?JTvjKLMUu}Z#cC(N0**JTIhlc~Pk)|8&>w8QAD!H#vPHZeYM@o9Shfo$W z`*R0&kee3#1t}ok+Ki`K!7*3F4*X7Z8zW&c3bO4iEXa1c5Jt;;BkQ{OXlQ7n0x`_? zF$I3}`>t9wFaX^snB{}^TlB%9%Ejv}#_X)5J1nLn!Z_biQ6aow58RJBhA7{C(5<^r zIdzaX>8dXKubqB>P+3Fc8(2g0r9b+ivaX3MX;j&Im3RC!0Ru+xD@$Ow9x1OABA7VlO38<+Y zCFt)RlDc6H4^Iz>A)1=_Awxr0jZhSU_<3k#L{4-N5&=KnqkPoFqcGeuFt7n@iztUs z0l_;B$GLOIhcMhvs?h;KHG8gVGw=Mpi!SFQpsJ#Tym*mcD$r4~zyj97Kb2)Yc{dFW z3E?Q%PQK?4^)PQ0Bt{Vw*mvauBnFPW>+l!R>FMdHWfbJvm>z>Bzkz`PaFia<@x;lL zn3ittEoW7W%DflXn=SW@=I?!LLN_=(Of(4qs_q`dD|XgN#q6Z9cgA*X!n$6>sFP9K zUcs$fw-(H#r=$qsC2;0UDK1ok*ak4I$hQ?iVj-kD3?5>Px0?|8Bq(XtXu1PiF>1*e ze?E9ET)NvlKltOIP?8r`)1B;3*XZcze2b?1l%H#FA`30v0YeQ94d-FXyhicPalGd6 zvhis7@w`WTCr*f+y8tUBgjE8$x3t6_!QFH4{-F4rzbJN6vSd_(+>+_F{me8CE`wpD zShuvt9tcdUm zs&i@6+`^_p5@25Shx}kp5Lw~bdCz(sv)phY_ei0QBAcGr&5oe-b)Ku>L7YUCpLuZx z>FBv@za%Aifa6mC`#td@{{LTxKkwqe;(ikp5U{qfadvXb4;mO4z(&-q$Nwr4S)!J+ z#4Zd$6L@Z)lAD{GlhayLDl9NDtq(_DLr@B`C1A}8#;vzKkL^}UI)Sb}D-ZF;L5A?e z{dB=zJ0O#9{O)z}P(H9svi)x2p1b@A`Rfdbm4ZOL~mA|m3* zLzrMI=6!b3as2k}8!ma#Bug@xC?v{=HkfY%Wmlc1>V-Sof_lQ*+S-mNeg3_%xg7H; z3$(PfLQG(O-O$i5*;^$+rb+RKBwSqk1206x9j;N8)SanO-=p{qhU& zijIwiQ{8n(%uN-Z5Fl%O7Z(?Yegr`G46Gq~NsunSvcB;D}N8DKw zWZ5(+si|-;!4Gyp9@la8fal|g=b!$rRX&7CcAG<-;s5H@0kSs5AK7wTJUl$%QFIi) zp(L57=pFON-yNu9xkq4)kvsG>uE|)Fl9m=EXvsm1J`|non%|+}z9jxQbsF}T8(9WpfY809 zUmp?6^vZDcl2tn_C@5GtNM`czroR5BZ`heu{RNWVRKLSCO}>04_6*iY5FML!AGU_Q zdv|onXcXDV@UT16i`9=_h#iMU{i8E;r}@@?ce;xTD&yJtLvKnCuD*EF>({UA+Tg8Y z)i;q9i?6=lAMjTwz-~LS_Wm)d)y02}ZG;OJoAl=vY004F(0uvgh53&{7wjng8Mut3#^`Lxnd-y=IKJ`rEgCX((4}4wbt8{r6rzLl%xk-=_Qfv>Y59ntHlq zn!EV_vuj@Crf@dG7RPcKUB3J}H&^79(CRNHGg&`8Jgnz{W@hKevW2N>X~n%HR@d<_G>WL#e3)}t{b;Ae|J(0n6fgj*DUI0C zT=c6?MulqYO~SjFm?vZzHT8{+694(tWMpxF1}Po&Jb%vX^zT3SKgQRLN4SMmR8^gf zG(|A9?MUG_u#C@*@h1OIpTy)KvXTa5S9_h!Yb_(vkhfxfOZ@k3#%wsMHC;RYvnNn_ zh?x{*LUw^^DfUITOzEQNUhe+A=gGde6K)cAcLLEsD-H=zcs#47sv5CYIApuU?Y8Wb zYl|gUpMEZAswhD`&c+-COJBuy`CCju3Jz$LDhhIGuSO3lMQ`elTN&+yV?FN$>gLxd zFmqJBI>8pOZ%wDJ&);|O{mtlAUubH;9vf@mo5_}AkE05vNrT;#OJv7`eJXZ#c0Bi1 zGrsS`94BNecTWl=D%@37v~}c9I{3c8Jvt{x*pSNQ9M7JA{a{h06$o2;c&ZC`?#1%I zr_vcPtZn)tI%ctYs>qBDOzW6<+n&kpuaL|;Gw^C$E{ZPW(CUg2BHaxXSU z#vEmy`X(hoZmP#kP*JdDH!*)R4f$Kmwe9~|WQ;@Gl%ag27upv0NdaA(Us_D@Fvwu-E1GeHT!Yv{Wx>$bd03uD{E%EDE2>@ zRyVjzZnESK6PA;|V>A_JJW{9ORP4xp5&qYSaWnqVUPCmeh_=+DeDFcgfY`Z)>dHS> z9Ir*A9m>$od?G2AO2v+AMaJzCrjYUDj#|`|JYvl{W7BDbg4Nw~Km9!l&O%50tQJQq z)wZ8oMShOo`WCY}kGY-I;4OJqCYRrbj4Zn~P}qj|L`@}hl@c3F#e2-Jgoar_2hva% zQnTyZU+Ijii~MIZ_FMN~Cw$8X3pxCnH`}1;OYf_s`{$ox|JZMYa8OpNYGh0K;C?|1 z!;6er%J|Dt8Fk<<9k3eI2%whT`TJMi-#l>M_4|e-W)?enYi&K^#aW6_=a)#UO6yRv zC)37_O4_%GDWE_L89sYPp|BMcZ?!R6VnVa+PMf$J~w9X@j zg98Hx$Znev`{+N1mFzy-<6dRyLp%Smx5)0hmHiH*WW_xoCofC7ySwk;y098P$%>=H z!@Fa2mT#>y7JagdbKAFtg$4cNkAKHB83nN3)_wSP7U}sv&J`2XCuet%xds1zG%~Vl z-v6iXKdeJjj`e?he0^hM%5Nx9WjFkOEo5Y$j{NuEXZ-au>Jx|kKWCWiJ2@<1L;rCH zo;Ei(|KF$lwH@au!B;|TAtql}SDT<8mJk^c(F1iRri_t58uN)()zI({3~X&|Y;0=U zos)wc;}IjdV_I4oQi2jeWX2nmuAv_}6T9uP~bX3 z%+_pkMLJ7GK$9Hvu|TjM@%Q%+?P5QO```&%7`bN6G?zYsT9%jZ!+>2daSmh%{5L0t zi{RQXhl(zQLdVyxCo<^=WJIDd9i=o#9ZqNT2ZBlS!8#cxjBTQD3*&Q`s?OF#({bw1XqvJ;U`sVs1ME8EIXJKb|WAgZY*gmYQ$5ohp z6HPgtg<9Vq6L3QVuyRLQ5gm6~*$ViU8I#&Zm)oHK2_0<0C<|PCM}{!3^F^Zzouun- zkFkt)CyGlScVGXV;hEF}VX4gPhvI^uM@NcGM|n6bb>j7Q=$}6Q(OG0FME}iB$r@@3 zcQ*=Xyq+*gsy1A7@XcG;_HI0C5zW_4tJj*cmVI$Wn9TEjO2UtP~d>B@H+)zSw+wd04V2pUdjr=|XX-%`xf7hq8NMaIoOh)bzBw{SPSgsOZlfu^qV^EC~19g%I4Z!cY){V!+DA z2B1e6{fTd=`d>Azd!P0VsT4eESl@l+lY6?11DdpdVh91n`-M%uDuB7A6wzu=F z2M{~Ln4;s`uC|owW;lP1vFB?nHygye$L{Bt`*hIbQCWw2Fv!T@mg|rf8VmR(WL8Tp zvIyKg&Y&Q>NHEy(NEIJc%zX6f)hqCh%`7d60e7OLNl^xQ+UG%Qqu#!q8^)6o_6$Kt zkudw0QM0RRWvB|9!KWF^O91?zNvxy64EFG&YW} z?-W*1Q3*j*P~h1xYBy>FzweH^62SAwqhZjtpcK4N2i9VcHuqHbLx!cxw{PEG!i*v- z^;$^GR{W}LBQi7ZP3=QnJ*;Nmt(H#~(Qcqp+bc4g3A>#zwWoqe)r&?Kb4P845=(vr zo;<1ccnOhb;STZqYvj*n;Pe$^4HLz>o!kBb`_9$qeF7BS7tmW$3y9J455aISY~Uu^ST z35(c8%`QXkKL0*%@jaW!2-wG{e;p%DO3&)Pu65?CbNjYEsK!}zsRM_|%3;Zxuz+${ zh;$I}DQYa9t}ak;=4?bfQL?$^HG*q|)2?3{E-hBp-MDC`dKJeM7*G7h0{qP2H?dM0 zyd+sj{g2dlY~Q_~nYu0ZQYs! zA1@f9TI=Yvqdx)v#iwZh5V2v|OLX1~NQtf*OgWT*z2MB`pS@{pOw8{=()ubeaQUF< z#6cj}1o@@=QU-hxOlE=UiVz!-@DIrVBS_&q$oFe9s35NqWJ9W zrCs(NyH=*;Nj&BiI3T2{;1wlIl)^8`oh#yWT0rTJ4MA`~(1e(UFP?kA`@>rNPT6hCCJLjv~ony*Z7k|w~A4Q=U54qi6&-qQU@&}kuZrZs_9$sF| zreMpa54oe?h6m%6IOHD0ypMA==ZjEf4SOzfU5=fNE#}?3Va}BwoKE39ZSaF2^5}fN zy!ncFyJ3UzN-$&?uwx?a2O?dcJaL%ObC~g1>GN>XIvS7!$cac8ESJWKF-p*|oLT1Y z8{3yRzR&mO_t_sOP9wwJ{}r6hPuK*z-F1wNjIz%);{GQMRjtG#Gw=pyYS*Rd#P<2X z{`KocXOl%2+wEs9JIla`eA-L9P~xyuVl@+pU<(i)P(HZu`sidJZ`gNzJPN126^-GL z1P8q8`yH_gZMc5W;UEzqIAEV8xVGHOv0P?oEG&J?TmHtzkxkP)gA4^6JUxB=J|ck$ z&?Tow`^uyn|EW_Y!Ht8N$K&FILSL;mgc{p+?u%l64{91(<+k7dVAzzd0ZuSK&qN_| z4&rbG@)>Iz8;>%J&X}LSQth7#pUj8Y$OVxnt`HF4WPX`D$ko>N0p4$%6JWabbvR))B6;GA;gBM$R0Tjjp&l_hB+T<8L zFaUJPN^hY|p?}x>k6n3l<4ARg3qrhf@SO3s8khi)=0!=&Pj440KJ@YOy8A_z4^5fN zm%rou4AD#qT)5Mnlo16gxgl#j)uOM%yf4hK$KlVw%E{?bd=r6P*9I7xr~C}mqFWlX zyDn*sAL2plko@XIsl5Fi@6BGt^=?P}e5Hb$n1oWTtb?Wmykg?wIFOz#x04nf3S#yh z?me}4nOIq*1S`6V_(K@5D6w=a+>$HTJfnw*^pSb=9W|n!w&s3jtF_(bTPVxX26W8- zw59CBhf}wLJiHN_`70H5bdY-T4D~@@gz)HUQi0gNRU@(o7-#=!Kf=yfHC~(Uu#yOo z-9;#}9<8abKf%sOJrhQ>wwb9F2L+Ztr5oXg5Iy@nd-n9 zHC_lYc`{WL5@)qM6%!6DH~h}gPDOxWsBN_dN}W7(mVWHwyr*i_KwQ~*w(}DTLEhPN z-VN_P5^06?l{``Ka0Em(|5at&0NxPf2v>RiXFOc92XP*X)-3?2xn$G6I*B&itnuX&)x2(wDdLp!S ziVcdBHtbmOb~I8_Qu;^n`|=W?h$j?R1G<<_%$FSQeJCkrTid&7Pq_W9-cJi&9I1u% zpS;hay=JeiEp0GJY=>SVe&QN@r<1orcu=vkf3B?5em<2un=?wJG)KOaYMMTFa&p?~ zQdnn$1SH=^o#LP~Pr^x&In;gkqfz~MEKoWOHx>+`*FcMJ- zhbajLSTWCC1%(7ka{;d(>(h$uyB~)E_dfp{srmjY1`z1(hv4=WI}(DJ@D-vMIAdfq z{3$dl5I5gG3w?lw2L~f9g7{4jyt)$74P0qxU|@lP)al@MY`~PiALgbUAk*t8Tk{3@0pDjJ@}NK$$7-#W;P9 zb3ROboXCYnZQ6oyChPu@54@c(nw`|*{J+Aom z>(_&pPoHORwxg7{?W)Y+D$@{XbGApyQ+zKF3oMLgFnlRw9F0ZBUF4x93#-I0*WCSx zi`v(g!#A5wjg#%CcoFmhmPw?)@EhWlisMUxo;NX|7Tct?mG1Da&3o_hq;kI?ozJ|v zvc#YIj3ZU9`VhyM*-29`pzQRVmw7BuLZXRVg=sFR_(q`$I4C7Ie%z4NTe>YKB7*6J zxvbQ!$1pwEDXYvne+=DqU1zjNAUKK}yC}hteIg4f)Ldz$!`&a@W{`s$Wb}aP?lg?( zYG8Gk$m)Ym-et1Y#Pt$zc<*~piZkKb_%}UU1gV3xgfwbt;AeK7U0ab=3y0kL&l@j2 zAJS?!TRr>Lv|`X|%b;ICuj@@$%rr_Z59Bmx;UM8YmUPcl`H1GpAP4=a4csiNsO-y~ zxI|I4EFxi@EoR6YcQ*!%pv%sTRIuotC+g`oc<}T)6Ta|JrwUTT(0kuTrdkyhHDCn} zq=W_2?9ll5Q6OJ6)Y9uu+xI!!>tD3e2a$$BPD+feZ8B(f3HjWV{8FL(7JtuhG!Hlm z`+dq?y!Plma~LYIXSZ`e0tzn+-dJHO6R1?d#8R+oeBVY>CEzsyN8y!T(o(R)NUwcd zcDB%7k2)`{JChaYpUW?VjKvb*g_8k?POfc}x@kjLOw1rr_jcadog!VFDLx==p$j`< zB;EasN|``eL>86b+fPZ@8-jjlII}q*1vuZ%P&~ZRVQk~Ui2II-zdI@G(0q&v%l{i8 zz)cq3vzFj55pf_Ix)g#Lylh-PP|x*5d#t666QO zfD+>I?%ur%lm$sPjoQfQC`_X4yaOVB!jaGhAocB>iL=aM8_Gt31ciu1W+R+j?0{1f zL)o!4Iw;g)e8#@SQ zhj*yB9W+H&x#es+ZdQ{}&%4ye{&TdLj%a_3M4EBHm6lm+q8jVET*PT_s_Ei~VZ9Cu z03v`kVc;97K(b$gzzyudWj|Q8(2$;+3Ju_*SM4lRjKh#6xLSr&Zuu&&?o?2pzc-I&;VKYuBwbLt*y(%igw%zZHd8XVTa{-(ln9!VnuDa zSeErO<{EQnQu_vk;NJjFlZWukMIQ3= zvFo7F*==|HCqpIs1`*oD#EJ;0$|R~r0;4{($*=A^tmZ-v%*bId-JZ>QQWE9bJ1eCH zb=)zFF({p_o`+m8Y|T2MO~=mD<7?mVtGJlAP#^Ea$5mPFHbsd7hCrz7xmfH-y)Z0c zs~qHxbIwJK1|;=5I{MZymGD8YtlYE$q7mRH$klRNPu{*j(QnL7F_KkNUg&+1ld@+Tn) z*5KK2)uB(fmx!sOC^1u5r8qpyk&G+!-l?GQ_>S#uc8m7y5x@9_N#veT5gj}bb&TcJ zAr~YHo59kxxnA(eqN?LhL7 zOb#Fcq}LE7jY$!=)HO%kPbFI+!d6S$mu&4s0=y|7s>Mq zy#{s`rJcrZ#^@$*X*v68pJV!Ao=}7$@CK&h;^R5@Mw*P{Bf&KTX-)+5&Dxw^;Zc`e z)Qb;A=N{suOO!@}iT1Uo#)Bqk<2#Pbsya0qM}&-O(;MM?f*NWKtW53E)DSL zzuIza*k7)zSx#JLR#p}$8uGt$;{UPN{|}DU<-ds1Anj*^i;qa#^e0xEc33|^D^5nB z%lwDNfDWCq>Mz0(8QE2|R8)`_qrIQsC|R0*xUJTPKT3O9PWl=-LaaPcOX&6 zg=A)RafjR2X_8iM8}@_q|EuDe3qry14(i;rG+yscH#x9yn>aKx#^6i@hUU|@1DF*y zgX|W!3MAiMs8z=i;wtNAj_O`}2xbasUUkjQc&M7kgC8LB)4X^w11j90prFU;5B_V3 za{EsrtD8HW1uPc=C)3*SjEuRoZ?{pax4}#n<6vri3Z- z__!O!1(b|8uZK~ume#?@_15Lxtm{sH-*Dr1lb-C2tE=m|1E#2ytR!R21TBVMA}{~6 zeBq9dC-NRJdjL;?rX48iV1LmOxDeoSZyz5gtjaD#drM@tQ{-Trv9Pk@!7bln(E^f^ z_P~3~`YkQPQFiuN(1@a=2Zo1@q{tonCZJLF^YWq+IF7bH#EQa0Ag&6b_Vw1RiAYPE zDRiN#sHh0lWQ2z&+Bh8%hl`5z{QV2)zyRg^I?i?F@ZrM%21D}E7=dJf)3O53;nap~ zkyBWV7;EongYzA}ftQbO7Q6@}0|OB3s}nXjj}Sakx4TU+Q>3*tfa=U}xR zIG$WyU7v@|%NSd}{3Au_$Sy5VPoiun$lM5J=I-5_4(1^ToeQKCEa=?l_jGSgxv9h1 zeP`}LZqw1wF%2bzN%R@3S0P3rjvbC$dYxvuvRP}o9n(3cjtM;(JEthv$*0%!*2(vf zP$*I4CPvlpI}1;-O>tz$bgY-o3AoBIY;yaps=VEcM70EdQK`4Qtv*`89a9RMdT;3Iab8S#9U0k6j4&D+%GTt>I{BhW zLLMSX@V=i@)dL5?kwWtw4NP6{PjVUMaHC+TVy0eD(NdyX$N^Aw5h=znx( zKN)$|VW~Jl+P%TiR6*t$S2OAo@RVSkYeXxB3XOQi^|U@VrrS$36R)1klwI3Vw9WXY zl?h>c34{jBI2#+Ag3!&&Tj^Q=a$0ZbcGcRpm4XlQNY`(B(okDReUd|>OB6aIyElkx zGAeI^rK%O9Jq;&|*&!bOsl&_rQUIw{cUMQjXs^)b%tKJ8g0%J6<@JUN=vUT4JkPjA zZFhK`Kle<{HW~s!x6<3a-!rgg_4<>kM$w(0hUDCOkU}&aD;<)OGWB31eG|mRxK{zE z9GTXuc%^dj;vC%6fCn5xKH`b}&K$xL?Fv zY4oHipSTbXw%{re4MWy-+gUOAH0}L+8?=&fcmZm36vEP^9R_Fwppc+&p^PXL6PAe# z5z!z+$Rgxp$TIQ~I03jaC-}i>SicFCSiyJ1+e2}@D0)Dra*lAaFWT%xXt#5TZQ4&2 z9A;o>xWzpGargm2obsqM*4LQhgeJ&Cj_IsS_@3>qUgk0b{k-;*!1(Tl#zsKu2#0OZ zPV2tE{PC};D4kjm@&dpXYT*%@8tYS9>?D(ljzV_Oae8WI#vUD2;k!LD-ZH(hvIG77 zn6BhnY~0mJuhyNIw+|7tqecj0977&L>^*8#BAA~E&lnS{*B#TFc5M-a({I~Ybpg#b zdLjN9j=bknurNf`;yjoR^z!!Jw)yJk0RaJHPLG^vw4d|8rxk)%{z1JruBmBhPJ+eU z-G%vsyEYo+-<21$aLPHEcyeh1E|tD+`KBx3kxlqcp-zG_3J;Z%3oZ)V9x_;9^*rvB0n-cR;}%G?Vts`K zH=;-75^NO?cv_({JF{`)^D~ULTLcS2^v{TkjF_s>FTURqZHM>h{)c48j>7&d+*iaQ zd;0ECAo>tbS2|B=UH2ta4*>7ERyryujVO*9Voyan8y8{&a$2g+{l#$w`|Ghk z^D7ZI5%x@OXWzYeO;1yn&aSCoQw#RB?x}oBUc0UFi5?ai{pkK!LHuXpxzF4_usBTr z`D#8j2C}p;L4`meVgig9bn7vy6`(L}%OjRVBm3Y(!47BARQq)3HLk;k($dnM9vzX( zCV{#!3PlXy%{Xbj3khLdzB)JTfD9fDrUKe7@?CDv*~G<^{>(L8xO3*h9czD|yS(@Xt}`RdYTL`H?_x;fR8>{?ce_0I^fhs;?$+H!$xu*fazuO2t!yV@Vu>5y zA7fujQ{_mN)}g|X#yxEtpIdIapx5+-T8{Fp@IuT!z0-;{aiGnQ*==szY$;QbOZC*L zxTHOGoY&l2mvZYjsMM^!!KL@>)Jdyi!RCH83sbTGR0!<|l_w4riPo#Bsc~v%a8VOv z3^-dd*-Z!I2G;E@mhRdjy%|1VQ06@fo|&AO0D8~4OT7=`8q)5_tw3w}jGDc3MS~2k zP5E7AV$U5yR3xbRxuQ=M9{H3gjlyQ^bwtDsq&&d2jvhUV%k3mI_z9;9&Lc%9yy!CO z(8eo({VQ&A-4x6uzo4RGj#OukLoIozGJ$z#yaBIu?xDKP?jUk#Xzu zqvHP$;J87{lZ8CTjR_ReIKsNB;W6}RSLzTV_&T+rj6xxi5}{t9XC)^EXqfm+JS zJjjO1j}H2{4m}#DY~8FAfV*%Rd2)!om1KVU@k;aX2KIf%N}PN{9R>uK#bg}ESgTuK zp5J6<=Gtm5=R_fzd{An+wl(c_As(7qLnE#mXO*k&^v# zN79s*xw0F{_3YGC3c-9YF}S)_W9-P)ADhj8gvnWnha3xuGUaF4R3&VPn^7WYH>I*B zqgTX2YepFd%mj5t6qXJYd9i9KTJ6cVJZevsug2tgzA%}?(Fj|xN&Uiw9pO2LyyndE zbKZ3Ay@*P;e{qe^Rn^~EKzu3RxV35^sF=yW8HZyz8RBzaMV5Ao8V0$cZbeU(0C_vL zp5DUyB{X+NMt0!$C{aCtXH)iK>uBzeOw*)ZG^PN#Jw1N7#SDEY;DTi|yJk0snjK<6 z^BCw%f2{^9>y4aFfMhVo0qM@a|4H{F^lJM0XZO&E| zsbi{-dUraQf|Wr>59)VKLe$H+q^SN}T+8ei6E5S$!7c5q0Om8_@vKR@%1;DSS!;!Tvdfzj;76d~d`)(94`F?7_g{W%EGZB}N07H+l z+fFO8l!lX$rbba{Bt%OJYlJ<;;y z^`^VVH*an>+GkvN)aTMZqQ8*6uU`=DoPw1xpNPF&4|(RhW-gK6sZd_ zn2@zyguZ?IQ&U)+cS8w#YpTauJ`sfrP?V?RQy6dr1D0V7 zq9p^Q-%h=E5>_OlGfh|f zAGlRl0)N5PiA{AOj)C%W%Us=MCx5gOTCvXKtmkXq{y6;1spv{v8_|2TK-vv5qHdG( z^nszxInoZ>L$-@J1$HP*MOX>=m^doiAEs7~y|_d6L6kjTaZ~qeK0lW|w3Dvf@=&|W z%GW;P%qJj2;(7>eN70y>mnVu+;JRcE<@oVi0CgyhTN8Y*VZhB3EI$DlA&_-wJhOkY z*~4P4;gP_2l7ZW`-@-| zK+U^_SP`CT4gfl2O~@kQ#LmpVXYXDCG(EBGP=aq4XnGY(wLqvuz zr-$E`>e2th-kZneytZBA7xuj=k_L)2&`27{P?`v-G$%!qgeaOc(YUt;keKnr{DWL@B6&}{62S|{n_>=UEkq6kMlUzvDP|dkwrq% zLm$UUA7`>gCR9^iOsp4vZgjkvpxQ{XIb5Y4+qIV(7uew6dmAGjs+-N(tir>4?xdvj zAz;%q?;kp$;k+5E8-0e6KY&*G_5efWB3;*Gk>F{;T4gINeP(W;&5+SfR7We zO718U1Y_h&CyZq_pvWW@h}i(p{s8w*Xj^Pl*t0a1Ox)HP$^=v{xzy$VEWwQF&;;H866j*S*T>{+Us}FBwP{jwr)|56ZtfNLPr0b5XzKb2**?71fp02_*I=3Q0UWy-i#rdE6rp3hQW z?RQ9to}Q|;Lu?jwFe1iRh5oJB8|TEUtCh`8z|s>CmO>w<|}#4uO0X*YDhu*{+!`>d>b(859)3h=KHol&DmqB3u5b8vJt-WY5rMf-*n-NxJR&N2a> zASKr2%e`|0nYS!b3zu^{w|8%`*Z3AwS|w0}`-$Npp5csXV_b z#+1|vrk8V7YmSCtrEM_66FG~zt7Pl*xS+7>NBz~Fy)f{1dQy-bJ}OpHm7p##D$tq7 zGe^B>lJDs5=9K#Q!fTL!k6w)6x)-;QG$Qs?b^^9Hc~s82QuU&MGV(cOk-$+&kLX<4 zBN=a-NY$k;_vdk#<&3m7^r;3Y{+`Rr!rslXE}7+7ruadbGjHC_=>|O0#ctO zeg!Kl(xIj==KJRtiOx2YDGlzZXst8buHk_Ijv@m(nXc@dyGr~3Y^rEjZsFC=FS?bL zwfz*-biXR4taS#qi#wt=2%1G=^S2{>(AT5D#X z{9y$MPz!)wL7jK{aO%MJpO4QAS(fi<5HWChrnBK40+cUb2qN6@JK%RjcL15oe77V= z%{}!Q5n4!=Iz6R29y=Ue1{B4=)F`T&=o+F&+LGx=$MBAa4ug-g5gv-&L)MJ$B!SQ_b|Wdce|;HQIInc5%z?241{)(XL?3ou`3Sh z%FsF8(T(5m>WO2)-BWVEQ#Rd2R|fDpoIsHsgs+-YL4`n!kcOUFdOBW_2ol8mSpBRd zXPZjdt5Yy{!6A3iMcMLF9c=pF@(o_#qO{LO63U29(a-PM5Tt}G%Dd4GcMoka8ZJ8Hmrf>k`ikT?Y(MV5_n`G2sdrX}`M!s9; z6?N90FC-Z}1`wZV@r{~M@Z2MSH89Y5HnMac(QJH$+$~obB(7BZ{Z#%sfE0~xr*JS* zK<#mo4KNnqhQUrxNZ_QD_~Vw5UG>FF3U@XNX484Z7BsdBouBJU+|?Xm$N(CE%fnwm z?+JQf6-7rXW6|e#IA4%6vnkjAJ-@7|6(0NT1?Zh4?Q^do^?bA&1`bM5Zc;E znK*~&$bI#j3}75fPILz?5cQhLC`+H zvUCHAROx_`R?QeWMn8#|dkz&6)$zlVld#-nU%k2xCnZ*cp$JjagFfqO!I9o6*sE5LX~T8OaAl zBL4bvof8slcgeFH9vs9D#eog@65gOjXu7hi^TK5xaxj-qT3Q%PrC=}hS&XgmShUtZ++;TZ!CF9H4eAgSagOQBe zF9-wI5j3Y<&5?oYd38vn=WW|Qdq#S$3svGn%H21Z>>;~gV>pfBAUb6KJ&?*lwU_`sGRlCAo)5>{|pnj zP_)48>a!oLz@ucOuIZquM}lGqTEPg4_28hrd1F_|f>Pov>?)DTkLW@9nzQx3s;FQT+rfVCbs92_*TLeN2?wt?b9w7D2ga<9LYH8i zQ2FO=6aD#y&`iw2Vgf}Q*Y(U~@{69%#((g7Mq7W_qG2>4~nWophTtT z4nD*}_-26z9w;|4ggy7OX>#4^n%#9{3Us4GmR;#HStA zEp~-y?ImP}ub);GllI$bytUP}bPS947Bko4#g?JWYaT%S$JXSahuvdBaB&+jSPo#9@S@#HS4Ey~aj)tkl=RC=y8HA^UdqhV|>| z85TJax*PNw&C2IU7X&z3A1E#7aN~v|PrexSp3}qoz%s)yG7psFFrwIi${e=MMAwDp zhcymtx&5RUiP{KngnQLAYRBEy#p&C1S>Mhr%v^438{ zgGa*MZUp=#7uS7a0&zSFxs@mp$t3b8DX)613@?0Bv07sIdVgh6rXXXKV6 zv#XGwL&l@n0xyiK;d%lq1ox|`s3;z2Kn*`uj7zxg)V0VPo7J?t4xL%aaAPuAskuX6W;goM(vGFb#si|w&U`ibAfK1K?pzO?Yj0{94|AL+NIl*2cq!DRI!RuJ)AuZUmTv6m^gDsEr$ z-T1^5t;>R?NQ)IqRW%|^;dxgBaTiXX1{{Z6a;}oBF(3akr@6UnG4+=}1T)}AdbysyxL)EVIV+Xb?XX!lqnY=A6> z23O#cG1V+*(b(7i(A^lODzFy;Cs7w(@#_M1nFG8Z#Er`jUf0%!ffELj z*o??xzozCgezuy6ceOJHOCmwdjnTFJs{Ijlh4Y)pea5ExM8?3OVf`3AQ*;?nb?((! zXGOySkA9$Zm*(0a_EoD?827+-VaT9y>;TSJKYxr4Mz zB6fqf0yZxD9YFGT=W8dlJtCr{eOp`X{#w`6y<;{N<2i#W0Fc;YWZ@q(v#`WYD+um*}67vzUB;*>c; zGSofPgIKjtl7S>V32Gpx+MSUeS^(NiW-|QWPIzc{OaX3s2CW|QNw;q$>Y;`l!rz39 zImigxi^I1A9MRl(>1CATypeUS=jJ)jk(GpGk5U@TmoQZemV(o`w>04dv)}HqDICby zH2b~Rr2!2zbs-wP$zoRZI)7)6EQCu;6M?t)8n?0s;3u@VwLObq-7W{;Oo(~+;8`1E z_ky(H!%1A=kQd3cX-sTfa!S2?`et`*e7rd_t{6>i?M+?l{8@|mz7dr}%=wsJICB)F z9_YX2Ey^gp5jHuSd5!ZeTVobEbId6xOYR|rV9K3UYq0iT0ILy??kCQSFnjR8V_FvF z(lF=(m>>$?laj^5I|(dSM$q<;N#d=3w8}n>VKt!6Bt*un{*VWzo6Y95$51ySZDH`n zYPjv#fIdAoyA;+Ms1;1=wO~}%WWoW`IWl=2DeQqzU}NL>6qgNPS@m_Yae5L??y3)s zmS+ut6kk-PelX?m&KhWthF{90+ER7K*2vlRASB#TB;Uy=RxV)TDoP|M+IIG*n+~3K(Iu*=OC|w zxBCS>QyoEklEgPltYFf)USB8i8TfLP>py`ZqGc}@41FZ3D`OR7>9 zjx}ErgVNF`eQm&sJ44(EF|Y(M&2MvPQVdp#e!avq{V|G{6xqM`Sc`4lzqw$@x-Mhm zsz7|WsID=R({G=?I}lp`4ZRW^ov1~%iujrRY=$-ANWv|^h=3IUQffsF9&ZD5WgG=BpL^CT;=T1s=!cNVNVoc> zV&WU;kk<@#pig_MOOUGaxBF)vEyF|JUH$0M?V}1}0$V@e2}MBt4ni%;$5`R|W^e#2 z+5MNCEbYv{ymvOxQi>iTqUPifPg~$KIA^rCU-Utz&nTSoc~LsdAEPU%C^XFHGdQT*VNemZpHtf@Bdz9|K~aUcn*enD9WIq zObw1r6u(i`*vS2~GL5)ZoncU|pTz>ce{a6SdV>6!+YN}=b_g0i1ayqxly!FwqoibT z0egX)#NH4QKrTT_s6}>Hm+O|X%@Um{I%~FVb6P*J{gF+Z&1A=iIhdHzG{&5Kc)8jg zn@OAN9jku*{Y~%AMyfWny-5%FamkdXbHQPw)=9Jt(sKYM#n5E#GH$E~7gJ>YdYBj1 zW}Y$MwX1-?6D?UnWUeU(pBTvx*tbZPO??R`xkCjCJaUR=$iPq>L|iY06FUF_-7g1K zM0ovrQ73N{&pGn>qSJ6 z2AOtLVxkU_${Xzo{1XKugztDccZTp|eY>HTAf-7biOt(~rO#>8L>U=-)*J}x;9P5` z=?zJ+;tl>pT?EJoS#E$fUO1R{TUejkF~h)?K!q;1#MqglYl%Ibx> z*4k-!`elNDXK@$$*Z|T^rN_s{1nc6Et`p^p7*2h8fCun;ypCc(hx!@WfiN6a!nt6d zm)mL7d(prS+%@qNv;Z{zX{iX&fl%2Irv=6Q%dtm3y>b|?3mFR;I4~DRZBAo!nvjY* zBTI5?D!@b}+LyJbar9y)^KL_21_Agnx&#VZWb;%%_-FRV5TlF)9h^@jc@Sg81e@1* za&dGpY%!-kEY?2bFMBB4@$^K?IUF=Zx`r~!*#inFOmi0}8j(08;J6Xh2YVA*Uz0g5 z;IKQJqj$`nL{`&NNTe_7&2JZJ#!`jrfKbG|-E0EFfSZB@$ z@GBIpKN%IBfV3UQJ}DsOixTCG49vQ;(2N1nzsnR|Ygj0#wK@EW)d2J5#kOZlgZV3W zmxW0Ps;H}kzUX|O{t+9%O2l{ff0}M?Nx_p!;RKZcV`FN{ z!zAM2(2aBwAYvCAc;q-qjfGg**!EU`V0R(XIcNF^6n|i;Th;6Vll2jfGazB!jZJZW zAb8R(2%e$NpT$n8BGo7W$=$HdM5_ZP2JCLSe|3iZY*hW>{ChaA9#%2j^6sucRpD*d)adKzkP?ifx3)FM>_L|$3Dy1k7d5!6>ttHe0H;AD zFAM$}HmgRX#1I!z7>mMYR8gk|RLqC)r%p=3QZf!_UA4@;T0R)vb*6cSmUE#fY(?^# z<)oO@y_b*>h8|uGFi*pQ2iSv*?tSNNrO@E1-VYDaurGyC+Gj>@LzHd@B4n5Bi_+2I z#)Mrj;}R+x8>hh~CQ?qr4yzNlLZ4}PCjuIbW z5caC39ExgJdld55FLm(+zZnKEim0AVIn?fus;@sBk?W4fYP~@8;+ly9^{!QHBqCyU zFiPvhU~~gf2~sJ!1cI&iGhZ-2AEs1UNp-AdUKc0tgWUhW^O(1D&L4 zx;CRuj(|T^baW$9Y_&%it2c^G7`LBYFAx-p7V?3y3Dv<*uyVu=i8yez2)!BtPtwCV z7;Fh#J1T|t#v^P&ma>lvJuXW%Kpv_&qPq8TP$hL7;?N5OIKGmNT3$6^{P}7lE@zT> zMv=~Caj>VqHjbGRUkqb$KEJ=+8P7 zbH38cYlyTz{B}13B_yF|ECx8hevY-CmOopsVG<}!BO-ds9@-#Y#7uYm=)1i}PZQU^ zH%}c3GGO)!M6`hrQkf>vz~wy&+R70_Flq`R<7G?I#1|GchABF~xrG>uH2;JG9x;*d z(i5@`+XgYQ3ar+bFK=RRAYv`6RxbOtz~tqopJ%k^4-TIcOx zMQdr#_0qfrB((1Ec}UF8Tee(%19$X)Rcjb>0}TYj74m0bd$1Lgg#E2r6wD8kAZ&S$ zQ!>e8i^MQx^$9Ch0vlr2yH9YsU{B%Tn^A)a@nKT{w0}`zoqK!+YsotLnKN^NqS>>v)XTN*HSX9261qKD}f=sAiM`Xi z(TlA7!Od&6>-3h@QLg;9XxYc%Ph6jen&!;XB-dU zXy_bA7uJ*_v;NR{u!m-un-re7C$m&m$_~Yovp%oju)LYOb;`;+v)wnM^_vsb{pSPz zj~`LK8agEqKOH!9XjL$}4IS^H+Un@+%*N<7!0AeBllB}>(pu3r@^RPPOz?i)gV3H>su7rU(&keMeJ%1mlJdE&?*3MAVQub#9&A8Q$ZmHiPGK@ z&d@8^*pOA~!XiWv(iFn2VKRH!_fJ3IPxP5_E(=vIdehJl^@r${DKE4-`s`?Tkoe=s z!C)g;g!3wfj9P~7k(wHMJ@-KB?%}gD*jjO}wO2$3Zc5C{n+AXixB>`?DLV$_eEP6* zU3Rt5CA+WPF|*5yhfNJ29I`aqRCIW+JZG{4BUvlwy|$yv&gSb$8XZUrIy*0o>l|P$!=r^`e2}2LC-v zFS4;i*m&7aQHFi78Gl<7d27FZckZJx2V{qlS$qc~8ZY2v#h9Tx%YnPC%`gU0Q0vIE z7$NiL=lvEchK7*m-`cIk{=?G!imqNq^;xJ~w6|sKgxCrG4SL|@^fa zvA^`tFf$eWYjm4%Y^$yyeE_sKgE6EXW&z$uQ6rXmM{H-Iu0!5_u_P3|rz&oAm(RFr zJ;bn-+qg#Ls*yVw&df!^)Z;Rf+hc;Wc21nIHv%&D6rd-rj(~tkC|jpcN*JM-JLFMH zEHzX??uPbi999r{ivHY}Y?EiPfQNAZ-v@6~LNAjO7f0>jji-cNNK8qQmQR26n5xk9 zAIV3J0yQ$uMTobEV-8zEtwEOkNhLHGny*EoGP*7De#IA>8b?DC)5?B4ayh6Z)0mU= zMqaW^`5e8K*16qF-XE?Iw(-@wf9@dZqA%5c!j&f`xA*HLxnxD6pqHO&kyjU<;#u=h zA0p}l2d=%}`j?W)2$c=`6cp0mp1~y&2r3+{>>wkRpDLb~WbH-g)Fx~z{E33rM#L_| zxgXq114e6t(Kpyor(gDZOpl|GM<#(J?;vKk3c@XjSo>u;Zu$%Xqlt${=n1vNn6SXrH#f3S*g;gnS0*42rGGb#}l8u?k?>i^=lDR1s|$bXYipb|ycY z8k3-d|5Knx-+Ar`N!!?a_f7zhXWDjtF7Ha^d1xXyAzAJo+1KKb-USR``n~n!fzT5h z&w`H7a^#-Zxti`|_rb)z7MQLTuG6{u{5%ANMZ~tqyA_{ECQKGjD}a%i14$#m2Xwer z%6*bYk5bIN{}O^`mEMxQ{dv>F)9NnG#PtIE1_|iYpvknuh%MGTp+qQ6!7VNajQ|iv zP zn?lx!U{3jQD=ECVF#p?aKjZOy$0r&>TciSxEbQIu*9b( z$2~@0n)pbBJq_u5P|W2*C^i6MA|m3W-N#VNYaRCv`3jf7+n_b(@S`HFkFsft_xkDq zq7UkEVoXkkQIur#-KFoH?kk6ug~56pNpOZSa>|P|BRw4*mWDU9Zq{V06<#6*<02E= z3DLkokJh^jp?YLe&1&To4>6AH66`_@7=#@#67G`=7Uij-AIV_gW76JNwA9)!rTxU( zx3kC8m1Cuj5&hpf4I6{?A0bQ@j-Q27a&RGbA2b?Hj*dn0J{5)L=mDZ%*494+qRz_p zeXZ^79-kzl&%#?R_$p!q8fXs@46NNd4Q$Po0fUp;BN&Ez=B3q_O?rrizy*$*?o4_( zPMhaJb^jF+LL1Z*L&gQhxvrxgJOrwb*&@?}zL_{=NBOdTfi3YQaEw|5mcwxi| zPkpeg5U{4D(l|b0hSVtWr)>??xvNTd)y6LN)pi&TA@xR_CgupmiE<#!PNT|&mV);B zN!QVrXqm-6Z`Cfn7TNc43|$@HVdKyn^6Lz5VK$Z}-Z}~z_0Wz1Iu8w)p=0sgbmjKR zQ-FsMy>u;XybS3o#!B|eF*`0FTGotiKb3##vxdhxv=E%nYcApNDr?rGllL)+NU4>m zkU7Iy@0>Z8FYQ>tXJ8XN`mm>ZW&AWwF1-<%DPms@a$5kG>=>MHd9tqEA%jEMhdaGA z-bTan2n}0k%A2;iw`>Ciia3b5qxEx%nPC^rY)WWjZ4n$HB3yfjF2c9JvM(UE=6de8 zJADb$1h-l^gjmrC;Hai81e1ogyG250!K89e7bk9cDb0UMx7jp7f- zKyEppt1H`=zrZv)8b&wZxQ1{p(R+#xJc2$!aDa;9(449%mP4a&xMHeSKJnf?XgGiU4zW%v~$mG<&fAV^lZ)G#v;_y z?KRTYCc_Isf=`qawraZI5>s-~S?cj(Hfrzt(F|ty7V7wY*q`EnWhm^+Ug16o1{ddm zjo=-}=ZBg=9(!kqXi-PZCSfiSXxn zk4+h|m{7w)h1T2E^&l~Eac>`v)4pnfiPXI;*XA<74HvS{{8?pnUc3mn$THnfPK#n+hQPxtUFh7FesC_G4Yb4W-wI^lw(Dx&F z&{`&*)c4e955wa~QRZ10<}D8rw@sK0V9V1>OF`=&dgE{Haom$9lp4^Bw|46^=)cz6 zUC*(!-O0V(2_O*(*f7*Pv6`y8XgrYFXm4p8R1f(8rm-$GNH3TO$3fLBI(H6c8uAmq z)8c%{jR;>fmCI?B_aBq6p4}s}P&oyFRO4iQqY;n|ast^t?LsjCGg*^Mf(#fip)s;? zNdo9cGLs)0ryO0IXiC!^fm1@OmFmox7N3vad{VoniI{Y?9IK=rr%iaY37Zh5IrHQA z-C@jPt5+Cr9Se`jN~8v6Y?L*tMohaN?>nMC!56?N!JyyU=wys2b46 zML1&Nzx$Xz&yU8|2Oln4abFZ&o+P8($l;NPfdZT666}LdQ3!w>`Mhkq)`4*cE5nJf zb9;`F#4v=rn_Ee#euL}!Y2r)@Fb_Cjf7d#i**G4sVw~C|mK;#EO55(m?&f$+b{PLP zN2JiymxYW(Z?~_0fcRa1+LkAE^3Cn4cB6la&a=LVG)-u# zEpOsF(l<9ZH~2VC1R}$4-DNxc3hcY!$?ZGYzaYO!2Zbcu3yt{>3u>6EQL80@R|0P- zHNfV(^$$1$_=L#A6szjMjSSr*Fod(sQ-1&+Eb-4HkpJGWcPO?nuCl7?3HW-pa(7bt zdZkwfhpZ2pN792Yfi#98_=|Wq=8hC&3qagpV}v6m_~Y}-CcruR18E8<^L#}}SH?I; zUsIEefF&IL7(o^Ll0uh&@uDNJ5$NT!s#Q*M*EKXWG&bgPe@R{1!X4+QI$LP&SV0Jj zF}_@fQjeCB&PA5Yfq>!8iR;q?CW@l5L~RO_QF;Hb2y)|(in$hZzaob}nM%(-O1Kii zPletgAj-o%(yI*9P6+(1c@Vj+%tF-(vv8bKccyL_UKfct z6PNM1=fV?{7Y~C+t<Q5%8D$_{T=|a^B$WRM0*r$C9Vd zSv*Tk-K0+4)pD{{xx3m=t|N#(crjyPv*Nufy##>Q%(A;qO0Pax>S+^0{QYc7PChf# z(?so!B%F_`deo6$k{Rr7Sk zL-hw|>eBa#8TIW+UQZ(VhPkbmxj$|m?mPX<5fq_*12$-ACpo(DjGibjPnN)1L-Qc0 z@TfaLL}G?aOnyN@Y8Tw?xAhVjNK+%l@&WQwfy|TqNH-1w<|V||q3a_HhYj$%#Y59$isY9MLgijA2Dl6l?gam!MHF_chyfUu| zV&?evKqkF(;9loD_3AgTePG;pEvQ(ItBl)U>S2qZ$_9@WqMAsiLJt}+7v;ItCB+;Q z3?ZQ{WBG=OJM_IDk*LApLNL3z`DqGrGth_vU9D2XVDRRKlanO+BRbcfu0N0y(zlR! zJ}Y%fUD6h)&V-9KW(m0>;}DPPGneR+TG9ey%4Gq`xout>6mddu)GS9)_1<&krAfYn zL`h!zAvkp6l-yHZQ-lljEF)2deP^d|!FnE<#Z>(Cx9DKYJ>HC;q2PFe(HL-80M`g4 z+9@}Hz8zU!i~|LW-@Sit?C=tSzc8`;wsKNc$SKZ|qHVcBPu0MVVEb zW(5K&4_*zz+5gVzWl`@cE4B3Wh}RHYR`WO<(h->}Mb2RPORhYNrr$*%9IVL;PxKLRLsCsj^%6+tJ<&ia@ zbTgHQmM4L+jeiOMZoiYq70Wk=e8DkKDA>T1(dK*m{_Coj=JMmUAl>fqbB~0th3SKO zjI)C8b)fnB$eTS#_K;^o%2398cM+1NRkc1>bwD&}e#_Ay(UP;a&YmQx_^yKNV`$)E zcQIT=ZR6FLh9`*3h`7EskTQkzIh={3$UZqI>_=k6gnAoE^2p0;=a}G#1Eya(8XE{7 zg^z78t3?J=Bxt=wFv4m66xBD*ab(1RN&eWI+sdRBDeYei#aIR zV+D57g``TPYG}ZavInH#=T{N6Nx~k(HGqKHdX3gH3TnQO#xS9MIY&kHnc1~Hu)G?< z+fJOuNf{^5i3Dkq*ezo_lVpnsW=EWsKRUC$e-;afVlE=Y>!Y$_V%DfQf%+E*a?3mS z9Zn_H=mcWtrKFlH<2A*x5%vgX-j9uRb(iOzCWBqK{SmaB#&#Cl5!wM0O!)@cu34Umc&(80 z&;1f4t(*<-koNay*;(gJI(|8Wd5PRnX*j`8jNI4%3q!NOhA!Aeiad~~DyysEYViaw zs&5-#l#^hN=7<@Yv<(QQ6)6(jRZY)#PVEZP`3dtJ%o)>FfB1(3b_qC&Ko?}N(#Q%R z!*isvm22GLK?M|$%*kkJxDM#km)l*EkM?+|b^q!|#Nx`ehFjwn^QoK$-A8}+#Y_Q* z$OfH}^g6(s$DxU&%Hrh7Q7T)mCTy(bni^qeCh6n=(kEF#fBpHlyg`G!J`X1%<7H~8tMwLtV>^3=OL`gmKU$*`dJSY6MD?37V#zp?R@ z*i>QONM)cqYk=*G&~->?nvl%1wB3f8k(Ulz(kC2{)Iv@NDTz%dQ5Mid0F_9iMc6mx z$Lt|Qrmi4Xt440$Vq8wDDtJVw8;z}p&4?AEkCoL@y)&&2u8`0anE3ZC6f~v)SM@-{ zhn5x$7v!s-sA77~gPKdMeSn0)0VbsKxA<9X)zhBL&W4YT(v&GR(EVE+u`_Qu+f2YU zxpJ{}>wxv;GRu`Sa##3Pd`1dE-IX`dCRQLgoWOz5(+N@+BE}kmC67eNWf{}XPY_#8 zkG_Q-%j8C;@jm)8(Re^IXUeV>tJ{G_A+hlvaT=Vlj_qlj%m#;V8fKyDK%v=>BJxah zRJad4ZZ#A`JvoZ8A587YVu-8K-kxqU5TRMui&{Gp0%3wUwGUL$-swrCcF261%@}zn z%AImUR-1xofREgj3erhmR)R!x-Gyz9uBcPO2o{CZbPAOKd}`(Xh-kBJ_qJdPk;k3l_;EvfA8%G2ZcsFQ{0&6`KNZ3XI<6ILMR?2K-3 zL0>2LYb9!^>p_AMS5Kw$_$0X8pT@*72h=}G zb~%!SkxMRxdpgnoetdkp##E~Yb!ma%7#O6qmT5G%!Lx8j{mwnjDxHu`zl%P5Jvb!e z4pc4IPEe+iBnQ`RkYP2tl;WH6F=HEl#5EK$&MTs)N4TfWEVAE#Zcc2guAs#zy)Ds1 za0sPM2XV}GIZs{prKe}=X5Q#c;5pm>Ce&I96a9<$;YV_Q9_ErmtiD5WQ5};Lp^LitplNic$G$+5zV}x;|7YFW6Fh?rF$g!@%rRM6@`mC@&l62WsL)rCrD=aleuO;Z0UW@e1C=;NU zTu5Ko4lF*`c2y5%Da5J>XQ?4L>qpjh_VgqHegM>JZC&B5Y>4+!a5(mP{#s_cZrp6o z-`yagFVb*dnO}c)ZzxtfFt^|W zAaDdGtpncko}an*eAn7_PNd&b^|RkXvDt;6O3c=S@#_XhFR$xM7c5d{_h0AurnLm^ z6GIo5xqda|=y=MQ(U6SGhnQMa)Cy4y`teOL`Vh~tm-$1*`Lp71{T*Q%1=+iF+X|ZDgr4eZ)g(C;}_3Uxj0t z)&}GOgr0{7%_laU#dQ|{-E}^II$L7RTD?rg+W)c6Zrk1;Ak{4*c7XKTLlO65a@Kz`g0&jBXvfXyR#Il$O@HKd-xj)r=w-t$0+}9mkQi8l+jg z1E{vPX>084QAV*+!qE<%7Y>v-10G~oNH;)9VzS{_=l|oAJUPHtUe+vPQ9I9S-`qRu zSp<*(u1sG#IwVORCW&$!KLiR<^c^=H1h?ckY->Q8hZ1VJ9>9-y)iCC*OQmsozryzc zVuM{>um-S`aE$l^3Xke`wfrRXLWH~Mz=u58ly41b8X)CvIpVBRqfFwONh5HR0v>n` zEB46ydtfh`^2M+htyYZ6I%xLWKwH^u-$%vFhFjStL zhT9sjOX@x{q-_CW=3;|@I$d@RNe-HKGdJ2_CR$qf%VdbO%{f3UqQgT`nD2pfK0#1q z#Z&)^)1fEEh~pSa-jF~{<>8A+x&jc1Ou6Kv>1(^3Z;o3;4Pz`=tif1X!W#*HM5G*& z5jOrvTDb>>JXPly0XBQ#7ZM4D9QZpr29C&#q(St9#;)pGBu)`5$6TBt{ejq)pd>1x zgpeifM{SJoTOf}3Lby=k;wkQvV^s%kqD4c-Qsk)A7&t${ zqg?VC55n|GLiZ*d(8t%TT)le9#x4tVnj!2{mX$(ZbjDXPb0ohY0l!s?z>bVM>fCOe zLZ;59OR(N-`MZcOUc{R|W>Y~Lze2>uGsn@`BC{yR>QK)StwpJ+mzH-1@D5T`aCjxM zlTgOJ@wq2_Q9sCTk6o`(O~chPal; z<(IR7IAld`e^*y$2xT;&Z%yb@;7RGv9P%8kh+-pN9FzCqb!cgcQI8jx&wO*@>DGjZF}B_U%Axf!rHhU5V_Sl{W+g8&UmSIB=%^+*R0SP{HcVS!&!tn<>V zy#X)_Ee4t_ZZQ2;D! z_eQVt^?4rCz+VgUQB-2S37IU2!tSo)kk@q~%+twSf53SRPm8L2i8*o`jUB6tIx3HL zM9yD88L8sb5|l2i^0G_1@Lm`Sfr#5YQfSl58`I6$Ov;;GZ76FGewhLs*&qE;Mhp=BXGPR@yBpmjZv(Z$gH3Hgv>4>-TG?!sc%hlb`h#FJIOg?KC`|b-A&o5+iF>|E1JMTHhgA(=J||4a0%IDm)}m1WLiCG$%UFm3G9Vue z+XgsT#3`T9JnDd>&%|x;U6rz1^V1ll@kN(+W3f#^^y1A{R-rMiDV<}_zN6;ucS#l) z$@J0@66Wd?C)g`cuoL>DCONLy5q#q-phKrBBp7GqXGzd?fxw;d(tmIdP!Y={2+ z+q7cm-O0`#N8QyQ2wc+i{1e46p~@j9{;@;a2D+dLD7w{y-2oqndw2#A#vHYL3E3v8 z+w1Bkp0j9pRWt0Ozlpz!<#>54Hlf*8KzjcX)BX70v@KggE?yE;3lAMSQOCft?X}^~ zi3g}W`P%FK3eCl2!VL|PTFNcqH4YQjtrHC)!UyZ*=Kf zS`ykQU5*6T2$PQ*8%=a{LLzkHZ`~rzprd(c5B|-M`Aqxn7ECW(aRlR2&;omNH;In7 z7L`996^gtqXsBh<0f!>8K7-R1M-5cj`;cqfu_Jt=DZfMmaOy4uA>?CN=n0SKA!*#h zy&1O+TbBWG9q-3XjyF#UqHo0zf1>rizTWb{v#nEc{gEG?oq4@mF7CbYfHX+(X*jGs zJO(g|bebe4gf}}#^*mKM+flYzD20>)s829Uw-h}oYM4li2^z-ech~;SC zA@)gPbxyQkN8{k-P)q~D)Vj|r#=Vu7TUm}j%0!TBdbw9TG3&ZrlIjE2S{dr`io9pV-8c8jFgx4;N4ei`<;CMBu@QNOc$&f#Ol)tu~S*&*u5&F*4-|jF>p_0c-LhL{ul`z=X zf;frJ^~!1X^m&(M`Zn8seOvM_H|aiPH4#JN{!et6+?N#gY1Z(hag{56cq?XL6AUtfPiyXJ(YPt6UiJBuTx(v)R5>f+JZZj zAiNQ9@;w|-aC)iQ@;He^5eUN37c)Ui6iJ!k66t9ISR@}`jy{9f^{sDJjg0D%u7t?@ zOhQSsNo7OVa=qsM+gsyMPKm%$_B1=7&$5b&X-pOjUA-#>2O!#7e9~qlY?kkG&a8SG z16&w5*Lwf0T*l7lpvys~)>7-2q$`1)O{I>rj9auip69u*EZb#}8?b{BzdK#3?IWsw31FnIczI$Dys4Z#T;n;#M z;I5S`nxMqd-XwQ|^faBp7#zk)itK9F01)v#)mFf(kf)*vW*YsyZQl}xycqt0Td?F& zT!UALg5(+WrI^fj@}W4M7>dDuEL5rzB&Ae!YLRJv>B&Ffo}LT125AxGr@ltsr$LaS z#;sAV*Kk$=URN{I4v+S-(KNk@Isj;O@kg%(b=d6K48%?{B@v4Y6IOlx@YW+LOqsu_ zOJ1=l(*9_fq%1k%>_zA*lCeS?_i3`9W!IK$PcT3>x7EK7dWfZr-b_Ws98p{WYvuH8 z&uhV-9Qi(Z&S&jocNdnnfIEEAsZXkWY7r`FC1+53*Q^0nZ zd^w2>7I;ZP;%-03F!1Ku@t*g&Sh4pZmnW0XoSP$nP4}6l%JO$~b>VbptA23s?Tuvt zLNe#z|LmNujC&xhAoSN}XdYTeB8iv#sgF-ZmWXB6;#8q(7W|Iw8*sgjh!K*EvV&z| z*J+p#-yiN}imogN;U^#!0LyU-G=Aund1XSg+K4h8g^aK1tMDzts~zf6B?dm5_Ti*o z6mrSJTcWS)afOcr>^xaD_Jq6Lf%zeR?1UTp~5RO=o}-(Me+X!4cc^*bd`k!5&0 zV^cmEgZyE&5j^=_H->?2!86vR?kp2WSA8E5A44h*u@$V!L;Zt96iHjv)yyM+B_M?F z2ha~C8nauy#qgRv)p98yURk4$fJq$RE=Pwr-+6d6u}r@J za{^bNwQG*|^`(lxIp?Mg@Y{QDvg_>~MROE?PurF3>=_P|+qI5-xB-mC-~45wdf(gY z%PtpO%|4nWlY%JM$am9Z4T=E^7-mqhrc{-e>!Yz-9lV3-|#T{r^qbuzxm%6W?iwq(BRG14x*7wF?l{XI^Oo+1@1{-dJ4p^3AS5mYd= zaBy~E^3zo$2}gj0A|F9{QxPrCJ#UBATm)5BYG+wXx#2liU{L-qWGp=Gs0VqoYRLeS zBk%$HjZ%Ae_L2RA^o2a$0`z0uQ?S|B*3mPv2l{{0NhaD#fo zs|Lv8yd6UToIUMJt=U1{EN!#PG!(u5vTZ<#{Cnnq8Uv&#N3VmR>>T}rrJTWn1IyF! z@IUO`C9v{h{XyTi5ylHT%zkO)$Fr=n7-gc|zF=OZkfMBw8YZN3UBoy z*eAPWh>60?PDoD%D`(k9VhUQq1)qCy)i2wYwEnolUw+9mIzuTD#W(cH;a}v-Mv#aH zGjGklN<6U=GI-2bBcF>!M&HNw=CF31dh4?F27S2{P53vJ0 zP3K%kZ4v=nUx8Ps(m#9vt98pFN+wS-za0*Q;3XcnZ zRK9qw(VD+o1F=F44NMffKYqQy_-6`mkzYnp-o11Gk5`F{{c)j`m2*4uUo5jY;C&%K z*2OQsOgw${-&kqAD}P;SKi{Lf!6(WUBUpVjx&83_Xi_I@%x)(Z_&Q`h|LWCu&%XLx z;a8~=?>EN)>i-vCEJ-jQ;RwScKlSo|yVjzpizwQBf`2Rf|F~K5w8wwEz5K|0BwDS1 zPT;Ry9%NVm{O6y)20tb&r5rql`)M%$v2l8S!+r9wkj$i+wuEB65Ie2{lnjlb>`ShDara1#3?eh5;~ISsBOOAl$8lw4AwY0 zIP9TZk;NN2Y;We)zT=cAnlQJ^Q3Ub2$AP~_=w$0Cd%RcIDXiV+t_>$ zOc~0qoQr?_W|5xpkE^yyBMa%d$=Uy_SoweYz~&Ct|6-axd-~idx&iF(pTEYswE`(T z3t9hb0k&wY{14~iB3Q^?`McNMyxYH$)5Ak!HBO87ZNzE}!5a1d8ZM|veyrze;9|Re z1VegWiW04JuGbfi9p|I0zWnC;%*_O*{pYW-)0zuCA3TO#R_`UYB1{?l^$a3s$58_k zg|zcd^UNNMKLg-sclf{Aqh$Hq7~%LmLF-?xpUI)bfAbgrA8hVswdtoXUcbKbcQL}) z#NJmeXV0+6Jf7?y8~x1Q(pID8WHDwi^&GQ(OJa{9IU$_58hO8;6P_E;fx~H|^5b~7 zNt`+EL!dpI`TBm`m$at7{>OcHWAuOcH2W7P7#aSd2tY^hU%aW)VGDM=CwEkNN3=uu zdgEbZeSLjxZEY}+!QmP}&v(Mrt$^IbZQxp%VAz2c20An7F2OM)bNtkRK@ffdxn29S zG!wB>88NX@v6T*k{ngM#2{WU2HKQua2%qo*o`v3X#vAl%-@S7{-w-Km$>g@dn|fGk zNE`Dzw5RJgbr~%B@}=coI~yy;v7pt(FDy$3m_cP~=1k7F0RU;MjdMO&GeQ%oC+MAs zRG?C~7WKSLXjI~)q^gtDBsg)@$D_1F(2C2A=d0O0bSWS{6$TKED_=E3`e6@jb-R6( zxo3+vyNB$C4)mRY-ahTgmDvAMIAJD&sV=C@XRiDH|fziP~!Hmgtoj8kg6aCH5j_Q-3t&q3Sblg(o zgoVZWb?d?acY(12p2fr!7yALrGDKc-y{ca}NZXA{e#NPeZGFYpfG&R%|%<;2HwC)BmXW5oO zeYk$Ovz0`UBLt8!3u{Pn0dKXaS z1g)FVE;`yjN3 ze>#}i?_ycW(AdcGn3*J(c(@xwm(}}wBwS3l1efepGI_^4)akZ8P+>+1$(|V~Xe@%$ zVRi-Hry2z+eOD4l3Dl;VrSVb@D?k>Guw|UDi$ves86E7IXKtsMVXBK&Vr86JN=H#$ zL>48c6d3B(eQ%~n3DxD;g|JX=EFuN{f+XC9`fX(uf#Rq)N>l)lQ_aEDPi~PBGW#)OotOB|n zJrcO$f)PjW4rw9s2defxO@2K#^RJBmT2+#yrt*&ayB(s#?^wvU3D6JsxAvTwFTRE( zlix!d`nWTBkT{dSgV@Vwa%U}&f0ESmO?@5zABBbil?$g8pINB7%s520KH$h(AkaYf zFVe*$&Fm@{irgC4h!{tsV^(57j#bY(mc~pANYaz)FV~sxbDR*@LiqZnq@)OlZ<{$C zcrM>OPRaMWg$py?(6*RduIdRZyZICrLMr#^{~Ifs*zz9OTnSfg^zNWNJ@GLfJ3x8z zkvNThT(#by=I{fK?chaV^NEUyF%n%PNDhki;8URMkQH0SWc>D#|NV>%$*D9o4p+2G zFMxT3I8u6K;MDDRI>Yb5zga4AYT44S=pe0GIp~l)c%R>Q^FRjGo!X%ACi=?I0~-}f z7V2ZrN4C8`T1d+P`$UW>K)9jwo(qS=e%fz7?Z!a0tIh-q_}_-O$zN*8T{L7S+6CV^ zCvCtT+0@sy{zeh_Z6;hyl*ms!-fHYAwesCN9pP{w z?vUK!XT5i}T3|Hq5iCVQ1)2DmH~BG0mLJ4R{l~}EBBsB^%XfEluxD1FMY92h@wmx@ zbS{g{h_LujaS@_$^+N+TqsrmD>B*y(C0jUO-s5uZ`wMu5Pb)flaPikSjM#kgjk(<9 zXPa8M@Qea17uaSDN;cZLyOB(Vmo%n@mr)|(q0QJMI zb;^3;1zAMfsBSWY!qvcU<`EpBWPHcBzpiTqYZ=MDXwnOXcn*mC5cH_dQ1DllU5Sn+ zF6KdlU~m|Z`jP3bFrU&McW8bF8i+L{%`2tXn2{4E1XbnHMEI9?vAl6vvIlG%! zXLoz}R_~E~N85j!2ym302<-#h?-Q;`gZB|4WubVb?J2k%4AJ;H_%1j@fJ-e6tgsnK zQo(4jz4BwkxHqv!M&=RNFc5?h@zsa6eV~ggf9l_$V%9B^W5upvftIo}$EzC~Tg)#S!#_}BtAW9IlWQK@b#)`-h;07gCUhq31vnyp~XTzs6qV9OrXWgVh{a~u{;&^;Y z1mC6p9O=r1oo5Rfwu6R@%BkNO^a>p(J17FkG#x(VGV}$Dd=Dbfuiv*qZ0WE#9wukN z7#f*o3{BxlhqT!%cmrjaldq+rU-d*Z7ViX-^ebMGIp8s)HFXoq7xz~XRj^aUD2q5^ z$Mp;W1A&^)xaKZ!5&w9Y(-}6~tT6@?BZwofF_op^ouBj;S~1#*0d*T9E<*>tp=*6B zSY+6Lbk)KUuORwOL{}A)xncOrZs`-%nVX7-pMN={pts3S?=o;i%FJfo_Fn^~&YaruTJG#w!ob72``?SHan}+=RU3GQ4+iRVx z5Q}`x;1Fow;S-d8U7pkKpz!MiU$uLo`xXSma9TILVuwOTb=IhCS35J0mbKpc?QPeI-%h{~aU1}zZ7RqMRn?rBSRUVDukeL>6srMWdIYiI*~{O4keE8(;{Yl7c3IRL zNFysNH-0H1Qc_>JZ)D&}9){rAN|)fePy>K-iS=LaEE={M`JHWW#2x`%AyKp#9UgPo zsuc&{g*Ri*I_Q5EX~4M8pZTTrX;38KhP$K?RRb30bakqP>$S*?Ji%4t;w_@rY;FLj z_DlM7Ur$8EKL5F34#+w%GvjhdOLM*(;oL$zJ&2-7dPyPh@I5UnlB{$34mkS-Y52Qb z#pbPul-ASN&%g`K$N;^$s*IkVKDb-1-&H>%NplbKRiL=uh6STKaGM!@|C zxAFkV$ZwqHe^|(O(n7(a4Gm+WFuKP9Bfy;ac+jf&a(369W3I?J1LnF`waZ0cCP7pk zyxeLlLT=Vyu-txaGgj{wb3|B3VJ;271Fk|UkXgKB^hrT;q6wHQAbGBa~i_AA*OP{#|La4`eJ8LAv!GZtTCRJsp9n+oLx^2 zo<$YVeAW|Wm=*l1{K+ga9mS_z&(-9$jvPzsTX8X|^0MK#XoIBFv4n)d}+qbnWFHsM-<@$8*3~hI{!@K6TA_6yomD+>VQdQQxC7#e# z!C0hHpL?WzYTF`!3@Yb;vG?BbT=)IkxX#Wt+DV8I%8Za*DH>9eY!V+Tt3EQbDv`4H zRz@}@JEKzesO(+&7#S%u`**x`Ue|eD*L~mL`~LrTeIAeVpA(lY9LE#J z)UndW&}b3+a6dH4J-Q*VTLgdPdieg})sKVUZIriOXni4OF#yf|15U4qbDWtqVk`G_ zQyIxGRk}f{oTuhfFvg>(o&otY^R~f|1piI!p0^=QB5Y*fo%r#4Pl*#)uTYwT8Hj%i zCj%}T$1Ot&iBpW>9Qj+~rOK)2SURx`R%t6MmXJ~jH#MnjP>LaOdi~_#Ck`>Q(CN;b zAxNi{z6zAKow{?_Hbg(|lw}&$>E!NFXx<6Z<1C>(a@}z-*34EABNAxzc7J(Gm|sd4 zKt+SckpK#3qtW}b_us$0ZjXVM|Jb4%228DD9E{|y-g?3HvDUvm(_iujPDeyw?cu)S zH!Eva%guLrZd!#AAX8seZt1EDrk^sp?>i*qm^<_HZZJ;e=T@Ng;}k)dIp+z2cED{S zbNKk}leg|G9(e@T$h(xQqdg_d8#_4XMV={y3XZq>D_47%Sme{T*zsKNHh))H4H~Zc zuVu(S@-}dob;1$Evn74wE2cIDf4h+Dn?bQC7n+GP3+hL%M38=AWA(1uAM{K3#eT0c zPea-NV+f42%^9EIKuHJsl4Nt)7zcO}qq76i%QDLY%N`>w8|1^ZXj9|k_ZKp}T}HRY zFEESA3ceq<<0kC2kW9RAfyVe`Z2mv)I{c2PLZLR%eIRmr&xxM&tGjy9U^r#Q-d&Mm z^cob*(j&1aCQgupg2P$Ae^kd-YBjZSNA{_u*NemdU5W(A0tD%L>M6lz#`~z-jCtiG zP4f4MyqyQWH@a`rKJ%0P(5*%MjIAZ*j2E8X?9Z!ur=b9`#_@j&5)MhSUDV;;!W*b| z(P&#=3AwfGUP9dfW>1{yGzj*DL(>w3Zz>uZVH)SH>OF+t*AX*H#J<3t>9o$9! zP&Y~)_<9vyHA9k8N>v9mkoN{&0GAa;y)y|P9wx=$B$ILtJX?}A(C4J2RJ-{XyFeTnYG&MrhI_;vOYh(FyO z9Soir5%d}M5DuWdmEHww3&dcw3}d020ReIjFg_d(J4Es}Nm&iFIEZ4(%;j2^@MWFL+%_1XJ=2-$d`n05SAlN&s=LSaMhQLD>hDC^C*b8JNfxaD$ z`{Q=Pp11|Gftt+_*t~JL<~nHEs-&v5+F&!!^f)F8tHFc)U+xKJ1_u~1HJIbsFo_ zdoffX8uka3y)>$v9tl8oii?*p9^|}3GuHR+AC@Hy;8=eRB@^Ca%`bQIS%VP-l`dHK z`*m48Zh#C<5U;R6F+>3V8W*E6l4KSR&0bSvQAZNik+ZSz;j0`w^CdNB#8!RYxU!UN zZwf2P!QOVP>y>C7sM6mD``slhQNbgah}4bhVvddtZ$cc@=1dY+Ch(`MqK9Pn3RlSI|N454!z0HupW@)K2sy-KNoQGE-?rQ zL~S^-E|jFXxgu{S=E74_wA|X<4_L*VD80}wtH=H`;xqsv9MnmhAN-U=E)Q~`_eHZo zmxi5+b%d}wE0^dC`Do(~khVG(W}RoF%F~BhRL-4rpEWPW=2@u3k2I*WmC+%B)hqHi zBrmCf6e}gjbZB2cDdY67fhw9N0&#)VNP8ke_Afzzp3alai7uaqZ4p{nAl$DnnF0s|gTV;3wUMi~5ISV$xTTx%?Vbe<_Wt zSk|qy$}X1Zu>^YuuE0Nh7R=|rOLJ1?*P{bR?N6X@_Nk2cMk;dXybT#|x55Ev&R-vv zSiI?@1%-!p&Fma&-F<#%_{!8nmf-lV>Vnd-G51)x|FtRfP@?q0@xu#C@6`^epn#yH z5e4bd@9tw-P@X3=mD@5aaF$2LVv6@WM2PiNnZ$I&xQWq6?%QH-^%y@a8+`k7bOog` zlWB3NAC>!_vtPpT-G zHSp(^w}Af$)N17}{tY{sT0vN@9Jg2jrwBCEga66OuWb(tj{xf81BSxJO}ifl*{;R^ zz7k`x?)+`H?arM94&>@y;>`L;b;YdRr@Hga_O35~vJg#1XFc?}Cz*B-U&JA9{o{o7 z5j;ks7Tw*GzQtkP3jX6>$0YfYNs)>ijj#IaQ^AF0aMM!XVf5%?-3st~lz2f7lBi|H z(VEAPc)zDO8m)lIEW!ES3DQyGzuV`#wTDDEQ*y2M?q6!!!(jCLy^n-_x6VIrV}!o2 zCGpSSPKu--kEr#R>8^0Rs69MQ{IfpB=~GLsgb-3jG!))g`Q~CT$bSL1bW86Ahi0f7 zEi#v8;!{0Vx~Jr(p87Z_D&}mk+6XqtZjPueRvzVzO+871)SI;W=rjl@ zEM18yv(M57Qytw}R(`?gz40YnxMLG8JeY{Q?|Y7&*0vK{D)%U`TWRiZcfsGQrvKF? z2x-jNQ_-j2w{or3o#pH+grqbK(M!@8Kw#a2EZLqnA-Fl4?-OplXtNz*?of8QRF zQ=UUZ#rJU<|5xfX>Nn?4Wm<<5*j zg$5V;v;TnwJQlujd*-}C_nI}%BL@>xQ$0Y=0CD#C$l&0^kdSnH&aYu#1L0;Nw|zS8 zB04A+E)by0kt4eg=@H24@b2$vgZqdbdhxw=dV5Dl#`D_R0=J-%B|X-E;ZzXfgVJ}) zbvrn(dTi^V&5zI6M!EouA87!BKiqqXjNG5F7eh+@CB2r=Xb<*ph=u$-{nYw_WbQ8j z0}!jdIJ{6b0nYHzD!82l9eShb+nu4X8af4F_(-!RD`wFhe;cb8KG^(g#7Kr1Ucx43D^M~*X@4LGh<*&iW1>uMgW1kO*d`_Q#ub$dC8<5f(nUOg7Q=GZK!AElyGq zo)D}O!@Y_A{n4PjaL)d^E=8){fyAEtIrT?6i~n)}VcH%6f3}zKmgG>(ve*tc075bj zTMnhK3kXwycVVS9Fo3?%4oK1XJ(l0o=@Q*fayN<)bTQsO2QOaUwCw6Jl)u;%xiBhcleWqC~-s3?T@33c!A8p=I%7Z7ZZ@N;p6fbb9XR}7=FipmuH&C5m) z09OE(Q4SebZ_ksPGS}cK0QMBf6toPIa4d)IfH@E~atA^*dM>*s029hX88w|7$3I64(oRE`>)w$)ayl+VHpMLFfo@=>FU=Y z%2#F8z=*Z|L19RVI>X7RUR!jW98´i_>bb*7QJ^0tXZ5CLvw|Ubo=Udf`4&;Za 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 U+