From f490098af681d324519c399c3e75ade058357531 Mon Sep 17 00:00:00 2001 From: "cubecraft-agents[bot]" <3458173+cubecraft-agents[bot]@users.noreply.github.com> Date: Sat, 25 Apr 2026 19:02:57 +0000 Subject: [PATCH] initial commit --- README.md | 0 api-client/package.json | 32 + api-client/src/models/index.ts | 5 + api-client/src/models/types.ts | 426 + api-client/src/services/http-client.ts | 272 + api-client/src/utils/config.ts | 75 + api-client/src/utils/index.ts | 6 + api-client/src/utils/status-mapper.ts | 178 + api-client/src/websocket/index.ts | 5 + api-client/src/websocket/ws-client.ts | 584 ++ api-client/tsconfig.json | 21 + design/command-hub-spec.md | 511 ++ design/mockups/desktop-kiosk-view.jpg | Bin 0 -> 1955995 bytes design/mockups/mobile-view.jpg | Bin 0 -> 2089334 bytes design/mockups/quick-jump-drawer.jpg | Bin 0 -> 2117631 bytes frontend/.editorconfig | 17 + frontend/.gitignore | 44 + frontend/.prettierrc | 12 + frontend/.vscode/extensions.json | 4 + frontend/.vscode/launch.json | 20 + frontend/.vscode/mcp.json | 9 + frontend/.vscode/tasks.json | 42 + frontend/README.md | 59 + frontend/angular.json | 97 + frontend/package-lock.json | 8034 +++++++++++++++++ frontend/package.json | 34 + frontend/public/favicon.ico | Bin 0 -> 15086 bytes frontend/src/app/app.config.ts | 13 + frontend/src/app/app.html | 344 + frontend/src/app/app.routes.ts | 22 + frontend/src/app/app.scss | 4 + frontend/src/app/app.ts | 16 + .../bottom-nav/bottom-nav.component.html | 24 + .../bottom-nav/bottom-nav.component.scss | 76 + .../layout/bottom-nav/bottom-nav.component.ts | 24 + .../header-bar/header-bar.component.html | 45 + .../header-bar/header-bar.component.scss | 76 + .../layout/header-bar/header-bar.component.ts | 25 + frontend/src/app/layout/index.ts | 4 + .../layout-shell/layout-shell.component.html | 17 + .../layout-shell/layout-shell.component.scss | 57 + .../layout-shell/layout-shell.component.ts | 21 + .../layout/nav-rail/nav-rail.component.html | 44 + .../layout/nav-rail/nav-rail.component.scss | 112 + .../app/layout/nav-rail/nav-rail.component.ts | 32 + frontend/src/app/models/agent.model.ts | 54 + frontend/src/app/models/index.ts | 2 + frontend/src/app/models/nav.model.ts | 19 + .../src/app/pages/hub/hub-page.component.ts | 26 + .../src/app/pages/logs/logs-page.component.ts | 10 + .../pages/projects/projects-page.component.ts | 10 + .../pages/sessions/sessions-page.component.ts | 10 + .../pages/settings/settings-page.component.ts | 10 + .../src/app/services/agent-status.service.ts | 47 + frontend/src/index.html | 25 + frontend/src/main.ts | 6 + frontend/src/styles.scss | 212 + frontend/tsconfig.app.json | 15 + frontend/tsconfig.json | 30 + frontend/tsconfig.spec.json | 15 + 60 files changed, 11934 insertions(+) create mode 100644 README.md create mode 100644 api-client/package.json create mode 100644 api-client/src/models/index.ts create mode 100644 api-client/src/models/types.ts create mode 100644 api-client/src/services/http-client.ts create mode 100644 api-client/src/utils/config.ts create mode 100644 api-client/src/utils/index.ts create mode 100644 api-client/src/utils/status-mapper.ts create mode 100644 api-client/src/websocket/index.ts create mode 100644 api-client/src/websocket/ws-client.ts create mode 100644 api-client/tsconfig.json create mode 100644 design/command-hub-spec.md create mode 100644 design/mockups/desktop-kiosk-view.jpg create mode 100644 design/mockups/mobile-view.jpg create mode 100644 design/mockups/quick-jump-drawer.jpg create mode 100644 frontend/.editorconfig create mode 100644 frontend/.gitignore create mode 100644 frontend/.prettierrc create mode 100644 frontend/.vscode/extensions.json create mode 100644 frontend/.vscode/launch.json create mode 100644 frontend/.vscode/mcp.json create mode 100644 frontend/.vscode/tasks.json create mode 100644 frontend/README.md create mode 100644 frontend/angular.json create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/public/favicon.ico create mode 100644 frontend/src/app/app.config.ts create mode 100644 frontend/src/app/app.html create mode 100644 frontend/src/app/app.routes.ts create mode 100644 frontend/src/app/app.scss create mode 100644 frontend/src/app/app.ts create mode 100644 frontend/src/app/layout/bottom-nav/bottom-nav.component.html create mode 100644 frontend/src/app/layout/bottom-nav/bottom-nav.component.scss create mode 100644 frontend/src/app/layout/bottom-nav/bottom-nav.component.ts create mode 100644 frontend/src/app/layout/header-bar/header-bar.component.html create mode 100644 frontend/src/app/layout/header-bar/header-bar.component.scss create mode 100644 frontend/src/app/layout/header-bar/header-bar.component.ts create mode 100644 frontend/src/app/layout/index.ts create mode 100644 frontend/src/app/layout/layout-shell/layout-shell.component.html create mode 100644 frontend/src/app/layout/layout-shell/layout-shell.component.scss create mode 100644 frontend/src/app/layout/layout-shell/layout-shell.component.ts create mode 100644 frontend/src/app/layout/nav-rail/nav-rail.component.html create mode 100644 frontend/src/app/layout/nav-rail/nav-rail.component.scss create mode 100644 frontend/src/app/layout/nav-rail/nav-rail.component.ts create mode 100644 frontend/src/app/models/agent.model.ts create mode 100644 frontend/src/app/models/index.ts create mode 100644 frontend/src/app/models/nav.model.ts create mode 100644 frontend/src/app/pages/hub/hub-page.component.ts create mode 100644 frontend/src/app/pages/logs/logs-page.component.ts create mode 100644 frontend/src/app/pages/projects/projects-page.component.ts create mode 100644 frontend/src/app/pages/sessions/sessions-page.component.ts create mode 100644 frontend/src/app/pages/settings/settings-page.component.ts create mode 100644 frontend/src/app/services/agent-status.service.ts create mode 100644 frontend/src/index.html create mode 100644 frontend/src/main.ts create mode 100644 frontend/src/styles.scss create mode 100644 frontend/tsconfig.app.json create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.spec.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/api-client/package.json b/api-client/package.json new file mode 100644 index 0000000..01c0dfb --- /dev/null +++ b/api-client/package.json @@ -0,0 +1,32 @@ +{ + "name": "@control-center/openclaw-api-client", + "version": "0.1.0", + "description": "OpenClaw Gateway API client and WebSocket integration for the Control Center Command Hub", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "watch": "tsc --watch", + "test": "jest", + "lint": "eslint src/" + }, + "dependencies": { + "ws": "^8.16.0", + "rxjs": "^7.8.1", + "eventsource": "^2.0.2" + }, + "devDependencies": { + "typescript": "^5.4.0", + "@types/ws": "^8.5.10", + "@types/node": "^20.11.0", + "jest": "^29.7.0", + "ts-jest": "^29.1.0", + "@types/jest": "^29.5.0", + "eslint": "^8.56.0" + }, + "peerDependencies": { + "rxjs": "^7.8.1" + }, + "license": "UNLICENSED", + "private": true +} \ No newline at end of file diff --git a/api-client/src/models/index.ts b/api-client/src/models/index.ts new file mode 100644 index 0000000..0c4a57d --- /dev/null +++ b/api-client/src/models/index.ts @@ -0,0 +1,5 @@ +/** + * @fileoverview Re-export barrel for all model types. + */ + +export * from './types'; \ No newline at end of file diff --git a/api-client/src/models/types.ts b/api-client/src/models/types.ts new file mode 100644 index 0000000..d6aece7 --- /dev/null +++ b/api-client/src/models/types.ts @@ -0,0 +1,426 @@ +/** + * @fileoverview Core data models for the OpenClaw Control Center API client. + * + * These models bridge OpenClaw Gateway primitives (session keys, status events, + * tool responses) to the AgentCardData interface consumed by the Angular frontend. + * + * Design principle: every model maps 1:1 to a Gateway concept. Transformation + * to UI-specific shapes (like AgentCardData) happens in the service layer. + */ + +// ─── Agent Status ────────────────────────────────────────────────────────── + +/** + * Agent operational status, derived from session activity and Gateway events. + * + * Mapping from OpenClaw session `status` field: + * - "running" → ACTIVE — agent is currently processing a turn + * - "done" → IDLE — agent completed its last turn, no active work + * - "streaming"→ THINKING — LLM call in flight, tokens streaming + * - "error" → ERROR — agent encountered an unhandled error + * - (no session) → IDLE — no active session exists for this agent + */ +export type AgentStatus = 'active' | 'idle' | 'thinking' | 'error'; + +/** + * Extended status including offline — not all agents have active sessions. + * Used internally; the UI only sees AgentStatus (offline maps to idle). + */ +export type AgentLifecycleStatus = AgentStatus | 'offline'; + +// ─── Agent Card Data ─────────────────────────────────────────────────────── + +/** + * The primary data shape consumed by Agent Card components in the Command Hub. + * Matches the TypeScript interface from the design spec exactly. + */ +export interface AgentCardData { + /** Agent identifier, e.g. "otto", "dex", "hex" */ + id: string; + + /** Human-readable name, e.g. "Otto", "Dex" */ + displayName: string; + + /** Role description from agent identity, e.g. "Orchestrator Agent" */ + role: string; + + /** Current operational status */ + status: AgentStatus; + + /** Description of the current task, if any */ + currentTask?: string; + + /** Task progress percentage (0–100), if trackable */ + taskProgress?: number; + + /** Elapsed time string, e.g. "04m 12s" */ + taskElapsed?: string; + + /** Full session key, e.g. "agent:otto:telegram:direct:8787451565" */ + sessionKey: string; + + /** Channel the agent is operating on, e.g. "telegram", "discord" */ + channel: string; + + /** ISO timestamp of last activity */ + lastActivity: string; + + /** Error message when status is 'error' */ + errorMessage?: string; +} + +// ─── OpenClaw Gateway Primitives ─────────────────────────────────────────── + +/** + * Raw agent configuration from OpenClaw's config (openclaw.json agents.list[]). + * Not all fields are always present — this is a partial reflection. + */ +export interface OpenClawAgentConfig { + id: string; + name?: string; + emoji?: string; + avatar?: string; + model?: string | { primary: string }; + workspace?: string; + agentDir?: string; + identity?: { + name?: string; + emoji?: string; + avatar?: string; + theme?: string; + }; + routing?: Array<{ + channel: string; + account?: string; + peer?: string; + group?: string; + }>; + subagents?: { + allowAgents?: string[]; + maxChildrenPerAgent?: number; + }; +} + +/** + * Session entry returned by `sessions.list` RPC or `/tools/invoke sessions_list`. + * This is the raw shape from the Gateway. + */ +export interface OpenClawSession { + key: string; + kind: string; + channel: string; + origin?: { + provider: string; + accountId?: string; + peerId?: string; + }; + displayName?: string; + deliveryContext?: { + to?: string; + channel?: string; + accountId?: string; + peerId?: string; + }; + updatedAt: number; // epoch ms + sessionId: string; // UUID + model?: string; + modelProvider?: string; + contextTokens?: number; + totalTokens?: number; + totalTokensFresh?: boolean; + estimatedCostUsd?: number; + status?: 'done' | 'running' | 'streaming' | 'error' | 'aborted'; + startedAt?: number; // epoch ms + endedAt?: number; // epoch ms + runtimeMs?: number; + systemSent?: boolean; + abortedLastRun?: boolean; + lastTo?: string; + label?: string; + transcriptPath?: string; + agentId?: string; + inputTokens?: number; + outputTokens?: number; + providerOverride?: string; + modelOverride?: string; +} + +/** + * Gateway health response from `/health` endpoint. + */ +export interface GatewayHealth { + ok: boolean; + status: 'live' | 'stale' | 'down'; +} + +/** + * Identity payload for an agent, returned by `agent.identity.get` RPC. + */ +export interface AgentIdentity { + agentId: string; + name: string; + emoji?: string; + avatar?: string; + theme?: string; + description?: string; +} + +// ─── WebSocket Protocol Frames ───────────────────────────────────────────── + +/** + * Base frame shape for the OpenClaw Gateway WebSocket protocol v3. + * - Request: { type: "req", id, method, params } + * - Response: { type: "res", id, ok, payload|error } + * - Event: { type: "event", event, payload, seq?, stateVersion? } + */ +export type WsFrame = + | WsRequest + | WsResponse + | WsEvent; + +export interface WsRequest { + type: 'req'; + id: string; + method: string; + params: Record; +} + +export interface WsResponse { + type: 'res'; + id: string; + ok: boolean; + payload?: unknown; + error?: { + type: string; + message: string; + details?: Record; + }; +} + +export interface WsEvent { + type: 'event'; + event: string; + payload: unknown; + seq?: number; + stateVersion?: number; +} + +// ─── Event Payloads ──────────────────────────────────────────────────────── + +/** + * Event names emitted by the Gateway that the Command Hub subscribes to. + */ +export type GatewayEventName = + | 'sessions.changed' + | 'session.message' + | 'session.tool' + | 'presence' + | 'health' + | 'tick' + | 'heartbeat' + | 'cron' + | 'shutdown' + | 'exec.approval.requested' + | 'exec.approval.resolved' + | 'node.pair.requested' + | 'node.pair.resolved' + | 'chat' + | 'device.pair.requested' + | 'device.pair.resolved'; + +/** + * sessions.changed event payload — broadcast when the session index updates. + */ +export interface SessionsChangedPayload { + added?: string[]; // session keys added + removed?: string[]; // session keys removed + updated?: string[]; // session keys with metadata changes + snapshot?: OpenClawSession[]; +} + +/** + * session.message event payload — transcript message for a subscribed session. + */ +export interface SessionMessagePayload { + sessionKey: string; + role: 'user' | 'assistant' | 'system' | 'tool'; + content: string; + timestamp: number; + toolCalls?: ToolCallInfo[]; +} + +/** + * session.tool event payload — tool execution event for a subscribed session. + */ +export interface SessionToolPayload { + sessionKey: string; + toolName: string; + status: 'started' | 'completed' | 'error'; + duration?: number; + result?: string; + error?: string; +} + +/** + * Presence event payload — devices connected to the gateway. + */ +export interface PresencePayload { + devices: Array<{ + deviceId: string; + roles: string[]; + scopes: string[]; + connected: boolean; + lastSeen?: number; + }>; +} + +/** + * Health event payload — gateway health snapshot update. + */ +export interface HealthEventPayload { + ok: boolean; + status: string; + uptime?: number; + agentsOnline?: number; +} + +/** + * Simplified tool call info extracted from transcript events. + */ +export interface ToolCallInfo { + name: string; + status: 'started' | 'completed' | 'error'; + args?: Record; + result?: string; + error?: string; +} + +// ─── API Request/Response Types ──────────────────────────────────────────── + +/** + * Tools invoke request body for the HTTP `/tools/invoke` endpoint. + */ +export interface ToolsInvokeRequest { + tool: string; + action?: string; + args?: Record; + sessionKey?: string; + dryRun?: boolean; +} + +/** + * Tools invoke success response. + */ +export interface ToolsInvokeResponse { + ok: true; + result: { + content?: Array<{ type: string; text: string }>; + details?: T; + }; +} + +/** + * Tools invoke error response. + */ +export interface ToolsInvokeErrorResponse { + ok: false; + error: { + type: string; + message: string; + }; +} + +// ─── Connect Handshake ──────────────────────────────────────────────────── + +/** + * Connect request params for the WebSocket handshake. + */ +export interface ConnectParams { + minProtocol: number; + maxProtocol: number; + client: { + id: string; + version: string; + platform: string; + mode: 'operator' | 'node'; + }; + role: 'operator' | 'node'; + scopes: string[]; + caps?: string[]; + commands?: string[]; + permissions?: Record; + auth: { + token?: string; + password?: string; + deviceToken?: string; + }; + locale?: string; + userAgent?: string; + device?: { + id: string; + publicKey?: string; + signature?: string; + signedAt?: number; + nonce?: string; + }; +} + +/** + * Hello-ok response payload after successful connect. + */ +export interface HelloOkPayload { + type: 'hello-ok'; + protocol: number; + policy: { + tickIntervalMs: number; + }; + auth?: { + deviceToken?: string; + role: string; + scopes: string[]; + deviceTokens?: Array<{ + deviceToken: string; + role: string; + scopes: string[]; + }>; + }; +} + +/** + * Connect challenge event from the gateway. + */ +export interface ConnectChallengePayload { + nonce: string; + ts: number; +} + +// ─── Agent Role Map ──────────────────────────────────────────────────────── + +/** + * Known agent roles. This maps agent IDs to their functional descriptions + * for display in the Command Hub. Can be overridden by agent identity config. + */ +export const AGENT_ROLES: Record = { + main: 'Primary Assistant', + otto: 'Orchestrator Agent', + dave: 'Network Admin Agent', + bob: 'Content Writer Agent', + stuart: 'Image & Creative Agent', + phil: 'Home Automation Agent', + carl: 'Security Agent', + larry: 'Business Agent', + mel: 'E-Commerce Agent', + norbert: 'Product Agent', + jerry: 'Market Research Agent', + rex: 'Frontend Dev Agent', + dex: 'Backend Dev Agent', + hex: 'Database Agent', + pip: 'Raspberry Pi Agent', + nano: 'ESP32/Firmware Agent', + axiom: 'Utility Agent', + bonnie: 'Music Agent', + sketch: 'UI/UX Design Agent', + flip: 'Mobile Dev Agent', + buzz: 'SEO Agent', + aries: 'Companion Agent', +}; \ No newline at end of file diff --git a/api-client/src/services/http-client.ts b/api-client/src/services/http-client.ts new file mode 100644 index 0000000..342f6b1 --- /dev/null +++ b/api-client/src/services/http-client.ts @@ -0,0 +1,272 @@ +/** + * @fileoverview OpenClaw Gateway HTTP API client. + * + * Wraps the Gateway's REST surface: + * - `GET /health` — gateway health check + * - `POST /tools/invoke` — invoke a tool directly via HTTP + * - `GET /v1/models` — OpenAI-compatible model list (if enabled) + * + * The primary data-fetching path is `/tools/invoke`, which provides access to: + * - `sessions_list` — list active sessions across agents + * - `agents_list` — list configured agents + * - `linear_*` — Linear issue management (if plugin enabled) + * + * Note: Many WS RPC methods are NOT available over HTTP due to the built-in + * deny list (exec, spawn, shell, fs_write, sessions_spawn, etc.). + * For full Gateway access, use the WebSocket client. + */ + +import { OpenClawClientConfig } from '../utils/config'; +import type { + OpenClawSession, + OpenClawAgentConfig, + GatewayHealth, + ToolsInvokeRequest, + ToolsInvokeResponse, + ToolsInvokeErrorResponse, +} from '../models/types'; + +/** + * Parsed session list from the tools invoke response. + */ +export interface SessionListResult { + count: number; + sessions: OpenClawSession[]; +} + +/** + * Parsed agent list from the tools invoke response. + */ +export interface AgentListResult { + requester: string; + allowAny: boolean; + agents: Array<{ id: string; configured: boolean }>; +} + +/** + * Event types for the HTTP client. + */ +export type HttpClientEvent = + | { type: 'request-sent'; method: string; url: string } + | { type: 'response-received'; method: string; url: string; status: number } + | { type: 'error'; method: string; url: string; error: Error }; + +export class OpenClawHttpClient { + private readonly baseUrl: string; + private readonly authToken: string; + private readonly authMode: string; + private readonly timeoutMs: number; + + constructor(private readonly config: OpenClawClientConfig) { + this.baseUrl = config.gatewayUrl.replace(/\/+$/, ''); + this.authToken = config.authToken; + this.authMode = config.authMode; + this.timeoutMs = config.httpTimeoutMs; + } + + // ─── Health ─────────────────────────────────────────────────────────── + + /** + * Check gateway health. This is the simplest endpoint — no auth required. + * `GET /health` + */ + async getHealth(): Promise { + return this.get('/health'); + } + + // ─── Tools Invoke ──────────────────────────────────────────────────── + + /** + * Invoke a tool via the HTTP endpoint. + * `POST /tools/invoke` + * + * This is the primary way to fetch data without a WebSocket connection. + * Available tools are filtered by the Gateway's HTTP deny list. + */ + async invokeTool( + tool: string, + args: Record = {}, + sessionKey?: string + ): Promise { + const body: ToolsInvokeRequest = { + tool, + args, + ...(sessionKey ? { sessionKey } : {}), + }; + + const response = await this.post | ToolsInvokeErrorResponse>( + '/tools/invoke', + body + ); + + if (!response.ok) { + const err = response as ToolsInvokeErrorResponse; + throw new Error(`Tool invoke failed: ${err.error.type} — ${err.error.message}`); + } + + const success = response as ToolsInvokeResponse; + return (success.result.details ?? success.result.content) as T; + } + + // ─── Session Queries ───────────────────────────────────────────────── + + /** + * List sessions across all agents via `/tools/invoke`. + * Maps to the `sessions_list` tool. + * + * @param activeMinutes Only return sessions updated within the last N minutes + * @param allAgents Aggregate across all configured agents + */ + async listSessions( + activeMinutes?: number, + allAgents: boolean = true + ): Promise { + const args: Record = { allAgents }; + if (activeMinutes !== undefined) { + args.activeMinutes = activeMinutes; + } + + const raw = await this.invokeTool('sessions_list', args); + + // The tools invoke response wraps session data in `details` or `content` + // Normalize it here + if (raw && typeof raw === 'object' && 'sessions' in raw) { + return raw as SessionListResult; + } + + // Fallback: try to parse from content text + const contentResult = await this.invokeTool<{ content?: Array<{ type: string; text: string }> }>( + 'sessions_list', + args + ); + if (contentResult?.content?.[0]?.text) { + return JSON.parse(contentResult.content[0].text) as SessionListResult; + } + + return { count: 0, sessions: [] }; + } + + /** + * Get sessions for a specific agent. + */ + async listAgentSessions( + agentId: string, + activeMinutes?: number + ): Promise { + const args: Record = { allAgents: false, agentId }; + if (activeMinutes !== undefined) { + args.activeMinutes = activeMinutes; + } + + const raw = await this.invokeTool('sessions_list', args); + + if (raw && typeof raw === 'object' && 'sessions' in raw) { + return raw as SessionListResult; + } + + return { count: 0, sessions: [] }; + } + + // ─── Agent Queries ─────────────────────────────────────────────────── + + /** + * List configured agents via `/tools/invoke`. + * Maps to the `agents_list` tool. + * + * Note: This only returns agents the current session's policy allows. + * For the full agent list, prefer reading from config or using the WS protocol. + */ + async listAgents(): Promise { + return this.invokeTool('agents_list', {}); + } + + // ─── Model Queries ────────────────────────────────────────────────── + + /** + * List available models via the OpenAI-compatible endpoint. + * Requires `gateway.http.endpoints.chatCompletions.enabled: true`. + * `GET /v1/models` + */ + async listModels(): Promise { + return this.get('/v1/models'); + } + + // ─── HTTP Primitives ───────────────────────────────────────────────── + + private async get(path: string): Promise { + const url = `${this.baseUrl}${path}`; + const headers: Record = { + 'Accept': 'application/json', + }; + + if (this.authMode === 'token' && this.authToken) { + headers['Authorization'] = `Bearer ${this.authToken}`; + } else if (this.authMode === 'password' && this.authToken) { + headers['Authorization'] = `Bearer ${this.authToken}`; + } + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs); + + try { + const response = await fetch(url, { + method: 'GET', + headers, + signal: controller.signal, + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return response.json() as Promise; + } catch (err) { + if (err instanceof DOMException && err.name === 'AbortError') { + throw new Error(`Request timeout (${this.timeoutMs}ms): GET ${path}`); + } + throw err; + } finally { + clearTimeout(timeoutId); + } + } + + private async post(path: string, body: unknown): Promise { + const url = `${this.baseUrl}${path}`; + const headers: Record = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }; + + if (this.authMode === 'token' && this.authToken) { + headers['Authorization'] = `Bearer ${this.authToken}`; + } else if (this.authMode === 'password' && this.authToken) { + headers['Authorization'] = `Bearer ${this.authToken}`; + } + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs); + + try { + const response = await fetch(url, { + method: 'POST', + headers, + body: JSON.stringify(body), + signal: controller.signal, + }); + + if (!response.ok) { + const text = await response.text().catch(() => ''); + throw new Error(`HTTP ${response.status}: ${response.statusText} — ${text}`); + } + + return response.json() as Promise; + } catch (err) { + if (err instanceof DOMException && err.name === 'AbortError') { + throw new Error(`Request timeout (${this.timeoutMs}ms): POST ${path}`); + } + throw err; + } finally { + clearTimeout(timeoutId); + } + } +} \ No newline at end of file diff --git a/api-client/src/utils/config.ts b/api-client/src/utils/config.ts new file mode 100644 index 0000000..fdccfac --- /dev/null +++ b/api-client/src/utils/config.ts @@ -0,0 +1,75 @@ +/** + * @fileoverview Configuration for the OpenClaw API client. + * + * Centralizes gateway URL, auth token, WebSocket options, and polling intervals. + * Designed for Angular environment injection — values can be overridden at build time + * or via Angular's environment.ts files. + */ + +export interface OpenClawClientConfig { + /** Gateway HTTP base URL, e.g. "http://10.60.1.145:18789" */ + gatewayUrl: string; + + /** Gateway WebSocket URL, e.g. "ws://10.60.1.145:18789" */ + gatewayWsUrl: string; + + /** Auth token (from OPENCLAW_GATEWAY_TOKEN or configured secret) */ + authToken: string; + + /** Auth mode — must match gateway.auth.mode in openclaw.json */ + authMode: 'token' | 'password' | 'none' | 'trusted-proxy'; + + /** How often (ms) to poll for session status when WS is not connected */ + pollIntervalMs: number; + + /** How often (ms) to poll for agent list refresh */ + agentListPollMs: number; + + /** WebSocket reconnect delay (ms), with exponential backoff */ + wsReconnectDelayMs: number; + + /** Maximum WebSocket reconnect attempts before giving up */ + wsMaxReconnectAttempts: number; + + /** Request timeout for HTTP calls (ms) */ + httpTimeoutMs: number; + + /** Protocol version for WS handshake */ + protocolVersion: number; + + /** Client identity for WS handshake */ + clientId: string; + + /** Client version for WS handshake */ + clientVersion: string; + + /** Client platform for WS handshake */ + clientPlatform: string; +} + +/** + * Default configuration — targets the LAN-bound gateway on port 18789. + * Override specific fields via Angular environment injection. + */ +export const DEFAULT_CONFIG: OpenClawClientConfig = { + gatewayUrl: 'http://10.60.1.145:18789', + gatewayWsUrl: 'ws://10.60.1.145:18789', + authToken: '', + authMode: 'token', + pollIntervalMs: 5_000, + agentListPollMs: 30_000, + wsReconnectDelayMs: 1_000, + wsMaxReconnectAttempts: 50, + httpTimeoutMs: 15_000, + protocolVersion: 3, + clientId: 'control-center', + clientVersion: '0.1.0', + clientPlatform: 'web', +}; + +/** + * Create a resolved config by merging partial overrides onto defaults. + */ +export function createConfig(overrides: Partial = {}): OpenClawClientConfig { + return { ...DEFAULT_CONFIG, ...overrides }; +} \ No newline at end of file diff --git a/api-client/src/utils/index.ts b/api-client/src/utils/index.ts new file mode 100644 index 0000000..e7381a8 --- /dev/null +++ b/api-client/src/utils/index.ts @@ -0,0 +1,6 @@ +/** + * @fileoverview Re-export barrel for utilities. + */ + +export * from './config'; +export * from './status-mapper'; \ No newline at end of file diff --git a/api-client/src/utils/status-mapper.ts b/api-client/src/utils/status-mapper.ts new file mode 100644 index 0000000..eae640f --- /dev/null +++ b/api-client/src/utils/status-mapper.ts @@ -0,0 +1,178 @@ +/** + * @fileoverview Status mapping utilities. + * + * Translates OpenClaw Gateway session statuses and timestamps + * into the AgentStatus enum and elapsed-time strings used by AgentCardData. + */ + +import { AgentStatus, AgentLifecycleStatus, OpenClawSession, AGENT_ROLES } from '../models/types'; + +/** + * Map an OpenClaw session status to an AgentStatus for the UI. + * + * Mapping logic: + * - "running" or "streaming" with no tokens yet → ACTIVE (agent processing turn) + * - "streaming" with output tokens → THINKING (LLM call in flight) + * - "done" → IDLE (agent finished, waiting for next input) + * - "error" → ERROR + * - "aborted" → IDLE (abort = intentional stop, not an error) + * - null/undefined → IDLE (no active session = idle) + */ +export function mapSessionStatusToAgentStatus(sessionStatus?: string | null): AgentStatus { + switch (sessionStatus) { + case 'running': + return 'active'; + case 'streaming': + return 'thinking'; + case 'done': + return 'idle'; + case 'error': + return 'error'; + case 'aborted': + return 'idle'; + default: + return 'idle'; + } +} + +/** + * Determine lifecycle status for an agent that may not have an active session. + * If no session exists, the agent is considered "offline". + */ +export function determineLifecycleStatus( + session?: OpenClawSession | null +): AgentLifecycleStatus { + if (!session) return 'offline'; + return mapSessionStatusToAgentStatus(session.status); +} + +/** + * Convert an OpenClaw session to the lifecycle status used by the UI. + * Offline agents are presented as "idle" to the UI. + */ +export function lifecycleToUiStatus(lifecycle: AgentLifecycleStatus): AgentStatus { + return lifecycle === 'offline' ? 'idle' : lifecycle; +} + +/** + * Format elapsed time from a session's startedAt timestamp. + * Returns a human-readable string like "04m 12s" or "1h 23m". + * + * If the session hasn't started or is done, returns undefined. + */ +export function formatElapsedTime( + session?: OpenClawSession | null, + nowMs?: number +): string | undefined { + if (!session?.startedAt) return undefined; + const endTime = session.endedAt || nowMs || Date.now(); + const elapsedMs = endTime - session.startedAt; + if (elapsedMs < 0) return undefined; + + const totalSeconds = Math.floor(elapsedMs / 1000); + const hours = Math.floor(totalSeconds / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const seconds = totalSeconds % 60; + + if (hours > 0) { + return `${hours}h ${String(minutes).padStart(2, '0')}m`; + } + return `${String(minutes).padStart(2, '0')}m ${String(seconds).padStart(2, '0')}s`; +} + +/** + * Extract the channel name from a session key. + * Session key format: "agent::::" + * Or from the session's channel/origin fields. + */ +export function extractChannelFromSession(session: OpenClawSession): string { + // Prefer explicit channel field + if (session.channel && session.channel !== 'heartbeat') { + return session.channel; + } + // Fall back to origin provider + if (session.origin?.provider) { + return session.origin.provider; + } + // Parse session key: "agent:otto:telegram:direct:8787451565" + const parts = session.key.split(':'); + if (parts.length >= 3 && parts[0] === 'agent') { + return parts[2]; + } + return 'unknown'; +} + +/** + * Extract the agent ID from a session key. + * Session key format: "agent::..." + */ +export function extractAgentIdFromSessionKey(sessionKey: string): string { + const parts = sessionKey.split(':'); + if (parts[0] === 'agent' && parts.length >= 2) { + return parts[1]; + } + return sessionKey; +} + +/** + * Get the role description for an agent ID. + * Falls back to AGENT_ROLES map, then to a generic string. + */ +export function getAgentRole(agentId: string): string { + return AGENT_ROLES[agentId] || `${agentId} Agent`; +} + +/** + * Estimate task progress based on session state. + * + * OpenClaw doesn't natively expose a 0–100 progress percentage. + * We derive an approximation: + * - "running" session → 10–50% (early in the task, we don't know) + * - "streaming" → 60–90% (LLM is responding) + * - "done" → 100% + * - "error" → whatever progress was made before the error + * + * This is intentionally coarse. A future enhancement could parse + * sub-agent progress or tool call completion ratios. + */ +export function estimateTaskProgress(session?: OpenClawSession | null): number | undefined { + if (!session) return undefined; + + switch (session.status) { + case 'running': + return 25; // processing, early stage + case 'streaming': + return 75; // LLM responding + case 'done': + return 100; + case 'error': + return undefined; // unknown how far it got + case 'aborted': + return undefined; + default: + return undefined; + } +} + +/** + * Derive a current-task description from session metadata. + * Uses the session label (for cron sessions) or constructs a generic description. + */ +export function deriveCurrentTask(session?: OpenClawSession | null): string | undefined { + if (!session) return undefined; + + // Cron sessions have a label + if (session.label) return session.label; + + // Sessions with displayName that isn't generic + if (session.displayName && session.displayName !== 'direct' && session.displayName !== 'heartbeat') { + return session.displayName; + } + + // Active sessions get a generic description + if (session.status === 'running' || session.status === 'streaming') { + return 'Processing task...'; + } + + return undefined; +} \ No newline at end of file diff --git a/api-client/src/websocket/index.ts b/api-client/src/websocket/index.ts new file mode 100644 index 0000000..57341ae --- /dev/null +++ b/api-client/src/websocket/index.ts @@ -0,0 +1,5 @@ +/** + * @fileoverview Re-export barrel for WebSocket module. + */ + +export * from './ws-client'; \ No newline at end of file diff --git a/api-client/src/websocket/ws-client.ts b/api-client/src/websocket/ws-client.ts new file mode 100644 index 0000000..e834599 --- /dev/null +++ b/api-client/src/websocket/ws-client.ts @@ -0,0 +1,584 @@ +/** + * @fileoverview OpenClaw Gateway WebSocket client. + * + * Implements the Gateway WS protocol v3: + * 1. Connect and receive `connect.challenge` + * 2. Send `connect` request with auth + * 3. Receive `hello-ok` (or error) + * 4. Subscribe to session events (`sessions.subscribe`, `sessions.messages.subscribe`) + * 5. Listen for real-time events (session changes, messages, tool calls, presence) + * 6. Send RPC requests (agents.list, sessions.list, sessions.abort, etc.) + * + * Uses RxJS subjects for reactive event streaming — the Angular frontend + * can subscribe to specific event types without callback hell. + */ + +import { Subject, Observable, BehaviorSubject, Subscription } from 'rxjs'; +import { filter, map, share } from 'rxjs/operators'; +import { + WsFrame, + WsRequest, + WsResponse, + WsEvent, + ConnectParams, + HelloOkPayload, + ConnectChallengePayload, + GatewayEventName, + SessionsChangedPayload, + SessionMessagePayload, + SessionToolPayload, + PresencePayload, + HealthEventPayload, + OpenClawSession, +} from '../models/types'; +import { OpenClawClientConfig } from '../utils/config'; + +// ─── Connection State ────────────────────────────────────────────────────── + +export type WsConnectionState = + | 'disconnected' + | 'connecting' + | 'challenged' + | 'authenticating' + | 'connected' + | 'reconnecting' + | 'error'; + +export interface WsConnectionInfo { + state: WsConnectionState; + protocol?: number; + sessionId?: string; + reconnectAttempt?: number; + lastError?: string; +} + +// ─── RPC Pending Request ────────────────────────────────────────────────── + +interface PendingRpc { + resolve: (payload: unknown) => void; + reject: (error: Error) => void; + timeoutId: ReturnType; +} + +// ─── Client ─────────────────────────────────────────────────────────────── + +export class OpenClawWebSocketClient { + private ws: WebSocket | null = null; + private rpcId = 0; + private pendingRpc = new Map(); + private challengeNonce = ''; + private reconnectAttempt = 0; + private reconnectTimer: ReturnType | null = null; + private subscriptions = new Set(); // session keys we've subscribed to + private sessionSubscriptionsActive = false; + + // ─── RxJS Streams ──────────────────────────────────────────────────── + + /** All raw WebSocket events */ + private readonly rawEvent$ = new Subject(); + + /** Connection state changes */ + readonly connectionState$ = new BehaviorSubject({ + state: 'disconnected', + }); + + /** Session index changed events */ + readonly sessionsChanged$ = this.rawEvent$.pipe( + filter((e) => e.event === 'sessions.changed'), + map((e) => e.payload as SessionsChangedPayload), + share() + ); + + /** Session message events (for subscribed sessions) */ + readonly sessionMessage$ = this.rawEvent$.pipe( + filter((e) => e.event === 'session.message'), + map((e) => e.payload as SessionMessagePayload), + share() + ); + + /** Session tool events (for subscribed sessions) */ + readonly sessionTool$ = this.rawEvent$.pipe( + filter((e) => e.event === 'session.tool'), + map((e) => e.payload as SessionToolPayload), + share() + ); + + /** Presence events */ + readonly presence$ = this.rawEvent$.pipe( + filter((e) => e.event === 'presence'), + map((e) => e.payload as PresencePayload), + share() + ); + + /** Health events */ + readonly health$ = this.rawEvent$.pipe( + filter((e) => e.event === 'health'), + map((e) => e.payload as HealthEventPayload), + share() + ); + + /** Tick/keepalive events */ + readonly tick$ = this.rawEvent$.pipe( + filter((e) => e.event === 'tick'), + share() + ); + + /** Gateway shutdown notification */ + readonly shutdown$ = this.rawEvent$.pipe( + filter((e) => e.event === 'shutdown'), + share() + ); + + /** Any event with a specific name */ + on(event: GatewayEventName): Observable { + return this.rawEvent$.pipe( + filter((e) => e.event === event), + map((e) => e.payload) + ); + } + + // ─── Constructor ───────────────────────────────────────────────────── + + constructor(private readonly config: OpenClawClientConfig) {} + + // ─── Connection Lifecycle ─────────────────────────────────────────── + + /** + * Connect to the Gateway WebSocket. + * Handles the full handshake: challenge → connect → hello-ok. + */ + connect(): void { + if (this.ws?.readyState === WebSocket.OPEN) { + return; // already connected + } + + this.updateState({ state: 'connecting' }); + const url = this.config.gatewayWsUrl; + + try { + this.ws = new WebSocket(url); + } catch (err) { + this.updateState({ state: 'error', lastError: `Failed to create WebSocket: ${err}` }); + this.scheduleReconnect(); + return; + } + + this.ws.onopen = () => { + // Waiting for challenge from gateway + this.updateState({ state: 'challenged' }); + }; + + this.ws.onmessage = (event) => { + this.handleMessage(event.data); + }; + + this.ws.onerror = (event) => { + this.updateState({ + state: 'error', + lastError: `WebSocket error: ${event.type}`, + }); + }; + + this.ws.onclose = (event) => { + this.updateState({ + state: 'disconnected', + lastError: event.reason || `WebSocket closed (code: ${event.code})`, + }); + this.clearPendingRpcs(new Error(`WebSocket closed: ${event.code}`)); + this.scheduleReconnect(); + }; + } + + /** + * Disconnect from the Gateway WebSocket. + * Cancels reconnect timer and cleans up. + */ + disconnect(): void { + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + this.reconnectAttempt = 0; + + if (this.ws) { + this.ws.onclose = null; // prevent reconnect + this.ws.close(1000, 'Client disconnect'); + this.ws = null; + } + + this.clearPendingRpcs(new Error('Client disconnected')); + this.updateState({ state: 'disconnected' }); + } + + /** + * Check if the WebSocket is currently connected. + */ + get isConnected(): boolean { + return this.connectionState$.value.state === 'connected'; + } + + // ─── RPC Methods ───────────────────────────────────────────────────── + + /** + * Send an RPC request and wait for the response. + * Returns a promise that resolves with the response payload + * or rejects on error/timeout. + * + * @param method RPC method name (e.g. "sessions.list", "agents.list") + * @param params Method parameters + * @param timeoutMs Optional timeout override + */ + async rpc( + method: string, + params: Record = {}, + timeoutMs?: number + ): Promise { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { + throw new Error('WebSocket not connected'); + } + + const id = `cc-${++this.rpcId}`; + const request: WsRequest = { + type: 'req', + id, + method, + params, + }; + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + this.pendingRpc.delete(id); + reject(new Error(`RPC timeout: ${method} (${timeoutMs || this.config.httpTimeoutMs}ms)`)); + }, timeoutMs || this.config.httpTimeoutMs); + + this.pendingRpc.set(id, { + resolve: resolve as (p: unknown) => void, + reject, + timeoutId: timeout, + }); + + this.ws!.send(JSON.stringify(request)); + }); + } + + // ─── High-Level API ───────────────────────────────────────────────── + + /** + * Subscribe to session change events for the current WS client. + * After subscribing, `sessions.changed` events will be emitted. + */ + async subscribeToSessions(): Promise { + if (this.sessionSubscriptionsActive) return; + await this.rpc('sessions.subscribe', {}); + this.sessionSubscriptionsActive = true; + } + + /** + * Unsubscribe from session change events. + */ + async unsubscribeFromSessions(): Promise { + if (!this.sessionSubscriptionsActive) return; + await this.rpc('sessions.unsubscribe', {}); + this.sessionSubscriptionsActive = false; + } + + /** + * Subscribe to message/transcript events for a specific session. + * Required before session.message and session.tool events fire. + * + * @param sessionKey Full session key, e.g. "agent:otto:telegram:direct:8787451565" + */ + async subscribeToSessionMessages(sessionKey: string): Promise { + if (this.subscriptions.has(sessionKey)) return; + await this.rpc('sessions.messages.subscribe', { sessionKey }); + this.subscriptions.add(sessionKey); + } + + /** + * Unsubscribe from message events for a session. + */ + async unsubscribeFromSessionMessages(sessionKey: string): Promise { + if (!this.subscriptions.has(sessionKey)) return; + await this.rpc('sessions.messages.unsubscribe', { sessionKey }); + this.subscriptions.delete(sessionKey); + } + + /** + * Fetch the current session list via WS RPC. + * More complete than the HTTP tools/invoke version. + */ + async fetchSessions(allAgents = true, activeMinutes?: number): Promise { + const params: Record = { allAgents }; + if (activeMinutes !== undefined) { + params.activeMinutes = activeMinutes; + } + + const result = await this.rpc<{ sessions: OpenClawSession[]; count: number }>( + 'sessions.list', + params + ); + + return result.sessions || []; + } + + /** + * Fetch the agent list via WS RPC. + */ + async fetchAgents(): Promise> { + const result = await this.rpc<{ agents: Array<{ id: string; configured: boolean }> }>( + 'agents.list', + {} + ); + return result.agents || []; + } + + /** + * Fetch agent identity via WS RPC. + */ + async fetchAgentIdentity(agentId: string): Promise { + return this.rpc('agent.identity.get', { agentId }); + } + + /** + * Get a transcript preview for a session. + */ + async fetchSessionPreview( + sessionKey: string, + limit = 20 + ): Promise { + return this.rpc('sessions.preview', { sessionKey, limit }); + } + + /** + * Abort an active session. + */ + async abortSession(sessionKey: string): Promise { + await this.rpc('sessions.abort', { sessionKey }); + } + + /** + * Steer (interrupt + inject message) an active session. + */ + async steerSession(sessionKey: string, message: string): Promise { + await this.rpc('sessions.steer', { sessionKey, message }); + } + + /** + * Fetch gateway status (admin-scoped). + */ + async fetchStatus(): Promise { + return this.rpc('status', {}); + } + + /** + * Fetch gateway health snapshot. + */ + async fetchHealth(): Promise { + return this.rpc('health', {}); + } + + /** + * Fetch system presence. + */ + async fetchPresence(): Promise { + return this.rpc('system-presence', {}); + } + + /** + * Fetch channel status for all configured channels. + */ + async fetchChannelStatus(): Promise { + return this.rpc('channels.status', {}); + } + + /** + * Fetch log tail from the gateway. + */ + async fetchLogTail( + cursor?: string, + limit?: number, + maxBytes?: number + ): Promise { + const params: Record = {}; + if (cursor) params.cursor = cursor; + if (limit) params.limit = limit; + if (maxBytes) params.maxBytes = maxBytes; + return this.rpc('logs.tail', params); + } + + // ─── Internal Handlers ────────────────────────────────────────────── + + private handleMessage(data: string): void { + let frame: WsFrame; + + try { + frame = JSON.parse(data); + } catch { + console.warn('[OpenClaw WS] Failed to parse frame:', data); + return; + } + + switch (frame.type) { + case 'event': + this.handleEvent(frame as WsEvent); + break; + case 'res': + this.handleResponse(frame as WsResponse); + break; + default: + console.warn('[OpenClaw WS] Unknown frame type:', frame.type); + } + } + + private handleEvent(event: WsEvent): void { + // Handle connect challenge specially + if (event.event === 'connect.challenge') { + this.handleChallenge(event.payload as ConnectChallengePayload); + return; + } + + // Emit to all subscribers + this.rawEvent$.next(event); + } + + private handleChallenge(challenge: ConnectChallengePayload): void { + this.challengeNonce = challenge.nonce; + this.updateState({ state: 'authenticating' }); + + // Send connect request + const connectParams: ConnectParams = { + minProtocol: this.config.protocolVersion, + maxProtocol: this.config.protocolVersion, + client: { + id: this.config.clientId, + version: this.config.clientVersion, + platform: this.config.clientPlatform, + mode: 'operator', + }, + role: 'operator', + scopes: ['operator.read', 'operator.write'], + auth: { + token: this.config.authToken, + }, + locale: 'en-US', + userAgent: `control-center/${this.config.clientVersion}`, + }; + + this.sendRequest('connect', connectParams); + } + + private handleResponse(response: WsResponse): void { + // Handle hello-ok (connect response) + if (response.id && response.id.startsWith('cc-')) { + const pending = this.pendingRpc.get(response.id); + if (pending) { + clearTimeout(pending.timeoutId); + this.pendingRpc.delete(response.id); + + if (response.ok) { + pending.resolve(response.payload); + } else { + pending.reject( + new Error( + `RPC error: ${response.error?.type || 'unknown'} — ${response.error?.message || 'no details'}` + ) + ); + } + return; + } + } + + // Handle connect response (sent without cc- prefix) + if (response.ok && response.payload) { + const payload = response.payload as HelloOkPayload; + if (payload.type === 'hello-ok') { + this.reconnectAttempt = 0; + this.updateState({ + state: 'connected', + protocol: payload.protocol, + }); + + // Auto-subscribe to session changes + this.subscribeToSessions().catch((err) => { + console.warn('[OpenClaw WS] Failed to subscribe to sessions:', err); + }); + + return; + } + } + + // Handle connect error + if (!response.ok && response.error) { + this.updateState({ + state: 'error', + lastError: `Connect failed: ${response.error.type} — ${response.error.message}`, + }); + this.scheduleReconnect(); + } + } + + private sendRequest(method: string, params: Record): void { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { + return; + } + + const request: WsRequest = { + type: 'req', + id: `rpc-${++this.rpcId}`, + method, + params, + }; + + this.ws.send(JSON.stringify(request)); + } + + private updateState(partial: Partial): void { + const current = this.connectionState$.value; + this.connectionState$.next({ ...current, ...partial } as WsConnectionInfo); + } + + private scheduleReconnect(): void { + if (this.reconnectAttempt >= this.config.wsMaxReconnectAttempts) { + this.updateState({ + state: 'error', + lastError: `Max reconnect attempts reached (${this.config.wsMaxReconnectAttempts})`, + }); + return; + } + + this.reconnectAttempt++; + const delay = Math.min( + this.config.wsReconnectDelayMs * Math.pow(1.5, this.reconnectAttempt - 1), + 30_000 // cap at 30s + ); + + this.updateState({ + state: 'reconnecting', + reconnectAttempt: this.reconnectAttempt, + }); + + this.reconnectTimer = setTimeout(() => { + this.reconnectTimer = null; + this.connect(); + }, delay); + } + + private clearPendingRpcs(error: Error): void { + for (const [id, pending] of this.pendingRpc.entries()) { + clearTimeout(pending.timeoutId); + pending.reject(error); + } + this.pendingRpc.clear(); + } + + /** + * Clean up all subscriptions and disconnect. + */ + destroy(): void { + this.disconnect(); + this.rawEvent$.complete(); + this.connectionState$.complete(); + this.subscriptions.clear(); + this.sessionSubscriptionsActive = false; + } +} \ No newline at end of file diff --git a/api-client/tsconfig.json b/api-client/tsconfig.json new file mode 100644 index 0000000..23671f5 --- /dev/null +++ b/api-client/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "node", + "lib": ["ES2022", "DOM"], + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} \ No newline at end of file diff --git a/design/command-hub-spec.md b/design/command-hub-spec.md new file mode 100644 index 0000000..6d90f9a --- /dev/null +++ b/design/command-hub-spec.md @@ -0,0 +1,511 @@ +# OpenClaw Control Center — Command Hub +## Senior Product Designer Specification (7-Point) + +**Version:** 1.0 +**Date:** 2026-04-21 +**Designer:** Sketch (UI/UX Agent) +**Status:** Implementation-Ready +**Design System:** Material Design 3 (https://m3.material.io/) +**Aesthetic:** Tactical Dark Mode + +--- + +## 1. Objective + +Design the **Command Hub** — the primary entry point and nerve centre for the OpenClaw Control Center. This screen enables operators to **monitor all active agents at a glance**, assess fleet health in under 3 seconds, and **jump directly into any agent session** without friction. + +The design must serve two primary mental models: +- **Triage Mode** — scan for errors, stalls, and anomalies fast +- **Navigation Mode** — get into a specific agent session instantly + +This is a utility-first, operator-grade interface. It is not a marketing page. Every pixel earns its place. + +--- + +## 2. Screen Inventory + +| Screen | Description | Trigger | +|--------|-------------|---------| +| **Command Hub** | Fleet Status grid + global nav | App launch / Home | +| **Agent Session View** | Full session detail for one agent | Quick-Jump tap | +| **Session Log** | Full scrollable log history | Long-press agent card or log icon | +| **Task Breakdown** | Subtask waterfall for active task | Tap active task chip on card | +| **Settings / Config** | Global config, theme, agents list | Nav Rail → Settings | +| **Notifications Panel** | Alert history and system events | Bell icon / swipe | + +--- + +## 3. Layout Specification + +### 3.1 Kiosk Layout (≥ 1024px — Fixed Display / Raspberry Pi HDMI) + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ NAV RAIL (72px) │ MAIN CONTENT AREA │ +│ │ │ +│ [☰] OpenClaw │ ┌─ HEADER BAR ─────────────────────────┐ │ +│ │ │ "Command Hub" [🔔 3] [●Live] [⚙] │ │ +│ [⚡] Command Hub │ └──────────────────────────────────────┘ │ +│ [📋] Projects │ │ +│ [🗂] Sessions │ ┌─ FLEET STATUS ────────────────────────┐ │ +│ [📊] Logs │ │ FILTER: [All ▾] [Active] [Error] │ │ +│ ───────── │ │ │ │ +│ [⚙] Settings │ │ ┌──────────┐ ┌──────────┐ ┌──────┐ │ │ +│ [👤] Profile │ │ │ AGENT │ │ AGENT │ │AGENT │ │ │ +│ │ │ │ CARD │ │ CARD │ │ CARD │ │ │ +│ │ │ └──────────┘ └──────────┘ └──────┘ │ │ +│ │ │ ┌──────────┐ ┌──────────┐ ... │ │ +│ │ │ │ AGENT │ │ AGENT │ │ │ +│ │ │ │ CARD │ │ CARD │ │ │ +│ │ │ └──────────┘ └──────────┘ │ │ +│ │ └────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +**Nav Rail:** 72px collapsed, 256px expanded. M3 `NavigationRail` component. Always visible on kiosk. + +**Main Content:** 3-column agent card grid (CSS Grid, `repeat(auto-fill, minmax(340px, 1fr))`). 24px gutter. + +**Header Bar:** 64px tall. App title + live indicator + notification bell + settings. + +--- + +### 3.2 Mobile Layout (< 768px — Phone / Tablet) + +``` +┌───────────────────────────────┐ +│ ┌─ HEADER ──────────────────┐│ +│ │ ≡ Command Hub 🔔 ⚙ ││ +│ └────────────────────────────│ +│ │ +│ [All ▾] [Active 4] [Error 1] │ +│ │ +│ ┌──────────────────────────┐ │ +│ │ AGENT CARD (full width) │ │ +│ └──────────────────────────┘ │ +│ ┌──────────────────────────┐ │ +│ │ AGENT CARD (full width) │ │ +│ └──────────────────────────┘ │ +│ ┌──────────────────────────┐ │ +│ │ AGENT CARD (full width) │ │ +│ └──────────────────────────┘ │ +│ │ +│ ───────────────────────────── │ +│ [⚡Hub] [📋Projects] [📊Logs] │ +│ BOTTOM NAV BAR │ +└───────────────────────────────┘ +``` + +**Bottom Navigation:** M3 `NavigationBar`. 3–5 destinations. Active destination uses M3 indicator pill. + +**Cards:** Single-column, full-width. Scrollable list. 16px horizontal padding. + +--- + +### 3.3 Agent Card Component (Core Unit) + +``` +┌─────────────────────────────────────────────────────┐ +│ ● ACTIVE [otto] [→ Jump] │ +│ ─────────────────────────────────────────────────── │ +│ 🤖 Otto │ +│ Orchestrator Agent │ +│ │ +│ TASK: Reviewing PR #42 — extrudex-ui │ +│ ████████████░░░░░░ 62% — 04m 12s elapsed │ +│ │ +│ SESSION: agent:otto:telegram:direct:8787... │ +│ CHANNEL: Telegram [●] Last msg: 2m ago │ +└─────────────────────────────────────────────────────┘ +``` + +**Card anatomy:** +- **Status Badge** (top-left): Color pill — Active / Idle / Thinking / Error +- **Agent Tag** (top-center): Short ID chip +- **Quick-Jump CTA** (top-right): `→ Jump` icon button — M3 `FilledTonalIconButton` +- **Avatar + Name + Role** (mid-section): M3 List tile pattern +- **Active Task Row**: Task name + linear progress indicator +- **Session Footer**: Session key truncated + channel + last activity timestamp + +**Card states:** +- **Default** — surface container background, subtle border +- **Active** — cyan left-border accent (4px), slightly elevated `+2dp` +- **Error** — red border, pulsing status dot +- **Thinking** — purple status + animated shimmer on task row +- **Idle** — muted, desaturated, lower elevation +- **Hover/Focus** — M3 state layer overlay (8% primary) + +--- + +### 3.4 Quick-Jump Mechanism + +On tap of `→ Jump` button on any agent card: + +1. **Modal Bottom Sheet** (mobile) — M3 `ModalBottomSheet` slides up with: + - Agent name + status + - Last 3 messages (truncated) + - [Open Full Session] FAB + - [View Logs] secondary action + +2. **Side Drawer Panel** (kiosk/desktop) — M3 `NavigationDrawer` (modal variant) slides in from right (480px wide) with: + - Agent name + session context + - Live log tail (last 20 lines, auto-scroll) + - [Open Full Session] primary button + - [Pin to Dashboard] secondary action + +**Keyboard / touch shortcut:** Long-press card = opens Session Log directly (bypasses Quick-Jump drawer). + +--- + +### 3.5 Global Navigation Structure + +| Destination | Icon | Badge | +|-------------|------|-------| +| Command Hub | ⚡ Flash | None | +| Projects | 📋 Clipboard | Active count | +| Sessions | 🗂 Stack | Unread count | +| Logs | 📊 Chart | Error count | +| Settings | ⚙ Gear | — | + +**Rail behavior:** Collapsed by default on kiosk. Tap hamburger `☰` to expand with labels. +**Bottom nav (mobile):** Always shows labels. Max 5 destinations. Overflow → menu. + +--- + +## 4. UX Rationale + +### Why Navigation Rail (Desktop) + Bottom Nav (Mobile)? +M3's adaptive navigation pattern — `NavigationRail` is the correct M3 component for persistent navigation on expanded screens. It keeps the full content width for the fleet grid. On mobile, `NavigationBar` (bottom) is ergonomically correct for thumb reach. The two layouts share identical navigation destinations but adapt the component. + +### Why Card Grid, Not Table? +- Cards are **touch-friendly** (minimum 56px tap area per cell) +- Cards support **rich visual status** — tables flatten the hierarchy +- Cards allow **progressive disclosure** (collapsed vs expanded state) +- Tables are for data comparison; cards are for status triage + +### Why Quick-Jump Drawer, Not Page Navigation? +Operators often want to **peek** at an agent without losing the fleet overview. A drawer preserves context — the fleet grid remains visible behind it. Full navigation to the agent session view is a secondary action requiring intentional tap. + +### Why Left-Border Accent on Active Cards? +It's a scannable, pre-attentive visual cue. Eyes detect vertical color stripes faster than color fills. Lifted from terminal/IDE design patterns (like VS Code activity indicators). Consistent with Tactical Dark Mode aesthetics. + +### Information Hierarchy on Card +Status → Identity → Current Task → Session Context +This ordering maps to the triage mental model: "Is it ok? Who is it? What's it doing? Where is it?" + +--- + +## 5. Visual Direction + +### 5.1 Color Palette (M3 Dynamic Color — Custom Dark Scheme) + +| Role | Token | Hex | Usage | +|------|-------|-----|-------| +| Background | `md.sys.color.background` | `#0D0F12` | App background | +| Surface | `md.sys.color.surface` | `#13161A` | Card background | +| Surface Container | `md.sys.color.surface-container` | `#1C2027` | Elevated cards | +| Surface Container High | `md.sys.color.surface-container-high` | `#252B33` | Header, Nav Rail | +| On Surface | `md.sys.color.on-surface` | `#E2E8F0` | Primary text | +| On Surface Variant | `md.sys.color.on-surface-variant` | `#8A9BB0` | Secondary text | +| Outline | `md.sys.color.outline` | `#2D3748` | Card borders | +| Primary | `md.sys.color.primary` | `#38BDF8` | Active state, CTAs (Electric Blue) | +| On Primary | `md.sys.color.on-primary` | `#0C1825` | Text on primary | +| Secondary | `md.sys.color.secondary` | `#64748B` | Nav inactive | +| Tertiary | `md.sys.color.tertiary` | `#A78BFA` | Thinking state (Amethyst) | + +**Status Colors (Semantic — outside M3 tonal system):** + +| Status | Color | Hex | Dot Animation | +|--------|-------|-----|---------------| +| Active | Electric Blue | `#38BDF8` | Steady pulse (2s) | +| Idle | Muted Teal | `#2DD4BF` | Static | +| Thinking | Amethyst | `#A78BFA` | Slow pulse (3s) | +| Error | Vivid Red | `#F87171` | Fast pulse (0.8s) | + +### 5.2 Typography + +M3 Type Scale using **Inter** (primary) with **Roboto Mono** for session IDs and log output. + +| Role | M3 Token | Size | Weight | Usage | +|------|----------|------|--------|-------| +| Display Small | `displaySmall` | 36sp | 400 | Screen title | +| Headline Medium | `headlineMedium` | 28sp | 400 | Agent name | +| Title Large | `titleLarge` | 22sp | 500 | Section headers | +| Title Medium | `titleMedium` | 16sp | 500 | Card title | +| Body Medium | `bodyMedium` | 14sp | 400 | Task description | +| Label Large | `labelLarge` | 14sp | 500 | Buttons, badges | +| Label Small | `labelSmall` | 11sp | 500 | Status chips, timestamps | +| Mono Body | — | 13sp | 400 | Session IDs, log text | + +### 5.3 Elevation & Surfaces + +Using M3's tonal surface elevation (not drop shadows): + +| Level | Overlay | Usage | +|-------|---------|-------| +| Level 0 | 0% | App background | +| Level 1 | 5% | Cards (default) | +| Level 2 | 8% | Cards (active/hover) | +| Level 3 | 11% | Navigation Rail | +| Level 4 | 12% | Header / App Bar | +| Level 5 | 14% | Modal overlays | + +### 5.4 Spacing & Grid + +- Base unit: **8px** +- Card padding: **20px** (2.5 units) +- Card gap (grid): **16px** (2 units) +- Section padding: **24px** (3 units) +- Nav Rail width collapsed: **72px** +- Nav Rail width expanded: **256px** +- Card min-width: **320px**, preferred: **360px** +- Card border-radius: **16px** (M3 ExtraLarge shape token) +- Status dot size: **10px** + +### 5.5 Iconography + +M3 Material Symbols (Outlined weight, `FILL=0`, `wght=400`, `GRAD=0`, `opsz=24`). + +Key icons: +- Agent status dot: Custom SVG animated circle +- Quick-Jump: `arrow_forward` or `open_in_new` +- Active: `bolt` +- Thinking: `psychology` or `autorenew` +- Error: `error_outline` +- Idle: `pause_circle` +- Settings: `settings` +- Notifications: `notifications` + +--- + +## 6. Responsiveness + +### Breakpoints (M3 Adaptive Layout) + +| Breakpoint | Width | Layout | Navigation | Columns | +|------------|-------|---------|------------|---------| +| Compact | 0–599px | Mobile | Bottom Nav | 1 | +| Medium | 600–1023px | Tablet | Nav Rail (collapsed) | 2 | +| Expanded | ≥ 1024px | Desktop/Kiosk | Nav Rail (expandable) | 3+ | + +### Adaptive Behaviors + +**Compact (Phone):** +- Cards: Full-width, stacked vertically +- Quick-Jump: Modal Bottom Sheet +- Header: Small top app bar (M3 `SmallTopAppBar`) +- Status filter: Horizontal scroll chip group + +**Medium (Tablet):** +- Cards: 2-column grid +- Nav Rail: Collapsed (icons only) +- Quick-Jump: Side panel (half-width drawer) + +**Expanded (Kiosk/Desktop):** +- Cards: 3+ column auto-fill grid +- Nav Rail: Expandable with labels +- Quick-Jump: Side drawer (480px) +- Header: Medium top app bar with subtitle + +### Touch Targets +- All interactive elements: minimum **48×48dp** (M3 spec) +- Card tap area: full card (not just button) +- Quick-Jump button: **56×56dp** (touch-optimized) +- Bottom nav items: minimum **48dp** height + +--- + +## 7. Developer Handoff Notes + +### 7.1 Technology Stack +- **Frontend:** Angular 17+ with Angular Material (M3 theming) +- **Realtime:** SignalR WebSocket hub — `AgentStatusHub` +- **State Management:** NgRx (recommended) or Angular Signals +- **Styling:** Angular Material theming + custom CSS custom properties for status colors + +### 7.2 M3 Theme Configuration (Angular Material) + +```scss +// _theme.scss +@use '@angular/material' as mat; + +$dark-theme: mat.define-theme(( + color: ( + theme-type: dark, + primary: mat.$cyan-palette, // #38BDF8 family + tertiary: mat.$violet-palette, // #A78BFA family + ), + typography: ( + brand-family: 'Inter, sans-serif', + plain-family: 'Inter, sans-serif', + bold-weight: 600, + medium-weight: 500, + regular-weight: 400, + ), + density: ( + scale: 0, + ), +)); + +// Custom CSS vars for status colors +:root { + --status-active: #38BDF8; + --status-idle: #2DD4BF; + --status-thinking: #A78BFA; + --status-error: #F87171; + --status-active-bg: rgba(56, 189, 248, 0.12); + --status-error-bg: rgba(248, 113, 113, 0.12); +} +``` + +### 7.3 Agent Card Component Interface + +```typescript +interface AgentCardData { + id: string; // e.g., "otto" + displayName: string; // e.g., "Otto" + role: string; // e.g., "Orchestrator Agent" + status: AgentStatus; // 'active' | 'idle' | 'thinking' | 'error' + currentTask?: string; // e.g., "Reviewing PR #42" + taskProgress?: number; // 0–100 + taskElapsed?: string; // e.g., "04m 12s" + sessionKey: string; // full session key + channel: string; // e.g., "telegram" + lastActivity: Date; + errorMessage?: string; // populated on error state +} + +type AgentStatus = 'active' | 'idle' | 'thinking' | 'error'; +``` + +### 7.4 SignalR Integration + +```typescript +// agent-status.service.ts +export class AgentStatusService { + private hubConnection: HubConnection; + + connect() { + this.hubConnection = new HubConnectionBuilder() + .withUrl('/hubs/agent-status') + .withAutomaticReconnect() + .build(); + + this.hubConnection.on('AgentStatusChanged', (update: AgentStatusUpdate) => { + this.store.dispatch(AgentActions.statusUpdated({ update })); + }); + + this.hubConnection.on('AgentTaskProgress', (progress: TaskProgressUpdate) => { + this.store.dispatch(AgentActions.taskProgressUpdated({ progress })); + }); + } +} +``` + +### 7.5 Animation Specs + +```css +/* Status dot pulse animations */ +@keyframes pulse-active { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.7; transform: scale(1.15); } +} + +@keyframes pulse-error { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.4; } +} + +@keyframes pulse-thinking { + 0%, 100% { opacity: 0.8; } + 50% { opacity: 0.4; } +} + +.status-dot--active { animation: pulse-active 2s ease-in-out infinite; } +.status-dot--error { animation: pulse-error 0.8s ease-in-out infinite; } +.status-dot--thinking { animation: pulse-thinking 3s ease-in-out infinite; } +.status-dot--idle { /* static */ } + +/* Quick-jump card border accent */ +.agent-card--active { border-left: 4px solid var(--status-active); } +.agent-card--error { border-left: 4px solid var(--status-error); } +.agent-card--thinking { border-left: 4px solid var(--status-thinking); } +.agent-card--idle { border-left: 2px solid var(--md-sys-color-outline); } +``` + +### 7.6 Angular Component Structure + +``` +src/app/ + command-hub/ + command-hub.component.ts ← Shell, grid layout + command-hub.component.html + command-hub.component.scss + components/ + agent-card/ + agent-card.component.ts ← Core card unit + agent-card.component.html + agent-card.component.scss + fleet-status-filter/ + fleet-status-filter.component.ts + quick-jump-drawer/ + quick-jump-drawer.component.ts ← Side drawer (desktop) + quick-jump-sheet/ + quick-jump-sheet.component.ts ← Bottom sheet (mobile) + services/ + agent-status.service.ts + store/ + agent.actions.ts + agent.reducer.ts + agent.selectors.ts + agent.effects.ts +``` + +### 7.7 Accessibility + +- All status indicators must have text alternatives (`aria-label="Status: Active"`) +- Cards are `role="article"` with `aria-labelledby` pointing to agent name +- Color is never the ONLY differentiator — icons accompany all status colors +- Focus ring: M3 focus indicator (3dp ring, `md.sys.color.secondary`) +- Reduced motion: all pulse animations respect `prefers-reduced-motion: reduce` +- Live region for status changes: `aria-live="polite"` on status badge + +### 7.8 Performance Notes + +- Agent cards use `@defer` (Angular 17) for lazy rendering below the fold +- SignalR updates trigger only targeted card updates via NgRx selectors (no full re-render) +- Status dot animations use `will-change: transform, opacity` for GPU compositing +- Limit fleet grid to visible viewport; virtual scroll for > 20 agents (`@angular/cdk/scrolling`) + +--- + +## Appendix A: Status Color Reference Card + +``` +● #38BDF8 ACTIVE — Agent currently processing a task +● #2DD4BF IDLE — Agent online, no active task +● #A78BFA THINKING — Agent processing/reasoning (LLM call in flight) +● #F87171 ERROR — Agent encountered an unhandled error +○ #64748B OFFLINE — Agent unreachable / session terminated +``` + +## Appendix B: Quick-Jump UX Flow + +``` +User taps [→ Jump] on Agent Card + │ + ├─ Desktop/Kiosk ──► Right side drawer slides in (480px) + │ Shows: live log tail, last msgs, [Open Full Session] + │ + └─ Mobile ──────────► Modal Bottom Sheet rises + Shows: agent name, 3 recent msgs, [Open Full Session] + +User taps [Open Full Session] + │ + └─ Router navigate to /sessions/:agentId + Full page Agent Session View +``` + +--- + +*Spec v1.0 — Ready for implementation. Questions → Sketch (UI/UX Agent)* diff --git a/design/mockups/desktop-kiosk-view.jpg b/design/mockups/desktop-kiosk-view.jpg new file mode 100644 index 0000000000000000000000000000000000000000..282fa8783730086d55ff12990e924f6b5fed8538 GIT binary patch literal 1955995 zcmdSAd010fvp2dkkN{#(aY&i~QKC^1NCHB_9Bmmy1BxvwgTa7+Ldd|3KmzU95g{~a zYYZqV4h=XUqd?GZAfgPSf&vmvFw7u>$fS2`+x^<-^!dK`IrqMQ+&s^I*lX`vYgO%4 zwW{j3icf8yjxLS>1pKXotOI~g{mYJRb_XB;@cCMna3CUdA9$}1{`F^)*?z!6 zU;!Tht^<^F9N3o!Al0s~R8D_Th5nr?g3Ge{Olh^xpUI@|^QRi`x1?Gao0*eI#`^-u zWMgv@h3RL>+;72T62ZB2AYX@D{l{=j-hZ3agclsh@(YjP27vU7nwk5;xKDoEjWD_h zS%qGnv>RqmOoCYxlc3UU7!(49E(Rn8OZEw_xj>hCIe*%9Efjx7zk#TUP+xn*xxp3= zgAuop<`Fdzupi(E9q11pBpu?y&kap2O{cK1z>omkmMt4e#zdpf4`EvPuManp-D$Y3 z0sg^$IFA5sWH2*;haev>v?3d%&h8*d%{xlJ*b}{_~VN z-%g1oe)oCGc{(~@r}T*7g$IPh)Iy0L=?!2o;C#X2@Z!r5mKVT z=k`iCnz$69ZiIlSsUcx-geG_gha~s-LLo%0&jdQ3J3xs5QSWmL1z!qZoEp|dJ+%D% zsH5U&#)5>U1^ZK#Lw}5B_<1z`;+Ju+D+<@0I4ocA{NUNz&F`kW-dhqw{+tB@fisA{ z#8rQ`iI5}T1%-#RtxQcnYoT!%lgBn;9}G4L1eey7X#N)+^vLS3bbr15+`%;T^9mc2 z*v7$3KjT0%6DBvDxa>2xIQRl03KZinp&$?<2BGn%h%op!uV9))vwyvU!&hnHi6ko1 z%-rA1*wV~wzpA4*oi=PaQ3%})m4nvP0bb0n7DSuA zyxb{r`L36V=hl5XIk!|pf5`4z=7<~rS)Ko8K?Yw|8ML6K-&)Z2u&{94ziehbaXpbj zCYxE1EkK(wBYxBTPnc;WalyYVL(ND42UfuL0q_#TZjZ$mGhgN+gE?*AmoF|z{~_ZF zY%%Wl5}`HC@Q!D9JMYbZK0NiT?+v>-HlMcKZ`&fRq2t#o zz4s>6-k-m0!uW$L-8$tuz zFON*lhajQZNk|#^=yxatMw(BYhtOagafSrI;NT`ENI}f|N~f^^0?$r_#}nhA5Xjd} z;du;H6G9{r=YP{j4S?fr^xK?0xvuKf$=`0v?mk%D$%$CpGECw+ya{C(As1o=u^zeW+V#9a=rN;A_AO>57C6GN0Hj17Z$YG zXD6v;gJuh|3w8U$bag^j4GlJlI|d@z&o;h&=}xy2`~J;xxBnZKYXWM{7vHz?Tg%-T z#tp#j2o4P}_6YY2Wq)g-5SNnv((`G?)1&D&V|K{ZW zCm*)+WoNYJ2Bwkdo^19i)@JjG+9lup&4<1E;k1wuZVh{LpzwP{z-hmS-5*l^gFcK2 zemdiDp5MRa!#rTbS#e>-^zDQm>fS~v_~iZ!bw!n zaqL#7axY5Wm2lJ%LDbm2rZR z)2B3w{l~v$>_6p)%>U+x{u3tiKRBYC_{nz_>T3-)?OrM0K6MS(^|9UL)TZTaZko>? z!#JIRYuD{&NyT_FcSTGoG;MF`m4{AQxxbt9bP{}jU-^A6X+?_pvy!DBzpK7s)>pH9 zouS(&h*DmiI6^u2J2!8ot7eX1hA8;(@4F$LFUs*>G~@pluIC?3?!UVpS`Ff)-sVG) zjOX231>uJnK9ATNyf!cVk@@NJ(T<<-ZGuNvr<}IEjWjtH8N7OzI|oW+&%0vWqJQr9 z-Ua7?g({En!Ie3^>kgx82Urn3M#G1$}=cxOWqS ziT?pzg8t*4|E=H37F+^Ti$6V_2I$uQ=EjKFf4x&9X@ALNzVSMy|9T52t$^e9`7U*% zZRVghivor6^ zy0@4-zkaVdGBok`#puF|zh-U0WQKHyeq-5CxFhjicm5)_FX`$|OaOPM zUkL9)(C0kh^BtHjj)CdouqFh^PJ;D-x5_VpI+(B!!Kf(-_6&T~OcV@U*g$mnH{90O zoDHU<^CdrY+vwqr+aADUhq3$vxZim2^=9CnvWF=6nJ7pD)!@HI2)7@l%C2RRM?8Jq zlkT-7mo#fM)$EeToz<)9xxQrS;959N{N3DMy9Dnfr_8(IqaOB(q~Ngp`NfX!*$ZxE zP;a%{{&U*P2ma238F5ec!apCG5lJ9h;G=IpJj@v&Zh|fcJ_atNQ{&vxHpQE(JkTB% z+H&vTuSk%O%}!+gDyCEiL2-+=M!k72+3^I~8smzyHJjG$Tk>Yt`a&Czjo#gM^;NI4 zlc2}Iz5GY;;)56R&+PoeW%DVa)a*g!))lFqJ@=QGzCHR41OLJ79E2c9W@cn_v(MSN z1#$h?CV9{Q`<4Db1S8CXP4xR7DOXN!N_~~yq+xU6X7SCt1|jWLaX|Azy&K*vzKh8f z+Q*KcuU#}anW8jOd+h{`B-vX3>4Aj5>Ga3L5gF*x&(LdW@I`Qu(&)Wa8FEuz+ z4fuAgzsCP7?d8bf`G;t4SKgOTulhY_vm5PwMpb@+5uuVTT6=Xl{t>qtP1@PHJ{BL6 zi2Hs6l=waC^hwX2eDn9V{^k0*3AcOWcG=wSe>b;ss1*n)^z7;|X=^uDvDHc@8r(W) zX|@hbwx9nlXAnR7|K#Tnjz*>d?{_(5zPK67>S$SObuD6qe944Vw$%D*(oLNclV>11 zOiR!`BX8HxDa#i5_W$Y?o%hR~ZP-mQ1uOEra)&nfRO{*08-T!D@mBzh@D%_H`R@Q2 zGsG`A)FhG&G7mZS8UMO`AehAq_hT^wT)vOSQv#S&zX1RB#^m)>im^Y5L^KZAx1MBd zZb>HknUO5~{mACwU&h!Q7{ChP`h|ya_Xe}}hh6k%`Gp2VulO_M{tR(_3GKiITL%nZ|W)iW85|Ww{brYX5#M`?)PJxzY=05wn7bz!de14)F9 z@^$<+b<(@(;J+ZoXv}{d`>$6_zmUNFydXdFdJ6!6Zu`1yYyMa^zkk3j|0V5btrLIk z@CVQdF5-V*=fBwgOds-jp)VZ>h-QXFeEIZ(ivi%8bHf+!4+&reh6iDmo%n{OuaYhq z`IIpBHv5c4Kqu<;iA04>!r|DZ$-&!irv3FHU#?T;lKSse*Bv-|H#|FmI~|% z@%YT#g}|>5lXCX|aToGsmA~r4LH__AlN-$byu{JCjejT!ZePrw0OnUE3yuuH(OgV8 z{=tF4;eH_=VG&$rz>XMpK(ynZNM57~I11Awj2mdm3W(w{!@!YEztZ!*(1(Ncrr}(_ zU{-)XEhO-BmKYQo%>06p{>j|IFU*1X9;mB-<83vK%?=3;@W%y*vID@$L5}xs$R0ac?55^kDH{SxpWyC^WDl-gw<=-nyuHU{>c((Y{Fb?+KRlzmn`P!Tu%JOF#<-0T&Op5U>Y6){hKu z9hA55`gBDK9f>4RFchg;nSeHROeM@9z%Aj*5l90CJC1$Y4e6o6$Ha0Gb*e1MNk0cd zqpnA^e2~M$<8u97TFI$qU56GZ7Y7nIrTHA9M5$mWmd0sp>8Wo&%pXWJ=3@OouF~1O-~9B zL=QQ%r#fzq=)0tq5>F=bBveBHYN-ga|9#`kX|Hmx;)%G}lSZ~ni;7Qcc}QE+qhv^ijT-=#48+2$A9>KSD(Ggm(lOj3OO3l~VDHK%q_p+e}1C zZYdg2Ye*Q)z$RIb#HMtZdx^nxUypP-y)ja+F zlyNG3^9YiV)&%tepkxF>+bH>YYGz1^gRB}qDU}g0$hS&0WOid9ujPY_T!iBF)nb4Y zDa#&L-S=#Mb&XlWP)91TFJXJGTC`!LrYSZfeEJixvr1WWvAE^qchARXo=ueS(~?|} zXt+O{O%f#xaZ6zMlO@#0*6`P*K%n?tBR0L-AesT7EJhO!n5AIZ)+z}{n6-hX{|Sho zbDc4VPr61fBo6j`XQt!Q~RIbKYku?|{xw<2_Mt~2pqQK&rMwtJC0U95mu6cZQ$n;i~U7ZBwE25E^l4u2}nFaiIc^0 zZsIX48_+GvsI&R`iQA5IFCTvbs-G1+%QIQViFNWV>Mi=|cA3}857uaA!xN-!1CiGu z9>f!+F#N(=ym(R`s+xbQ;(ki>+Kv%O2MvZ5!3O!-$j)++L#LR*)IHWNR%YHYplc{3 zbeGr1Yr1Xieo!mj*0B3@Kzw-f%jabSc@Zs-7L`|J6h+-`9dfN7UDDvP@+Qgwr>_Jw zY|kjQ7+B`>i!uUEB_eAKkz{hznI5v8uT@LZ$UH5P69F3t%fzz*n|#=U4ddp`r`%4r zR=U7MoF-wQP@zBGns@fa#9oV6m3~oqr)TxDGSAJn`XnFZ?vkk2ME7xol4NUvOmDMz z;xeHCFAyK<(0Vs{Y9Gb6z3W7<%$9H1B>`ZZaa(6V0ZhoE-8&XIdWv`{4&BG407H-> zh1BAUqvDH;6Y*u2bF;03ex2x@woZJon}l#Nh(qEQ>0Y~k{x^kT64641R`*#H_J>RTu^OP2x@3W zw@#9W>p*c%W&Q1niDwnDH-g%pwoTNJ<*t`8jro zucwfAaicLxZu>F$AUxAJNkak5|7`(axNbnerzl5 ziSDgBSf-&^4iStovJHnZE=ISyUn~e?@wFi|a=tl0lkyu{q zJ>tsXxNf#6!naPG~1T+l5_~7?!r20Kg&(u2t zPMRI~)&#<}9H9V8omt!v{>WPA`>!1bLThNN@;=gsXH9*g2A*_ImJH9ucg41sZB)kx zq7}($Np1$D#!iY2gVkP$75=PQYb$76e&+$F!3`%I0pMC##;xsSDX9hp!HU_s`CLjP z0A0r7jG2*ti@@q6IWXdutgCMTq>cC_4ZY2t&Bg(D&n0$N47|!akQMcIsB-9d*U0An z9}>&!bJ~rQcAbAwT^}f3Bf}?MI#OeiLWUGvqwHM@CBHmM_UkZS3Zy=?QCX1hWZOX5>S6E)PbE4JNd%fHD zXPYCRd;(51+ntMAoZf1jo|YOXMpc=@@|PKequTix*>H+8fm&@%!z&CW;E8_Ag@-0@ z{f4yhfmdrZjCzQ4#{o4oVc-x9BlsBa;LMwa3jvltVR4J6yvVW2~;-9`ftC z*0}t<+`z#7L*vc6LI;aCO&S>mO^4l{`)*?@S-p|#T*F4i5Re2-K+Ex!dWZKio2=6X zhT`;U>}xhpP~)r%G~%`BkZr<+B8LWbtY}AC?L*>5Cd+|}+ee#U3~pq)0st$5tR*gH zCZEiP&fgsNOQ?U@1gq1-Gqz%Tzv}w)zK5NI1qDu(!(pXCXK<;}vOZ^=QXo{YG_|FC<3@pg~#Y#ZQ+V+$)j$=%x06>J)8}`_l=!6rbiDLB6 zbnISYwne?n8=s1$l~(3=TAmI0m%=aIXE}O@oAmq zx8P~T@Nr5#^8mkI^d_f(%@HQi>FRnEf+4`We`8A?aK4 zMywF>%*}foV8WN!Fyvv7XuL?48~)zSt9{lk?qz@Il3`Y1L1@U?=1cGQM0r{7t*`fo z$Qrk#qUbSpVy!@d05mw5EVA>#sh^Xgk<=dR$bHTLtaOJKEPXTgg{+p+2>Ng_pXl6Q z%>ppWyR>R*U0r`88!&`*%eCLv!E}!VuacbFS{Uh7QV|hX92ybkIuTrWexhYlXJ}}~ z*7Oeurk$CYEK!Tvb7jQQ+(esb2Q#(ef>3q?MwmGa4)Wm7u0}p~%oplMm-PUr_m@MKCm_sU>sRKG!m?QGE&cNE*%mhL+d@bpVu$#HNjk zFt;6$MbDJj;v@}9P2_}9)vco9*;Ig4gV4se3nL9yS1U&sGqZjXG$6OO0k3cGE>7tG zKJ)DO=xj@|@0$tNoIN3Po$GhbCLDN4lJ$!tY6*It*lB%E3LV&%x>a|FQmH1JC*&hx z*!kryb#j9tfn>{>&Zhvy)6W;M4KcKQZobZ*qepK!u0fA~(eR00^J9 z@1N|-o8Id>m0-~~>^d~M_x*6@%dq^e51N+t0u}cBCAl)ZdYyw*1#!ZQmh8mKxz4`4 zYWt|UCoV9UF=$wpOKJvEuDc=gxkQAmx}N<{Xd7i_YS-^4%a71wM{Y3(2MBn)N}Eke8wj7va_-%AH>~!5R*tK=jMICH zic$v}DRP}Lozd4v(JKWwp%5T17D!iKY2!5|b~&J8K-f9o7Qnz^X`-V@7@#$=f53TO zrcIG^4d9*i^TNVAmSVsOGclFICc8|fI4FQXT?#JU>(%qgL|>ojqV~$F*vkB&kJnqf z4hF}?#Dr*B#t%_#V0>EbcDJ=LQg!<(UfX0{a0T5-GR_}#9pQ(l$l%BLoV ztjnX?y~>9#?(F^O`ZTB!tq0NliBnfY=2gq20C$6sB#Z9g)IJ2Wj~=Hj5_KE?1*alR z)Y9%afYOJGeOGVk?$9oE=M*mFKSChkC|h2utt>>zY=TCn0yS^Wm3Nj~+m&-?J^QBj zjP^g>=~?Bo%qL+=LSxhK z@}36Vgo<{d5b{e}i?*83>ZmY75!qIKpW_p_aLM0|EG zc0R`W#qEy^OnZwy0S`OF+qa4iem`3UufZp=9qR0(#|_`Yi_!R00Lek)^smobD56x4 zH`*qX704zAHbd9>F_rzu4dAsF5RiDjIt9muG4)DO&#CBb)hgUrE$*s3)lt2kiH7TJ zXmR^se`>0|DfHFLP}aH7+#gzYRz0kHaIy{X*YZ~pt{*Xk8j{|*-N!CL;?EtoUA)EyU|S6u z4cYCZ*rWp0x+ms82z!0<-Y4KrQZ!y)Md0W=*@lDS{A!TQ^*^j0`IXpJ#IJv0RT$q4!{Yo z9l{Z&KAb>OG=ipQX^jbu${*>od z+of zfT6&|A}z^>TDSLt<(xWI6>NB6lG$TGqJrIO;7vL-$RR_}N!%)px0w~+9r$?G+ADW! zy!)P)J!fy1Kd;|9bLDx~a-3)>({l__~G=C)`4zg1bnUqFm%ib~LMQa8w8N9S{NSXVdXa zn5MIm??!>y=%e5FR6EpgH;3-5#%c4?TMb2?X=1sSYDi9kpDt!~otT=6S9IkXi+s;T zMg_OW8wZXuFoYqolFJj0XyZ&Y@M{1aA&U14>UABE6x~QcCd<4TqmUl}g(g}3O1C4D zCOIFE^lR9)0I*S@>tx(p6pR#UYbL9cHTwLLq{iwYutt&QhhqFx4(3S&~@#l&_97pXm*2-E>t{od2@j?XXer_0XW_ zuiX6cW0GHL^?y9It&U)e#6twa)P*3rB95w)YXzcrDxp>*L~T6G&=%qJT7uxo<~N0F zIly7UE3ah@O;lCx4=@peR$T`KLY#-TZ}Yqz`|^4DUd!S)1|vG{uA})~Bj)3`vE%|h zyS{lZC6bVzzW2-6mAPY{DBmVb*+U4}Hh{ z_T$@i(S|VndlA*L>blMrSndU7=Ci5E&d}Vq>#v+6_|F(KS*H(fohVM<{E$AIN+dhz zar&sWa8WJ~d3Y^5M!NIn&GGD?n6zAOpSD{WBUI~lo|z|aa{ zoDKegDwE>n*KBt|WHxF_zLidC+zEEA?lH{A6=W*TUnLH_Vl~&?*0RAg2Ro^{uoglc zaKkb=x=8!=%U%w5x4iuX7#H7csS4F~%kMhfmv{4>tXHU|rLVzQtffFRoNIM&74l@% z&O2zdTqztv+lawf4VcG1Ls*aT2EiU>+z}islY-+yuvA@|d{Oyvii&WdS|$Sm#QG?r zg`D(|kKwr#I2M&ZGdG!jEfbpv-_ zgHr^WqD+E;x!A)LGNr#I)y!4osDYKJH`u*z`YqKGilD$EH9rA`KR7TT4VF-KZwZ@< zkF?7gI(0@_GW_CF=855)&6A!(lh5*|bN$n+T)hfhdnZFD!*!F>8hF=JW*7mm>2qGfuX-3E3RS39`NSn65;3`xk{R87Ue42xjc74k9X?D-`P z0HyI-O^53GyA*X~h9YknYq}`sa@EM<&hp+#Y?&aQ85c%R_2kxIBeTjG%}C9qtv`5=zNW#n(rRJc$DQc^V_7scoafUuh8 zB=YvKoZFSC#ai{9_i#(5_vb@l;*5-bRLN8LD`XCxyc}8lr17J{A zVv~8t@(`a^qcLQm6h+E~DbOJ)Oya6d!Jd2|Ol}$>)78C4*g#lmt(G^85oj}3l03T$ zhJQ~GY9VHF^n!!R`UcDU{qy!u#@j{>o!x3zbay(UcaBi-h_(AzU3){5W1JA+N*SX> zB;_SBCU*^pP$M=ZX#j|+9YT>h%fa0_WC^m=8&;?%oshvOhQTr${pkCHo&1W?jfIEd z{`vzB1RSJ35I2en9S z!fKznS?y705H4PTaw0{jC~&=8yw<|xBswY83F{wfqkNjIr!K8U;uq1IAp3|;+eVp@ zP1)(PgV#Z;)`l6TAbA}iR@S7}zcu*ccAv0GTc7r@#;t{{`(ZwNMtyuU``f*|KI)yj zl~Q|AskmDEii*{@+xwn|QM+cT6sHe<5Tvyh#Td{YuTfo92({S`a8WF*1wBAYI~(N9 zZBqfv6uFj8oyzc3fP7nM*xaE{Kz_khFIDUG$9t0oB1dk!8w{@ykGSA?jjdPKxRT^5 zG{rE|jzuL@ZA*Ka{8XHoVl~-24G|@XvmV&?N(*#vo${3=i=2B;=AwJ~yvG_{Ilk8) zw~s&F_xgBWT8&qxmb|{rX@GR>5){rg7jhGPUCbwB3c$3W z_0%sV6nP3MtIS2-v{{~L@4%^k9oHRQ!@0|)nl=_s^sG!Mo655eD*!iW#8}N6ho0Ot zp&XlDW0of1yVc8N*<-a7Dxgf!Hn4P9JbG_6{A5mQu+$8CPf?2Jq#`szAak!s;t*_7 z@X?#Pl{sP+mKB0Z#x5++@RkH07;YIJEt~KhEp{Dx=^EGDY!v*q=+!V-IN5={K0bs% z0aM4Tu?t5P_EA@}{YSYa3aWLCLKF=MQ;@jO8g3BMHfnXF$Jj!NRa#062Hio-L7p}H zfa26d_g34+G6n5Hs#WKIJ>5Au&nIG#HSHB&*uv`Er0N0~*i@<_lj^OLN@`V$9do8o zsfO8V$FT2ODHAnTa;@!|b^ZKot*nG)gg&CW!N7#jNTGFMmkZ>CYK(|QZo!w8X&3!dh zI@5+;m-wUn2IK0-g~H6hirKQRh|5*=AA!ObF(GBWxt$*(dzZUYOl5GW8xrq-sIBAD zEkKK9Bq+bO;V2H3oH13Zdkl$}&}Cs`h=n_yxZB?8dK>Y=tIDUse%m=OiS%8@{voST zs9c=iefA`tOn)}-TzGoV`=ZlD=XM`_VGK(v z#0uKRYS?CDj0G_;n3kue+a8|*S{;K56Xl4UJOoKmuoP{h2dpbYFRicEMCngSYn+vl za&E%5#+CC(SQSQ|E^DCFAF6N)fr&I4wl0l&^2)2cudumqP}QBg4L`BDwY7L+%DjuS zIh7l&z{mjAyd(S3FTGMIl$R*>0UJTbVJxadW!n%x24X0%Jey*7Yi7ZW7f(hp6he`< z2)E@qT~)B$n;1e?ZJ%uqeXcCDr>ekq`qbXNUNMor6?T5<9ci@&*)Pr7Ml}rRh59tO zn3?jR9yI@rS+aOeyT|GiAbBs=Si*ED)dBc({$E$yW9f8JDH*-*ZfTFcFt2&Cy`VO zyD&?t2~MayYM`7$((h^Kv@}l4egd`)w@kNy38zU#Ez+-J6u?R(V##@zS~=zNmvncH)!a|uApMmTTUf(IZ#w>`%;{!wcddexqfL4lHS4WDeA2p9&!!)paBG& z+8)y@p&@ajZd|&Xo)zybO`>}DUW)E-!PIs?InALWaSLm6bM=#9dI*PV)#4`gMuOh$ z_*gk}YMOcTiox@)kuLTp;9Qqp*|${ZqWD^FU`nil(%TMe zwYAh~;zI_HaNP>_78DmKrsAH=R=utqD)Smzw$(V^KCjX%zWVc0VH&q6khZo~QPQDc z7nyRbozR+cX_AT=qOVb>PL;EfJ=9MC$~vwI!4nIGsN@nr6ph3&R4J12+T7mJ!4IY- z)LQ&ZAOJ+yxYUA&bqTijM`Kkf;PEqRK3l-p& z=>Lei#H)h|xg`MhtWMcr-ml{cm02xCr+2yrgl#Q5R8_DIXoSnT8dPZ`e29R?)IOvd zhHc;Kk}<_~hBK-5jxquoZI})3<~{kPU3@(W1S&NkTsOU&MCt(T?az`91;ro*i2?M< z8hh^{n)_Yr zRJ0U>8|p7lxunRasoSxVb&s#Ut_!&{##B4%J&srC@-oz@`!q)#_NT7eoGcjgCjt5( zkfwWLj1Oo@mjK*H=L=g;|Iq!+*Vy7*Y*kj?i`IgziTMvwoO?V0eeL<>rFi}h(T2lF zn*!|F{f|-~p!JVN!DA&B>8X@ziY7(o4$)h@x+hMl5*I=Ab};6D0yrGL0}Xc*4WCpM zQ1OTBSjjMPC>lq)e6gt0KL2&-4;9vt%~L^TmoNIdbcozoe3cLzn4VTR3T!)Kzg8e@ zQD5f@#E!%Y4K}dL-6nrv)7^QG*K}~%NX)bfzsT!q2ouoC%HZ&W%_JPblq60$LhAu) z2Eg;xtE&dr=fv)*|Jd38;!s)SVwDI%Bn@!6j&AjZA}K#7N|VTSK6i=&9#_uR4hLp7 z)~9r>R-V66w<;rMA15W5W9w)!_lD|ABniRvSnG+(h6%%_KUJYIHj0zE-I$qdfB$na zgL8>zjQZooi+#*nnnN$TZYp+WyCF*`-FK3>E}dzG&>axIG_-~sjC9a=Nd-hS;GK+u zrmVFJ)O93lB%*6^`ZU8U@h+N_d0-@{=~4OS$#sBAz>OjeBN617KFZ6*688xF!^ylb z$MapIoi2pP@#gU=-#qJx!TeX78>;jDj zJC=qFgh+)cR;J!(X-N(?S^|lSp8fHoa0cJek_nS+e5gum5ppRqY`{%xh60dZRh^A` z-nBk=tIb4fOUn(r_{7UCEqfpBkH+Akj?@VpGhF~4Mfsnvh`FRH$Fi5<1#=xx`YSQ# z6fG&(r5Yzh{X(tlLE8Hwnj1T3+GKrRBXfZA)PqyGdGlZ7=~l5M*=2j<5`8xaBzZ!% zHK(?IjKL7+JF?B1CcOEabUx5uoLmDT09ZxGY)L~9TE(fJ;$&?UMi{=sjZ>sHd`Zql z)mlzUiHaa1gV}5gm=6#V$9hNO6XhhJ^XY7f)bOxOI(3{P?Q!mJ61KeePcP@AP;@T) zl5)h6rs-%l$zF3~3~TUnYN0qJb2(0hzX36zYj3D? z-+6Yp+0uW)>l5HJb1HGnr%ImU{s7Yx>sJP~P+_X!nGf?daHiNrdbJom2%sF%g^B1E zS!Y7s$qulCOCQBB;>lfWz|t0h=*6@@q~uU`lI60i#=?=rdv46RH*ppga_w`v8&Ow$ z`@hf1>w58I_{UZ?Pg5-90BnPa2Gi=n#fG_)M;Cb5}PLqfKE@u8GPbr4pCv z9@C^Q|0R5Q(snTC`Qyq{)1iC)2M=1CRG#RH9qd8#o`Ps0{yh#NdI68`1^{#tpjrtK z()pxX!iN&I+wGer4mg0lAW&EMqOK-kk{v6&SzT9SHhLI9NVoO*fkjFvJ7%9VR?O9) zSpUE!Ubt87Ohkq|s0;C$Y)g4AY7hx^L zrU(OMLx9Z!wKSJ00zjZ-66A=7L;Hos}-3E$%aS>FDYBOU$~TlT|E>%)7H0( zk4o6SSSu@)_t@H~>!WV*>kqL>aRr$b=5AYC;wPH5;Gck{Y=~ZhuQTkeAsRnegK&Ci z7k2hEhgFKMYk1>?*IGm;IvC=3C$pk;wi<~=>VTYtq~JN}4dfCyq)u9mxeO0PE~jB7 zZjS}k6orvgH#QV2l5VvuZ!gM9oL$zVv76O(^|dIbZU9B6O8b6e(;a#x=V{k0(&~^Y zMRfP~;yS!D(hE;WQ9 zIjh0KApi!big?uoT}~=RcLb>Rej+3)hLWEuRm0k!AMBcJ`Kcm467mTo%z&aPxO>F@$fHq$`fQRa-9< zuErP!Rlhy5wG+{VKt9S{eyc-)lb^ic7^x^W;&UVAQMNLbI0K#{OfM{oyIhdl@G350^KyeL%aE;iglxA>eMBmy zy>*oY(pwxjseh#gEs7y<>*4Jo6 zUXTsMXaevtbdAP}DxX8ovtq$2mlD&tNLE*3!Re0XR7Wl{G&dWU5>pNL_j8a^|^5Vd#Bjd3#*t^*1g983p-gCwdc~Kkqqz=8cQoWU&@h_Zowf6pb4N zV5vm)zWX;KUT%25{1{kCR12Z2qjl*PDZk}6pDM4$V#hLT5Q-F(=7^(%ruK%qOA___ zS#KJ+T!2MamkYt7@WLp3*FkHq{I2;0m1Vu7t?svdCR-nuAN&N&_rLq{rFLxyM2MBl z`inRn1!#B@q%SyInFa==gu^qXco9IrG4m~i6XekSn(FpMB&WZDRL^NFex*bu9B{~i z;Zp9UI% zw*-$G1`bmRYOr*H&>tYu>2gmb&+iBd(F9{+CoX%WLa+vJa8L~?R_y2UaJHOg$iOHs z_@2?Tibq+CAXrE>Bxw+7b1T%-^U0iPsQHuO+=-|U1*UsH#C_btQLB6po*j)P5qTn- zhvX-r{Mws~eJ|uD7D@BMaRAu`jWL9Qp@YB2&v&qCg#af_oZcZKKRB5Ku%zxzuNXh# zw0P;0carFkdi|Vi#jqmGAo_GsR@CYHzrHH#zf$&bQyJ*H>~ExKX#gW;heO1EC4G*X znRoy}2gT#&7TAtLFz`T31O16zh@V3pB$~QUdNNHEJ;d&eHZWu2QIsOawF?le1olt*2iAO2*{;l@{XAHb5KS^S|(fkPLUQVbIH5A#X=3za<&0@sWpR6B{I6A}88atb^-B#*kobJvl-;+9F6}jD zlTMFd9+nhF%-X8e_zaA_ubrP*t

-ddbnFj=%y(BGP6|Ad~u(g8x&kT+(Oe%*h19 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

2N4dJOBL8ho+W4l&93-0=!N0cm8 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