@openvole/volenet 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +75 -0
- package/dist/index.d.ts +1673 -0
- package/dist/index.js +6363 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/chat-outbox.ts","../src/keys.ts","../src/logger.ts","../src/seal.ts","../src/node-crypto.ts","../src/protocol.ts","../src/discovery.ts","../src/files.ts","../src/blob-store.ts","../src/file-crypto.ts","../src/leader.ts","../src/remote-task.ts","../src/result-outbox.ts","../src/sync.ts","../src/tools.ts","../src/transport.ts"],"sourcesContent":["/**\n * VoleNet Manager — lifecycle management for the distributed networking layer.\n * Initializes transport, discovery, and key management.\n * Starts/stops with the engine.\n */\n\nimport { randomUUID } from 'node:crypto'\nimport * as fs from 'node:fs/promises'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\nimport {\n\tChatOutbox,\n\tDEFAULT_NOTICE_TTL_MS,\n\tDEFAULT_OUTBOX_TTL_MS,\n\ttype OutboxEntry,\n\ttype RelayNotice,\n\tRelayNotices,\n} from './chat-outbox.js'\nimport { type DiscoveryConfig, VoleNetDiscovery } from './discovery.js'\nimport { type TransferInfo, VoleNetFiles, type VoleNetFilesConfig } from './files.js'\nimport {\n\ttype VoleKeyPair,\n\taddRelayAccept,\n\tgenerateKeyPair,\n\tloadAuthorizedVoles,\n\tloadKeyPair,\n\tloadRelayAccepts,\n\tparsePublicKey,\n\tremoveRelayAccept,\n\ttrustPeer,\n} from './keys.js'\nimport { VoleNetLeader } from './leader.js'\nimport { createLogger } from './logger.js'\nimport {\n\ttype RemoteToolInfo,\n\ttype VoleNetInstance,\n\ttype VoleNetMessage,\n\ttype VoleNetMessageType,\n\tcreateMessage,\n\tverifyMessage,\n} from './protocol.js'\nimport { RemoteTaskManager } from './remote-task.js'\nimport { DEFAULT_RESULT_TTL_MS, ResultOutbox } from './result-outbox.js'\nimport { type SealedBox, relayPairAllow, seal, unseal } from './seal.js'\nimport { type SyncConfig, VoleNetSync } from './sync.js'\nimport { type ToolProvider, isControlPlanePaw } from './tools.js'\nimport { type TransportConfig, VoleNetTransport } from './transport.js'\n\nconst logger = createLogger('volenet')\n\n/**\n * Message types NEVER wrapped in a sealed:direct envelope: the handshake/transport types that must\n * be readable to bootstrap encryption (they carry no secrets — public keys, endpoints, liveness),\n * the seal envelopes themselves, the relay envelopes (already end-to-end sealed to a third party),\n * and high-frequency leader election (not confidential). Everything else is sealed when enabled.\n */\n/**\n * Anywhere this node's events can be published. Only `emit` is ever called, so any emitter shape\n * satisfies it — OpenVole's mitt bus, an EventEmitter, or a few lines in a host that has neither.\n */\nexport interface EventSink {\n\t// biome-ignore lint/suspicious/noExplicitAny: the point is to accept any host's emitter\n\temit: (type: any, event?: any) => void\n}\n\n/** An EventSink that can also be listened to, for a host that has no bus of its own. */\nexport interface EventBus extends EventSink {\n\t// biome-ignore lint/suspicious/noExplicitAny: mirrors emit — any host handler shape\n\ton: (type: any, handler: (event?: any) => void) => void\n\t// biome-ignore lint/suspicious/noExplicitAny: mirrors on\n\toff: (type: any, handler?: (event?: any) => void) => void\n}\n\n/**\n * A minimal event bus, so a host without one can still hear what this node is doing. OpenVole\n * passes its own typed bus instead; both satisfy EventSink.\n */\nexport function createEventBus(): EventBus {\n\tconst handlers = new Map<string, Array<(event?: unknown) => void>>()\n\treturn {\n\t\temit: (type: string, event?: unknown) => {\n\t\t\tfor (const h of handlers.get(type) ?? []) h(event)\n\t\t\tfor (const h of handlers.get('*') ?? []) h(event)\n\t\t},\n\t\ton: (type: string, handler: (event?: unknown) => void) => {\n\t\t\thandlers.set(type, [...(handlers.get(type) ?? []), handler])\n\t\t},\n\t\toff: (type: string, handler?: (event?: unknown) => void) => {\n\t\t\tif (!handler) handlers.delete(type)\n\t\t\telse\n\t\t\t\thandlers.set(\n\t\t\t\t\ttype,\n\t\t\t\t\t(handlers.get(type) ?? []).filter((h) => h !== handler),\n\t\t\t\t)\n\t\t},\n\t}\n}\n\ntype MessageBus = EventSink\n\n/** The hub's verdict on a sealed envelope we sent it. `timeout` means a hub too old to give one. */\ntype RelayOutcome =\n\t| { kind: 'ack' }\n\t| { kind: 'held' }\n\t| { kind: 'error'; reason: string }\n\t| { kind: 'timeout' }\n/** How long to wait for that verdict before assuming an older hub (which never answers). */\nconst RELAY_VERDICT_TIMEOUT_MS = 4000\n\nconst PLAINTEXT_DIRECT_TYPES = new Set<VoleNetMessageType>([\n\t'discover',\n\t'discover:response',\n\t'ping',\n\t'pong',\n\t'auth:challenge',\n\t'auth:response',\n\t'auth:result',\n\t'sealed',\n\t'sealed:direct',\n\t'relay:deliver',\n\t'relay:error',\n\t'relay:ack',\n\t'relay:pending',\n\t'roster',\n\t'leader:heartbeat',\n\t'leader:claim',\n\t'leader:ack',\n])\n\nconst DEFAULT_CHAT_MAX_MESSAGES = 1000\nconst DEFAULT_CHAT_MAX_AGE_DAYS = 90\nconst CHAT_PRUNE_INTERVAL_MS = 6 * 60 * 60_000 // prune stale chat sessions every 6h\n\n/** Glob-ish tool-name match: exact, '*' wildcard, or 'prefix*'. */\n/**\n * Display prefix for a peer's namespaced tools. Peer names are self-announced labels —\n * identity is the key-derived instanceId — so when two peers share a name, the prefix\n * is disambiguated with a short id suffix: alice~3f9c/tool.\n */\nexport function peerPrefix(peerName: string, peerId: string, duplicateName: boolean): string {\n\treturn duplicateName ? `${peerName}~${peerId.substring(0, 4)}` : peerName\n}\n\n/** Whether a tool passes the share-level allowlist (empty/absent allows all). */\nexport function isSharedTool(name: string, toolAllow?: string[]): boolean {\n\tif (!toolAllow || toolAllow.length === 0) return true\n\treturn toolAllow.some((p) => matchToolPattern(p, name))\n}\n\nfunction matchToolPattern(pattern: string, name: string): boolean {\n\tif (pattern === '*' || pattern === name) return true\n\tif (pattern.endsWith('*')) return name.startsWith(pattern.slice(0, -1))\n\treturn false\n}\n\nexport interface VoleNetConfig {\n\tenabled?: boolean\n\tinstanceName?: string\n\trole?: 'coordinator' | 'worker' | 'peer'\n\tport?: number\n\t/**\n\t * Hostname this instance advertises to peers (the host in its discovery endpoint).\n\t * Defaults to the first non-internal IPv4 address. Set this to your public domain\n\t * (e.g. \"hub.example.com\") when running with TLS so the advertised endpoint matches\n\t * the certificate — otherwise peers connecting over wss/https hit a name mismatch.\n\t * Overridable at runtime via the VOLE_NET_HOSTNAME env var.\n\t */\n\thostname?: string\n\t/**\n\t * Full endpoint advertised to peers INSTEAD of `<scheme>://<hostname>:<port>` — for running\n\t * VoleNet behind a reverse proxy so the raw listen port never has to be exposed. Example:\n\t * \"https://club.example.com/mesh\", with nginx proxying that path (WebSocket upgrade included)\n\t * to the local VoleNet port. Peers join with this URL and are told to reconnect to it; the\n\t * joining side needs nothing — all peer traffic is endpoint-relative and the WS upgrade is\n\t * accepted on any path. Env override: VOLE_NET_PUBLIC_URL.\n\t */\n\tpublicUrl?: string\n\tkeyPath?: string\n\t/**\n\t * Remember a peer learned at runtime — accepting a pair request, or joining a hub — so it\n\t * survives a restart. The host owns its own config format; without this the entry is\n\t * live-only, which is the right default for a library.\n\t */\n\tpersistPeer?: (url: string) => Promise<void>\n\tpeers?: Array<{\n\t\t/**\n\t\t * Where to reach this peer. Also how the entry is matched to a connected peer, by\n\t\t * port or host. Omit it for a peer that has no address of its own — a phone, or\n\t\t * anything behind NAT that can only dial us — and identify it with `id`/`name`.\n\t\t */\n\t\turl?: string\n\t\t/**\n\t\t * Match by instance id instead of address (a full id, or a prefix of at least 8\n\t\t * characters). This is the only way to name a peer that advertises no endpoint,\n\t\t * and the only stable one for a peer whose address moves.\n\t\t */\n\t\tid?: string\n\t\t/** Match by announced instance name. Weaker than `id` — a name is not proof of identity. */\n\t\tname?: string\n\t\t/** What this peer can do on OUR instance. Defaults to 'full' — set it for a guest. */\n\t\ttrust?: 'full' | 'tool' | 'read'\n\t\tallowTools?: string[]\n\t\tdenyTools?: string[]\n\t\t/** Allow this peer to use our Brain for their tasks (LLM cost on us) */\n\t\tallowBrain?: boolean\n\t}>\n\tshare?: {\n\t\ttools?: boolean\n\t\tmemory?: boolean\n\t\tsession?: boolean\n\t\t/**\n\t\t * Patterns limiting WHICH tools are shared (advertised + callable) to peers without\n\t\t * an explicit per-peer allowTools entry — e.g. [\"club_*\"]. Empty/absent = all tools.\n\t\t * Essential for public hubs: share one curated tool set with strangers.\n\t\t */\n\t\ttoolAllow?: string[]\n\t}\n\t/** Retention for node-to-node chat sessions (volenet:<peer>). */\n\tchatRetention?: {\n\t\t/** Max messages kept per peer transcript (oldest trimmed). Default 1000. */\n\t\tmaxMessages?: number\n\t\t/** Clear chat sessions idle longer than this many days. Default 90; 0 disables. */\n\t\tmaxAgeDays?: number\n\t}\n\n\t/**\n\t * Brain source for brainless workers:\n\t * - \"local\" (default): use local brain paw\n\t * - \"remote\": delegate thinking to a peer that allows brain sharing\n\t * - \"<instanceName>\": delegate to a specific peer's brain\n\t */\n\tbrainSource?: 'local' | 'remote' | string\n\ttls?: {\n\t\tcert: string\n\t\tkey: string\n\t}\n\t/** Max concurrent inbound VoleNet WebSocket connections (DoS). Default 1000. */\n\tmaxConnections?: number\n\t/** Close inbound WS that don't send a verified message within this many ms (DoS). Default 10000. */\n\tauthTimeoutMs?: number\n\t/** Global inbound message ceiling per second across all sources (load shed). Default 5000. */\n\tmaxMessagesPerSecond?: number\n\tdiscovery?: 'manual' | 'mdns'\n\trouting?: Record<string, string>\n\n\t/**\n\t * Leader selection mode:\n\t * - \"auto\" (default): lowest instance ID wins, automatic failover\n\t * - \"<instanceName>\": force a specific instance as leader\n\t */\n\tleader?: 'auto' | string\n\n\t/**\n\t * Heartbeat mode:\n\t * - \"leader\" (default): only the leader runs heartbeat/schedules\n\t * - \"independent\": each instance runs its own heartbeat independently\n\t */\n\theartbeatMode?: 'leader' | 'independent'\n\n\t/**\n\t * Brain load balancing:\n\t * - \"local\" (default): each instance handles its own tasks\n\t * - \"loadbalance\": route incoming tasks to the least-loaded brain across peers\n\t */\n\tbrainMode?: 'local' | 'loadbalance'\n\n\t/**\n\t * Task overflow behavior when local queue is full:\n\t * - \"reject\" (default): reject the task\n\t * - \"forward\": forward to the least-loaded peer automatically\n\t */\n\ttaskOverflow?: 'reject' | 'forward'\n\n\t/** Max queued tasks before overflow triggers (default: 10) */\n\tmaxQueuedTasks?: number\n\n\t/**\n\t * Public self-join — let unknown peers register over HTTP and join at a restricted\n\t * \"guest\" trust level (for a public mesh hub). Off by default. Guests are NEVER 'full'.\n\t */\n\tpublicJoin?: {\n\t\tenabled?: boolean\n\t\t/** Trust granted to self-joined guests. Never 'full'. Default 'tool'. */\n\t\ttrustLevel?: 'read' | 'tool'\n\t\t/** Let guests use OUR Brain (LLM cost on us). Default false. */\n\t\tallowBrain?: boolean\n\t\t/** Max trusted peers before new joins are refused. Default 200. */\n\t\tmaxPeers?: number\n\t\t/** Join requests allowed per minute per IP. Default 5. */\n\t\tratePerMinute?: number\n\t\t/** Queue joins to pending_joins.jsonl for manual `vole net trust` instead of auto-trusting. */\n\t\trequireApproval?: boolean\n\t}\n\t/**\n\t * Blind relay (hub side): forward sealed member↔member envelopes the hub cannot read.\n\t * v1 carries end-to-end encrypted chat only — no tool calls or delegation ride the relay.\n\t */\n\trelay?: {\n\t\tenabled?: boolean\n\t\t/** Forwards allowed per minute per (sender, recipient) pair. Default 30. */\n\t\tmaxPerMinutePerPair?: number\n\t\t/** Max sealed envelope size in bytes. Default 65536. */\n\t\tmaxBytes?: number\n\t\t/**\n\t\t * Member side: who may reach ME over a relay. Sharing a hub is not consent — a member's\n\t\t * relayed chat is dropped until I accept it. Default (unset): only peers I've explicitly\n\t\t * approved (a connect-request) or already directly trust. '*' opens me to any hub member\n\t\t * (community-hub behaviour). A list pre-authorises peers by name or instanceId prefix.\n\t\t */\n\t\tacceptFrom?: '*' | string[]\n\t\t/**\n\t\t * Hub side: how long a \"somebody tried to reach you\" notice is kept for a member who is\n\t\t * away, in hours. Default 168 (a week). A notice is sender, count and times — the message\n\t\t * itself is never held by the hub.\n\t\t */\n\t\tnoticeTtlHours?: number\n\t\t/**\n\t\t * Member side: how long an undelivered chat message waits in THIS node's own outbox for\n\t\t * its recipient to reappear, in hours. Default 168 (a week).\n\t\t */\n\t\toutboxTtlHours?: number\n\t}\n\t/**\n\t * Direct end-to-end encryption. When true, post-handshake messages to a peer that supports it\n\t * (announces an ML-KEM key) are sealed with the hybrid X25519 + ML-KEM-768 KEM before sending —\n\t * confidentiality independent of TLS, and post-quantum. Opportunistic: peers that don't support\n\t * it (older versions) still receive plaintext, so a mixed-version mesh keeps working. Default off.\n\t */\n\tencrypt?: boolean\n\t/**\n\t * Publish peer display names (the live announced `instanceName`) in the public /volenet/info\n\t * response. Off by default — names are an enumeration surface. Turn on for a public hub whose\n\t * members are meant to be seen (e.g. a social wall), so external tooling can read live names\n\t * without the authenticated dashboard.\n\t */\n\tpublishNames?: boolean\n\t/**\n\t * VoleDrop — E2E-encrypted file transfer (files stream over /volenet/blob/*; the per-transfer\n\t * key is sealed with the PQ-hybrid seal). `acceptFrom` mirrors relay.acceptFrom: unset means\n\t * every offer waits for an explicit accept; '*' or a name/id-prefix list auto-accepts (use for\n\t * your own fleet). Hubs with relay enabled also store ciphertext blobs for NAT'd member pairs,\n\t * bounded by relayQuotaBytes/relayTtlHours.\n\t */\n\tfiles?: VoleNetFilesConfig\n}\n\n/** A hub-vouched mesh member, learned from a relay hub's roster broadcast. */\nexport interface RosterMember {\n\tinstanceId: string\n\tname: string\n\tpublicKey: string\n\txPublicKey?: string\n\tmlkemPublicKey?: string\n\tconnected: boolean\n}\n\n/** A single human-capable peer-chat message, stored per peer for the dashboard. */\nexport interface ChatEntry {\n\tdir: 'in' | 'out'\n\ttext: string\n\tfromName: string\n\ttimestamp: number\n\tmessageId: string\n\t/** True when this message travelled through a relay hub as a sealed envelope. */\n\trelayed?: boolean\n}\n\nexport class VoleNetManager {\n\tprivate keyPair: VoleKeyPair | null = null\n\tprivate transport: VoleNetTransport | null = null\n\tprivate discovery: VoleNetDiscovery | null = null\n\tprivate remoteTaskMgr: RemoteTaskManager | null = null\n\tprivate sync: VoleNetSync | null = null\n\tprivate leader: VoleNetLeader | null = null\n\tprivate toolProviders = new Map<string, string[]>()\n\t/** registered remote tool name → owning instanceId (identity-keyed routing; never by peer name) */\n\tprivate remoteToolOwners = new Map<string, string>()\n\tprivate config: VoleNetConfig\n\tprivate projectRoot: string\n\tprivate toolRegistry: ToolProvider | null = null\n\tprivate started = false\n\t/** Per-IP join timestamps for public-join rate limiting. */\n\tprivate joinTimestamps = new Map<string, number[]>()\n\t/** Relay: per-(from,to) forward windows (hub side). */\n\tprivate relayWindows = new Map<string, number[]>()\n\t/** Relay: hub-vouched member rosters, keyed by hub instanceId. */\n\tprivate hubRosters = new Map<string, Map<string, RosterMember>>()\n\t/** Relay: inner-message replay guard — the transport's outer guard can't see re-wraps. */\n\tprivate seenSealed = new Map<string, number>()\n\tprivate rosterTimer: ReturnType<typeof setTimeout> | undefined\n\t/** Relay consent gate: instanceIds whose relayed chat I accept (persisted to relay_accepts). */\n\tprivate relayAcceptIds = new Set<string>()\n\tprivate messageBus: MessageBus | null = null\n\t/** Sender side: chat the hub could not forward, waiting on its recipient. Own disk only. */\n\tprivate chatOutbox: ChatOutbox | null = null\n\t/** Sender side: envelopes awaiting the hub's verdict, keyed by the ref on the outer envelope. */\n\tprivate relayInflight = new Map<string, { to: string; settle: (o: RelayOutcome) => void }>()\n\tprivate outboxFlushing = false\n\t/** Member side: who tried to reach me while I was away, as a hub told me on reconnect. */\n\tprivate chatPending = new Map<string, RelayNotice & { viaHub: string }>()\n\t/** Member side: when I last received chat from each sender — a notice older than that is stale. */\n\tprivate lastChatFrom = new Map<string, number>()\n\t/** Hub side: notices for members who are away. */\n\tprivate relayNotices: RelayNotices | null = null\n\t/** Brain answers whose asker had gone by the time they were ready — see result-outbox.ts. */\n\tprivate resultOutbox: ResultOutbox | null = null\n\t/** Peers a flush is already running for, so a burst of pings does not send an answer twice. */\n\tprivate flushingResults = new Set<string>()\n\t/** The polling timers waiting on delegated tasks, so stopping cancels them. */\n\tprivate delegationTimers = new Set<ReturnType<typeof setInterval>>()\n\t/** Relay: inbound connect-requests awaiting my approval (in-memory; keyed by requester id). */\n\tprivate relayRequests = new Map<\n\t\tstring,\n\t\t{ id: string; name: string; viaHub: string; viaHubName: string; note?: string; ts: number }\n\t>()\n\t/** Relay: connect-requests I sent that aren't confirmed yet (for the \"awaiting\" UI hint). */\n\tprivate relayOutgoing = new Set<string>()\n\t/** Relay: peers that have accepted MY request (in-memory; distinguishes connected vs awaiting). */\n\tprivate relayConfirmed = new Set<string>()\n\t/** Per-peer human chat logs (in-memory; keyed by peer instanceId). */\n\tprivate chatLog = new Map<string, ChatEntry[]>()\n\t/** Periodically re-attempts configured peers — self-heals start-order races + drops. */\n\tprivate peerConnectTimer?: ReturnType<typeof setInterval>\n\t/** Periodic chat-session retention prune. */\n\tprivate chatPruneTimer?: ReturnType<typeof setInterval>\n\t/** VoleDrop file transfer engine (net.files). */\n\tprivate files: VoleNetFiles | null = null\n\t/** Consent-based pairing: inbound requests awaiting the operator (persisted to pair_requests.json). */\n\tprivate pairRequests = new Map<\n\t\tstring,\n\t\t{ id: string; name: string; publicKey: string; endpoint?: string; note?: string; ts: number }\n\t>()\n\t/** Per-IP pair-request timestamps (rate limiting, same shape as publicJoin's). */\n\tprivate pairTimestamps = new Map<string, number[]>()\n\n\tconstructor(config: VoleNetConfig, projectRoot: string) {\n\t\tthis.config = config\n\t\tthis.projectRoot = projectRoot\n\t}\n\n\t/**\n\t * Start VoleNet — load keys, start transport, connect to peers.\n\t */\n\tasync start(toolRegistry?: ToolProvider, bus?: EventSink): Promise<void> {\n\t\tif (this.started) return\n\t\tif (!this.config.enabled) return\n\n\t\tthis.toolRegistry = toolRegistry ?? null\n\t\tconst messageBus = bus\n\t\tthis.messageBus = bus ?? null\n\t\tconst netDir = this.getNetDir()\n\n\t\t// Load or generate keypair\n\t\tthis.keyPair = await loadKeyPair(netDir)\n\t\tif (!this.keyPair) {\n\t\t\tlogger.info('No keypair found — generating new Ed25519 keypair')\n\t\t\tthis.keyPair = await generateKeyPair(netDir, this.config.instanceName ?? 'vole')\n\t\t}\n\n\t\tlogger.info(`Instance ID: ${this.keyPair.instanceId}`)\n\t\tlogger.info(`Public key: ${this.keyPair.publicKeyString}`)\n\t\t// Activate the post-quantum signing key (when this keypair has one) for hybrid signatures.\n\n\t\t// Relay consent: load previously-approved senders so past approvals survive restarts.\n\t\ttry {\n\t\t\tthis.relayAcceptIds = new Set((await loadRelayAccepts(netDir)).keys())\n\t\t} catch {\n\t\t\tthis.relayAcceptIds = new Set()\n\t\t}\n\n\t\t// Chat that cannot be delivered waits on this node, never on a hub — see chat-outbox.ts.\n\t\tconst hoursToMs = (h: number | undefined, fallback: number) =>\n\t\t\ttypeof h === 'number' && h > 0 ? h * 60 * 60 * 1000 : fallback\n\t\tthis.chatOutbox = new ChatOutbox(\n\t\t\tpath.join(netDir, 'chat_outbox.json'),\n\t\t\thoursToMs(this.config.relay?.outboxTtlHours, DEFAULT_OUTBOX_TTL_MS),\n\t\t)\n\t\tawait this.chatOutbox.load().catch(() => undefined)\n\t\t// An answer nobody could receive waits here rather than being written to a dead socket.\n\t\tthis.resultOutbox = new ResultOutbox(\n\t\t\tpath.join(netDir, 'result_outbox.json'),\n\t\t\thoursToMs(this.config.relay?.outboxTtlHours, DEFAULT_RESULT_TTL_MS),\n\t\t)\n\t\tawait this.resultOutbox.load().catch(() => undefined)\n\t\tif (this.config.relay?.enabled) {\n\t\t\tthis.relayNotices = new RelayNotices(\n\t\t\t\tpath.join(netDir, 'relay_notices.json'),\n\t\t\t\thoursToMs(this.config.relay?.noticeTtlHours, DEFAULT_NOTICE_TTL_MS),\n\t\t\t)\n\t\t\tawait this.relayNotices.load().catch(() => undefined)\n\t\t}\n\n\t\t// Start transport\n\t\tconst port = this.config.port ?? 9700\n\t\tconst transportConfig: TransportConfig = {\n\t\t\tport,\n\t\t\ttls: this.config.tls,\n\t\t\tmaxConnections: this.config.maxConnections,\n\t\t\tauthTimeoutMs: this.config.authTimeoutMs,\n\t\t\tmaxMessagesPerSecond: this.config.maxMessagesPerSecond,\n\t\t\tpublishNames: this.config.publishNames,\n\t\t}\n\t\tthis.transport = new VoleNetTransport(transportConfig)\n\t\t// Live display names for /volenet/info (opt-in): the announced instanceName from discovery,\n\t\t// with the hub-vouched roster as a fallback for relay-only members.\n\t\tthis.transport.setNameResolver(\n\t\t\t(id) =>\n\t\t\t\tthis.discovery?.getInstances().find((i) => i.id === id)?.name ??\n\t\t\t\tthis.findRosterMember(id)?.name,\n\t\t)\n\t\tawait this.transport.start()\n\n\t\t// Start discovery\n\t\tconst endpoint = buildAdvertisedEndpoint({\n\t\t\tpublicUrl: this.config.publicUrl ?? process.env.VOLE_NET_PUBLIC_URL,\n\t\t\ttls: !!this.config.tls,\n\t\t\thostname: this.getHostname(),\n\t\t\tport,\n\t\t})\n\n\t\tconst discoveryConfig: DiscoveryConfig = {\n\t\t\tnetDir,\n\t\t\tinstanceId: this.keyPair.instanceId,\n\t\t\tinstanceName: this.config.instanceName ?? 'vole',\n\t\t\trole: this.config.role ?? 'peer',\n\t\t\tendpoint,\n\t\t\tcapabilities: this.getCapabilities(),\n\t\t\tprivateKey: this.keyPair.privateKey,\n\t\t\tpqPrivateKey: this.keyPair.pqPrivateKey,\n\t\t\tpublicKeyString: this.keyPair.publicKeyString,\n\t\t\tconfiguredPeerUrls: (this.config.peers ?? [])\n\t\t\t\t.map((p) => p.url)\n\t\t\t\t.filter((u): u is string => typeof u === 'string' && u.length > 0),\n\t\t\txPublicKeyB64: this.keyPair.xPublicKeyB64,\n\t\t\tmlkemPublicKeyB64: this.keyPair.mlkemPublicKeyB64,\n\t\t}\n\t\tthis.discovery = new VoleNetDiscovery(this.transport, discoveryConfig)\n\t\tawait this.discovery.start()\n\n\t\t// Bind the transport's WS authentication + inline HTTP discovery reply to discovery's\n\t\t// keystore: sockets are only bound to a peer id after a verified message, and NAT'd peers\n\t\t// learn our identity from their own discover request's response body.\n\t\tconst discovery = this.discovery\n\t\tthis.transport.setVerifier((m) => discovery.verifyMessageFrom(m))\n\t\tthis.transport.setResponder((m) => discovery.buildDiscoverResponse(m))\n\n\t\t// Public self-join: accept HTTP join requests from unknown peers (restricted guest trust).\n\t\tif (this.config.publicJoin?.enabled) {\n\t\t\tthis.transport.setJoinHandler((body, ip) => this.handlePublicJoin(body, ip))\n\t\t\tlogger.info(\n\t\t\t\t`Public join enabled — guests get '${this.config.publicJoin.trustLevel ?? 'tool'}' trust, allowBrain=${this.config.publicJoin.allowBrain ?? false}`,\n\t\t\t)\n\t\t}\n\n\t\t// Consent-based pairing: always on (nothing is trusted without the operator's accept).\n\t\t// The initiator fetches our public key from /volenet/info to fingerprint us first.\n\t\tthis.transport.setIdentityProvider(() => ({\n\t\t\tpublicKey: this.keyPair?.publicKeyString ?? '',\n\t\t\tinstanceId: this.keyPair?.instanceId,\n\t\t\t...(this.config.publishNames ? { name: this.config.instanceName ?? 'vole' } : {}),\n\t\t}))\n\t\tthis.transport.setPairHandler((body, ip) => this.handlePairRequest(body, ip, messageBus))\n\t\tawait this.loadPairRequests()\n\n\t\t// Anything verified from a peer proves its channel is live, which is the moment to hand\n\t\t// over an answer it was not around to receive. A phone announces and then pings, so this\n\t\t// fires within a second of it reopening — there is nothing to poll and nothing to ask for.\n\t\tthis.transport.onMessage((message) => {\n\t\t\tif (!this.resultOutbox?.has(message.from)) return\n\t\t\tif (!this.discovery?.verifyMessageFrom(message)) return\n\t\t\tvoid this.flushResultsFor(message.from)\n\t\t})\n\n\t\t// Handle tool:list requests — respond with our local tools\n\t\tthis.transport.onMessage((message) => {\n\t\t\tif (message.type === 'tool:list' && this.toolRegistry && this.keyPair) {\n\t\t\t\tif (!this.discovery?.verifyMessageFrom(message)) return\n\t\t\t\tif (!this.peerToolsEnabled(message.from)) return\n\t\t\t\tconst tools: RemoteToolInfo[] = this.toolRegistry\n\t\t\t\t\t.list()\n\t\t\t\t\t.filter((t) => !t.pawName.startsWith('__volenet')) // don't echo remote tools back\n\t\t\t\t\t// Never lend the control plane. A shared tool executes on its owner, so a peer\n\t\t\t\t\t// calling one of these would act with the owner's authority — every local check\n\t\t\t\t\t// passes, because by then the call genuinely is the owner's.\n\t\t\t\t\t.filter((t) => !isControlPlanePaw(t.pawName))\n\t\t\t\t\t.filter((t) => isSharedTool(t.name, this.config.share?.toolAllow))\n\t\t\t\t\t.map((t) => ({\n\t\t\t\t\t\tname: t.name,\n\t\t\t\t\t\tdescription: t.description,\n\t\t\t\t\t\tpawName: t.pawName,\n\t\t\t\t\t\tinstanceId: this.keyPair!.instanceId,\n\t\t\t\t\t\tinstanceName: this.config.instanceName ?? 'vole',\n\t\t\t\t\t}))\n\t\t\t\tconst response = createMessage(\n\t\t\t\t\t'tool:list:response',\n\t\t\t\t\tthis.keyPair.instanceId,\n\t\t\t\t\tmessage.from,\n\t\t\t\t\ttools,\n\t\t\t\t\tthis.keyPair.privateKey,\n\t\t\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\t\t)\n\t\t\t\tthis.transport!.sendToPeer(message.from, response)\n\t\t\t}\n\t\t})\n\n\t\t// Handle incoming tool:call — execute locally and return result\n\t\tthis.transport.onMessage(async (message) => {\n\t\t\tif (message.type === 'tool:call' && this.toolRegistry && this.keyPair) {\n\t\t\t\tif (!this.discovery?.verifyMessageFrom(message)) {\n\t\t\t\t\tlogger.warn(`Rejected unverified tool:call from ${message.from.substring(0, 8)}`)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst { callId, toolName, params } = message.payload as {\n\t\t\t\t\tcallId: string\n\t\t\t\t\ttoolName: string\n\t\t\t\t\tparams: unknown\n\t\t\t\t}\n\t\t\t\tlogger.info(\n\t\t\t\t\t`Remote tool call from ${message.from.substring(0, 8)}: ${toolName}(${JSON.stringify(params)})`,\n\t\t\t\t)\n\n\t\t\t\tif (!this.isPeerAllowedTool(message.from, toolName)) {\n\t\t\t\t\tlogger.warn(`Tool access denied for ${message.from.substring(0, 8)}: ${toolName}`)\n\t\t\t\t\tthis.transport!.sendToPeer(\n\t\t\t\t\t\tmessage.from,\n\t\t\t\t\t\tcreateMessage(\n\t\t\t\t\t\t\t'tool:result',\n\t\t\t\t\t\t\tthis.keyPair.instanceId,\n\t\t\t\t\t\t\tmessage.from,\n\t\t\t\t\t\t\t{ callId, success: false, error: `Tool access not allowed: ${toolName}` },\n\t\t\t\t\t\t\tthis.keyPair.privateKey,\n\t\t\t\t\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst tool = this.toolRegistry.get(toolName)\n\t\t\t\tlet response\n\t\t\t\tif (!tool) {\n\t\t\t\t\tlogger.warn(`Remote tool call failed: \"${toolName}\" not found`)\n\t\t\t\t\tresponse = createMessage(\n\t\t\t\t\t\t'tool:result',\n\t\t\t\t\t\tthis.keyPair.instanceId,\n\t\t\t\t\t\tmessage.from,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcallId,\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: `Tool \"${toolName}\" not found`,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tthis.keyPair.privateKey,\n\t\t\t\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst startTime = Date.now()\n\t\t\t\t\t\t// Attach the transport-verified caller so tools can attribute actions\n\t\t\t\t\t\t// (e.g. paw-club posts). Always overwritten — a peer-supplied __caller can\n\t\t\t\t\t\t// never impersonate another instance.\n\t\t\t\t\t\tconst callerName = this.discovery\n\t\t\t\t\t\t\t?.getInstances()\n\t\t\t\t\t\t\t.find((i) => i.id === message.from)?.name\n\t\t\t\t\t\tconst output = await tool.execute(withVerifiedCaller(params, message.from, callerName))\n\t\t\t\t\t\tconst durationMs = Date.now() - startTime\n\t\t\t\t\t\tconst outputPreview =\n\t\t\t\t\t\t\ttypeof output === 'string'\n\t\t\t\t\t\t\t\t? output.substring(0, 200)\n\t\t\t\t\t\t\t\t: JSON.stringify(output).substring(0, 200)\n\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t`Remote tool call completed: ${toolName} → success (${durationMs}ms) — ${outputPreview}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tmessageBus?.emit('volenet:tool:executed', {\n\t\t\t\t\t\t\ttoolName,\n\t\t\t\t\t\t\tfromInstance: message.from.substring(0, 8),\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tdurationMs,\n\t\t\t\t\t\t})\n\t\t\t\t\t\tresponse = createMessage(\n\t\t\t\t\t\t\t'tool:result',\n\t\t\t\t\t\t\tthis.keyPair.instanceId,\n\t\t\t\t\t\t\tmessage.from,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcallId,\n\t\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\t\toutput,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tthis.keyPair.privateKey,\n\t\t\t\t\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\t\t\t\t)\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tconst errorMsg = err instanceof Error ? err.message : String(err)\n\t\t\t\t\t\tlogger.error(`Remote tool call failed: ${toolName} → ${errorMsg}`)\n\t\t\t\t\t\tmessageBus?.emit('volenet:tool:executed', {\n\t\t\t\t\t\t\ttoolName,\n\t\t\t\t\t\t\tfromInstance: message.from.substring(0, 8),\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\tdurationMs: 0,\n\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t})\n\t\t\t\t\t\tresponse = createMessage(\n\t\t\t\t\t\t\t'tool:result',\n\t\t\t\t\t\t\tthis.keyPair.instanceId,\n\t\t\t\t\t\t\tmessage.from,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcallId,\n\t\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tthis.keyPair.privateKey,\n\t\t\t\t\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tawait this.transport!.sendToPeer(message.from, response)\n\t\t\t\tlogger.info(`Remote tool result sent to ${message.from.substring(0, 8)}`)\n\t\t\t}\n\t\t})\n\n\t\t// When peer tool lists arrive, register them as remote tools in local registry\n\t\tthis.transport.onMessage((message) => {\n\t\t\tif (\n\t\t\t\tmessage.type === 'tool:list:response' &&\n\t\t\t\tthis.toolRegistry &&\n\t\t\t\tthis.keyPair &&\n\t\t\t\tthis.remoteTaskMgr\n\t\t\t) {\n\t\t\t\tconst tools = message.payload as RemoteToolInfo[]\n\t\t\t\tif (!Array.isArray(tools) || tools.length === 0) return\n\n\t\t\t\tconst remoteTaskMgr = this.remoteTaskMgr\n\t\t\t\tconst sourceInstanceId = message.from\n\n\t\t\t\tconst peerInstance = this.discovery?.getInstances().find((i) => i.id === message.from)\n\t\t\t\tconst peerName = peerInstance?.name ?? message.from.substring(0, 8)\n\t\t\t\t// Names are labels, not identity: disambiguate everything by id when peers collide.\n\t\t\t\tconst sourceDup =\n\t\t\t\t\t(this.discovery?.getInstances() ?? []).filter((i) => i.name === peerName).length > 1\n\t\t\t\tif (sourceDup) {\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`Two peers share the name \"${peerName}\" — tools disambiguated as ${peerPrefix(peerName, message.from, true)}/<tool>`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tconst pawLabel = `__volenet:${peerPrefix(peerName, message.from, sourceDup)}__`\n\n\t\t\t\t// Track which peers provide which tools (for load-balanced routing)\n\t\t\t\tfor (const t of tools) {\n\t\t\t\t\tif (!this.toolProviders) this.toolProviders = new Map()\n\t\t\t\t\tconst providers = this.toolProviders.get(t.name) ?? []\n\t\t\t\t\tif (!providers.includes(sourceInstanceId)) {\n\t\t\t\t\t\tproviders.push(sourceInstanceId)\n\t\t\t\t\t\tthis.toolProviders.set(t.name, providers)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst discovery = this.discovery!\n\t\t\t\tconst toolProviders = this.toolProviders!\n\n\t\t\t\t// Create wrapper tool definitions — handle duplicates with peer-specific names\n\t\t\t\tconst remoteToolDefs: Array<{\n\t\t\t\t\tname: string\n\t\t\t\t\tdescription: string\n\t\t\t\t\tparameters: any\n\t\t\t\t\texecute: (params: unknown) => Promise<unknown>\n\t\t\t\t}> = []\n\n\t\t\t\tfor (const t of tools) {\n\t\t\t\t\tconst existingTool = this.toolRegistry!.get(t.name)\n\t\t\t\t\tconst isLocalTool = existingTool && !existingTool.pawName.startsWith('__volenet')\n\n\t\t\t\t\tif (isLocalTool) {\n\t\t\t\t\t\t// Don't override local tools — skip\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// Re-announcement from the same peer (no other provider) — nothing to conflict with.\n\t\t\t\t\tconst otherProviders = (toolProviders.get(t.name) ?? []).filter(\n\t\t\t\t\t\t(id) => id !== sourceInstanceId,\n\t\t\t\t\t)\n\t\t\t\t\tif (existingTool && otherProviders.length === 0) continue\n\n\t\t\t\t\tif (existingTool) {\n\t\t\t\t\t\t// Another peer already registered this tool name — we have a conflict.\n\t\t\t\t\t\t// Route by IDENTITY: the recorded owner of the plain-name registration,\n\t\t\t\t\t\t// never a name lookup. No fall-back to \"some other provider\": once the\n\t\t\t\t\t\t// plain name has been converted to a load-balanced alias its owner entry\n\t\t\t\t\t\t// is gone, and guessing an owner here re-renamed the alias to whichever\n\t\t\t\t\t\t// peer announced LAST — mislabeled cross-peer tools on every cycle.\n\t\t\t\t\t\tconst existingOwnerId = this.remoteToolOwners.get(t.name)\n\n\t\t\t\t\t\t// Only rename existing if it hasn't been renamed yet (still plain name),\n\t\t\t\t\t\t// and never on the owner's own re-announcement.\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!existingTool.name.includes('/') &&\n\t\t\t\t\t\t\texistingOwnerId &&\n\t\t\t\t\t\t\texistingOwnerId !== sourceInstanceId\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tconst existingInst = discovery.getInstances().find((i) => i.id === existingOwnerId)\n\t\t\t\t\t\t\tconst existingName = existingInst?.name ?? existingOwnerId.substring(0, 8)\n\t\t\t\t\t\t\tconst existingDup =\n\t\t\t\t\t\t\t\texistingName === peerName ||\n\t\t\t\t\t\t\t\tdiscovery.getInstances().filter((i) => i.name === existingName).length > 1\n\t\t\t\t\t\t\tconst renamedTo = `${peerPrefix(existingName, existingOwnerId, existingDup)}/${t.name}`\n\t\t\t\t\t\t\tconst ownerId = existingOwnerId\n\t\t\t\t\t\t\tconst renamedExisting = {\n\t\t\t\t\t\t\t\tname: renamedTo,\n\t\t\t\t\t\t\t\tdescription: existingTool.description,\n\t\t\t\t\t\t\t\tparameters: { parse: () => {} } as any,\n\t\t\t\t\t\t\t\tasync execute(params: unknown) {\n\t\t\t\t\t\t\t\t\tconst result = await remoteTaskMgr.executeRemoteTool(ownerId, t.name, params)\n\t\t\t\t\t\t\t\t\tif (result.success) return result.output\n\t\t\t\t\t\t\t\t\tthrow new Error(result.error ?? 'Remote tool execution failed')\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthis.toolRegistry!.register(existingTool.pawName, [renamedExisting], false)\n\t\t\t\t\t\t\tthis.remoteToolOwners.set(renamedTo, ownerId)\n\t\t\t\t\t\t\t// The plain name lives on as a load-balanced alias across all providers.\n\t\t\t\t\t\t\tthis.remoteToolOwners.delete(t.name)\n\t\t\t\t\t\t\tlogger.info(`Renamed remote tool ${t.name} → ${renamedTo} (conflict resolution)`)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Register the new peer's tool under its (possibly id-suffixed) prefix\n\t\t\t\t\t\tconst newDup =\n\t\t\t\t\t\t\tsourceDup ||\n\t\t\t\t\t\t\t(existingOwnerId !== undefined &&\n\t\t\t\t\t\t\t\t(discovery.getInstances().find((i) => i.id === existingOwnerId)?.name ?? '') ===\n\t\t\t\t\t\t\t\t\tpeerName)\n\t\t\t\t\t\tconst prefixedName = `${peerPrefix(peerName, sourceInstanceId, newDup)}/${t.name}`\n\t\t\t\t\t\t// Idempotence: this peer's prefixed tool is already registered — a plain\n\t\t\t\t\t\t// re-announcement (every discovery cycle) must not re-register it, or the\n\t\t\t\t\t\t// registry emits tool:registered spam and mangles duplicate names.\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tthis.toolRegistry!.get(prefixedName) &&\n\t\t\t\t\t\t\tthis.remoteToolOwners.get(prefixedName) === sourceInstanceId\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.remoteToolOwners.set(prefixedName, sourceInstanceId)\n\t\t\t\t\t\tremoteToolDefs.push({\n\t\t\t\t\t\t\tname: prefixedName,\n\t\t\t\t\t\t\tdescription: `[remote: ${peerName}] ${t.description}`,\n\t\t\t\t\t\t\tparameters: { parse: () => {} } as any,\n\t\t\t\t\t\t\tasync execute(params: unknown) {\n\t\t\t\t\t\t\t\tconst result = await remoteTaskMgr.executeRemoteTool(\n\t\t\t\t\t\t\t\t\tsourceInstanceId,\n\t\t\t\t\t\t\t\t\tt.name,\n\t\t\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tif (result.success) return result.output\n\t\t\t\t\t\t\t\tthrow new Error(result.error ?? 'Remote tool execution failed')\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// First registration — plain name, load-balanced across providers by id.\n\t\t\t\t\t\t// Record the owner so a future conflict can rename it correctly.\n\t\t\t\t\t\tthis.remoteToolOwners.set(t.name, sourceInstanceId)\n\t\t\t\t\t\tremoteToolDefs.push({\n\t\t\t\t\t\t\tname: t.name,\n\t\t\t\t\t\t\tdescription: `[remote: ${peerName}] ${t.description}`,\n\t\t\t\t\t\t\tparameters: { parse: () => {} } as any,\n\t\t\t\t\t\t\tasync execute(params: unknown) {\n\t\t\t\t\t\t\t\t// Pick best peer: least loaded among all providers of this tool\n\t\t\t\t\t\t\t\tconst providers = toolProviders.get(t.name) ?? [sourceInstanceId]\n\t\t\t\t\t\t\t\tlet targetId = providers[0]\n\n\t\t\t\t\t\t\t\tif (providers.length > 1) {\n\t\t\t\t\t\t\t\t\tconst instances = discovery.getInstances()\n\t\t\t\t\t\t\t\t\tlet bestLoad = Number.POSITIVE_INFINITY\n\t\t\t\t\t\t\t\t\tfor (const pid of providers) {\n\t\t\t\t\t\t\t\t\t\tconst inst = instances.find((i) => i.id === pid)\n\t\t\t\t\t\t\t\t\t\tif (inst && inst.load < bestLoad) {\n\t\t\t\t\t\t\t\t\t\t\tbestLoad = inst.load\n\t\t\t\t\t\t\t\t\t\t\ttargetId = pid\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst result = await remoteTaskMgr.executeRemoteTool(targetId, t.name, params)\n\t\t\t\t\t\t\t\tif (result.success) return result.output\n\t\t\t\t\t\t\t\tthrow new Error(result.error ?? 'Remote tool execution failed')\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (remoteToolDefs.length > 0) {\n\t\t\t\t\tthis.toolRegistry!.register(pawLabel, remoteToolDefs, false)\n\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t`Registered ${remoteToolDefs.length} remote tools from ${peerName} as ${pawLabel}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\t\t// Handle incoming task:delegate — run task with our brain if allowed\n\t\tthis.transport.onMessage(async (message) => {\n\t\t\tif (message.type === 'task:delegate' && this.keyPair) {\n\t\t\t\tif (!this.discovery?.verifyMessageFrom(message)) {\n\t\t\t\t\tlogger.warn(`Rejected unverified task:delegate from ${message.from.substring(0, 8)}`)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst request = message.payload as {\n\t\t\t\t\ttaskId: string\n\t\t\t\t\tinput: string\n\t\t\t\t\tmaxIterations?: number\n\t\t\t\t\tfromName?: string\n\t\t\t\t}\n\t\t\t\tif (!request?.input) return\n\n\t\t\t\t// Check if this peer is allowed to use our brain\n\t\t\t\tif (!this.isPeerAllowedBrain(message.from)) {\n\t\t\t\t\tlogger.warn(`Brain access denied for peer ${message.from.substring(0, 8)}`)\n\t\t\t\t\tawait this.deliverTaskResult(message.from, {\n\t\t\t\t\t\ttaskId: request.taskId,\n\t\t\t\t\t\tstatus: 'failed',\n\t\t\t\t\t\terror: 'Brain access not allowed. Coordinator must set allowBrain: true for this peer.',\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tlogger.info(\n\t\t\t\t\t`Brain delegation from ${message.from.substring(0, 8)}: \"${request.input.substring(0, 80)}\"`,\n\t\t\t\t)\n\t\t\t\tmessageBus?.emit('task:queued', { taskId: request.taskId })\n\n\t\t\t\t// Enqueue the task locally — it will run through our brain\n\t\t\t\tconst taskQueue = (globalThis as any).__volenet_taskqueue__\n\t\t\t\tif (taskQueue) {\n\t\t\t\t\t// Chat messages (net_message) carry fromName — frame as a peer message and\n\t\t\t\t\t// run in a per-peer session for conversational continuity. Tasks stay one-shot.\n\t\t\t\t\tconst isChat = typeof request.fromName === 'string' && request.fromName.length > 0\n\t\t\t\t\tconst runInput = isChat\n\t\t\t\t\t\t? `[Message from peer agent \"${request.fromName}\"] ${request.input}`\n\t\t\t\t\t\t: request.input\n\t\t\t\t\tconst runSource = isChat ? `net:${message.from.substring(0, 8)}` : 'agent'\n\t\t\t\t\tconst task = taskQueue.enqueue(runInput, runSource, {\n\t\t\t\t\t\t// A peer's chat is a conversation, so it gets a session: history loads, the\n\t\t\t\t\t\t// answer is appended to it, and — because the reply address is derived from\n\t\t\t\t\t\t// the session — the report goes back to this peer instead of the dashboard.\n\t\t\t\t\t\t// Without it every rule in replyAddressFor fell through to 'dashboard', so the\n\t\t\t\t\t\t// agent wrote a status report about the peer to its human and that text was\n\t\t\t\t\t\t// what the peer received. A one-shot task is not a conversation and gets none.\n\t\t\t\t\t\t...(isChat ? { sessionId: runSource } : {}),\n\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\tmaxIterations: request.maxIterations ?? 10,\n\t\t\t\t\t\t\tremotePeerId: message.from,\n\t\t\t\t\t\t\tremoteTaskId: request.taskId,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\t// Wait for completion and send the result back. The asker may well be gone by\n\t\t\t\t\t// then — thinking takes as long as it takes — so an undelivered answer waits.\n\t\t\t\t\tconst checkInterval = setInterval(async () => {\n\t\t\t\t\t\tconst t = taskQueue.get(task.id)\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tt &&\n\t\t\t\t\t\t\t(t.status === 'completed' || t.status === 'failed' || t.status === 'cancelled')\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tclearInterval(checkInterval)\n\t\t\t\t\t\t\tthis.delegationTimers.delete(checkInterval)\n\t\t\t\t\t\t\tawait this.deliverTaskResult(message.from, {\n\t\t\t\t\t\t\t\ttaskId: request.taskId,\n\t\t\t\t\t\t\t\tstatus: t.status,\n\t\t\t\t\t\t\t\tresult: t.result,\n\t\t\t\t\t\t\t\terror: t.error,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t}, 1000)\n\t\t\t\t\tthis.delegationTimers.add(checkInterval)\n\t\t\t\t} else {\n\t\t\t\t\tawait this.deliverTaskResult(message.from, {\n\t\t\t\t\t\ttaskId: request.taskId,\n\t\t\t\t\t\tstatus: 'failed',\n\t\t\t\t\t\terror: 'Task queue not available',\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\t\t// Handle incoming chat:message — human-capable peer chat. Unlike task:delegate,\n\t\t// this does NOT run the brain: it verifies the sender, stores the message, and\n\t\t// emits a bus event so the dashboard can surface it for a human (or brain) reply.\n\t\tthis.transport.onMessage((message) => {\n\t\t\tif (message.type !== 'chat:message') return\n\t\t\tif (!this.discovery?.verifyMessageFrom(message)) {\n\t\t\t\tlogger.warn(`Rejected unverified chat message from ${message.from.substring(0, 8)}`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst payload = message.payload as { text?: string; fromName?: string }\n\t\t\tif (!payload?.text) return\n\t\t\tconst fromName = payload.fromName || message.from.substring(0, 8)\n\t\t\tvoid this.appendChat(message.from, {\n\t\t\t\tdir: 'in',\n\t\t\t\ttext: payload.text,\n\t\t\t\tfromName,\n\t\t\t\ttimestamp: message.timestamp,\n\t\t\t\tmessageId: message.id,\n\t\t\t})\n\t\t\tmessageBus?.emit('volenet:chat', {\n\t\t\t\tfrom: message.from,\n\t\t\t\tfromName,\n\t\t\t\ttext: payload.text,\n\t\t\t\tmessageId: message.id,\n\t\t\t\ttimestamp: message.timestamp,\n\t\t\t})\n\t\t})\n\n\t\t// ── Blind relay (v1: sealed end-to-end chat) ──────────────────────────────\n\t\tconst relayCfg = this.config.relay\n\t\tconst relayMaxBytes = relayCfg?.maxBytes ?? 65536\n\t\tconst relayMaxPerMinute = relayCfg?.maxPerMinutePerPair ?? 30\n\n\t\t// Hub side: forward sealed envelopes between members. The hub verifies WHO sent the\n\t\t// envelope and applies policy — it cannot read what it forwards.\n\t\tthis.transport.onMessage((message) => {\n\t\t\tif (message.type !== 'sealed' || !this.keyPair || !this.transport) return\n\t\t\tif (!this.discovery?.verifyMessageFrom(message)) {\n\t\t\t\tlogger.warn(`Rejected unverified sealed envelope from ${message.from.substring(0, 8)}`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst payload = message.payload as { to?: string; box?: SealedBox; ref?: string }\n\t\t\tif (!payload?.to || !payload.box) return\n\t\t\tif (payload.to === this.keyPair.instanceId) {\n\t\t\t\t// addressed to us — we are the recipient, not a relay\n\t\t\t\tthis.deliverSealed(message.from, payload.box, messageBus)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!relayCfg?.enabled) return // not a relay — drop\n\t\t\tconst to = payload.to\n\t\t\t// An opaque reference the sender attached so it can tell which envelope a verdict is\n\t\t\t// about. The hub cannot see inside, so without it a sender with two messages in\n\t\t\t// flight could not know which one was refused. Older senders attach none.\n\t\t\tconst ref = typeof payload.ref === 'string' ? payload.ref : undefined\n\t\t\tconst reject = (reason: string, held = false) => {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Relay refused ${message.from.substring(0, 8)}→${to.substring(0, 8)}: ${reason}`,\n\t\t\t\t)\n\t\t\t\tconst err = createMessage(\n\t\t\t\t\t'relay:error',\n\t\t\t\t\tthis.keyPair!.instanceId,\n\t\t\t\t\tmessage.from,\n\t\t\t\t\t{ to, reason, ...(ref ? { ref } : {}), ...(held ? { held: true } : {}) },\n\t\t\t\t\tthis.keyPair!.privateKey,\n\t\t\t\t\tthis.keyPair!.pqPrivateKey,\n\t\t\t\t)\n\t\t\t\tvoid this.transport!.sendToPeer(message.from, err)\n\t\t\t}\n\t\t\t// The member is away. Remember that somebody tried — sender, count, when — and nothing\n\t\t\t// else. The envelope goes back to the sender's own outbox; chat-outbox.ts says why the\n\t\t\t// hub deliberately does not keep the ciphertext.\n\t\t\tconst holdForLater = () => {\n\t\t\t\tconst fromName =\n\t\t\t\t\tthis.discovery?.getInstances().find((i) => i.id === message.from)?.name ?? ''\n\t\t\t\tvoid this.relayNotices?.record(to, message.from, fromName)\n\t\t\t\treject('peer-unreachable', true)\n\t\t\t}\n\t\t\tif (JSON.stringify(message).length > relayMaxBytes) return reject('too-large')\n\t\t\tif (!relayPairAllow(this.relayWindows, message.from, to, relayMaxPerMinute))\n\t\t\t\treturn reject('rate-limited')\n\t\t\tconst bound = this.transport.getPeers().find((p) => p.peerId === to && p.connected)\n\t\t\tif (!bound) return holdForLater()\n\t\t\tconst wrap = createMessage(\n\t\t\t\t'relay:deliver',\n\t\t\t\tthis.keyPair.instanceId,\n\t\t\t\tto,\n\t\t\t\t{ from: message.from, box: payload.box },\n\t\t\t\tthis.keyPair.privateKey,\n\t\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\t)\n\t\t\tvoid this.transport.sendToPeer(to, wrap).then((sent) => {\n\t\t\t\tif (!sent) return holdForLater()\n\t\t\t\t// A sender that attached a ref is waiting to hear the envelope went through, so its\n\t\t\t\t// outbox can let the message go. One that attached none expects nothing.\n\t\t\t\tif (!ref || !this.keyPair || !this.transport) return\n\t\t\t\tconst ack = createMessage(\n\t\t\t\t\t'relay:ack',\n\t\t\t\t\tthis.keyPair.instanceId,\n\t\t\t\t\tmessage.from,\n\t\t\t\t\t{ to, ref },\n\t\t\t\t\tthis.keyPair.privateKey,\n\t\t\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\t\t)\n\t\t\t\tvoid this.transport.sendToPeer(message.from, ack)\n\t\t\t})\n\t\t})\n\n\t\t// Member side: a relay hub handed us a sealed envelope from another member.\n\t\tthis.transport.onMessage((message) => {\n\t\t\tif (message.type !== 'relay:deliver' || !this.keyPair) return\n\t\t\tif (!this.discovery?.verifyMessageFrom(message)) return // the relay must be authorized\n\t\t\tconst payload = message.payload as { from?: string; box?: SealedBox }\n\t\t\tif (!payload?.from || !payload.box) return\n\t\t\t// message.from is the delivering hub — file transfers use it for relay-mode routing.\n\t\t\tthis.deliverSealed(payload.from, payload.box, messageBus, message.from)\n\t\t})\n\n\t\t// Member side: hub-vouched member directory, enables sealed addressing of members\n\t\t// we have no direct trust relationship with. Directory data only — never authority.\n\t\tthis.transport.onMessage((message) => {\n\t\t\tif (message.type !== 'roster') return\n\t\t\tif (!this.discovery?.verifyMessageFrom(message)) return\n\t\t\tconst payload = message.payload as { members?: RosterMember[] }\n\t\t\tif (!Array.isArray(payload?.members)) return\n\t\t\tconst map = new Map<string, RosterMember>()\n\t\t\tfor (const m of payload.members) {\n\t\t\t\tif (m?.instanceId && m.instanceId !== this.keyPair?.instanceId) map.set(m.instanceId, m)\n\t\t\t}\n\t\t\tthis.hubRosters.set(message.from, map)\n\t\t\t// Anyone we are holding a message for may just have come back.\n\t\t\tvoid this.flushChatOutbox()\n\t\t})\n\n\t\tthis.transport.onMessage((message) => {\n\t\t\tif (message.type !== 'relay:error') return\n\t\t\tif (!this.discovery?.verifyMessageFrom(message)) return\n\t\t\tconst payload = message.payload as {\n\t\t\t\tto?: string\n\t\t\t\treason?: string\n\t\t\t\tref?: string\n\t\t\t\theld?: boolean\n\t\t\t}\n\t\t\tlogger.warn(\n\t\t\t\t`Relay could not deliver to ${payload?.to?.substring(0, 8) ?? '?'}: ${payload?.reason ?? 'unknown'}`,\n\t\t\t)\n\t\t\tthis.settleRelay(payload)\n\t\t\tmessageBus?.emit('volenet:relay:error', { via: message.from, ...payload })\n\t\t})\n\n\t\t// Member side: the hub forwarded an envelope we attached a ref to.\n\t\tthis.transport.onMessage((message) => {\n\t\t\tif (message.type !== 'relay:ack') return\n\t\t\tif (!this.discovery?.verifyMessageFrom(message)) return\n\t\t\tconst payload = message.payload as { to?: string; ref?: string }\n\t\t\tif (typeof payload?.ref !== 'string') return\n\t\t\tthis.relayInflight.get(payload.ref)?.settle({ kind: 'ack' })\n\t\t})\n\n\t\t// Member side: a hub telling us, on reconnect, who tried to reach us while we were away.\n\t\t// Names and counts only. The messages themselves are still on the senders' machines and\n\t\t// arrive once those senders see us in a roster — which the hub broadcasts right after this.\n\t\tthis.transport.onMessage((message) => {\n\t\t\tif (message.type !== 'relay:pending') return\n\t\t\tif (!this.discovery?.verifyMessageFrom(message)) return\n\t\t\tconst payload = message.payload as { senders?: RelayNotice[] }\n\t\t\tif (!Array.isArray(payload?.senders) || payload.senders.length === 0) return\n\t\t\tconst now = Date.now()\n\t\t\tconst notices: RelayNotice[] = []\n\t\t\tfor (const n of payload.senders) {\n\t\t\t\tif (!n || typeof n.from !== 'string' || !n.from) continue\n\t\t\t\t// Their message may already be here: on reconnect the hub sends this notice to us\n\t\t\t\t// and the roster to them, and the sender's delivery can win that race. A notice\n\t\t\t\t// about attempts made before we last heard from them announces nothing.\n\t\t\t\tconst heard = this.lastChatFrom.get(n.from)\n\t\t\t\tif (heard !== undefined && heard >= (Number(n.last) || 0)) continue\n\t\t\t\tconst notice: RelayNotice = {\n\t\t\t\t\tfrom: n.from,\n\t\t\t\t\tfromName:\n\t\t\t\t\t\ttypeof n.fromName === 'string' && n.fromName ? n.fromName : this.rosterName(n.from),\n\t\t\t\t\tcount: Math.max(1, Number(n.count) || 1),\n\t\t\t\t\tfirst: Number(n.first) || now,\n\t\t\t\t\tlast: Number(n.last) || now,\n\t\t\t\t}\n\t\t\t\tthis.chatPending.set(n.from, { ...notice, viaHub: message.from })\n\t\t\t\tnotices.push(notice)\n\t\t\t}\n\t\t\tif (notices.length === 0) return\n\t\t\tlogger.info(\n\t\t\t\t`${notices.length} sender(s) tried to reach me while I was away (via ${message.from.substring(0, 8)})`,\n\t\t\t)\n\t\t\tmessageBus?.emit('volenet:chat:pending', { via: message.from, from: notices })\n\t\t})\n\n\t\t// Hub side: broadcast the roster on membership changes (debounced).\n\t\tif (relayCfg?.enabled) {\n\t\t\tconst schedule = () => {\n\t\t\t\tif (this.rosterTimer) clearTimeout(this.rosterTimer)\n\t\t\t\tthis.rosterTimer = setTimeout(() => this.broadcastRoster(), 400)\n\t\t\t}\n\t\t\tthis.transport.setOnConnect(schedule)\n\t\t\tthis.transport.setOnDisconnect(schedule)\n\t\t\t// A member that just (re)connected is told who tried to reach it while it was away.\n\t\t\t// On the connect itself rather than inferred from roster diffs, which have to trust\n\t\t\t// that every disconnect was noticed.\n\t\t\tthis.transport.setOnConnect((id) => void this.sendRelayNotices(id))\n\t\t\tlogger.info(\n\t\t\t\t`Relay enabled — sealed member↔member envelopes, ${relayMaxPerMinute}/min per pair, ${relayMaxBytes}B cap`,\n\t\t\t)\n\t\t}\n\n\t\t// ── Direct end-to-end encryption (opt-in) ────────────────────────────────\n\t\t// Outbound: seal post-handshake messages to capable peers into sealed:direct envelopes.\n\t\tthis.transport.setSealer((peerId, msg) => this.maybeSealDirect(peerId, msg))\n\t\t// Inbound: unwrap a sealed:direct envelope and re-inject the inner message so every handler\n\t\t// (tool calls, sync, chat) processes it with full verification — transparent decryption.\n\t\tthis.transport.onMessage((message) => {\n\t\t\tif (message.type !== 'sealed:direct' || !this.keyPair?.xPrivateKey || !this.transport) return\n\t\t\tif (!this.discovery?.verifyMessageFrom(message)) return // outer envelope must be signed\n\t\t\tconst payload = message.payload as { box?: SealedBox }\n\t\t\tif (!payload?.box) return\n\t\t\tconst aad = `${message.from}|${this.keyPair.instanceId}`\n\t\t\tconst plain = unseal(this.keyPair.xPrivateKey, payload.box, aad, this.keyPair.mlkemPrivateKey)\n\t\t\tif (!plain) {\n\t\t\t\tlogger.warn(`sealed:direct from ${message.from.substring(0, 8)} failed to unseal`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet inner: VoleNetMessage\n\t\t\ttry {\n\t\t\t\tinner = JSON.parse(plain.toString('utf8')) as VoleNetMessage\n\t\t\t} catch {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// The envelope's sender must be the inner author — no wrapping another identity's message.\n\t\t\tif (inner.from !== message.from) return\n\t\t\tthis.transport.injectMessage(inner)\n\t\t})\n\t\tif (this.config.encrypt)\n\t\t\tlogger.info('Direct encryption enabled — hybrid X25519 + ML-KEM-768 seal')\n\n\t\t// Initialize remote task manager\n\t\tthis.remoteTaskMgr = new RemoteTaskManager(\n\t\t\tthis.transport,\n\t\t\tthis.discovery,\n\t\t\tthis.keyPair.instanceId,\n\t\t\tthis.keyPair.privateKey,\n\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\tthis.config.routing,\n\t\t)\n\n\t\t// Initialize sync manager\n\t\tconst syncConfig: SyncConfig = {\n\t\t\tmemory: this.config.share?.memory ?? false,\n\t\t\tsession: this.config.share?.session ?? false,\n\t\t}\n\t\tthis.sync = new VoleNetSync(\n\t\t\tthis.transport,\n\t\t\tthis.discovery,\n\t\t\tthis.keyPair.instanceId,\n\t\t\tthis.config.instanceName ?? 'vole',\n\t\t\tthis.keyPair.privateKey,\n\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\tsyncConfig,\n\t\t)\n\n\t\t// Set up session sync handler — write remote session entries to local transcript\n\t\tthis.sync.setSessionWriteHandler(async (entry) => {\n\t\t\tconst fs = await import('node:fs/promises')\n\t\t\tconst pathMod = await import('node:path')\n\t\t\tconst sessionDir = pathMod.resolve(\n\t\t\t\tthis.projectRoot,\n\t\t\t\t'.openvole',\n\t\t\t\t'paws',\n\t\t\t\t'paw-session',\n\t\t\t\tentry.sessionId.replace(/[/\\\\]/g, '_'),\n\t\t\t)\n\t\t\tawait fs.mkdir(sessionDir, { recursive: true })\n\t\t\tconst transcriptPath = pathMod.join(sessionDir, 'transcript.md')\n\t\t\tconst timestamp = new Date(entry.timestamp).toTimeString().slice(0, 8)\n\t\t\tconst line = `[${timestamp}] ${entry.role}: ${entry.content.replace(/\\n/g, ' ').substring(0, 2000)}\\n`\n\t\t\tawait fs.appendFile(transcriptPath, line, 'utf-8')\n\t\t\tlogger.info(\n\t\t\t\t`Session sync received: ${entry.sessionId} — ${entry.role} from ${entry.instanceId.substring(0, 8)}`,\n\t\t\t)\n\t\t})\n\n\t\t// Initialize leader election\n\t\tthis.leader = new VoleNetLeader(\n\t\t\tthis.transport,\n\t\t\tthis.discovery,\n\t\t\tthis.keyPair.instanceId,\n\t\t\tthis.config.instanceName ?? 'vole',\n\t\t\tthis.keyPair.privateKey,\n\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\tthis.config.leader,\n\t\t)\n\t\tthis.leader.start(\n\t\t\t() => logger.info('This instance is now the VoleNet leader (owns heartbeat/schedules)'),\n\t\t\t() => logger.info('This instance lost VoleNet leadership'),\n\t\t)\n\n\t\t// Re-elect leader immediately when peers join/leave\n\t\tthis.discovery.setOnPeerChanged(() => {\n\t\t\tthis.leader?.reelect()\n\t\t})\n\n\t\t// VoleDrop — file transfer engine. Constructed with closures so it can use the\n\t\t// manager's private relay/chat plumbing without owning any of it.\n\t\tthis.files = new VoleNetFiles({\n\t\t\tconfig: this.config.files ?? {},\n\t\t\trelayEnabled: !!this.config.relay?.enabled,\n\t\t\tprojectRoot: this.projectRoot,\n\t\t\tnetDir,\n\t\t\tkeyPair: this.keyPair,\n\t\t\ttransport: this.transport,\n\t\t\tinstanceName: this.config.instanceName ?? 'vole',\n\t\t\tadvertisedEndpoint: endpoint,\n\t\t\tbus: messageBus,\n\t\t\tgetInstances: () => this.getInstances(),\n\t\t\tresolveRelayPeer: (ref) => this.resolveRelayPeer(ref),\n\t\t\tgetHubForMember: (peerId) => {\n\t\t\t\tfor (const [hubId, roster] of this.hubRosters) {\n\t\t\t\t\tif (!roster.has(peerId)) continue\n\t\t\t\t\tconst hub = this.transport?.getPeers().find((x) => x.peerId === hubId && x.connected)\n\t\t\t\t\tif (hub) return hubId\n\t\t\t\t}\n\t\t\t\treturn undefined\n\t\t\t},\n\t\t\tsealToMemberViaRelay: (ref, type, payload) => this.sealToMemberViaRelay(ref, type, payload),\n\t\t\tappendChat: (peerId, entry) => this.appendChat(peerId, entry),\n\t\t})\n\t\tthis.transport.setBlobHandler((req, res, pathname) =>\n\t\t\tthis.files ? this.files.handleBlobRequest(req, res, pathname) : false,\n\t\t)\n\t\t// Direct file-transfer + relay-blob control messages (relayed ones arrive via deliverSealed).\n\t\tthis.transport.onMessage((message) => {\n\t\t\tif (!message.type.startsWith('file:') && !message.type.startsWith('relay:blob:')) return\n\t\t\tif (!this.discovery?.verifyMessageFrom(message)) return\n\t\t\tthis.files?.handleMessage(message, {})\n\t\t})\n\n\t\t// Make VoleNet accessible to core tools via globalThis\n\t\t;(globalThis as any).__volenet__ = this\n\n\t\t// Connect to configured peers\n\t\tif (this.config.peers) {\n\t\t\t// An entry with no url is identity-only (a phone, or anything that can only dial\n\t\t\t// us): there is nothing to connect to, it just says what that peer may do here.\n\t\t\tfor (const peer of this.config.peers) {\n\t\t\t\tif (!peer.url) continue\n\t\t\t\tlogger.info(`Connecting to peer: ${peer.url}`)\n\t\t\t\tawait this.discovery.connectToPeer(peer.url)\n\t\t\t}\n\t\t\t// Re-attempt configured peers periodically so the mesh self-heals from\n\t\t\t// start-order races (a peer not up yet) and transient drops. connectToPeer\n\t\t\t// pings first and is idempotent, so re-announcing to connected peers is cheap.\n\t\t\tthis.peerConnectTimer = setInterval(() => {\n\t\t\t\tfor (const peer of this.config.peers ?? []) {\n\t\t\t\t\tif (!peer.url) continue\n\t\t\t\t\tthis.discovery?.connectToPeer(peer.url).catch(() => {})\n\t\t\t\t}\n\t\t\t}, 15_000)\n\t\t}\n\n\t\t// Periodically prune stale node-chat sessions (retention).\n\t\tvoid this.pruneChatSessions()\n\t\tthis.chatPruneTimer = setInterval(() => void this.pruneChatSessions(), CHAT_PRUNE_INTERVAL_MS)\n\n\t\tthis.started = true\n\t\tlogger.info(`VoleNet started — ${this.config.role ?? 'peer'} mode, port ${port}`)\n\t}\n\n\t/**\n\t * Stop VoleNet — disconnect peers, stop transport.\n\t */\n\t/**\n\t * Send one brain answer to the peer that asked for it, and keep it if that fails.\n\t *\n\t * Failure here is ordinary: thinking takes as long as it takes, and a peer that asked from\n\t * a phone is very likely closed by the time there is something to say. Nobody else holds a\n\t * copy — the asker has the question and we have the only answer — so it waits, and goes out\n\t * the next time that peer speaks to us.\n\t */\n\tprivate async deliverTaskResult(\n\t\tpeerId: string,\n\t\tpayload: { taskId: string; status: string; result?: string; error?: string },\n\t): Promise<boolean> {\n\t\tif (!this.keyPair || !this.transport) return false\n\t\tconst message = createMessage(\n\t\t\t'task:result',\n\t\t\tthis.keyPair.instanceId,\n\t\t\tpeerId,\n\t\t\tpayload,\n\t\t\tthis.keyPair.privateKey,\n\t\t\tthis.keyPair.pqPrivateKey,\n\t\t)\n\t\tconst sent = await this.transport.sendToPeer(peerId, message).catch(() => false)\n\t\tconst who = peerId.substring(0, 8)\n\t\tif (sent) {\n\t\t\tawait this.resultOutbox?.remove(peerId, payload.taskId)\n\t\t\tlogger.info(`Brain delegation result sent to ${who}: ${payload.status}`)\n\t\t\treturn true\n\t\t}\n\t\tawait this.resultOutbox?.add({ peerId, ...payload, at: Date.now() })\n\t\tlogger.info(`Brain delegation result for ${who} is waiting — the peer is not reachable`)\n\t\treturn false\n\t}\n\n\t/**\n\t * A peer just spoke to us, so anything we were holding for it can go out now.\n\t *\n\t * Re-signed rather than replayed: a stored message carries the timestamp it was written\n\t * with, and receivers enforce freshness.\n\t */\n\tprivate async flushResultsFor(peerId: string): Promise<void> {\n\t\tconst outbox = this.resultOutbox\n\t\tif (!outbox || !outbox.has(peerId) || this.flushingResults.has(peerId)) return\n\t\tthis.flushingResults.add(peerId)\n\t\ttry {\n\t\t\tfor (const entry of outbox.forPeer(peerId)) {\n\t\t\t\tconst ok = await this.deliverTaskResult(peerId, {\n\t\t\t\t\ttaskId: entry.taskId,\n\t\t\t\t\tstatus: entry.status,\n\t\t\t\t\tresult: entry.result,\n\t\t\t\t\terror: entry.error,\n\t\t\t\t})\n\t\t\t\tif (!ok) {\n\t\t\t\t\tawait outbox.noteAttempt(peerId, entry.taskId)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.flushingResults.delete(peerId)\n\t\t}\n\t}\n\n\tasync stop(): Promise<void> {\n\t\tif (!this.started) return\n\n\t\tif (this.peerConnectTimer) clearInterval(this.peerConnectTimer)\n\t\tthis.peerConnectTimer = undefined\n\t\tif (this.chatPruneTimer) clearInterval(this.chatPruneTimer)\n\t\tthis.chatPruneTimer = undefined\n\t\tfor (const t of this.delegationTimers) clearInterval(t)\n\t\tthis.delegationTimers.clear()\n\t\tif (this.rosterTimer) clearTimeout(this.rosterTimer)\n\t\tthis.rosterTimer = undefined\n\t\tthis.files?.stop()\n\t\tthis.files = null\n\t\tthis.leader?.stop()\n\t\tthis.sync?.dispose()\n\t\tthis.remoteTaskMgr?.dispose()\n\t\tthis.discovery?.stop()\n\t\tawait this.transport?.stop()\n\n\t\tthis.leader = null\n\t\tthis.sync = null\n\t\tthis.remoteTaskMgr = null\n\t\tthis.discovery = null\n\t\tthis.transport = null\n\t\tthis.started = false\n\t\t;(globalThis as any).__volenet__ = undefined\n\n\t\tlogger.info('VoleNet stopped')\n\t}\n\n\t/**\n\t * Get connected instances.\n\t */\n\tgetInstances(): VoleNetInstance[] {\n\t\treturn this.discovery?.getInstances() ?? []\n\t}\n\n\t// ── VoleDrop file transfer (passthroughs to the files engine) ─────────────\n\n\t/** Send a file to a peer (direct or relay-rostered). Async — completion via bus events. */\n\tasync sendFile(\n\t\tpeerRef: string,\n\t\tfilePath: string,\n\t\tnote?: string,\n\t): Promise<{ ok: boolean; transferId?: string; error?: string }> {\n\t\tif (!this.files) return { ok: false, error: 'VoleNet not started' }\n\t\treturn this.files.sendFile(peerRef, filePath, note)\n\t}\n\n\tasync acceptFile(transferId: string): Promise<{ ok: boolean; error?: string }> {\n\t\tif (!this.files) return { ok: false, error: 'VoleNet not started' }\n\t\treturn this.files.acceptFile(transferId)\n\t}\n\n\tasync rejectFile(transferId: string, reason?: string): Promise<{ ok: boolean }> {\n\t\tif (!this.files) return { ok: false }\n\t\treturn this.files.rejectFile(transferId, reason)\n\t}\n\n\tasync cancelFileTransfer(transferId: string): Promise<{ ok: boolean }> {\n\t\tif (!this.files) return { ok: false }\n\t\treturn this.files.cancelTransfer(transferId)\n\t}\n\n\tlistFileTransfers(): TransferInfo[] {\n\t\treturn this.files?.listTransfers() ?? []\n\t}\n\n\tgetFileTransfer(transferId: string): TransferInfo | undefined {\n\t\treturn this.files?.getTransfer(transferId)\n\t}\n\n\t/** Session ID for a peer's human-chat transcript (persisted via paw-session). */\n\tprivate chatSessionId(peerId: string): string {\n\t\treturn `volenet:${peerId}`\n\t}\n\n\t/**\n\t * Append a chat entry for a peer. Persists via paw-session's session_append tool\n\t * when available; otherwise falls back to an in-memory log (capped at 200).\n\t */\n\tprivate async appendChat(peerId: string, entry: ChatEntry): Promise<void> {\n\t\tconst tool = this.toolRegistry?.get('session_append')\n\t\tif (tool) {\n\t\t\ttry {\n\t\t\t\tawait tool.execute({\n\t\t\t\t\tsessionId: this.chatSessionId(peerId),\n\t\t\t\t\trole: entry.dir,\n\t\t\t\t\tcontent: entry.text,\n\t\t\t\t\tmaxMessages: this.config.chatRetention?.maxMessages ?? DEFAULT_CHAT_MAX_MESSAGES,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t} catch (err) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`session_append failed, using memory: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tconst log = this.chatLog.get(peerId) ?? []\n\t\tlog.push(entry)\n\t\tif (log.length > 200) log.splice(0, log.length - 200)\n\t\tthis.chatLog.set(peerId, log)\n\t}\n\n\t/**\n\t * Get the human-chat history with a peer. Reads from paw-session when available,\n\t * otherwise the in-memory fallback.\n\t */\n\tasync getChatHistory(peerId: string): Promise<ChatEntry[]> {\n\t\t// Only read from the session store when writes also go there (session_append present),\n\t\t// so a half-upgraded paw-session can't shadow the in-memory log with an empty session.\n\t\tconst tool = this.toolRegistry?.get('session_append')\n\t\t\t? this.toolRegistry?.get('session_history')\n\t\t\t: undefined\n\t\tif (tool) {\n\t\t\ttry {\n\t\t\t\tconst res = (await tool.execute({\n\t\t\t\t\tsessionId: this.chatSessionId(peerId),\n\t\t\t\t\tmaxMessages: 500,\n\t\t\t\t})) as { ok?: boolean; history?: Array<{ ts?: string; role: string; content: string }> }\n\t\t\t\tif (res?.history) {\n\t\t\t\t\tconst myName = this.getInstanceName()\n\t\t\t\t\tconst peerName =\n\t\t\t\t\t\tthis.getInstances().find((i) => i.id === peerId)?.name ?? peerId.substring(0, 8)\n\t\t\t\t\treturn res.history.map((m) => ({\n\t\t\t\t\t\tdir: m.role === 'out' ? ('out' as const) : ('in' as const),\n\t\t\t\t\t\ttext: m.content,\n\t\t\t\t\t\tfromName: m.role === 'out' ? myName : peerName,\n\t\t\t\t\t\ttimestamp: m.ts ? Date.parse(m.ts) || 0 : 0,\n\t\t\t\t\t\tmessageId: '',\n\t\t\t\t\t}))\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`session_history failed, using memory: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\treturn this.chatLog.get(peerId) ?? []\n\t}\n\n\t/** Clear the human-chat history with a peer (paw-session or in-memory). */\n\tasync clearChat(peerId: string): Promise<void> {\n\t\tconst tool = this.toolRegistry?.get('session_append')\n\t\t\t? this.toolRegistry?.get('session_clear')\n\t\t\t: undefined\n\t\tif (tool) {\n\t\t\ttry {\n\t\t\t\tawait tool.execute({ sessionId: this.chatSessionId(peerId) })\n\t\t\t\treturn\n\t\t\t} catch {\n\t\t\t\t// fall through to in-memory\n\t\t\t}\n\t\t}\n\t\tthis.chatLog.delete(peerId)\n\t}\n\n\t/** Clear chat sessions (volenet:*) idle longer than the retention age cap. */\n\tprivate async pruneChatSessions(): Promise<void> {\n\t\tconst maxAgeDays = this.config.chatRetention?.maxAgeDays ?? DEFAULT_CHAT_MAX_AGE_DAYS\n\t\tif (!maxAgeDays || maxAgeDays <= 0) return\n\t\tconst listTool = this.toolRegistry?.get('session_list')\n\t\tconst clearTool = this.toolRegistry?.get('session_clear')\n\t\tif (!listTool || !clearTool) return\n\t\ttry {\n\t\t\tconst res = (await listTool.execute({})) as {\n\t\t\t\tsessions?: Array<{ sessionId: string; lastActive?: string | null }>\n\t\t\t}\n\t\t\tconst cutoff = Date.now() - maxAgeDays * 86_400_000\n\t\t\tfor (const s of res.sessions ?? []) {\n\t\t\t\tif (!s.sessionId.startsWith('volenet:')) continue\n\t\t\t\tconst last = s.lastActive ? Date.parse(s.lastActive) : 0\n\t\t\t\tif (last && last < cutoff) {\n\t\t\t\t\tawait clearTool.execute({ sessionId: s.sessionId })\n\t\t\t\t\tlogger.info(`Pruned stale chat session (idle > ${maxAgeDays}d): ${s.sessionId}`)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tlogger.warn(\n\t\t\t\t`Chat retention prune failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Send a human chat message to a peer. Does NOT invoke any brain.\n\t * Resolves the peer by id or name, signs + sends a chat:message, and logs it locally.\n\t */\n\tasync sendChat(\n\t\tpeerId: string,\n\t\ttext: string,\n\t): Promise<{ ok: boolean; delivered?: boolean; relayed?: boolean; error?: string }> {\n\t\tif (!this.keyPair || !this.transport) return { ok: false, error: 'VoleNet not started' }\n\t\tconst target = this.getInstances().find(\n\t\t\t(i) => i.id === peerId || i.name === peerId || i.id.startsWith(peerId),\n\t\t)\n\t\t// Not directly known — try a relay hub's roster (sealed end-to-end delivery).\n\t\tif (!target) return this.sendChatViaRelay(peerId, text)\n\t\tconst fromName = this.getInstanceName()\n\t\tconst msg = createMessage(\n\t\t\t'chat:message',\n\t\t\tthis.keyPair.instanceId,\n\t\t\ttarget.id,\n\t\t\t{ text, fromName },\n\t\t\tthis.keyPair.privateKey,\n\t\t\tthis.keyPair.pqPrivateKey,\n\t\t)\n\t\tconst delivered = await this.transport.sendToPeer(target.id, msg)\n\t\tawait this.appendChat(target.id, {\n\t\t\tdir: 'out',\n\t\t\ttext,\n\t\t\tfromName,\n\t\t\ttimestamp: msg.timestamp,\n\t\t\tmessageId: msg.id,\n\t\t})\n\t\treturn { ok: true, delivered }\n\t}\n\n\t/**\n\t * Find a hub-rostered member by ref, seal an inner message to it, and forward via its hub.\n\t * Shared by relayed chat and the consent handshake — the hub sees only ciphertext.\n\t */\n\tprivate async sealToMemberViaRelay(\n\t\tpeerRef: string,\n\t\tinnerType: VoleNetMessageType,\n\t\tinnerPayload: Record<string, unknown>,\n\t\tref?: string,\n\t): Promise<{\n\t\tok: boolean\n\t\tdelivered?: boolean\n\t\tmember?: RosterMember\n\t\thubId?: string\n\t\tinner?: VoleNetMessage\n\t\terror?: string\n\t}> {\n\t\tif (!this.keyPair || !this.transport) return { ok: false, error: 'VoleNet not started' }\n\t\tfor (const [hubId, roster] of this.hubRosters) {\n\t\t\tconst member = [...roster.values()].find(\n\t\t\t\t(m) => m.instanceId === peerRef || m.name === peerRef || m.instanceId.startsWith(peerRef),\n\t\t\t)\n\t\t\tif (!member) continue\n\t\t\tif (!member.xPublicKey)\n\t\t\t\treturn { ok: false, error: `Peer \"${peerRef}\" announces no sealed-envelope key` }\n\t\t\tconst inner = createMessage(\n\t\t\t\tinnerType,\n\t\t\t\tthis.keyPair.instanceId,\n\t\t\t\tmember.instanceId,\n\t\t\t\tinnerPayload,\n\t\t\t\tthis.keyPair.privateKey,\n\t\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\t)\n\t\t\tconst box = seal(\n\t\t\t\tmember.xPublicKey,\n\t\t\t\tBuffer.from(JSON.stringify(inner), 'utf8'),\n\t\t\t\t`${this.keyPair.instanceId}|${member.instanceId}`,\n\t\t\t\tmember.mlkemPublicKey,\n\t\t\t)\n\t\t\tif (!box) return { ok: false, error: 'Failed to seal envelope' }\n\t\t\tconst outer = createMessage(\n\t\t\t\t'sealed',\n\t\t\t\tthis.keyPair.instanceId,\n\t\t\t\thubId,\n\t\t\t\t{ to: member.instanceId, box, ...(ref ? { ref } : {}) },\n\t\t\t\tthis.keyPair.privateKey,\n\t\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\t)\n\t\t\tconst delivered = await this.transport.sendToPeer(hubId, outer)\n\t\t\treturn { ok: true, delivered, member, hubId, inner }\n\t\t}\n\t\treturn { ok: false, error: `No connected peer: \"${peerRef}\"` }\n\t}\n\n\t/**\n\t * Seal a chat message to a hub-rostered member, send it via that hub, and wait for the hub's\n\t * verdict. Forwarded, or held: when the member is away the message goes to this node's own\n\t * outbox and leaves when the member reappears in a roster. A hub too old to give a verdict\n\t * is treated the way it always was — the write to the hub counts as the delivery.\n\t */\n\tprivate async sendChatViaRelay(\n\t\tpeerRef: string,\n\t\ttext: string,\n\t): Promise<{\n\t\tok: boolean\n\t\tdelivered?: boolean\n\t\tqueued?: boolean\n\t\trelayed?: boolean\n\t\terror?: string\n\t}> {\n\t\tconst fromName = this.getInstanceName()\n\t\tconst sentAt = Date.now()\n\t\tconst ref = randomUUID()\n\t\tconst member = this.resolveRelayPeer(peerRef)\n\t\tconst verdict = this.awaitRelayVerdict(ref, member?.instanceId ?? peerRef)\n\t\tconst r = await this.sealToMemberViaRelay(\n\t\t\tpeerRef,\n\t\t\t'chat:message',\n\t\t\t{ text, fromName, sentAt },\n\t\t\tref,\n\t\t)\n\t\tif (!r.ok || !r.member || !r.inner) {\n\t\t\tthis.relayInflight.get(ref)?.settle({ kind: 'error', reason: r.error ?? 'unsealable' })\n\t\t\treturn { ok: false, error: r.error ?? `No connected peer: \"${peerRef}\"` }\n\t\t}\n\t\tawait this.appendChat(r.member.instanceId, {\n\t\t\tdir: 'out',\n\t\t\ttext,\n\t\t\tfromName,\n\t\t\ttimestamp: sentAt,\n\t\t\tmessageId: r.inner.id,\n\t\t\trelayed: true,\n\t\t})\n\t\tconst o = await verdict\n\t\tswitch (o.kind) {\n\t\t\tcase 'ack':\n\t\t\t\treturn { ok: true, delivered: true, relayed: true }\n\t\t\tcase 'held': {\n\t\t\t\tconst entry = await this.chatOutbox?.add({\n\t\t\t\t\tto: r.member.instanceId,\n\t\t\t\t\ttoName: r.member.name,\n\t\t\t\t\ttext,\n\t\t\t\t\tsentAt,\n\t\t\t\t})\n\t\t\t\tif (entry) {\n\t\t\t\t\tlogger.info(`${r.member.name} is away — holding the message here until they are back`)\n\t\t\t\t\tthis.messageBus?.emit('volenet:chat:queued', {\n\t\t\t\t\t\tto: entry.to,\n\t\t\t\t\t\ttoName: entry.toName,\n\t\t\t\t\t\tref: entry.ref,\n\t\t\t\t\t\ttext,\n\t\t\t\t\t\tsentAt,\n\t\t\t\t\t\tvia: r.hubId ?? '',\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn { ok: true, delivered: false, queued: true, relayed: true }\n\t\t\t}\n\t\t\tcase 'error':\n\t\t\t\treturn { ok: true, delivered: false, relayed: true, error: o.reason }\n\t\t\tdefault:\n\t\t\t\treturn { ok: true, delivered: r.delivered, relayed: true }\n\t\t}\n\t}\n\n\t/**\n\t * The hub's verdict on an envelope we sent: forwarded, held because the member is away,\n\t * refused — or silence, from a hub too old to give one.\n\t */\n\tprivate awaitRelayVerdict(ref: string, to: string): Promise<RelayOutcome> {\n\t\treturn new Promise<RelayOutcome>((resolve) => {\n\t\t\tconst finish = (o: RelayOutcome) => {\n\t\t\t\tclearTimeout(timer)\n\t\t\t\tthis.relayInflight.delete(ref)\n\t\t\t\tresolve(o)\n\t\t\t}\n\t\t\tconst timer = setTimeout(() => finish({ kind: 'timeout' }), RELAY_VERDICT_TIMEOUT_MS)\n\t\t\tthis.relayInflight.set(ref, { to, settle: finish })\n\t\t})\n\t}\n\n\t/**\n\t * Route a hub's relay:error to the envelope it is about. Older hubs echo no ref, so a verdict\n\t * without one settles everything in flight to that member — in practice the one message.\n\t */\n\tprivate settleRelay(p: { to?: string; reason?: string; ref?: string; held?: boolean }): void {\n\t\tconst outcome: RelayOutcome =\n\t\t\tp.held || p.reason === 'peer-unreachable'\n\t\t\t\t? { kind: 'held' }\n\t\t\t\t: { kind: 'error', reason: p.reason ?? 'unknown' }\n\t\tif (typeof p.ref === 'string') {\n\t\t\tthis.relayInflight.get(p.ref)?.settle(outcome)\n\t\t\treturn\n\t\t}\n\t\tif (!p.to) return\n\t\tfor (const f of [...this.relayInflight.values()]) if (f.to === p.to) f.settle(outcome)\n\t}\n\n\t/**\n\t * Send what is waiting for anyone who is back. Runs on every roster update, so a member\n\t * reappearing on a hub is what triggers delivery — nothing polls. One message at a time and\n\t * in order, so a conversation arrives the way it was written.\n\t */\n\tprivate async flushChatOutbox(): Promise<void> {\n\t\tif (!this.chatOutbox || this.outboxFlushing || this.chatOutbox.size === 0) return\n\t\tthis.outboxFlushing = true\n\t\ttry {\n\t\t\tfor (const e of await this.chatOutbox.sweep()) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Gave up on a message to ${e.toName || e.to.substring(0, 8)} written ${new Date(e.sentAt).toISOString()} — they never came back`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst stillAway = new Set<string>()\n\t\t\tfor (const entry of this.chatOutbox.list()) {\n\t\t\t\tif (stillAway.has(entry.to)) continue\n\t\t\t\tconst member = this.resolveRelayPeer(entry.to)\n\t\t\t\tif (!member?.connected) continue\n\t\t\t\tconst ref = randomUUID()\n\t\t\t\tconst verdict = this.awaitRelayVerdict(ref, entry.to)\n\t\t\t\t// Re-signed now: a signature is fresh for a minute and this may have waited days.\n\t\t\t\t// `sentAt` tells the recipient when it was actually written.\n\t\t\t\tconst kind = entry.kind ?? 'chat'\n\t\t\t\tconst fromName = this.getInstanceName()\n\t\t\t\tconst r = await this.sealToMemberViaRelay(\n\t\t\t\t\tentry.to,\n\t\t\t\t\tkind === 'chat' ? 'chat:message' : `relay:${kind}`,\n\t\t\t\t\tkind === 'chat'\n\t\t\t\t\t\t? { text: entry.text, fromName, sentAt: entry.sentAt }\n\t\t\t\t\t\t: kind === 'connect-request'\n\t\t\t\t\t\t\t? { fromName, ...(entry.note ? { note: entry.note } : {}) }\n\t\t\t\t\t\t\t: { fromName },\n\t\t\t\t\tref,\n\t\t\t\t)\n\t\t\t\tif (!r.ok) {\n\t\t\t\t\tthis.relayInflight.get(ref)?.settle({ kind: 'error', reason: r.error ?? 'unsealable' })\n\t\t\t\t\tawait this.chatOutbox.noteAttempt(entry.ref, r.error)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst o = await verdict\n\t\t\t\tif (o.kind === 'ack' || o.kind === 'timeout') {\n\t\t\t\t\tawait this.chatOutbox.remove(entry.ref)\n\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t`Delivered to ${entry.toName || entry.to.substring(0, 8)} a message that waited since ${new Date(entry.sentAt).toISOString()}`,\n\t\t\t\t\t)\n\t\t\t\t\tthis.messageBus?.emit('volenet:chat:flushed', {\n\t\t\t\t\t\tto: entry.to,\n\t\t\t\t\t\ttoName: entry.toName,\n\t\t\t\t\t\tref: entry.ref,\n\t\t\t\t\t\tsentAt: entry.sentAt,\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tawait this.chatOutbox.noteAttempt(entry.ref, o.kind === 'error' ? o.reason : 'held')\n\t\t\t\t\tif (o.kind === 'held') stillAway.add(entry.to)\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.outboxFlushing = false\n\t\t}\n\t}\n\n\t/**\n\t * Outbound direct-seal transform. Wraps a message in a sealed:direct envelope when encryption is\n\t * enabled and the target peer supports it (announces an ML-KEM key ⟹ 4.12.0+, can unwrap). The\n\t * inner message is already signed; the recipient recovers and re-verifies it. Handshake, relay,\n\t * and leader types pass through untouched. Never throws — falls back to plaintext on any issue.\n\t */\n\tprivate maybeSealDirect(peerId: string, message: VoleNetMessage): VoleNetMessage {\n\t\tif (!this.config.encrypt || !this.keyPair?.privateKey) return message\n\t\tif (PLAINTEXT_DIRECT_TYPES.has(message.type)) return message\n\t\tconst peer = this.discovery?.getInstances().find((i) => i.id === peerId)\n\t\t// Seal only to a peer that announced BOTH agreement keys (the post-quantum-capable path).\n\t\tif (!peer?.xPublicKey || !peer.mlkemPublicKey) return message\n\t\tconst aad = `${this.keyPair.instanceId}|${peerId}`\n\t\tconst box = seal(\n\t\t\tpeer.xPublicKey,\n\t\t\tBuffer.from(JSON.stringify(message), 'utf8'),\n\t\t\taad,\n\t\t\tpeer.mlkemPublicKey,\n\t\t)\n\t\tif (!box) return message\n\t\treturn createMessage(\n\t\t\t'sealed:direct',\n\t\t\tthis.keyPair.instanceId,\n\t\t\tpeerId,\n\t\t\t{ box },\n\t\t\tthis.keyPair.privateKey,\n\t\t\tthis.keyPair.pqPrivateKey,\n\t\t)\n\t}\n\n\t/** Resolve a relay-member ref (id / name / id-prefix) against every hub roster. */\n\tprivate resolveRelayPeer(ref: string): RosterMember | undefined {\n\t\tfor (const roster of this.hubRosters.values()) {\n\t\t\tconst m = [...roster.values()].find(\n\t\t\t\t(x) => x.instanceId === ref || x.name === ref || x.instanceId.startsWith(ref),\n\t\t\t)\n\t\t\tif (m) return m\n\t\t}\n\t\treturn undefined\n\t}\n\n\t/** Persist consent to a peer, pinning its roster-vouched key line in relay_accepts. */\n\tprivate async persistRelayAccept(member: RosterMember): Promise<void> {\n\t\ttry {\n\t\t\tawait addRelayAccept(this.getNetDir(), member.publicKey)\n\t\t} catch (e) {\n\t\t\tlogger.warn(\n\t\t\t\t`Failed to persist relay consent for ${member.instanceId.substring(0, 8)}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Ask a rostered member to accept relay contact. Initiating implies consent to receive its\n\t * reply, so the peer is added to my accept list immediately (persisted).\n\t */\n\tasync requestRelayConnect(\n\t\tpeerRef: string,\n\t\tnote?: string,\n\t): Promise<{ ok: boolean; queued?: boolean; error?: string }> {\n\t\tconst fromName = this.getInstanceName()\n\t\tconst r = await this.sendConsentViaRelay(peerRef, 'connect-request', { fromName, note }, note)\n\t\tif (!r.ok || !r.member)\n\t\t\treturn { ok: false, error: r.error ?? `No connected peer: \"${peerRef}\"` }\n\t\tthis.relayAcceptIds.add(r.member.instanceId)\n\t\tthis.relayOutgoing.add(r.member.instanceId)\n\t\tawait this.persistRelayAccept(r.member)\n\t\treturn { ok: true, ...(r.queued ? { queued: true } : {}) }\n\t}\n\n\t/**\n\t * Consent traffic goes through the same verdict-and-hold path as chat. It used to be\n\t * fire-and-forget, so a request to a member who had just locked their phone simply vanished —\n\t * and, on the older hub, so did the fact that anything had been sent.\n\t */\n\tprivate async sendConsentViaRelay(\n\t\tpeerRef: string,\n\t\tkind: 'connect-request' | 'connect-accept' | 'connect-deny',\n\t\tpayload: Record<string, unknown>,\n\t\tnote?: string,\n\t): Promise<{ ok: boolean; member?: RosterMember; queued?: boolean; error?: string }> {\n\t\tconst member = this.resolveRelayPeer(peerRef)\n\t\tconst ref = randomUUID()\n\t\tconst verdict = this.awaitRelayVerdict(ref, member?.instanceId ?? peerRef)\n\t\tconst r = await this.sealToMemberViaRelay(peerRef, `relay:${kind}`, payload, ref)\n\t\tif (!r.ok || !r.member) {\n\t\t\tthis.relayInflight.get(ref)?.settle({ kind: 'error', reason: r.error ?? 'unsealable' })\n\t\t\treturn { ok: false, error: r.error }\n\t\t}\n\t\tconst o = await verdict\n\t\tif (o.kind === 'held') {\n\t\t\tawait this.chatOutbox?.add({\n\t\t\t\tkind,\n\t\t\t\tto: r.member.instanceId,\n\t\t\t\ttoName: r.member.name,\n\t\t\t\ttext: '',\n\t\t\t\tnote,\n\t\t\t\tsentAt: Date.now(),\n\t\t\t})\n\t\t\tlogger.info(`${r.member.name} is away — holding the ${kind} here until they are back`)\n\t\t\treturn { ok: true, member: r.member, queued: true }\n\t\t}\n\t\tif (o.kind === 'error') return { ok: false, member: r.member, error: o.reason }\n\t\treturn { ok: true, member: r.member }\n\t}\n\n\t/** Approve an inbound connect-request: consent to the peer, persist it, and notify the peer. */\n\tasync approveRelayConnect(\n\t\tpeerRef: string,\n\t): Promise<{ ok: boolean; queued?: boolean; error?: string }> {\n\t\tconst member = this.resolveRelayPeer(peerRef)\n\t\tif (!member) return { ok: false, error: `No relay member: \"${peerRef}\"` }\n\t\tthis.relayAcceptIds.add(member.instanceId)\n\t\tthis.relayConfirmed.add(member.instanceId) // they requested me → mutual\n\t\tthis.relayRequests.delete(member.instanceId)\n\t\tawait this.persistRelayAccept(member)\n\t\tconst fromName = this.getInstanceName()\n\t\tconst r = await this.sendConsentViaRelay(member.instanceId, 'connect-accept', { fromName })\n\t\treturn { ok: true, ...(r.queued ? { queued: true } : {}) }\n\t}\n\n\t/** Deny an inbound connect-request: clear it and (best-effort) tell the peer. */\n\tasync denyRelayConnect(peerRef: string): Promise<{ ok: boolean; error?: string }> {\n\t\tconst member = this.resolveRelayPeer(peerRef)\n\t\tthis.relayRequests.delete(member?.instanceId ?? peerRef)\n\t\tif (member) {\n\t\t\tconst fromName = this.getInstanceName()\n\t\t\tawait this.sendConsentViaRelay(member.instanceId, 'connect-deny', { fromName })\n\t\t}\n\t\treturn { ok: true }\n\t}\n\n\t/** Withdraw previously-granted relay consent for a peer (removes it from relay_accepts). */\n\tasync revokeRelayConnect(peerRef: string): Promise<{ ok: boolean }> {\n\t\tconst id = this.resolveRelayPeer(peerRef)?.instanceId ?? peerRef\n\t\tthis.relayAcceptIds.delete(id)\n\t\tthis.relayConfirmed.delete(id)\n\t\tthis.relayOutgoing.delete(id)\n\t\tthis.relayRequests.delete(id)\n\t\tawait removeRelayAccept(this.getNetDir(), id).catch(() => false)\n\t\treturn { ok: true }\n\t}\n\n\t/** Inbound relay connect-requests awaiting my approval (newest first). */\n\tgetRelayRequests(): Array<{\n\t\tid: string\n\t\tname: string\n\t\tviaHub: string\n\t\tviaHubName: string\n\t\tnote?: string\n\t\tts: number\n\t}> {\n\t\treturn [...this.relayRequests.values()].sort((a, b) => b.ts - a.ts)\n\t}\n\n\t/** Chat waiting on this node for recipients who are away. Own disk only, never a hub's. */\n\tgetChatOutbox(): OutboxEntry[] {\n\t\treturn this.chatOutbox?.list() ?? []\n\t}\n\n\t/** Who tried to reach me while I was away, per the hub that told me. Cleared as their messages arrive. */\n\tgetChatPending(): Array<RelayNotice & { viaHub: string }> {\n\t\treturn [...this.chatPending.values()].sort((a, b) => b.last - a.last)\n\t}\n\n\t/** Unseal, verify, and ingest an envelope addressed to us. Allowlist: chat, consent, files. */\n\tprivate deliverSealed(fromId: string, box: SealedBox, bus?: EventSink, viaHub?: string): void {\n\t\tif (!this.keyPair?.xPrivateKey) return\n\t\tconst plain = unseal(\n\t\t\tthis.keyPair.xPrivateKey,\n\t\t\tbox,\n\t\t\t`${fromId}|${this.keyPair.instanceId}`,\n\t\t\tthis.keyPair.mlkemPrivateKey,\n\t\t)\n\t\tif (!plain) {\n\t\t\tlogger.warn(`Sealed envelope from ${fromId.substring(0, 8)} failed to unseal`)\n\t\t\treturn\n\t\t}\n\t\tlet inner: VoleNetMessage\n\t\ttry {\n\t\t\tinner = JSON.parse(plain.toString('utf8')) as VoleNetMessage\n\t\t} catch {\n\t\t\treturn\n\t\t}\n\t\t// Relay allowlist: end-to-end chat, the consent handshake, and file-transfer control.\n\t\t// Nothing executable rides — file bytes stream separately with their own consent gate.\n\t\tconst permitted =\n\t\t\tinner.type === 'chat:message' ||\n\t\t\tinner.type === 'relay:connect-request' ||\n\t\t\tinner.type === 'relay:connect-accept' ||\n\t\t\tinner.type === 'relay:connect-deny' ||\n\t\t\tinner.type.startsWith('file:')\n\t\tif (!permitted) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dropped relayed '${inner.type}' from ${fromId.substring(0, 8)} — not a permitted relay message`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tif (inner.from !== fromId || inner.to !== this.keyPair.instanceId) return\n\t\t// Inner replay guard: a relay could re-wrap the same inner message in fresh outers.\n\t\tconst seenKey = `${inner.from}:${inner.id}`\n\t\tconst now = Date.now()\n\t\tfor (const [k, ts] of this.seenSealed) if (now - ts > 120_000) this.seenSealed.delete(k)\n\t\tif (this.seenSealed.has(seenKey)) return\n\t\tthis.seenSealed.set(seenKey, now)\n\t\t// Verify the inner signature: a directly-trusted key wins; else the hub-vouched roster key.\n\t\tconst directlyTrusted = this.discovery?.verifyMessageFrom(inner) ?? false\n\t\tlet valid = directlyTrusted\n\t\tif (!valid) {\n\t\t\tconst member = this.findRosterMember(fromId)\n\t\t\tconst parsed = member ? parsePublicKey(member.publicKey) : null\n\t\t\tif (parsed && parsed.instanceId === fromId)\n\t\t\t\tvalid = verifyMessage(inner, parsed.publicKey, parsed.pqPublicKey).valid\n\t\t}\n\t\tif (!valid) {\n\t\t\tlogger.warn(\n\t\t\t\t`Relayed '${inner.type}' from ${fromId.substring(0, 8)} failed signature verification`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\t// File-transfer control: its own consent gate (files.acceptFrom + explicit accept)\n\t\t// lives in VoleNetFiles — the chat relay-consent below does not apply to files.\n\t\tif (inner.type.startsWith('file:')) {\n\t\t\tthis.files?.handleMessage(inner, { relayed: true, viaHub })\n\t\t\treturn\n\t\t}\n\n\t\t// Consent handshake. Sharing a hub is not consent — a member's relayed chat is dropped\n\t\t// (surfaced as a pending connect-request) until I accept it. A directly-trusted peer is\n\t\t// exempt: I'd already accept its direct connection, so the relay isn't a new trust grant.\n\t\tif (inner.type === 'relay:connect-request') {\n\t\t\tconst p = inner.payload as { fromName?: string; note?: string }\n\t\t\tthis.recordRelayRequest(fromId, p?.fromName, p?.note)\n\t\t\tbus?.emit('volenet:relay:request', {\n\t\t\t\tfrom: fromId,\n\t\t\t\tfromName: p?.fromName || this.rosterName(fromId),\n\t\t\t\tnote: p?.note,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tif (inner.type === 'relay:connect-accept') {\n\t\t\tthis.relayOutgoing.delete(fromId)\n\t\t\tthis.relayConfirmed.add(fromId)\n\t\t\tbus?.emit('volenet:relay:accepted', { from: fromId, fromName: this.rosterName(fromId) })\n\t\t\treturn\n\t\t}\n\t\tif (inner.type === 'relay:connect-deny') {\n\t\t\tthis.relayOutgoing.delete(fromId)\n\t\t\tbus?.emit('volenet:relay:denied', { from: fromId, fromName: this.rosterName(fromId) })\n\t\t\treturn\n\t\t}\n\n\t\t// chat:message\n\t\tconst payload = inner.payload as { text?: string; fromName?: string; sentAt?: number }\n\t\tif (!payload?.text) return\n\t\tconst fromName = payload.fromName || fromId.substring(0, 8)\n\t\t// A message that waited in the sender's outbox says when it was written. That is the time\n\t\t// to keep — not when the re-signed envelope finally crossed the hub. Bounded so a sender\n\t\t// cannot claim a future, beyond ordinary clock skew.\n\t\tconst sentAt =\n\t\t\ttypeof payload.sentAt === 'number' &&\n\t\t\tpayload.sentAt > 0 &&\n\t\t\tpayload.sentAt <= inner.timestamp + 60_000\n\t\t\t\t? payload.sentAt\n\t\t\t\t: inner.timestamp\n\t\tif (!directlyTrusted && !this.isRelayAccepted(fromId, fromName)) {\n\t\t\tthis.recordRelayRequest(fromId, fromName)\n\t\t\tbus?.emit('volenet:relay:request', { from: fromId, fromName })\n\t\t\tlogger.info(\n\t\t\t\t`Held relayed chat from unaccepted ${fromName} (${fromId.substring(0, 8)}) — awaiting consent`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tthis.chatPending.delete(fromId)\n\t\tthis.lastChatFrom.set(fromId, Math.max(inner.timestamp, this.lastChatFrom.get(fromId) ?? 0))\n\t\tvoid this.appendChat(fromId, {\n\t\t\tdir: 'in',\n\t\t\ttext: payload.text,\n\t\t\tfromName,\n\t\t\ttimestamp: sentAt,\n\t\t\tmessageId: inner.id,\n\t\t\trelayed: true,\n\t\t})\n\t\tbus?.emit('volenet:chat', {\n\t\t\tfrom: fromId,\n\t\t\tfromName,\n\t\t\ttext: payload.text,\n\t\t\tmessageId: inner.id,\n\t\t\ttimestamp: sentAt,\n\t\t\trelayed: true,\n\t\t})\n\t}\n\n\t/** True when a relay sender may reach me: '*' policy, an acceptFrom match, or a prior approval. */\n\tprivate isRelayAccepted(fromId: string, fromName?: string): boolean {\n\t\tconst policy = this.config.relay?.acceptFrom\n\t\tif (policy === '*') return true\n\t\tif (Array.isArray(policy)) {\n\t\t\tfor (const raw of policy) {\n\t\t\t\tconst t = typeof raw === 'string' ? raw.trim() : ''\n\t\t\t\tif (!t) continue\n\t\t\t\tif (t === '*') return true\n\t\t\t\tif (t === fromId || fromId.startsWith(t)) return true\n\t\t\t\tif (fromName && t === fromName) return true\n\t\t\t}\n\t\t}\n\t\treturn this.relayAcceptIds.has(fromId)\n\t}\n\n\t/** Record (or refresh) an inbound relay connect-request awaiting my approval. */\n\tprivate recordRelayRequest(fromId: string, fromName?: string, note?: string): void {\n\t\tlet viaHub = ''\n\t\tlet viaHubName = ''\n\t\tfor (const [hubId, roster] of this.hubRosters) {\n\t\t\tif (roster.has(fromId)) {\n\t\t\t\tviaHub = hubId\n\t\t\t\tviaHubName = this.getInstances().find((i) => i.id === hubId)?.name ?? hubId.substring(0, 8)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tthis.relayRequests.set(fromId, {\n\t\t\tid: fromId,\n\t\t\tname: fromName || this.rosterName(fromId),\n\t\t\tviaHub,\n\t\t\tviaHubName,\n\t\t\tnote,\n\t\t\tts: Date.now(),\n\t\t})\n\t}\n\n\tprivate rosterName(id: string): string {\n\t\treturn this.findRosterMember(id)?.name ?? id.substring(0, 8)\n\t}\n\n\tprivate findRosterMember(id: string): RosterMember | undefined {\n\t\tfor (const roster of this.hubRosters.values()) {\n\t\t\tconst m = roster.get(id)\n\t\t\tif (m) return m\n\t\t}\n\t\treturn undefined\n\t}\n\n\t/** Hub: push the current member directory to every connected member. */\n\tprivate broadcastRoster(): void {\n\t\tif (!this.keyPair || !this.transport || !this.discovery) return\n\t\tconst connected = new Set(\n\t\t\tthis.transport\n\t\t\t\t.getPeers()\n\t\t\t\t.filter((p) => p.connected)\n\t\t\t\t.map((p) => p.peerId),\n\t\t)\n\t\tconst members: RosterMember[] = this.discovery.getInstances().map((i) => ({\n\t\t\tinstanceId: i.id,\n\t\t\tname: i.name,\n\t\t\tpublicKey: i.publicKey,\n\t\t\txPublicKey: i.xPublicKey,\n\t\t\tmlkemPublicKey: i.mlkemPublicKey,\n\t\t\tconnected: connected.has(i.id),\n\t\t}))\n\t\tfor (const id of connected) {\n\t\t\tconst msg = createMessage(\n\t\t\t\t'roster',\n\t\t\t\tthis.keyPair.instanceId,\n\t\t\t\tid,\n\t\t\t\t{ members },\n\t\t\t\tthis.keyPair.privateKey,\n\t\t\t\tthis.keyPair.pqPrivateKey,\n\t\t\t)\n\t\t\tvoid this.transport.sendToPeer(id, msg)\n\t\t}\n\t}\n\n\t/** Hand a member that just reconnected everything the hub noted for it while it was away. */\n\tprivate async sendRelayNotices(memberId: string): Promise<void> {\n\t\tif (!this.relayNotices || !this.keyPair || !this.transport) return\n\t\tconst senders = await this.relayNotices.take(memberId)\n\t\tif (senders.length === 0) return\n\t\tconst msg = createMessage(\n\t\t\t'relay:pending',\n\t\t\tthis.keyPair.instanceId,\n\t\t\tmemberId,\n\t\t\t{ senders },\n\t\t\tthis.keyPair.privateKey,\n\t\t\tthis.keyPair.pqPrivateKey,\n\t\t)\n\t\tconst sent = await this.transport.sendToPeer(memberId, msg)\n\t\t// Dropped off again before we could say so — keep them for next time.\n\t\tif (!sent) await this.relayNotices.restore(memberId, senders)\n\t}\n\n\t/** Rosters received from relay hubs (hub instanceId → member directory). */\n\tgetRosters(): Map<string, Map<string, RosterMember>> {\n\t\treturn this.hubRosters\n\t}\n\n\t/**\n\t * Relay-reachable members from all hub rosters, flattened for the dashboard.\n\t * A member we ALSO connect to directly is omitted here — direct trumps relay, the same\n\t * precedence sendChat() uses — so the UI never lists one peer in two places.\n\t */\n\tgetRelayMembers(): Array<{\n\t\tid: string\n\t\tname: string\n\t\tviaHub: string\n\t\tviaHubName: string\n\t\tconnected: boolean\n\t\t/** I accept this member's relayed chat ('*'/acceptFrom match or an approval). */\n\t\taccepted: boolean\n\t\t/** This member has an inbound connect-request awaiting my approval. */\n\t\tincoming: boolean\n\t\t/** I've requested this member but it hasn't accepted yet. */\n\t\tawaiting: boolean\n\t}> {\n\t\tconst direct = new Set(this.getInstances().map((i) => i.id))\n\t\tconst out: Array<{\n\t\t\tid: string\n\t\t\tname: string\n\t\t\tviaHub: string\n\t\t\tviaHubName: string\n\t\t\tconnected: boolean\n\t\t\taccepted: boolean\n\t\t\tincoming: boolean\n\t\t\tawaiting: boolean\n\t\t}> = []\n\t\tconst seen = new Set<string>()\n\t\tfor (const [hubId, roster] of this.hubRosters) {\n\t\t\tconst hubName = this.getInstances().find((i) => i.id === hubId)?.name ?? hubId.substring(0, 8)\n\t\t\tfor (const m of roster.values()) {\n\t\t\t\tif (m.instanceId === this.keyPair?.instanceId) continue // self\n\t\t\t\tif (direct.has(m.instanceId)) continue // reachable directly — shown as a direct peer\n\t\t\t\tif (seen.has(m.instanceId)) continue // same member vouched by two hubs — list once\n\t\t\t\tseen.add(m.instanceId)\n\t\t\t\tout.push({\n\t\t\t\t\tid: m.instanceId,\n\t\t\t\t\tname: m.name,\n\t\t\t\t\tviaHub: hubId,\n\t\t\t\t\tviaHubName: hubName,\n\t\t\t\t\tconnected: m.connected,\n\t\t\t\t\taccepted: this.isRelayAccepted(m.instanceId, m.name),\n\t\t\t\t\tincoming: this.relayRequests.has(m.instanceId),\n\t\t\t\t\tawaiting: this.relayOutgoing.has(m.instanceId) && !this.relayConfirmed.has(m.instanceId),\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\treturn out\n\t}\n\n\t/**\n\t * Get all remote tools.\n\t */\n\tgetRemoteTools(): RemoteToolInfo[] {\n\t\treturn this.discovery?.getRemoteTools() ?? []\n\t}\n\n\t/**\n\t * Find which peer owns a tool.\n\t */\n\tfindToolOwner(toolName: string): { instanceId: string; instance: VoleNetInstance } | null {\n\t\treturn this.discovery?.findToolOwner(toolName) ?? null\n\t}\n\n\t/**\n\t * Get the remote task manager.\n\t */\n\tgetRemoteTaskManager(): RemoteTaskManager | null {\n\t\treturn this.remoteTaskMgr\n\t}\n\n\t/**\n\t * Get the sync manager (for memory/session propagation).\n\t */\n\tgetSync(): VoleNetSync | null {\n\t\treturn this.sync\n\t}\n\n\t/**\n\t * Get the leader election manager.\n\t */\n\tgetLeader(): VoleNetLeader | null {\n\t\treturn this.leader\n\t}\n\n\t/**\n\t * Check if this instance is the VoleNet leader.\n\t */\n\tisLeader(): boolean {\n\t\treturn this.leader?.isLeader() ?? true // standalone = always leader\n\t}\n\n\t/**\n\t * Check if this instance should run heartbeat.\n\t * In \"independent\" mode, every instance runs heartbeat.\n\t * In \"leader\" mode (default), only the leader runs it.\n\t */\n\tshouldRunHeartbeat(): boolean {\n\t\tif (!this.started) return true // standalone = always run\n\t\tif (this.config.heartbeatMode === 'independent') return true\n\t\treturn this.isLeader()\n\t}\n\n\t/**\n\t * Find the best peer for load-balanced task routing.\n\t * Returns null if local should handle it (or no peers available).\n\t */\n\tfindLeastLoadedPeer(): VoleNetInstance | null {\n\t\tif (this.config.brainMode !== 'loadbalance') return null\n\t\tconst instances = this.discovery?.getInstances() ?? []\n\t\tif (instances.length === 0) return null\n\t\t// Sort by load ascending, pick lowest\n\t\tconst sorted = [...instances].sort((a, b) => a.load - b.load)\n\t\t// Only forward if the peer has lower load than us\n\t\t// (our load is estimated from task queue size)\n\t\treturn sorted[0].load < 0.8 ? sorted[0] : null\n\t}\n\n\t/**\n\t * Check if a task should be forwarded to a peer (overflow mode).\n\t * Returns the target peer or null if local should handle it.\n\t */\n\tshouldForwardTask(currentQueueSize: number): VoleNetInstance | null {\n\t\tif (this.config.taskOverflow !== 'forward') return null\n\t\tconst maxQueued = this.config.maxQueuedTasks ?? 10\n\t\tif (currentQueueSize < maxQueued) return null\n\t\t// Forward to least-loaded peer\n\t\tconst instances = this.discovery?.getInstances() ?? []\n\t\tif (instances.length === 0) return null\n\t\tconst sorted = [...instances].sort((a, b) => a.load - b.load)\n\t\treturn sorted[0]\n\t}\n\n\t/**\n\t * Check if this instance should delegate thinking to a remote brain.\n\t * Returns the target peer instance ID, or null if local brain should be used.\n\t */\n\tshouldDelegateBrain(): string | null {\n\t\tconst brainSource = this.config.brainSource\n\t\tif (!brainSource || brainSource === 'local') return null\n\n\t\tconst instances = this.discovery?.getInstances() ?? []\n\t\tif (instances.length === 0) return null\n\n\t\tif (brainSource === 'remote') {\n\t\t\t// Find any peer — preferring coordinators\n\t\t\tconst sorted = [...instances].sort((a, b) => {\n\t\t\t\tif (a.role === 'coordinator' && b.role !== 'coordinator') return -1\n\t\t\t\tif (b.role === 'coordinator' && a.role !== 'coordinator') return 1\n\t\t\t\treturn 0\n\t\t\t})\n\t\t\treturn sorted[0]?.id ?? null\n\t\t}\n\n\t\t// Specific instance name\n\t\tconst target = instances.find((i) => i.name === brainSource)\n\t\treturn target?.id ?? null\n\t}\n\n\t/** Handle a public self-join request (HTTP POST /volenet/join). Returns status + JSON body. */\n\tasync handlePublicJoin(body: unknown, ip: string): Promise<{ status: number; json: unknown }> {\n\t\tconst pj = this.config.publicJoin\n\t\tif (!pj?.enabled) return { status: 404, json: { error: 'public join disabled' } }\n\n\t\tconst { publicKey, name } = (body ?? {}) as { publicKey?: string; name?: string }\n\t\tif (!publicKey || !parsePublicKey(publicKey)) {\n\t\t\treturn { status: 400, json: { error: 'invalid public key' } }\n\t\t}\n\n\t\t// Rate limit per IP.\n\t\tconst now = Date.now()\n\t\tconst rpm = pj.ratePerMinute ?? 5\n\t\t// Prune stale per-IP windows so the join map can't grow unbounded under IP-spray.\n\t\tif (this.joinTimestamps.size > 4096) {\n\t\t\tfor (const [k, ts] of this.joinTimestamps) {\n\t\t\t\tif (ts.length === 0 || now - ts[ts.length - 1] > 60_000) this.joinTimestamps.delete(k)\n\t\t\t}\n\t\t}\n\t\tconst recent = (this.joinTimestamps.get(ip) ?? []).filter((t) => now - t < 60_000)\n\t\tif (recent.length >= rpm) return { status: 429, json: { error: 'rate limited' } }\n\t\trecent.push(now)\n\t\tthis.joinTimestamps.set(ip, recent)\n\n\t\tconst netDir = this.getNetDir()\n\t\tawait fs.mkdir(netDir, { recursive: true })\n\t\tconst safeName = (name ?? 'guest').slice(0, 64)\n\n\t\t// Manual-approval mode: queue the request, do not auto-trust.\n\t\tif (pj.requireApproval) {\n\t\t\tawait fs.appendFile(\n\t\t\t\tpath.join(netDir, 'pending_joins.jsonl'),\n\t\t\t\t`${JSON.stringify({ publicKey, name: safeName, ip, at: new Date().toISOString() })}\\n`,\n\t\t\t\t'utf-8',\n\t\t\t)\n\t\t\treturn {\n\t\t\t\tstatus: 202,\n\t\t\t\tjson: { ok: true, pending: true, message: 'Join request received — pending approval.' },\n\t\t\t}\n\t\t}\n\n\t\t// Peer cap.\n\t\tconst existing = await loadAuthorizedVoles(netDir)\n\t\tif (existing.size >= (pj.maxPeers ?? 200)) {\n\t\t\treturn { status: 503, json: { error: 'mesh is full' } }\n\t\t}\n\n\t\tawait trustPeer(netDir, publicKey, { allowUpgrade: false })\n\t\tawait this.discovery?.reloadAuthorized()\n\t\tlogger.info(`Public join: trusted guest \"${safeName}\" from ${ip}`)\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\tjson: {\n\t\t\t\tok: true,\n\t\t\t\thubPublicKey: this.keyPair?.publicKeyString,\n\t\t\t\tinstanceName: this.config.instanceName ?? 'vole',\n\t\t\t\tport: this.config.port ?? 9700,\n\t\t\t},\n\t\t}\n\t}\n\n\t// ── Consent-based pairing (vole net pair) ─────────────────────────────────\n\n\tprivate pairRequestsPath(): string {\n\t\treturn path.join(this.getNetDir(), 'pair_requests.json')\n\t}\n\n\tprivate async loadPairRequests(): Promise<void> {\n\t\ttry {\n\t\t\tconst raw = JSON.parse(await fs.readFile(this.pairRequestsPath(), 'utf-8')) as Array<{\n\t\t\t\tid: string\n\t\t\t\tname: string\n\t\t\t\tpublicKey: string\n\t\t\t\tendpoint?: string\n\t\t\t\tnote?: string\n\t\t\t\tts: number\n\t\t\t}>\n\t\t\tfor (const r of raw) if (r?.id && r.publicKey) this.pairRequests.set(r.id, r)\n\t\t} catch {\n\t\t\t/* none yet */\n\t\t}\n\t}\n\n\tprivate async persistPairRequests(): Promise<void> {\n\t\ttry {\n\t\t\tawait fs.writeFile(\n\t\t\t\tthis.pairRequestsPath(),\n\t\t\t\tJSON.stringify([...this.pairRequests.values()], null, 2),\n\t\t\t)\n\t\t} catch (err) {\n\t\t\tlogger.warn(`Could not persist pair requests: ${err instanceof Error ? err.message : err}`)\n\t\t}\n\t}\n\n\t/** Handle POST /volenet/pair — queue the introduction; trust NOTHING until acceptPair. */\n\tasync handlePairRequest(\n\t\tbody: unknown,\n\t\tip: string,\n\t\tbus?: EventSink,\n\t): Promise<{ status: number; json: unknown }> {\n\t\tconst { publicKey, name, note, endpoint } = (body ?? {}) as {\n\t\t\tpublicKey?: string\n\t\t\tname?: string\n\t\t\tnote?: string\n\t\t\tendpoint?: string\n\t\t}\n\t\tif (!publicKey || !parsePublicKey(publicKey)) {\n\t\t\treturn { status: 400, json: { error: 'invalid public key' } }\n\t\t}\n\t\t// Rate limit per IP (same policy shape as publicJoin: 5/min).\n\t\tconst now = Date.now()\n\t\tif (this.pairTimestamps.size > 4096) {\n\t\t\tfor (const [k, ts] of this.pairTimestamps) {\n\t\t\t\tif (ts.length === 0 || now - ts[ts.length - 1] > 60_000) this.pairTimestamps.delete(k)\n\t\t\t}\n\t\t}\n\t\tconst window = (this.pairTimestamps.get(ip) ?? []).filter((t) => now - t < 60_000)\n\t\tif (window.length >= 5) return { status: 429, json: { error: 'rate limited' } }\n\t\twindow.push(now)\n\t\tthis.pairTimestamps.set(ip, window)\n\n\t\tconst parsed = parsePublicKey(publicKey)!\n\t\tconst already = await loadAuthorizedVoles(this.getNetDir())\n\t\tif (already.has(parsed.instanceId)) {\n\t\t\treturn { status: 200, json: { ok: true, alreadyTrusted: true } }\n\t\t}\n\t\tif (this.pairRequests.size >= 32 && !this.pairRequests.has(parsed.instanceId)) {\n\t\t\treturn { status: 503, json: { error: 'too many pending pair requests' } }\n\t\t}\n\t\tconst safeName =\n\t\t\t(typeof name === 'string' ? name : '').slice(0, 64) || parsed.instanceId.substring(0, 8)\n\t\tthis.pairRequests.set(parsed.instanceId, {\n\t\t\tid: parsed.instanceId,\n\t\t\tname: safeName,\n\t\t\tpublicKey,\n\t\t\tendpoint: typeof endpoint === 'string' ? endpoint.slice(0, 200) : undefined,\n\t\t\tnote: typeof note === 'string' ? note.slice(0, 200) : undefined,\n\t\t\tts: now,\n\t\t})\n\t\tawait this.persistPairRequests()\n\t\tbus?.emit('volenet:pair:request', {\n\t\t\tfrom: parsed.instanceId,\n\t\t\tfromName: safeName,\n\t\t\tnote: typeof note === 'string' ? note.slice(0, 200) : undefined,\n\t\t})\n\t\tlogger.info(`Pair request from \"${safeName}\" (${parsed.instanceId.substring(0, 8)}) via ${ip}`)\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\tjson: {\n\t\t\t\tok: true,\n\t\t\t\tpending: true,\n\t\t\t\tmessage: 'Pair request received — awaiting operator approval.',\n\t\t\t},\n\t\t}\n\t}\n\n\tlistPairRequests(): Array<{\n\t\tid: string\n\t\tname: string\n\t\tendpoint?: string\n\t\tnote?: string\n\t\tts: number\n\t}> {\n\t\treturn [...this.pairRequests.values()].map(({ publicKey: _pk, ...rest }) => rest)\n\t}\n\n\t/** Operator consent: trust the requester's pinned key, live-reload, dial back if possible. */\n\tasync acceptPair(ref: string): Promise<{ ok: boolean; name?: string; error?: string }> {\n\t\tconst req = [...this.pairRequests.values()].find(\n\t\t\t(r) => r.id === ref || r.name === ref || r.id.startsWith(ref),\n\t\t)\n\t\tif (!req) return { ok: false, error: `no pending pair request matching \"${ref}\"` }\n\t\tawait trustPeer(this.getNetDir(), req.publicKey)\n\t\tawait this.discovery?.reloadAuthorized()\n\t\tthis.pairRequests.delete(req.id)\n\t\tawait this.persistPairRequests()\n\t\t// Save the peer, exactly as initiatePair does for the side that asked. Accepting used to\n\t\t// trust the key and dial the endpoint without ever recording it, so pairing came out\n\t\t// one-sided: chat and file transfer worked over the connection the other node had opened,\n\t\t// but this node had no peer of its own — nothing to reconnect to after a restart, and an\n\t\t// empty peer list until the operator added it by hand or sent a request back.\n\t\tif (req.endpoint) {\n\t\t\tawait this.addPeerEntry(req.endpoint) // also dials it\n\t\t}\n\t\tlogger.info(\n\t\t\t`Pair accepted: \"${req.name}\" (${req.id.substring(0, 8)}) is now trusted${req.endpoint ? ` and saved as a peer (${req.endpoint})` : ' (no endpoint advertised — it must connect to us)'}`,\n\t\t)\n\t\treturn { ok: true, name: req.name }\n\t}\n\n\tasync denyPair(ref: string): Promise<{ ok: boolean }> {\n\t\tconst req = [...this.pairRequests.values()].find(\n\t\t\t(r) => r.id === ref || r.name === ref || r.id.startsWith(ref),\n\t\t)\n\t\tif (!req) return { ok: false }\n\t\tthis.pairRequests.delete(req.id)\n\t\tawait this.persistPairRequests()\n\t\treturn { ok: true }\n\t}\n\n\t/** Fetch a remote node's identity for the operator to fingerprint before pairing. */\n\tasync probePair(url: string): Promise<{\n\t\tok: boolean\n\t\tname?: string\n\t\tfingerprint?: string\n\t\tpublicKey?: string\n\t\talreadyTrusted?: boolean\n\t\terror?: string\n\t}> {\n\t\tconst base = url.replace(/\\/$/, '')\n\t\tlet info: { publicKey?: string; name?: string }\n\t\ttry {\n\t\t\tconst r = await fetch(`${base}/volenet/info`, { signal: AbortSignal.timeout(8000) })\n\t\t\tinfo = (await r.json()) as typeof info\n\t\t} catch (err) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `could not reach ${base}: ${err instanceof Error ? err.message : err}`,\n\t\t\t}\n\t\t}\n\t\tconst parsed = info.publicKey ? parsePublicKey(info.publicKey) : null\n\t\tif (!parsed) return { ok: false, error: 'peer offered no public key (openvole < 4.13?)' }\n\t\tconst trusted = await loadAuthorizedVoles(this.getNetDir())\n\t\treturn {\n\t\t\tok: true,\n\t\t\tname: info.name,\n\t\t\tfingerprint: parsed.instanceId,\n\t\t\tpublicKey: info.publicKey,\n\t\t\talreadyTrusted: trusted.has(parsed.instanceId),\n\t\t}\n\t}\n\n\t/** Treat a URL as a configured peer and dial it now. The inverse of {@link forgetPeer}. */\n\tasync addPeer(url: string): Promise<void> {\n\t\tawait this.addPeerEntry(url.replace(/\\/$/, ''))\n\t}\n\n\t/**\n\t * Stop treating a URL as a configured peer.\n\t *\n\t * The reconnect loop re-dials everything in `config.peers` every 15 seconds, so dropping the\n\t * socket alone would have it back within a tick. Both halves belong together: forget the entry\n\t * and close the connection. Trust is untouched — this says \"do not dial\", not \"do not trust\".\n\t */\n\tforgetPeer(url: string): boolean {\n\t\tconst base = url.replace(/\\/$/, '')\n\t\tconst before = this.config.peers?.length ?? 0\n\t\tthis.config.peers = (this.config.peers ?? []).filter((p) => p.url?.replace(/\\/$/, '') !== base)\n\t\tconst peer = this.discovery?.getInstances().find((i) => i.endpoint.replace(/\\/$/, '') === base)\n\t\tif (peer) this.transport?.removePeer(peer.id)\n\t\treturn (this.config.peers.length ?? 0) < before || !!peer\n\t}\n\n\t/** Record a peer URL in the live config, ask the host to remember it, then dial it. */\n\tprivate async addPeerEntry(url: string): Promise<void> {\n\t\tconst base = url.replace(/\\/$/, '')\n\t\tthis.config.peers = this.config.peers ?? []\n\t\tif (!this.config.peers.some((p) => p.url === base)) {\n\t\t\tthis.config.peers.push({ url: base, trust: 'full' })\n\t\t}\n\t\ttry {\n\t\t\t// Writing it down is the host's business: this library does not know what shape its\n\t\t\t// config file is, and guessing would corrupt one. A host that wants a learned peer to\n\t\t\t// survive a restart supplies persistPeer; without it the entry is live-only.\n\t\t\tawait this.config.persistPeer?.(base)\n\t\t} catch (err) {\n\t\t\tlogger.warn(`Could not persist peer entry: ${err instanceof Error ? err.message : err}`)\n\t\t}\n\t\tvoid this.discovery?.connectToPeer(base).catch(() => {})\n\t}\n\n\t/**\n\t * Dashboard-initiated pairing: trust the probed key (the operator confirmed the\n\t * fingerprint client-side), persist + dial the peer, and file the pair request for\n\t * the other operator. Fully live — no restart needed on this side.\n\t */\n\tasync initiatePair(\n\t\turl: string,\n\t\tpublicKey: string,\n\t\tnote?: string,\n\t): Promise<{ ok: boolean; pending?: boolean; alreadyTrusted?: boolean; error?: string }> {\n\t\tif (!this.keyPair) return { ok: false, error: 'VoleNet not started' }\n\t\tconst base = url.replace(/\\/$/, '')\n\t\tif (!parsePublicKey(publicKey)) return { ok: false, error: 'invalid public key' }\n\t\tawait trustPeer(this.getNetDir(), publicKey)\n\t\tawait this.discovery?.reloadAuthorized()\n\t\tawait this.addPeerEntry(base)\n\t\ttry {\n\t\t\tconst r = await fetch(`${base}/volenet/pair`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tpublicKey: this.keyPair.publicKeyString,\n\t\t\t\t\tname: this.config.instanceName ?? 'vole',\n\t\t\t\t\tnote,\n\t\t\t\t\tendpoint: this.transport\n\t\t\t\t\t\t? buildAdvertisedEndpoint({\n\t\t\t\t\t\t\t\tpublicUrl: this.config.publicUrl ?? process.env.VOLE_NET_PUBLIC_URL,\n\t\t\t\t\t\t\t\ttls: !!this.config.tls,\n\t\t\t\t\t\t\t\thostname: this.getHostname(),\n\t\t\t\t\t\t\t\tport: this.config.port ?? 9700,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: undefined,\n\t\t\t\t}),\n\t\t\t\tsignal: AbortSignal.timeout(8000),\n\t\t\t})\n\t\t\tconst resp = (await r.json()) as {\n\t\t\t\tok?: boolean\n\t\t\t\tpending?: boolean\n\t\t\t\talreadyTrusted?: boolean\n\t\t\t\terror?: string\n\t\t\t}\n\t\t\tif (!resp.ok) return { ok: false, error: resp.error ?? `pair request failed (${r.status})` }\n\t\t\treturn { ok: true, pending: resp.pending, alreadyTrusted: resp.alreadyTrusted }\n\t\t} catch (err) {\n\t\t\treturn { ok: false, error: err instanceof Error ? err.message : String(err) }\n\t\t}\n\t}\n\n\t/** Dashboard-initiated public-hub join (the vole net join flow, in-process and live). */\n\tasync initiateJoin(\n\t\turl: string,\n\t): Promise<{ ok: boolean; pending?: boolean; hubName?: string; error?: string }> {\n\t\tif (!this.keyPair) return { ok: false, error: 'VoleNet not started' }\n\t\tconst base = url.replace(/\\/$/, '')\n\t\ttry {\n\t\t\tconst r = await fetch(`${base}/volenet/join`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tpublicKey: this.keyPair.publicKeyString,\n\t\t\t\t\tname: this.config.instanceName ?? 'vole',\n\t\t\t\t}),\n\t\t\t\tsignal: AbortSignal.timeout(10000),\n\t\t\t})\n\t\t\tconst text = await r.text()\n\t\t\tif (r.status === 404 || !text.trim()) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: 'not a public hub (publicJoin is not enabled there) — use pair instead',\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst resp = JSON.parse(text) as {\n\t\t\t\tok?: boolean\n\t\t\t\tpending?: boolean\n\t\t\t\thubPublicKey?: string\n\t\t\t\tinstanceName?: string\n\t\t\t\terror?: string\n\t\t\t}\n\t\t\tif (!r.ok || !resp.ok) return { ok: false, error: resp.error ?? `join failed (${r.status})` }\n\t\t\tif (resp.pending) return { ok: true, pending: true, hubName: resp.instanceName }\n\t\t\tif (resp.hubPublicKey && parsePublicKey(resp.hubPublicKey)) {\n\t\t\t\tawait trustPeer(this.getNetDir(), resp.hubPublicKey)\n\t\t\t\tawait this.discovery?.reloadAuthorized()\n\t\t\t}\n\t\t\tawait this.addPeerEntry(base)\n\t\t\treturn { ok: true, hubName: resp.instanceName }\n\t\t} catch (err) {\n\t\t\treturn { ok: false, error: err instanceof Error ? err.message : String(err) }\n\t\t}\n\t}\n\n\t/**\n\t * Check if a specific peer is allowed to use our brain.\n\t */\n\tisPeerAllowedBrain(peerId: string): boolean {\n\t\treturn this.getPeerTrust(peerId)?.allowBrain === true\n\t}\n\n\t/**\n\t * Whether this peer may call our tools at all — tool/full trust (from net.peers OR a\n\t * publicJoin guest's granted trustLevel), or share.tools. Uses getPeerTrust, not\n\t * matchPeerConfig, so a publicJoin guest's `trustLevel: \"tool\"` actually grants tool\n\t * access — matching the documented behavior and how isPeerAllowedBrain already works.\n\t * (Per-tool curation still applies via share.toolAllow in isPeerAllowedTool.)\n\t */\n\tprivate peerToolsEnabled(peerId: string): boolean {\n\t\tconst trust = this.getPeerTrust(peerId)\n\t\tif (trust && (trust.trust === 'full' || trust.trust === 'tool')) return true\n\t\treturn this.config.share?.tools === true\n\t}\n\n\t/** Whether this peer may call a specific tool (honors per-peer allow/deny lists). */\n\tisPeerAllowedTool(peerId: string, toolName: string): boolean {\n\t\tconst explicit = this.matchPeerConfig(peerId)\n\t\tif (explicit?.denyTools?.some((p) => matchToolPattern(p, toolName))) return false\n\t\tif (explicit?.allowTools && explicit.allowTools.length > 0) {\n\t\t\treturn explicit.allowTools.some((p) => matchToolPattern(p, toolName))\n\t\t}\n\t\t// Peers without an explicit entry (e.g. public joiners) honor the share-level allowlist.\n\t\tif (!isSharedTool(toolName, this.config.share?.toolAllow)) return false\n\t\treturn this.peerToolsEnabled(peerId)\n\t}\n\n\t/**\n\t * Match a connected peer to its config entry.\n\t * Matches by port (handles localhost vs real IP) or by instance name.\n\t */\n\tprivate matchPeerConfig(peerId: string): {\n\t\turl?: string\n\t\tid?: string\n\t\tname?: string\n\t\ttrust?: string\n\t\tallowTools?: string[]\n\t\tdenyTools?: string[]\n\t\tallowBrain?: boolean\n\t} | null {\n\t\tif (!this.config.peers) return null\n\t\tconst instance = this.discovery?.getInstances().find((i) => i.id === peerId)\n\t\tif (!instance) return null\n\n\t\t// Identity first. A peer that advertises no endpoint — a phone, anything that can only\n\t\t// dial us — can never match by address, so this is the only way to say what it may do.\n\t\t// An id prefix must be long enough to mean something; a name is convenience, not proof.\n\t\tfor (const peerConfig of this.config.peers) {\n\t\t\tif (peerConfig.id) {\n\t\t\t\tconst want = peerConfig.id.trim()\n\t\t\t\tif (want.length >= 8 && (peerId === want || peerId.startsWith(want))) return peerConfig\n\t\t\t} else if (peerConfig.name && instance.name === peerConfig.name) {\n\t\t\t\treturn peerConfig\n\t\t\t}\n\t\t}\n\n\t\tfor (const peerConfig of this.config.peers) {\n\t\t\tif (!peerConfig.url) continue\n\t\t\ttry {\n\t\t\t\tconst configUrl = new URL(peerConfig.url)\n\t\t\t\tconst configPort = configUrl.port || (configUrl.protocol === 'https:' ? '443' : '80')\n\n\t\t\t\t// Match by port (handles localhost:9701 matching 192.168.x.x:9701)\n\t\t\t\tif (instance.endpoint.includes(`:${configPort}`)) {\n\t\t\t\t\treturn peerConfig\n\t\t\t\t}\n\n\t\t\t\t// Also match by exact host\n\t\t\t\tif (instance.endpoint.includes(configUrl.host)) {\n\t\t\t\t\treturn peerConfig\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\treturn null\n\t}\n\n\t/**\n\t * Get trust level for a peer.\n\t */\n\tgetPeerTrust(\n\t\tpeerId: string,\n\t): { trust: string; allowTools?: string[]; denyTools?: string[]; allowBrain?: boolean } | null {\n\t\tconst peerConfig = this.matchPeerConfig(peerId)\n\t\tif (peerConfig) {\n\t\t\treturn {\n\t\t\t\ttrust: peerConfig.trust ?? 'full',\n\t\t\t\tallowTools: peerConfig.allowTools,\n\t\t\t\tdenyTools: peerConfig.denyTools,\n\t\t\t\tallowBrain: peerConfig.allowBrain,\n\t\t\t}\n\t\t}\n\t\t// Self-joined guest: authenticated (a known discovery instance) but not in static\n\t\t// peers config. Grant the restricted publicJoin trust — never 'full'.\n\t\tconst pj = this.config.publicJoin\n\t\tif (pj?.enabled && this.discovery?.getInstances().some((i) => i.id === peerId)) {\n\t\t\treturn { trust: pj.trustLevel ?? 'tool', allowBrain: pj.allowBrain ?? false }\n\t\t}\n\t\treturn null\n\t}\n\n\t/**\n\t * Get the keypair (for CLI display).\n\t */\n\t/** Our instance name (for message framing). */\n\tgetInstanceName(): string {\n\t\treturn this.config.instanceName ?? 'vole'\n\t}\n\n\tgetKeyPair(): VoleKeyPair | null {\n\t\treturn this.keyPair\n\t}\n\n\t/**\n\t * Get the transport (for sending messages).\n\t */\n\tgetTransport(): VoleNetTransport | null {\n\t\treturn this.transport\n\t}\n\n\t/**\n\t * Get the discovery manager.\n\t */\n\tgetDiscovery(): VoleNetDiscovery | null {\n\t\treturn this.discovery\n\t}\n\n\t/**\n\t * Check if VoleNet is active.\n\t */\n\tisActive(): boolean {\n\t\treturn this.started\n\t}\n\n\tprivate getNetDir(): string {\n\t\tconst keyPath = this.config.keyPath ?? '.openvole/net/vole_key'\n\t\treturn path.resolve(this.projectRoot, path.dirname(keyPath))\n\t}\n\n\tprivate getHostname(): string {\n\t\t// Explicit override wins — required for TLS so the advertised host matches the cert.\n\t\tconst override = this.config.hostname ?? process.env.VOLE_NET_HOSTNAME\n\t\tif (override?.trim()) return override.trim()\n\t\tconst nets = os.networkInterfaces()\n\t\tif (!nets) return 'localhost'\n\t\t// Find first non-internal IPv4 address\n\t\tfor (const name of Object.keys(nets)) {\n\t\t\tfor (const net of nets[name] ?? []) {\n\t\t\t\tif (net.family === 'IPv4' && !net.internal) {\n\t\t\t\t\treturn net.address\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn 'localhost'\n\t}\n\n\tprivate getCapabilities(): string[] {\n\t\tconst caps: string[] = []\n\t\tif (this.toolRegistry) {\n\t\t\t// List paw categories as capabilities\n\t\t\tconst pawNames = new Set(this.toolRegistry.list().map((t) => t.pawName))\n\t\t\tfor (const name of pawNames) {\n\t\t\t\tif (name !== '__core__') caps.push(name)\n\t\t\t}\n\t\t}\n\t\treturn caps\n\t}\n}\n\n// Re-export key types and functions for CLI use\nexport {\n\tgenerateKeyPair,\n\tloadKeyPair,\n\ttrustPeer,\n\trevokePeer,\n\tloadAuthorizedVoles,\n\tloadRelayAccepts,\n\taddRelayAccept,\n\tremoveRelayAccept,\n\tparsePublicKey,\n} from './keys.js'\nexport type { VoleKeyPair } from './keys.js'\nexport type { VoleNetInstance, RemoteToolInfo } from './protocol.js'\nexport { RemoteTaskManager } from './remote-task.js'\nexport type {\n\tRemoteTaskRequest,\n\tRemoteTaskResult,\n\tRemoteToolCallRequest,\n\tRemoteToolCallResult,\n} from './remote-task.js'\nexport { VoleNetSync } from './sync.js'\nexport type {\n\tMemorySyncEntry,\n\tMemorySearchRequest,\n\tMemorySearchResult,\n\tSessionSyncEntry,\n} from './sync.js'\nexport { VoleNetLeader } from './leader.js'\nexport type { LeaderState } from './leader.js'\n\n/**\n * Merge the transport-verified caller identity into remote tool params.\n * Always overwrites any incoming `__caller` — a peer cannot impersonate another instance.\n * Tools that want attribution declare an optional `__caller` parameter; others ignore it.\n */\nexport function withVerifiedCaller(\n\tparams: unknown,\n\tinstanceId: string,\n\tname?: string,\n): Record<string, unknown> {\n\tconst base =\n\t\ttypeof params === 'object' && params !== null && !Array.isArray(params)\n\t\t\t? { ...(params as Record<string, unknown>) }\n\t\t\t: {}\n\tbase.__caller = { instanceId, name: name ?? instanceId.substring(0, 8) }\n\treturn base\n}\n\n/**\n * The endpoint an instance advertises to peers. An explicit publicUrl wins outright —\n * set it when VoleNet sits behind a reverse proxy so peers are told the proxy URL\n * (e.g. \"https://club.example.com/mesh\") instead of a raw listen port that may be firewalled.\n */\nexport function buildAdvertisedEndpoint(opts: {\n\tpublicUrl?: string\n\ttls?: boolean\n\thostname: string\n\tport: number\n}): string {\n\tconst override = opts.publicUrl?.trim().replace(/\\/+$/, '')\n\tif (override) return override\n\tconst scheme = opts.tls ? 'https' : 'http'\n\treturn `${scheme}://${opts.hostname}:${opts.port}`\n}\n\ntype PeerEntry = { url: string; trust?: string } & Record<string, unknown>\n\n/**\n * Add a peer URL to a config peers list, REPLACING any existing entry on the same hostname —\n * re-joining a hub at a new endpoint (a proxied /mesh path instead of a raw :9710 port) must\n * update the entry, not stack a dead duplicate beside it. The replaced entry's trust and\n * per-peer settings carry over. Returns the new list plus the URL it replaced, if any.\n */\nexport function upsertPeerUrl(\n\tpeers: PeerEntry[],\n\turl: string,\n): { peers: PeerEntry[]; replaced?: string } {\n\tconst norm = (u: string) => u.trim().replace(/\\/+$/, '')\n\tconst hostOf = (u: string): string | null => {\n\t\ttry {\n\t\t\treturn new URL(norm(u)).hostname || null\n\t\t} catch {\n\t\t\treturn null\n\t\t}\n\t}\n\tconst target = norm(url)\n\tif (peers.some((p) => norm(p.url) === target)) return { peers }\n\tconst targetHost = hostOf(target)\n\tconst stale = targetHost ? peers.find((p) => hostOf(p.url) === targetHost) : undefined\n\tconst kept = stale ? peers.filter((p) => p !== stale) : peers.slice()\n\tkept.push(stale ? { ...stale, url: target } : { url: target, trust: 'full' })\n\treturn { peers: kept, replaced: stale ? norm(stale.url) : undefined }\n}\n\n// ── The library's public surface ───────────────────────────────────────────────────────────\n// Everything a host needs to run a node, verify a peer, or seal a message, without reaching\n// into subpaths. Kept at the bottom so the file above reads as the manager it is.\n\nexport { createLogger, closeLogger, setLoggerFactory } from './logger.js'\nexport type { Logger, LoggerFactory } from './logger.js'\nexport { CONTROL_PLANE_PAWS, isControlPlanePaw } from './tools.js'\nexport type { SharedToolDefinition, SharedToolEntry, ToolProvider } from './tools.js'\nexport { createMessage, verifyMessage } from './protocol.js'\nexport type { VoleNetMessage, VoleNetMessageType } from './protocol.js'\nexport { seal, unseal } from './seal.js'\nexport type { SealedBox } from './seal.js'\nexport {\n\tChatOutbox,\n\tRelayNotices,\n\tDEFAULT_OUTBOX_TTL_MS,\n\tDEFAULT_NOTICE_TTL_MS,\n} from './chat-outbox.js'\nexport type { OutboxEntry, OutboxKind, RelayNotice } from './chat-outbox.js'\nexport { ResultOutbox, DEFAULT_RESULT_TTL_MS, MAX_RESULTS_PER_PEER } from './result-outbox.js'\nexport type { PendingResult } from './result-outbox.js'\nexport { VoleNetTransport } from './transport.js'\nexport { VoleNetDiscovery, findEndpointDrift } from './discovery.js'\n","/**\n * Chat that could not be delivered because the other side was away.\n *\n * Two pieces, one on each end of a relay hub, and neither holds a readable message:\n *\n * - **ChatOutbox** (sender side): messages the hub could not forward wait *here*, on the\n * sender's own machine, and go out when the recipient shows up in a roster again. The hub\n * never stores them. That is deliberate rather than tidy: an envelope is sealed to the\n * recipient's static keys with no ratchet, so ciphertext at rest anywhere but the endpoints\n * is retroactively readable if that key is ever compromised. A notice carries nothing to\n * decrypt.\n *\n * - **RelayNotices** (hub side): \"somebody tried to reach you while you were away\" — sender,\n * count, first and last time. Routing metadata the hub already sees for every envelope it\n * forwards, and nothing more. Handed to the member on reconnect and forgotten.\n *\n * Both persist as JSON under the node's net directory so a restart on either side loses\n * nothing: the sender's outbox because a phone gets closed and reopened, the hub's notices\n * because \"the hub restarted\" should not mean \"you were never told\".\n */\nimport * as crypto from 'node:crypto'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\n\n/** What an outbox entry is. Consent traffic waits the same way chat does — a request that\n * vanished because the other side was away was the first thing a phone user noticed. */\nexport type OutboxKind = 'chat' | 'connect-request' | 'connect-accept' | 'connect-deny'\n\n/** A message waiting on its recipient. `sentAt` is when it was written, which is what the recipient should see. */\nexport interface OutboxEntry {\n\tref: string\n\tto: string\n\ttoName: string\n\ttext: string\n\tsentAt: number\n\tattempts: number\n\tlastError?: string\n\t/** Absent on entries written before consent traffic was held: chat. */\n\tkind?: OutboxKind\n\t/** connect-request only. */\n\tnote?: string\n}\n\n/** What a hub tells a member on reconnect about one sender. */\nexport interface RelayNotice {\n\tfrom: string\n\tfromName: string\n\tcount: number\n\tfirst: number\n\tlast: number\n}\n\nexport const DEFAULT_OUTBOX_TTL_MS = 7 * 24 * 60 * 60 * 1000\nexport const DEFAULT_NOTICE_TTL_MS = 7 * 24 * 60 * 60 * 1000\n/** Ceiling on senders remembered per member, so a hub cannot be filled up with notices. */\nexport const MAX_NOTICE_SENDERS = 200\n\nasync function readJson<T>(file: string, fallback: T): Promise<T> {\n\ttry {\n\t\treturn JSON.parse(await fs.readFile(file, 'utf-8')) as T\n\t} catch {\n\t\treturn fallback\n\t}\n}\n\nasync function writeJson(file: string, value: unknown): Promise<void> {\n\tawait fs.mkdir(path.dirname(file), { recursive: true })\n\tconst tmp = `${file}.${process.pid}.tmp`\n\tawait fs.writeFile(tmp, JSON.stringify(value, null, 2), 'utf-8')\n\tawait fs.rename(tmp, file)\n}\n\nexport class ChatOutbox {\n\tprivate entries: OutboxEntry[] = []\n\n\tconstructor(\n\t\tprivate readonly file: string,\n\t\tprivate readonly ttlMs = DEFAULT_OUTBOX_TTL_MS,\n\t) {}\n\n\tasync load(now = Date.now()): Promise<void> {\n\t\tconst raw = await readJson<unknown>(this.file, [])\n\t\tthis.entries = Array.isArray(raw)\n\t\t\t? (raw as OutboxEntry[]).filter(\n\t\t\t\t\t(e) =>\n\t\t\t\t\t\te &&\n\t\t\t\t\t\ttypeof e.ref === 'string' &&\n\t\t\t\t\t\ttypeof e.to === 'string' &&\n\t\t\t\t\t\ttypeof e.text === 'string' &&\n\t\t\t\t\t\ttypeof e.sentAt === 'number',\n\t\t\t\t)\n\t\t\t: []\n\t\tawait this.sweep(now)\n\t}\n\n\tlist(): OutboxEntry[] {\n\t\treturn [...this.entries]\n\t}\n\n\tforPeer(to: string): OutboxEntry[] {\n\t\treturn this.entries.filter((e) => e.to === to)\n\t}\n\n\tget size(): number {\n\t\treturn this.entries.length\n\t}\n\n\tasync add(entry: Omit<OutboxEntry, 'ref' | 'attempts'> & { ref?: string }): Promise<OutboxEntry> {\n\t\tconst full: OutboxEntry = { ...entry, ref: entry.ref ?? crypto.randomUUID(), attempts: 0 }\n\t\tthis.entries.push(full)\n\t\tawait this.persist()\n\t\treturn full\n\t}\n\n\tasync remove(ref: string): Promise<boolean> {\n\t\tconst before = this.entries.length\n\t\tthis.entries = this.entries.filter((e) => e.ref !== ref)\n\t\tif (this.entries.length === before) return false\n\t\tawait this.persist()\n\t\treturn true\n\t}\n\n\tasync noteAttempt(ref: string, error?: string): Promise<void> {\n\t\tconst e = this.entries.find((x) => x.ref === ref)\n\t\tif (!e) return\n\t\te.attempts++\n\t\te.lastError = error\n\t\tawait this.persist()\n\t}\n\n\t/** Drop entries older than the TTL. Returns what was dropped so the owner can say so. */\n\tasync sweep(now = Date.now()): Promise<OutboxEntry[]> {\n\t\tconst expired = this.entries.filter((e) => now - e.sentAt > this.ttlMs)\n\t\tif (expired.length === 0) return []\n\t\tthis.entries = this.entries.filter((e) => now - e.sentAt <= this.ttlMs)\n\t\tawait this.persist()\n\t\treturn expired\n\t}\n\n\tprivate async persist(): Promise<void> {\n\t\tawait writeJson(this.file, this.entries)\n\t}\n}\n\nexport class RelayNotices {\n\t/** to → (from → notice) */\n\tprivate byMember = new Map<string, Map<string, RelayNotice>>()\n\n\tconstructor(\n\t\tprivate readonly file: string,\n\t\tprivate readonly ttlMs = DEFAULT_NOTICE_TTL_MS,\n\t) {}\n\n\tasync load(now = Date.now()): Promise<void> {\n\t\tconst raw = await readJson<Record<string, RelayNotice[]>>(this.file, {})\n\t\tthis.byMember = new Map()\n\t\tfor (const [to, list] of Object.entries(raw ?? {})) {\n\t\t\tif (!Array.isArray(list)) continue\n\t\t\tconst m = new Map<string, RelayNotice>()\n\t\t\tfor (const n of list) {\n\t\t\t\tif (n && typeof n.from === 'string' && typeof n.last === 'number') m.set(n.from, n)\n\t\t\t}\n\t\t\tif (m.size) this.byMember.set(to, m)\n\t\t}\n\t\tawait this.sweep(now)\n\t}\n\n\t/** Remember that `from` tried to reach `to` just now. */\n\tasync record(to: string, from: string, fromName: string, now = Date.now()): Promise<void> {\n\t\tlet m = this.byMember.get(to)\n\t\tif (!m) {\n\t\t\tm = new Map()\n\t\t\tthis.byMember.set(to, m)\n\t\t}\n\t\tconst existing = m.get(from)\n\t\tif (existing) {\n\t\t\texisting.count++\n\t\t\texisting.last = now\n\t\t\texisting.fromName = fromName || existing.fromName\n\t\t} else {\n\t\t\tif (m.size >= MAX_NOTICE_SENDERS) {\n\t\t\t\t// Evict the sender we heard from longest ago rather than refuse the newest.\n\t\t\t\tlet oldest: [string, RelayNotice] | undefined\n\t\t\t\tfor (const kv of m) if (!oldest || kv[1].last < oldest[1].last) oldest = kv\n\t\t\t\tif (oldest) m.delete(oldest[0])\n\t\t\t}\n\t\t\tm.set(from, { from, fromName, count: 1, first: now, last: now })\n\t\t}\n\t\tawait this.persist()\n\t}\n\n\t/** Hand over everything waiting for `to`, and forget it. */\n\tasync take(to: string, now = Date.now()): Promise<RelayNotice[]> {\n\t\tawait this.sweep(now)\n\t\tconst m = this.byMember.get(to)\n\t\tif (!m || m.size === 0) return []\n\t\tthis.byMember.delete(to)\n\t\tawait this.persist()\n\t\treturn [...m.values()].sort((a, b) => b.last - a.last)\n\t}\n\n\t/** Put notices back that could not be handed over after all (the member dropped off again). */\n\tasync restore(to: string, notices: RelayNotice[]): Promise<void> {\n\t\tif (notices.length === 0) return\n\t\tlet m = this.byMember.get(to)\n\t\tif (!m) {\n\t\t\tm = new Map()\n\t\t\tthis.byMember.set(to, m)\n\t\t}\n\t\tfor (const n of notices) {\n\t\t\tconst ex = m.get(n.from)\n\t\t\tif (ex) {\n\t\t\t\tex.count += n.count\n\t\t\t\tex.first = Math.min(ex.first, n.first)\n\t\t\t\tex.last = Math.max(ex.last, n.last)\n\t\t\t} else m.set(n.from, { ...n })\n\t\t}\n\t\tawait this.persist()\n\t}\n\n\tpeek(to: string): RelayNotice[] {\n\t\treturn [...(this.byMember.get(to)?.values() ?? [])]\n\t}\n\n\t/** Members with something waiting. */\n\tget size(): number {\n\t\treturn this.byMember.size\n\t}\n\n\tasync sweep(now = Date.now()): Promise<void> {\n\t\tlet changed = false\n\t\tfor (const [to, m] of this.byMember) {\n\t\t\tfor (const [from, n] of m) {\n\t\t\t\tif (now - n.last > this.ttlMs) {\n\t\t\t\t\tm.delete(from)\n\t\t\t\t\tchanged = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (m.size === 0) this.byMember.delete(to)\n\t\t}\n\t\tif (changed) await this.persist()\n\t}\n\n\tprivate async persist(): Promise<void> {\n\t\tconst out: Record<string, RelayNotice[]> = {}\n\t\tfor (const [to, m] of this.byMember) out[to] = [...m.values()]\n\t\tawait writeJson(this.file, out)\n\t}\n}\n","/**\n * VoleNet Ed25519 key management.\n * Each vole instance has a keypair for identity and message signing.\n *\n * Key format: \"vole-ed25519 <base64-ed25519-public-key> <instance-name> [base64-ml-dsa-public-key]\"\n * The 4th field (ML-DSA public key) is present when post-quantum support is available\n * — the identity is then a hybrid Ed25519 + ML-DSA keypair.\n * Storage: .openvole/net/vole_key (private), vole_key.pub (public), vole_key.pq (ML-DSA private)\n * .openvole/net/authorized_voles (trusted peer public keys)\n */\n\nimport * as crypto from 'node:crypto'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { createLogger } from './logger.js'\nimport { generateMlKemKeyPair } from './seal.js'\n\nconst logger = createLogger('volenet-keys')\n\nexport interface VoleKeyPair {\n\tpublicKey: crypto.KeyObject\n\tprivateKey: crypto.KeyObject\n\tpublicKeyString: string\n\tinstanceId: string\n\t/** Post-quantum (ML-DSA-65) keys — present when the runtime supports it (OpenSSL 3.5+ / Node 24+). */\n\tpqPublicKey?: crypto.KeyObject\n\tpqPrivateKey?: crypto.KeyObject\n\t/** X25519 key-agreement keys for sealed (end-to-end encrypted) envelopes. */\n\txPublicKey?: crypto.KeyObject\n\txPrivateKey?: crypto.KeyObject\n\t/** Base64 SPKI of xPublicKey — announced to peers via discovery. */\n\txPublicKeyB64?: string\n\t/** ML-KEM-768 keys — the post-quantum half of the hybrid seal (OpenSSL 3.5+ / Node 24+). */\n\tmlkemPublicKey?: crypto.KeyObject\n\tmlkemPrivateKey?: crypto.KeyObject\n\t/** Base64 SPKI of mlkemPublicKey — announced to peers via discovery alongside xPublicKeyB64. */\n\tmlkemPublicKeyB64?: string\n}\n\n/** Generate an ML-DSA-65 keypair if the runtime supports it, else null. */\nfunction generateMlDsa(): crypto.KeyPairKeyObjectResult | null {\n\ttry {\n\t\t// 'ml-dsa-65' needs OpenSSL 3.5+; cast since older @types/node lack the literal.\n\t\treturn crypto.generateKeyPairSync('ml-dsa-65' as 'ed25519')\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/** Parse an ML-DSA-65 SPKI public key from base64, or null if unsupported/invalid. */\nfunction parseMlDsaPublic(b64: string): crypto.KeyObject | undefined {\n\ttry {\n\t\treturn crypto.createPublicKey({ key: Buffer.from(b64, 'base64'), format: 'der', type: 'spki' })\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Generate a new Ed25519 keypair.\n * Saves to .openvole/net/vole_key and vole_key.pub\n */\nexport async function generateKeyPair(netDir: string, instanceName: string): Promise<VoleKeyPair> {\n\tawait fs.mkdir(netDir, { recursive: true })\n\n\tconst { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519')\n\n\t// Export keys\n\tconst privatePem = privateKey.export({ type: 'pkcs8', format: 'pem' }) as string\n\tconst publicRaw = publicKey.export({ type: 'spki', format: 'der' })\n\tconst publicB64 = Buffer.from(publicRaw).toString('base64')\n\t// Post-quantum (ML-DSA-65) keypair — best effort; appended to the key string as a 4th token.\n\tconst pq = generateMlDsa()\n\tlet pqB64 = ''\n\tif (pq) {\n\t\tpqB64 = Buffer.from(pq.publicKey.export({ type: 'spki', format: 'der' })).toString('base64')\n\t\tawait fs.writeFile(\n\t\t\tpath.join(netDir, 'vole_key.pq'),\n\t\t\tpq.privateKey.export({ type: 'pkcs8', format: 'pem' }) as string,\n\t\t\t{ mode: 0o600 },\n\t\t)\n\t}\n\tconst publicKeyString = pq\n\t\t? `vole-ed25519 ${publicB64} ${instanceName} ${pqB64}`\n\t\t: `vole-ed25519 ${publicB64} ${instanceName}`\n\n\t// Save private key (owner-only permissions)\n\tconst privateKeyPath = path.join(netDir, 'vole_key')\n\tawait fs.writeFile(privateKeyPath, privatePem, { mode: 0o600 })\n\n\t// Save public key\n\tconst publicKeyPath = path.join(netDir, 'vole_key.pub')\n\tawait fs.writeFile(publicKeyPath, publicKeyString + '\\n', 'utf-8')\n\n\t// Create authorized_voles if it doesn't exist\n\tconst authorizedPath = path.join(netDir, 'authorized_voles')\n\ttry {\n\t\tawait fs.access(authorizedPath)\n\t} catch {\n\t\tawait fs.writeFile(authorizedPath, '', 'utf-8')\n\t}\n\n\t// X25519 key-agreement pair for sealed envelopes (relay E2E). Separate from the signing\n\t// identity on purpose: signing keys sign, agreement keys agree.\n\tconst x = crypto.generateKeyPairSync('x25519')\n\tawait fs.writeFile(\n\t\tpath.join(netDir, 'vole_key.x25519'),\n\t\tx.privateKey.export({ type: 'pkcs8', format: 'pem' }) as string,\n\t\t{ mode: 0o600 },\n\t)\n\tconst xPublicKeyB64 = Buffer.from(x.publicKey.export({ type: 'spki', format: 'der' })).toString(\n\t\t'base64',\n\t)\n\tawait fs.writeFile(path.join(netDir, 'vole_key.x25519.pub'), `${xPublicKeyB64}\\n`, 'utf-8')\n\n\t// ML-KEM-768 encapsulation pair — the post-quantum half of the hybrid seal (best effort).\n\tconst mlkem = generateMlKemKeyPair()\n\tlet mlkemPublicKeyB64: string | undefined\n\tif (mlkem) {\n\t\tawait fs.writeFile(\n\t\t\tpath.join(netDir, 'vole_key.mlkem'),\n\t\t\tmlkem.privateKey.export({ type: 'pkcs8', format: 'pem' }) as string,\n\t\t\t{ mode: 0o600 },\n\t\t)\n\t\tmlkemPublicKeyB64 = Buffer.from(\n\t\t\tmlkem.publicKey.export({ type: 'spki', format: 'der' }),\n\t\t).toString('base64')\n\t\tawait fs.writeFile(path.join(netDir, 'vole_key.mlkem.pub'), `${mlkemPublicKeyB64}\\n`, 'utf-8')\n\t}\n\n\tconst instanceId = deriveInstanceId(publicB64)\n\tlogger.info(\n\t\t`Generated keypair — instance ID: ${instanceId}${pq ? ' (hybrid Ed25519 + ML-DSA)' : ' (Ed25519)'}`,\n\t)\n\n\treturn {\n\t\tpublicKey,\n\t\tprivateKey,\n\t\tpublicKeyString,\n\t\tinstanceId,\n\t\tpqPublicKey: pq?.publicKey,\n\t\tpqPrivateKey: pq?.privateKey,\n\t\txPublicKey: x.publicKey,\n\t\txPrivateKey: x.privateKey,\n\t\txPublicKeyB64,\n\t\tmlkemPublicKey: mlkem?.publicKey,\n\t\tmlkemPrivateKey: mlkem?.privateKey,\n\t\tmlkemPublicKeyB64,\n\t}\n}\n\n/**\n * Load existing keypair from disk.\n */\nexport async function loadKeyPair(netDir: string): Promise<VoleKeyPair | null> {\n\tconst privateKeyPath = path.join(netDir, 'vole_key')\n\tconst publicKeyPath = path.join(netDir, 'vole_key.pub')\n\n\ttry {\n\t\tconst privatePem = await fs.readFile(privateKeyPath, 'utf-8')\n\t\tconst publicKeyString = (await fs.readFile(publicKeyPath, 'utf-8')).trim()\n\n\t\tconst privateKey = crypto.createPrivateKey(privatePem)\n\t\tconst publicKey = crypto.createPublicKey(privateKey)\n\n\t\tconst parts = publicKeyString.split(' ')\n\t\tif (parts[0] !== 'vole-ed25519' || parts.length < 2) {\n\t\t\tlogger.error('Invalid public key format')\n\t\t\treturn null\n\t\t}\n\n\t\tconst instanceId = deriveInstanceId(parts[1])\n\n\t\t// Load the post-quantum key if present; auto-upgrade legacy keypairs when supported.\n\t\tlet pqPrivateKey: crypto.KeyObject | undefined\n\t\tlet pqPublicKey: crypto.KeyObject | undefined\n\t\tlet finalPublicKeyString = publicKeyString\n\t\ttry {\n\t\t\tconst pqPem = await fs.readFile(path.join(netDir, 'vole_key.pq'), 'utf-8')\n\t\t\tpqPrivateKey = crypto.createPrivateKey(pqPem)\n\t\t\tpqPublicKey = crypto.createPublicKey(pqPrivateKey)\n\t\t} catch {\n\t\t\tconst pq = generateMlDsa()\n\t\t\tif (pq) {\n\t\t\t\tpqPrivateKey = pq.privateKey\n\t\t\t\tpqPublicKey = pq.publicKey\n\t\t\t\tawait fs.writeFile(\n\t\t\t\t\tpath.join(netDir, 'vole_key.pq'),\n\t\t\t\t\tpq.privateKey.export({ type: 'pkcs8', format: 'pem' }) as string,\n\t\t\t\t\t{ mode: 0o600 },\n\t\t\t\t)\n\t\t\t\tif (parts.length < 4) {\n\t\t\t\t\tconst pqB64 = Buffer.from(pq.publicKey.export({ type: 'spki', format: 'der' })).toString(\n\t\t\t\t\t\t'base64',\n\t\t\t\t\t)\n\t\t\t\t\tfinalPublicKeyString = `vole-ed25519 ${parts[1]} ${parts[2] ?? instanceId.substring(0, 8)} ${pqB64}`\n\t\t\t\t\tawait fs.writeFile(\n\t\t\t\t\t\tpath.join(netDir, 'vole_key.pub'),\n\t\t\t\t\t\tfinalPublicKeyString + '\\n',\n\t\t\t\t\t\t'utf-8',\n\t\t\t\t\t)\n\t\t\t\t\tlogger.info('Upgraded keypair with a post-quantum (ML-DSA) key')\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// X25519 agreement key — load, or auto-upgrade older keypairs by generating one.\n\t\tlet xPrivateKey: crypto.KeyObject | undefined\n\t\tlet xPublicKey: crypto.KeyObject | undefined\n\t\ttry {\n\t\t\tconst xPem = await fs.readFile(path.join(netDir, 'vole_key.x25519'), 'utf-8')\n\t\t\txPrivateKey = crypto.createPrivateKey(xPem)\n\t\t\txPublicKey = crypto.createPublicKey(xPrivateKey)\n\t\t} catch {\n\t\t\tconst x = crypto.generateKeyPairSync('x25519')\n\t\t\txPrivateKey = x.privateKey\n\t\t\txPublicKey = x.publicKey\n\t\t\tawait fs.writeFile(\n\t\t\t\tpath.join(netDir, 'vole_key.x25519'),\n\t\t\t\tx.privateKey.export({ type: 'pkcs8', format: 'pem' }) as string,\n\t\t\t\t{ mode: 0o600 },\n\t\t\t)\n\t\t\tlogger.info('Upgraded keypair with an X25519 (sealed envelope) key')\n\t\t}\n\t\tconst xPublicKeyB64 = Buffer.from(xPublicKey.export({ type: 'spki', format: 'der' })).toString(\n\t\t\t'base64',\n\t\t)\n\t\tawait fs.writeFile(path.join(netDir, 'vole_key.x25519.pub'), `${xPublicKeyB64}\\n`, 'utf-8')\n\n\t\t// ML-KEM-768 encapsulation key — load, or auto-upgrade older keypairs by generating one\n\t\t// (best effort; a runtime without ML-KEM leaves these undefined and the seal stays X25519).\n\t\tlet mlkemPrivateKey: crypto.KeyObject | undefined\n\t\tlet mlkemPublicKey: crypto.KeyObject | undefined\n\t\tlet mlkemPublicKeyB64: string | undefined\n\t\ttry {\n\t\t\tconst mlkemPem = await fs.readFile(path.join(netDir, 'vole_key.mlkem'), 'utf-8')\n\t\t\tmlkemPrivateKey = crypto.createPrivateKey(mlkemPem)\n\t\t\tmlkemPublicKey = crypto.createPublicKey(mlkemPrivateKey)\n\t\t} catch {\n\t\t\tconst mlkem = generateMlKemKeyPair()\n\t\t\tif (mlkem) {\n\t\t\t\tmlkemPrivateKey = mlkem.privateKey\n\t\t\t\tmlkemPublicKey = mlkem.publicKey\n\t\t\t\tawait fs.writeFile(\n\t\t\t\t\tpath.join(netDir, 'vole_key.mlkem'),\n\t\t\t\t\tmlkem.privateKey.export({ type: 'pkcs8', format: 'pem' }) as string,\n\t\t\t\t\t{ mode: 0o600 },\n\t\t\t\t)\n\t\t\t\tlogger.info('Upgraded keypair with an ML-KEM-768 (post-quantum seal) key')\n\t\t\t}\n\t\t}\n\t\tif (mlkemPublicKey) {\n\t\t\tmlkemPublicKeyB64 = Buffer.from(\n\t\t\t\tmlkemPublicKey.export({ type: 'spki', format: 'der' }),\n\t\t\t).toString('base64')\n\t\t\tawait fs.writeFile(path.join(netDir, 'vole_key.mlkem.pub'), `${mlkemPublicKeyB64}\\n`, 'utf-8')\n\t\t}\n\n\t\treturn {\n\t\t\tpublicKey,\n\t\t\tprivateKey,\n\t\t\tpublicKeyString: finalPublicKeyString,\n\t\t\tinstanceId,\n\t\t\tpqPublicKey,\n\t\t\tpqPrivateKey,\n\t\t\txPublicKey,\n\t\t\txPrivateKey,\n\t\t\txPublicKeyB64,\n\t\t\tmlkemPublicKey,\n\t\t\tmlkemPrivateKey,\n\t\t\tmlkemPublicKeyB64,\n\t\t}\n\t} catch (err) {\n\t\tif ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n\t\t\treturn null\n\t\t}\n\t\tlogger.error(`Failed to load keypair: ${err instanceof Error ? err.message : String(err)}`)\n\t\treturn null\n\t}\n}\n\n/**\n * Sign a message with the private key.\n * Returns base64-encoded signature.\n */\nexport function sign(privateKey: crypto.KeyObject, data: string): string {\n\tconst signature = crypto.sign(null, Buffer.from(data), privateKey)\n\treturn signature.toString('base64')\n}\n\n/**\n * Verify a signature against a public key.\n */\nexport function verify(publicKey: crypto.KeyObject, data: string, signature: string): boolean {\n\ttry {\n\t\treturn crypto.verify(null, Buffer.from(data), publicKey, Buffer.from(signature, 'base64'))\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Parse a public key string (\"vole-ed25519 <base64> <name>\") into a KeyObject.\n */\nexport function parsePublicKey(keyString: string): {\n\tpublicKey: crypto.KeyObject\n\tinstanceId: string\n\tname: string\n\tpqPublicKey?: crypto.KeyObject\n} | null {\n\tconst parts = keyString.trim().split(' ')\n\tif (parts[0] !== 'vole-ed25519' || parts.length < 2) return null\n\n\ttry {\n\t\tconst der = Buffer.from(parts[1], 'base64')\n\t\tconst publicKey = crypto.createPublicKey({ key: der, format: 'der', type: 'spki' })\n\t\tconst instanceId = deriveInstanceId(parts[1])\n\t\tconst name = parts[2] ?? instanceId.substring(0, 8)\n\t\t// Optional 4th token: ML-DSA-65 public key (hybrid). Ignored on runtimes without PQ.\n\t\tconst pqPublicKey = parts[3] ? parseMlDsaPublic(parts[3]) : undefined\n\t\treturn { publicKey, instanceId, name, pqPublicKey }\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Load trusted peer public keys from authorized_voles file.\n */\nexport async function loadAuthorizedVoles(netDir: string): Promise<\n\tMap<\n\t\tstring,\n\t\t{\n\t\t\tpublicKey: crypto.KeyObject\n\t\t\tname: string\n\t\t\tpqPublicKey?: crypto.KeyObject\n\t\t}\n\t>\n> {\n\tconst authorizedPath = path.join(netDir, 'authorized_voles')\n\tconst trusted = new Map<\n\t\tstring,\n\t\t{ publicKey: crypto.KeyObject; name: string; pqPublicKey?: crypto.KeyObject }\n\t>()\n\n\ttry {\n\t\tconst content = await fs.readFile(authorizedPath, 'utf-8')\n\t\tfor (const line of content.split('\\n')) {\n\t\t\tconst trimmed = line.trim()\n\t\t\tif (!trimmed || trimmed.startsWith('#')) continue\n\n\t\t\tconst parsed = parsePublicKey(trimmed)\n\t\t\tif (parsed) {\n\t\t\t\ttrusted.set(parsed.instanceId, {\n\t\t\t\t\tpublicKey: parsed.publicKey,\n\t\t\t\t\tname: parsed.name,\n\t\t\t\t\tpqPublicKey: parsed.pqPublicKey,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// No authorized_voles file — empty trust list\n\t}\n\n\treturn trusted\n}\n\n/**\n * Add a peer's public key to authorized_voles.\n */\nexport async function trustPeer(\n\tnetDir: string,\n\tpublicKeyString: string,\n\topts?: { allowUpgrade?: boolean },\n): Promise<string> {\n\tconst parsed = parsePublicKey(publicKeyString)\n\tif (!parsed) throw new Error('Invalid public key format. Expected: vole-ed25519 <base64> <name>')\n\n\tconst authorizedPath = path.join(netDir, 'authorized_voles')\n\n\t// Check if already trusted\n\tconst existing = await loadAuthorizedVoles(netDir)\n\tconst current = existing.get(parsed.instanceId)\n\tif (current) {\n\t\t// Same identity (Ed25519) already trusted. Auto-upgrade an Ed25519-only entry to hybrid\n\t\t// when it gains a post-quantum key. Disabled (add-only) for untrusted self-join paths,\n\t\t// so a guest can never overwrite another peer's PQ key.\n\t\tconst allowUpgrade = opts?.allowUpgrade !== false\n\t\tif (allowUpgrade && !current.pqPublicKey && parsed.pqPublicKey) {\n\t\t\tawait replaceAuthorizedLine(netDir, parsed.instanceId, publicKeyString.trim())\n\t\t\tlogger.info(\n\t\t\t\t`Upgraded peer to hybrid PQ: ${parsed.name} (${parsed.instanceId.substring(0, 8)})`,\n\t\t\t)\n\t\t}\n\t\treturn parsed.instanceId\n\t}\n\n\t// Append\n\tawait fs.appendFile(authorizedPath, publicKeyString.trim() + '\\n', 'utf-8')\n\tlogger.info(`Trusted peer: ${parsed.name} (${parsed.instanceId.substring(0, 8)})`)\n\n\treturn parsed.instanceId\n}\n\n/** Replace the authorized_voles line for an instance ID with a new key string (in place). */\nasync function replaceAuthorizedLine(\n\tnetDir: string,\n\tinstanceId: string,\n\tnewLine: string,\n): Promise<void> {\n\tconst authorizedPath = path.join(netDir, 'authorized_voles')\n\tconst content = await fs.readFile(authorizedPath, 'utf-8').catch(() => '')\n\tlet replaced = false\n\tconst out = content.split('\\n').map((line) => {\n\t\tconst trimmed = line.trim()\n\t\tif (!trimmed || trimmed.startsWith('#')) return line\n\t\tconst parsed = parsePublicKey(trimmed)\n\t\tif (parsed && parsed.instanceId === instanceId) {\n\t\t\treplaced = true\n\t\t\treturn newLine\n\t\t}\n\t\treturn line\n\t})\n\tif (!replaced) out.push(newLine)\n\tawait fs.writeFile(authorizedPath, out.join('\\n'), 'utf-8')\n}\n\n/**\n * Remove a peer's public key from authorized_voles.\n */\nexport async function revokePeer(netDir: string, instanceIdOrKey: string): Promise<boolean> {\n\tconst authorizedPath = path.join(netDir, 'authorized_voles')\n\n\ttry {\n\t\tconst content = await fs.readFile(authorizedPath, 'utf-8')\n\t\tconst lines = content.split('\\n')\n\t\tconst filtered = lines.filter((line) => {\n\t\t\tconst trimmed = line.trim()\n\t\t\tif (!trimmed || trimmed.startsWith('#')) return true\n\t\t\tconst parsed = parsePublicKey(trimmed)\n\t\t\tif (!parsed) return true\n\t\t\treturn parsed.instanceId !== instanceIdOrKey && !trimmed.includes(instanceIdOrKey)\n\t\t})\n\n\t\tif (filtered.length === lines.length) return false\n\n\t\tawait fs.writeFile(authorizedPath, filtered.join('\\n'), 'utf-8')\n\t\tlogger.info(`Revoked peer: ${instanceIdOrKey.substring(0, 8)}`)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Relay consent store: peers whose relayed (hub-forwarded) chat this vole accepts. Distinct from\n * authorized_voles — accepting relay contact from a peer does NOT grant it direct-connect trust\n * (memory sync, tool sharing). Same on-disk line format so keys stay pinned to identities.\n * Storage: .openvole/net/relay_accepts\n */\nexport async function loadRelayAccepts(\n\tnetDir: string,\n): Promise<\n\tMap<string, { publicKey: crypto.KeyObject; name: string; pqPublicKey?: crypto.KeyObject }>\n> {\n\tconst acceptsPath = path.join(netDir, 'relay_accepts')\n\tconst accepts = new Map<\n\t\tstring,\n\t\t{ publicKey: crypto.KeyObject; name: string; pqPublicKey?: crypto.KeyObject }\n\t>()\n\ttry {\n\t\tconst content = await fs.readFile(acceptsPath, 'utf-8')\n\t\tfor (const line of content.split('\\n')) {\n\t\t\tconst trimmed = line.trim()\n\t\t\tif (!trimmed || trimmed.startsWith('#')) continue\n\t\t\tconst parsed = parsePublicKey(trimmed)\n\t\t\tif (parsed) {\n\t\t\t\taccepts.set(parsed.instanceId, {\n\t\t\t\t\tpublicKey: parsed.publicKey,\n\t\t\t\t\tname: parsed.name,\n\t\t\t\t\tpqPublicKey: parsed.pqPublicKey,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// No relay_accepts file — nothing accepted yet\n\t}\n\treturn accepts\n}\n\n/** Record consent to receive relayed contact from a peer (append its pinned key line). */\nexport async function addRelayAccept(netDir: string, publicKeyString: string): Promise<string> {\n\tconst parsed = parsePublicKey(publicKeyString)\n\tif (!parsed) throw new Error('Invalid public key format. Expected: vole-ed25519 <base64> <name>')\n\tconst acceptsPath = path.join(netDir, 'relay_accepts')\n\tconst existing = await loadRelayAccepts(netDir)\n\tif (existing.has(parsed.instanceId)) return parsed.instanceId\n\tawait fs.appendFile(acceptsPath, publicKeyString.trim() + '\\n', 'utf-8')\n\tlogger.info(`Accepted relay contact: ${parsed.name} (${parsed.instanceId.substring(0, 8)})`)\n\treturn parsed.instanceId\n}\n\n/** Withdraw relay consent for a peer (remove its line from relay_accepts). */\nexport async function removeRelayAccept(netDir: string, instanceIdOrKey: string): Promise<boolean> {\n\tconst acceptsPath = path.join(netDir, 'relay_accepts')\n\ttry {\n\t\tconst content = await fs.readFile(acceptsPath, 'utf-8')\n\t\tconst lines = content.split('\\n')\n\t\tconst filtered = lines.filter((line) => {\n\t\t\tconst trimmed = line.trim()\n\t\t\tif (!trimmed || trimmed.startsWith('#')) return true\n\t\t\tconst parsed = parsePublicKey(trimmed)\n\t\t\tif (!parsed) return true\n\t\t\treturn parsed.instanceId !== instanceIdOrKey && !trimmed.includes(instanceIdOrKey)\n\t\t})\n\t\tif (filtered.length === lines.length) return false\n\t\tawait fs.writeFile(acceptsPath, filtered.join('\\n'), 'utf-8')\n\t\tlogger.info(`Withdrew relay consent: ${instanceIdOrKey.substring(0, 8)}`)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Generate a random nonce for challenge-response auth.\n */\nexport function generateNonce(): string {\n\treturn crypto.randomBytes(32).toString('base64')\n}\n\n/**\n * Derive a deterministic instance ID from a public key.\n * Uses SHA-256 hash of the public key, truncated to 16 hex chars.\n */\nfunction deriveInstanceId(publicKeyB64: string): string {\n\treturn crypto.createHash('sha256').update(publicKeyB64).digest('hex').substring(0, 16)\n}\n","/**\n * Logging, without a dependency on whoever is hosting this.\n *\n * The protocol used to borrow the agent framework's logger, which is one of the three threads\n * that tied it to the framework. It keeps the same behaviour on its own — silent on the console,\n * written to VOLE_LOG_FILE when that is set — so an agent's logs look exactly as before, and a\n * host that already has a logger can inject it and get a single stream rather than two.\n */\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\nexport interface Logger {\n\terror: (msg: string, ...args: unknown[]) => void\n\twarn: (msg: string, ...args: unknown[]) => void\n\tinfo: (msg: string, ...args: unknown[]) => void\n\tdebug: (msg: string, ...args: unknown[]) => void\n\ttrace: (msg: string, ...args: unknown[]) => void\n}\n\nexport type LoggerFactory = (tag: string) => Logger\n\nconst LEVELS = { error: 0, warn: 1, info: 2, debug: 3, trace: 4 } as const\ntype Level = keyof typeof LEVELS\n\nlet stream: fs.WriteStream | undefined\nlet factory: LoggerFactory | undefined\n/** Loggers already handed out, so injecting a factory late still redirects them. */\nconst issued = new Map<string, { tag: string; current: Logger }>()\n\nfunction currentLevel(): number {\n\tconst level = (process.env.VOLE_LOG_LEVEL ?? 'info').toLowerCase() as Level\n\treturn LEVELS[level] ?? LEVELS.info\n}\n\nfunction fileStream(): fs.WriteStream | undefined {\n\tif (stream) return stream\n\tconst file = process.env.VOLE_LOG_FILE\n\tif (!file) return undefined\n\tfs.mkdirSync(path.dirname(file), { recursive: true })\n\tstream = fs.createWriteStream(file, { flags: 'a' })\n\treturn stream\n}\n\nfunction write(level: Level, prefix: string, msg: string, args: unknown[]): void {\n\tconst out = fileStream()\n\tif (!out || LEVELS[level] > currentLevel()) return\n\tconst extra = args.length\n\t\t? ` ${args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ')}`\n\t\t: ''\n\tout.write(`${new Date().toISOString()} [${level.toUpperCase()}] ${prefix} ${msg}${extra}\\n`)\n}\n\nfunction fileLogger(tag: string): Logger {\n\tconst prefix = `[${tag}]`\n\tconst at =\n\t\t(level: Level) =>\n\t\t(msg: string, ...args: unknown[]) =>\n\t\t\twrite(level, prefix, msg, args)\n\treturn {\n\t\terror: at('error'),\n\t\twarn: at('warn'),\n\t\tinfo: at('info'),\n\t\tdebug: at('debug'),\n\t\ttrace: at('trace'),\n\t}\n}\n\n/**\n * Hand this library the host's logger. Loggers created before this call are redirected too, so a\n * host can inject at startup without caring which modules have already been imported.\n */\nexport function setLoggerFactory(make: LoggerFactory): void {\n\tfactory = make\n\tfor (const entry of issued.values()) entry.current = make(entry.tag)\n}\n\nexport function createLogger(tag: string): Logger {\n\tconst entry = { tag, current: factory ? factory(tag) : fileLogger(tag) }\n\tissued.set(tag, entry)\n\t// Indirect on every call so a factory injected later takes effect for this logger as well.\n\tconst at =\n\t\t(level: keyof Logger) =>\n\t\t(msg: string, ...args: unknown[]) =>\n\t\t\tentry.current[level](msg, ...args)\n\treturn {\n\t\terror: at('error'),\n\t\twarn: at('warn'),\n\t\tinfo: at('info'),\n\t\tdebug: at('debug'),\n\t\ttrace: at('trace'),\n\t}\n}\n\n/** Close the log file this library opened. A host that injected its own logger owns its stream. */\nexport function closeLogger(): void {\n\tstream?.end()\n\tstream = undefined\n}\n","/**\n * Sealed envelopes — end-to-end encryption for VoleNet messages (relay and, opt-in, direct).\n *\n * Hybrid KEM: every seal mixes an X25519 ECDH shared secret with an ML-KEM-768 (post-quantum)\n * shared secret — HKDF-SHA256 over the concatenation — so confidentiality holds unless BOTH the\n * classical and the post-quantum KEM are broken. This closes the harvest-now-decrypt-later gap:\n * an adversary recording ciphertext today cannot read it with a future quantum computer. The\n * X25519 half uses a fresh ephemeral key per envelope (random nonces, no cross-envelope leakage);\n * the ML-KEM half encapsulates to the recipient's static PQ key. The AAD binds the envelope to\n * its routing (`from|to`), so a relay cannot re-address a ciphertext without breaking the tag.\n *\n * Backward-compatible: when the recipient announces no ML-KEM key (an older peer) or the runtime\n * lacks ML-KEM, the seal falls back to X25519-only (the `v1` scheme, byte-identical to before).\n * The scheme is bound into the KDF, so stripping the KEM ciphertext from a hybrid box yields the\n * wrong key and fails the tag — a downgrade cannot silently weaken an envelope, only drop it.\n *\n * The plaintext is a full, signed VoleNet message: sealing wraps the existing protocol, it does\n * not replace any of its checks — the recipient still verifies the inner signature, freshness,\n * and sender identity after unsealing.\n */\n\nimport * as crypto from 'node:crypto'\nimport { aead, aeadOpen, mlkem } from './node-crypto.js'\n\nexport interface SealedBox {\n\t/** Ephemeral X25519 public key, base64 SPKI DER. */\n\tepk: string\n\t/** ML-KEM-768 ciphertext, base64 — present only for hybrid (post-quantum) envelopes. */\n\tkem?: string\n\t/** ChaCha20-Poly1305 nonce, base64 (12 bytes). */\n\tn: string\n\t/** Ciphertext ‖ auth tag, base64. */\n\tc: string\n}\n\nconst SCHEME_X25519 = 'volenet-seal-v1' // X25519-only (legacy / fallback)\nconst SCHEME_HYBRID = 'volenet-seal-v2' // X25519 + ML-KEM-768\nconst NONCE_BYTES = 12\nconst TAG_BYTES = 16\n\nexport function generateXKeyPair(): { publicKey: crypto.KeyObject; privateKey: crypto.KeyObject } {\n\treturn crypto.generateKeyPairSync('x25519')\n}\n\n/** Generate an ML-KEM-768 keypair if the runtime supports it (OpenSSL 3.5+ / Node 24+), else null. */\nexport function generateMlKemKeyPair(): crypto.KeyPairKeyObjectResult | null {\n\ttry {\n\t\t// 'ml-kem-768' needs OpenSSL 3.5+; cast since older @types/node lack the literal.\n\t\treturn crypto.generateKeyPairSync('ml-kem-768' as 'x25519')\n\t} catch {\n\t\treturn null\n\t}\n}\n\nexport function exportXPublic(key: crypto.KeyObject): string {\n\treturn Buffer.from(key.export({ type: 'spki', format: 'der' })).toString('base64')\n}\n\nexport function parseXPublic(b64: string): crypto.KeyObject | undefined {\n\ttry {\n\t\treturn crypto.createPublicKey({ key: Buffer.from(b64, 'base64'), format: 'der', type: 'spki' })\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nexport function parseMlKemPublic(b64: string): crypto.KeyObject | undefined {\n\ttry {\n\t\treturn crypto.createPublicKey({ key: Buffer.from(b64, 'base64'), format: 'der', type: 'spki' })\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nfunction deriveKey(ikm: Buffer, scheme: string, aad: string): Buffer {\n\treturn Buffer.from(crypto.hkdfSync('sha256', ikm, Buffer.alloc(0), `${scheme}|${aad}`, 32))\n}\n\n/**\n * Seal plaintext to a recipient. When `recipientMlkemPubB64` is supplied (and ML-KEM is available),\n * produces a post-quantum hybrid envelope; otherwise an X25519-only one. Returns null on a bad key.\n */\nexport function seal(\n\trecipientXPubB64: string,\n\tplaintext: Buffer,\n\taad: string,\n\trecipientMlkemPubB64?: string,\n): SealedBox | null {\n\tconst recipient = parseXPublic(recipientXPubB64)\n\tif (!recipient) return null\n\tconst eph = crypto.generateKeyPairSync('x25519')\n\tconst ssX = crypto.diffieHellman({ privateKey: eph.privateKey, publicKey: recipient })\n\n\tlet ikm = ssX\n\tlet scheme = SCHEME_X25519\n\tlet kem: string | undefined\n\tif (recipientMlkemPubB64) {\n\t\tconst mlkemPub = parseMlKemPublic(recipientMlkemPubB64)\n\t\tif (mlkemPub) {\n\t\t\ttry {\n\t\t\t\tconst { sharedKey, ciphertext } = mlkem(crypto).encapsulate(mlkemPub)\n\t\t\t\tikm = Buffer.concat([ssX, Buffer.from(sharedKey)])\n\t\t\t\tscheme = SCHEME_HYBRID\n\t\t\t\tkem = Buffer.from(ciphertext).toString('base64')\n\t\t\t} catch {\n\t\t\t\t// ML-KEM unavailable at runtime — fall back to X25519-only.\n\t\t\t}\n\t\t}\n\t}\n\n\tconst key = deriveKey(ikm, scheme, aad)\n\tconst nonce = crypto.randomBytes(NONCE_BYTES)\n\tconst cipher = aead(\n\t\tcrypto.createCipheriv('chacha20-poly1305', key, nonce, { authTagLength: TAG_BYTES }),\n\t)\n\tcipher.setAAD(Buffer.from(aad, 'utf8'))\n\tconst ct = Buffer.concat([cipher.update(plaintext), cipher.final(), cipher.getAuthTag()])\n\treturn {\n\t\tepk: exportXPublic(eph.publicKey),\n\t\t...(kem ? { kem } : {}),\n\t\tn: nonce.toString('base64'),\n\t\tc: ct.toString('base64'),\n\t}\n}\n\n/**\n * Unseal with our X25519 private key (and ML-KEM private key for hybrid envelopes). The scheme is\n * chosen by whether the box carries a KEM ciphertext, so a stripped-KEM downgrade fails the tag\n * rather than decrypting under a weaker key. Returns null on any tampering or mismatch.\n */\nexport function unseal(\n\txPrivateKey: crypto.KeyObject,\n\tbox: SealedBox,\n\taad: string,\n\tmlkemPrivateKey?: crypto.KeyObject,\n): Buffer | null {\n\ttry {\n\t\tconst epk = parseXPublic(box.epk)\n\t\tif (!epk) return null\n\t\tconst ssX = crypto.diffieHellman({ privateKey: xPrivateKey, publicKey: epk })\n\n\t\tlet ikm = ssX\n\t\tlet scheme = SCHEME_X25519\n\t\tif (box.kem) {\n\t\t\tif (!mlkemPrivateKey) return null // hybrid envelope but we hold no ML-KEM key\n\t\t\tconst ssPq = mlkem(crypto).decapsulate(mlkemPrivateKey, Buffer.from(box.kem, 'base64'))\n\t\t\tikm = Buffer.concat([ssX, Buffer.from(ssPq)])\n\t\t\tscheme = SCHEME_HYBRID\n\t\t}\n\n\t\tconst key = deriveKey(ikm, scheme, aad)\n\t\tconst nonce = Buffer.from(box.n, 'base64')\n\t\tconst ct = Buffer.from(box.c, 'base64')\n\t\tif (nonce.length !== NONCE_BYTES || ct.length <= TAG_BYTES) return null\n\t\tconst decipher = aeadOpen(\n\t\t\tcrypto.createDecipheriv('chacha20-poly1305', key, nonce, { authTagLength: TAG_BYTES }),\n\t\t)\n\t\tdecipher.setAAD(Buffer.from(aad, 'utf8'))\n\t\tdecipher.setAuthTag(ct.subarray(ct.length - TAG_BYTES))\n\t\treturn Buffer.concat([decipher.update(ct.subarray(0, ct.length - TAG_BYTES)), decipher.final()])\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/** Sliding-window relay policy: at most `maxPerMinute` forwards per (from,to) pair. */\nexport function relayPairAllow(\n\twindows: Map<string, number[]>,\n\tfrom: string,\n\tto: string,\n\tmaxPerMinute: number,\n): boolean {\n\tconst key = `${from}>${to}`\n\tconst now = Date.now()\n\tif (windows.size > 4096) {\n\t\tfor (const [k, ts] of windows)\n\t\t\tif (ts.length === 0 || now - ts[ts.length - 1] > 60_000) windows.delete(k)\n\t}\n\tconst win = (windows.get(key) ?? []).filter((t) => now - t < 60_000)\n\tif (win.length >= maxPerMinute) {\n\t\twindows.set(key, win)\n\t\treturn false\n\t}\n\twin.push(now)\n\twindows.set(key, win)\n\treturn true\n}\n","/**\n * The parts of Node's crypto that `@types/node` does not describe yet.\n *\n * Node 24 on OpenSSL 3.5 ships ML-KEM (`encapsulate`/`decapsulate`), and accepts an\n * `authTagLength` option for ChaCha20-Poly1305 while the types model that option as CCM-only,\n * which makes them insist `setAAD` takes a second argument. Both are real gaps between a shipped\n * runtime and its published types, not unsound code — so they are narrowed here, once, with the\n * reason attached, rather than left as a repo-wide `tsc` failure or papered over with `any` at\n * each call site.\n *\n * Delete this file when @types/node catches up; the call sites will keep compiling.\n */\nimport type * as crypto from 'node:crypto'\n\n/** ML-KEM-768 key encapsulation, as Node implements it. */\nexport interface MlKemApi {\n\tencapsulate(key: crypto.KeyObject): { sharedKey: Uint8Array; ciphertext: Uint8Array }\n\tdecapsulate(key: crypto.KeyObject, ciphertext: Buffer): Uint8Array\n}\n\n/** An AEAD cipher whose `setAAD` takes only the AAD, which is the case for ChaCha20-Poly1305. */\nexport interface AeadCipher {\n\tsetAAD(aad: Buffer): unknown\n\tupdate(data: Buffer): Buffer\n\tfinal(): Buffer\n\tgetAuthTag(): Buffer\n}\n\nexport interface AeadDecipher {\n\tsetAAD(aad: Buffer): unknown\n\tsetAuthTag(tag: Buffer): unknown\n\tupdate(data: Buffer): Buffer\n\tfinal(): Buffer\n}\n\n/** The ML-KEM half of the crypto module. Call sites still guard for its absence at runtime. */\nexport const mlkem = (mod: typeof crypto): MlKemApi => mod as unknown as MlKemApi\n\nexport const aead = (cipher: unknown): AeadCipher => cipher as AeadCipher\nexport const aeadOpen = (decipher: unknown): AeadDecipher => decipher as AeadDecipher\n","/**\n * VoleNet Protocol — message types, serialization, validation.\n * All messages are signed with Ed25519 for integrity and authenticity.\n */\n\nimport * as crypto from 'node:crypto'\nimport type { KeyObject } from 'node:crypto'\nimport { sign, verify } from './keys.js'\n\nexport const VOLENET_VERSION = 2 // bumped: signatures now cover the full nested payload + id (the old shallow canonicalization left nested fields like tool-call params unsigned)\nexport const MAX_MESSAGE_AGE_MS = 60_000 // reject messages older than 60s\n\n/**\n * The local node's post-quantum (ML-DSA) signing key, set once at VoleNet start.\n * Module-level because there is exactly one signing identity per process — this lets\n */\n\nexport type VoleNetMessageType =\n\t| 'ping'\n\t| 'pong'\n\t| 'discover'\n\t| 'discover:response'\n\t| 'auth:challenge'\n\t| 'auth:response'\n\t| 'auth:result'\n\t| 'task:delegate'\n\t| 'task:result'\n\t| 'task:status'\n\t| 'memory:sync'\n\t| 'memory:search'\n\t| 'memory:results'\n\t| 'session:sync'\n\t| 'tool:list'\n\t| 'tool:list:response'\n\t| 'tool:call'\n\t| 'tool:result'\n\t| 'leader:heartbeat'\n\t| 'leader:claim'\n\t| 'leader:ack'\n\t| 'chat:message'\n\t| 'sealed'\n\t// Direct end-to-end encryption: any post-handshake message to a capable peer is wrapped in a\n\t// sealed:direct envelope and unwrapped on receipt, transparent to every other handler.\n\t| 'sealed:direct'\n\t| 'relay:deliver'\n\t| 'relay:error'\n\t// Hub → sender: the envelope it attached this ref to was forwarded. Lets an outbox let go.\n\t| 'relay:ack'\n\t// Hub → member, on reconnect: who tried to reach it while it was away. Names and counts\n\t// only — the messages themselves wait on the senders' own machines (see chat-outbox.ts).\n\t| 'relay:pending'\n\t| 'roster'\n\t// Relay consent handshake (sealed inner types): a member must be accepted before its\n\t// relayed chat is delivered. A requests, the recipient approves or denies.\n\t| 'relay:connect-request'\n\t| 'relay:connect-accept'\n\t| 'relay:connect-deny'\n\t// VoleDrop file transfer — control plane. Peer↔peer; the bulk bytes never ride\n\t// messages (they stream over /volenet/blob/*). Auto-sealed when net.encrypt is on.\n\t| 'file:offer'\n\t| 'file:accept'\n\t| 'file:reject'\n\t| 'file:relay-ready'\n\t| 'file:done'\n\t| 'file:error'\n\t| 'file:cancel'\n\t// Relay-blob control — member↔hub direct messages (never relayed inner payloads):\n\t// the hub allocates/serves ciphertext blob storage for NAT'd member pairs.\n\t| 'relay:blob:create'\n\t| 'relay:blob:grant'\n\t| 'relay:blob:deny'\n\t| 'relay:blob:fetch'\n\t| 'relay:blob:done'\n\nexport interface VoleNetMessage {\n\tversion: number\n\tid: string\n\ttype: VoleNetMessageType\n\tfrom: string // sender instance ID\n\tto: string | '*' // target instance or broadcast\n\ttimestamp: number\n\tsignature: string // Ed25519 signature of payload\n\tsigPq?: string // optional ML-DSA-65 (post-quantum) signature of the same canonical data\n\tpayload: unknown\n}\n\nexport interface VoleNetInstance {\n\tid: string\n\tname: string\n\tpublicKey: string // base64 public key\n\tendpoint: string // https://host:port\n\tcapabilities: string[] // [\"brain:ollama\", \"paw-browser\", \"paw-database\"]\n\trole: 'coordinator' | 'worker' | 'peer'\n\tload: number // 0-1 current task load\n\tmaxTasks: number\n\tlastSeen: number\n\tversion: string\n\t/** X25519 public key (base64 SPKI) for sealed envelopes, when the peer announces one. */\n\txPublicKey?: string\n\t/** ML-KEM-768 public key (base64 SPKI) — the post-quantum half of the hybrid seal. */\n\tmlkemPublicKey?: string\n}\n\nexport interface RemoteToolInfo {\n\tname: string\n\tdescription: string\n\tpawName: string\n\tinstanceId: string\n\tinstanceName: string\n}\n\n/**\n * Create a signed VoleNet message.\n */\nexport function createMessage(\n\ttype: VoleNetMessageType,\n\tfrom: string,\n\tto: string | '*',\n\tpayload: unknown,\n\tprivateKey: KeyObject,\n\tpqPrivateKey?: KeyObject,\n): VoleNetMessage {\n\tconst id = crypto.randomUUID()\n\tconst timestamp = Date.now()\n\n\t// Sign the canonical payload representation (Ed25519, plus ML-DSA when available — hybrid).\n\tconst dataToSign = canonicalize(id, type, from, to, timestamp, payload)\n\tconst signature = sign(privateKey, dataToSign)\n\tconst sigPq = pqPrivateKey ? sign(pqPrivateKey, dataToSign) : undefined\n\n\treturn {\n\t\tversion: VOLENET_VERSION,\n\t\tid,\n\t\ttype,\n\t\tfrom,\n\t\tto,\n\t\ttimestamp,\n\t\tsignature,\n\t\t...(sigPq ? { sigPq } : {}),\n\t\tpayload,\n\t}\n}\n\n/**\n * Verify a received message's signature and freshness.\n */\nexport function verifyMessage(\n\tmessage: VoleNetMessage,\n\tpublicKey: KeyObject,\n\tpqPublicKey?: KeyObject,\n): { valid: boolean; error?: string } {\n\t// Version check\n\tif (message.version !== VOLENET_VERSION) {\n\t\treturn { valid: false, error: `Unsupported protocol version: ${message.version}` }\n\t}\n\n\t// Freshness check (replay protection). Reject a missing/non-numeric timestamp — otherwise the\n\t// age math is NaN, both comparisons below are false, and the message would never expire.\n\tif (typeof message.timestamp !== 'number' || !Number.isFinite(message.timestamp)) {\n\t\treturn { valid: false, error: 'Invalid or missing timestamp' }\n\t}\n\tconst age = Date.now() - message.timestamp\n\tif (age > MAX_MESSAGE_AGE_MS) {\n\t\treturn { valid: false, error: `Message too old: ${age}ms (max: ${MAX_MESSAGE_AGE_MS}ms)` }\n\t}\n\tif (age < -5000) {\n\t\treturn { valid: false, error: `Message from the future: ${-age}ms ahead` }\n\t}\n\n\t// Signature verification\n\tconst dataToSign = canonicalize(\n\t\tmessage.id,\n\t\tmessage.type,\n\t\tmessage.from,\n\t\tmessage.to,\n\t\tmessage.timestamp,\n\t\tmessage.payload,\n\t)\n\tconst valid = verify(publicKey, dataToSign, message.signature)\n\tif (!valid) {\n\t\treturn { valid: false, error: 'Invalid signature' }\n\t}\n\n\t// Hybrid post-quantum: if we know the peer's ML-DSA key, the PQ signature is required\n\t// and must verify (downgrade-resistant). Legacy peers (no PQ key) stay Ed25519-only.\n\tif (pqPublicKey) {\n\t\tif (!message.sigPq) {\n\t\t\treturn { valid: false, error: 'Missing post-quantum signature' }\n\t\t}\n\t\tif (!verify(pqPublicKey, dataToSign, message.sigPq)) {\n\t\t\treturn { valid: false, error: 'Invalid post-quantum signature' }\n\t\t}\n\t}\n\n\treturn { valid: true }\n}\n\n/**\n * Serialize a message for transport.\n */\nexport function serialize(message: VoleNetMessage): string {\n\treturn JSON.stringify(message)\n}\n\n/**\n * Deserialize a message from transport.\n */\nexport function deserialize(data: string): VoleNetMessage | null {\n\ttry {\n\t\tconst parsed = JSON.parse(data)\n\t\tif (!parsed.version || !parsed.type || !parsed.from || !parsed.id) {\n\t\t\treturn null\n\t\t}\n\t\treturn parsed as VoleNetMessage\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Create canonical string for signing.\n * Deterministic representation: type + from + to + timestamp + sorted JSON payload.\n */\n/** Deterministic JSON: keys sorted recursively at every level, so nested payload data IS signed. */\nfunction canonicalJson(value: unknown): string {\n\tif (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null'\n\tif (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`\n\tconst obj = value as Record<string, unknown>\n\t// Skip undefined-valued properties: JSON.stringify omits them on the wire, so signing\n\t// them (as anything) guarantees a receiver-side canonical mismatch — the signature would\n\t// never verify after transport. Arrays already match (undefined items serialize as null).\n\treturn `{${Object.keys(obj)\n\t\t.sort()\n\t\t.filter((k) => obj[k] !== undefined)\n\t\t.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`)\n\t\t.join(',')}}`\n}\n\nfunction canonicalize(\n\tid: string,\n\ttype: string,\n\tfrom: string,\n\tto: string | '*',\n\ttimestamp: number,\n\tpayload: unknown,\n): string {\n\t// Sign the id + envelope + the FULL nested payload (recursively key-sorted). Binding the id\n\t// means a captured message can't be re-id'd to dodge the replay cache.\n\tconst payloadStr = payload !== undefined && payload !== null ? canonicalJson(payload) : ''\n\treturn `${VOLENET_VERSION}:${id}:${type}:${from}:${to}:${timestamp}:${payloadStr}`\n}\n","/**\n * VoleNet Discovery — peer registry, capability announcement, health monitoring.\n */\n\nimport type { KeyObject } from 'node:crypto'\nimport { loadAuthorizedVoles, parsePublicKey, trustPeer } from './keys.js'\nimport { createLogger } from './logger.js'\nimport {\n\ttype RemoteToolInfo,\n\ttype VoleNetInstance,\n\ttype VoleNetMessage,\n\tcreateMessage,\n\tverifyMessage,\n} from './protocol.js'\nimport type { VoleNetTransport } from './transport.js'\n\nconst logger = createLogger('volenet-discovery')\n\nconst HEALTH_INTERVAL_MS = 15_000 // ping peers every 15s\nconst PEER_TIMEOUT_MS = 45_000 // mark peer as disconnected after 45s\n\nexport interface DiscoveryConfig {\n\tnetDir: string\n\tinstanceId: string\n\tinstanceName: string\n\trole: 'coordinator' | 'worker' | 'peer'\n\tendpoint: string\n\tcapabilities: string[]\n\tprivateKey: KeyObject\n\t/** ML-DSA-65 signing key — hybrid signatures when present. */\n\tpqPrivateKey?: KeyObject\n\tpublicKeyString: string\n\t/** Peer URLs from vole.config.json — used only to warn when one goes stale (endpoint drift). */\n\tconfiguredPeerUrls?: string[]\n\t/** Our X25519 public key (base64 SPKI), announced so peers can seal envelopes to us. */\n\txPublicKeyB64?: string\n\t/** Our ML-KEM-768 public key (base64 SPKI), announced for post-quantum hybrid seals. */\n\tmlkemPublicKeyB64?: string\n}\n\n/**\n * The configured URL that has gone stale for a peer, or null. A peer's advertised endpoint is\n * what we actually reconnect to at runtime; if the config lists a DIFFERENT url on the SAME\n * hostname (e.g. the old \":9710\" after a hub moved behind a proxied \"/mesh\" path), the config\n * entry silently stops working on the next restart — worth a loud hint now. Different-host\n * advertisements (other mesh members, NAT'd guests) are none of the config's business.\n */\nexport function findEndpointDrift(\n\tconfiguredUrls: string[] | undefined,\n\tadvertised: string,\n): string | null {\n\tif (!configuredUrls?.length || !advertised) return null\n\tconst norm = (u: string) => u.trim().replace(/\\/+$/, '')\n\tconst hostOf = (u: string): string | null => {\n\t\ttry {\n\t\t\treturn new URL(norm(u)).hostname || null\n\t\t} catch {\n\t\t\treturn null\n\t\t}\n\t}\n\tconst adv = norm(advertised)\n\tconst advHost = hostOf(adv)\n\tif (!advHost) return null\n\tconst urls = configuredUrls.map(norm)\n\tif (urls.includes(adv)) return null // config already matches what the peer advertises\n\treturn urls.find((u) => hostOf(u) === advHost) ?? null\n}\n\nexport class VoleNetDiscovery {\n\tprivate instances = new Map<string, VoleNetInstance>()\n\tprivate remoteTools = new Map<string, RemoteToolInfo[]>()\n\tprivate transport: VoleNetTransport\n\tprivate config: DiscoveryConfig\n\tprivate healthTimer: ReturnType<typeof setInterval> | undefined\n\tprivate authorizedPeers = new Map<\n\t\tstring,\n\t\t{ publicKey: KeyObject; name: string; pqPublicKey?: KeyObject }\n\t>()\n\tprivate onPeerChanged?: () => void\n\t/** Instances already warned about a stale configured URL — hint once, not every health cycle. */\n\tprivate driftHinted = new Set<string>()\n\n\tconstructor(transport: VoleNetTransport, config: DiscoveryConfig) {\n\t\tthis.transport = transport\n\t\tthis.config = config\n\t}\n\n\t/**\n\t * Start discovery — load authorized peers, announce self, begin health checks.\n\t */\n\t/** Set callback for when peers join/leave (triggers leader re-election) */\n\tsetOnPeerChanged(handler: () => void): void {\n\t\tthis.onPeerChanged = handler\n\t}\n\n\tasync start(): Promise<void> {\n\t\t// Load authorized peers\n\t\tthis.authorizedPeers = await loadAuthorizedVoles(this.config.netDir)\n\t\tlogger.info(`Loaded ${this.authorizedPeers.size} authorized peer(s)`)\n\n\t\t// Register message handlers\n\t\tthis.transport.onMessage((message, peerId) => {\n\t\t\tthis.handleMessage(message, peerId)\n\t\t})\n\n\t\t// On WS (re)connect, ping immediately so the remote binds this socket without waiting\n\t\t// for the next heartbeat — keeps reverse delivery (hub→NAT'd follower) consistent.\n\t\tthis.transport.setOnConnect((peerId) => this.sendPing(peerId))\n\n\t\t// Start health monitoring\n\t\tthis.healthTimer = setInterval(() => this.healthCheck(), HEALTH_INTERVAL_MS)\n\n\t\tlogger.info(\n\t\t\t`Discovery started — instance: ${this.config.instanceName} (${this.config.instanceId.substring(0, 8)})`,\n\t\t)\n\t}\n\n\t/**\n\t * Stop discovery — cleanup timers.\n\t */\n\tstop(): void {\n\t\tif (this.healthTimer) {\n\t\t\tclearInterval(this.healthTimer)\n\t\t\tthis.healthTimer = undefined\n\t\t}\n\t\tthis.instances.clear()\n\t\tthis.remoteTools.clear()\n\t}\n\n\t/**\n\t * Connect to a peer and perform authentication.\n\t */\n\tasync connectToPeer(endpoint: string): Promise<string | null> {\n\t\t// Ping first\n\t\tconst reachable = await this.transport.pingPeer(endpoint)\n\t\tif (!reachable) {\n\t\t\tlogger.warn(`Peer unreachable: ${endpoint}`)\n\t\t\treturn null\n\t\t}\n\n\t\t// Send discovery announcement\n\t\tconst message = createMessage(\n\t\t\t'discover',\n\t\t\tthis.config.instanceId,\n\t\t\t'*',\n\t\t\t{\n\t\t\t\tname: this.config.instanceName,\n\t\t\t\tpublicKey: this.config.publicKeyString,\n\t\t\t\tendpoint: this.config.endpoint,\n\t\t\t\tcapabilities: this.config.capabilities,\n\t\t\t\trole: this.config.role,\n\t\t\t\tversion: '3.0.0',\n\t\t\t\txPublicKey: this.config.xPublicKeyB64,\n\t\t\t\tmlkemPublicKey: this.config.mlkemPublicKeyB64,\n\t\t\t} satisfies Partial<VoleNetInstance>,\n\t\t\tthis.config.privateKey,\n\t\t\tthis.config.pqPrivateKey,\n\t\t)\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${endpoint}/volenet/message`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\tbody: JSON.stringify(message),\n\t\t\t\tsignal: AbortSignal.timeout(10000),\n\t\t\t})\n\n\t\t\tif (response.ok) {\n\t\t\t\tlogger.info(`Discovery sent to ${endpoint}`)\n\t\t\t\t// The peer may inline its discover:response in the body (NAT-friendly — it can't\n\t\t\t\t// dial us back). Process it so we register them even when unreachable from their side.\n\t\t\t\ttry {\n\t\t\t\t\tconst data = (await response.json()) as { response?: VoleNetMessage }\n\t\t\t\t\tif (data?.response?.type === 'discover:response') {\n\t\t\t\t\t\tthis.handleMessage(data.response, data.response.from)\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* no inline response body */\n\t\t\t\t}\n\t\t\t\treturn message.from\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tlogger.warn(\n\t\t\t\t`Discovery failed for ${endpoint}: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t}\n\n\t\treturn null\n\t}\n\n\t/**\n\t * Handle incoming VoleNet messages.\n\t */\n\tprivate handleMessage(message: VoleNetMessage, _peerId: string): void {\n\t\tswitch (message.type) {\n\t\t\tcase 'discover':\n\t\t\t\tthis.handleDiscover(message)\n\t\t\t\tbreak\n\t\t\tcase 'discover:response':\n\t\t\t\tthis.handleDiscoverResponse(message)\n\t\t\t\tbreak\n\t\t\tcase 'ping':\n\t\t\t\tthis.handlePing(message)\n\t\t\t\tbreak\n\t\t\tcase 'pong':\n\t\t\t\tthis.handlePong(message)\n\t\t\t\tbreak\n\t\t\tcase 'tool:list:response':\n\t\t\t\tthis.handleToolList(message)\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t/**\n\t * Verify an inbound message is from an authorized peer (keystore + Ed25519 signature).\n\t * Used by non-discovery handlers (e.g. peer chat) that receive raw transport messages.\n\t */\n\tverifyMessageFrom(message: VoleNetMessage): boolean {\n\t\tconst authPeer = this.authorizedPeers.get(message.from)\n\t\tif (!authPeer) return false\n\t\treturn verifyMessage(message, authPeer.publicKey, authPeer.pqPublicKey).valid\n\t}\n\n\t/**\n\t * Handle discovery announcement from a peer.\n\t */\n\t/**\n\t * Build a signed discover:response for an inbound discover, with NO side effects.\n\t * Wired as the transport's HTTP responder so a peer behind NAT receives our identity\n\t * inline in its own request's response — it can't be reached by a dial-back. Returns\n\t * null unless the discover is from an authorized peer with a valid signature.\n\t */\n\tbuildDiscoverResponse(message: VoleNetMessage): VoleNetMessage | null {\n\t\tif (message.type !== 'discover') return null\n\t\tconst info = message.payload as Partial<VoleNetInstance>\n\t\tif (!info.publicKey || !info.name) return null\n\t\tconst parsed = parsePublicKey(info.publicKey)\n\t\tif (!parsed) return null\n\t\tconst authPeer = this.authorizedPeers.get(parsed.instanceId)\n\t\tif (!authPeer) return null\n\t\tconst result = verifyMessage(message, authPeer.publicKey, authPeer.pqPublicKey)\n\t\tif (!result.valid) return null\n\t\treturn createMessage(\n\t\t\t'discover:response',\n\t\t\tthis.config.instanceId,\n\t\t\tparsed.instanceId,\n\t\t\t{\n\t\t\t\tname: this.config.instanceName,\n\t\t\t\tpublicKey: this.config.publicKeyString,\n\t\t\t\tendpoint: this.config.endpoint,\n\t\t\t\tcapabilities: this.config.capabilities,\n\t\t\t\trole: this.config.role,\n\t\t\t\tversion: '3.0.0',\n\t\t\t\txPublicKey: this.config.xPublicKeyB64,\n\t\t\t\tmlkemPublicKey: this.config.mlkemPublicKeyB64,\n\t\t\t},\n\t\t\tthis.config.privateKey,\n\t\t\tthis.config.pqPrivateKey,\n\t\t)\n\t}\n\n\tprivate handleDiscover(message: VoleNetMessage): void {\n\t\tconst info = message.payload as Partial<VoleNetInstance>\n\t\tif (!info.publicKey || !info.name) return\n\n\t\t// Verify the peer is authorized\n\t\tconst parsed = parsePublicKey(info.publicKey)\n\t\tif (!parsed) {\n\t\t\tlogger.warn(`Invalid public key from peer: ${message.from.substring(0, 8)}`)\n\t\t\treturn\n\t\t}\n\n\t\tif (!this.authorizedPeers.has(parsed.instanceId)) {\n\t\t\tlogger.warn(\n\t\t\t\t`Unauthorized peer attempted connection: ${info.name} (${parsed.instanceId.substring(0, 8)})`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\t// Verify message signature\n\t\tconst authPeer = this.authorizedPeers.get(parsed.instanceId)!\n\t\tconst result = verifyMessage(message, authPeer.publicKey, authPeer.pqPublicKey)\n\t\tif (!result.valid) {\n\t\t\tlogger.warn(`Message verification failed from ${info.name}: ${result.error}`)\n\t\t\treturn\n\t\t}\n\n\t\t// Auto-upgrade trust to hybrid PQ when a known peer announces an ML-DSA key. The discover\n\t\t// was Ed25519-verified above, so the announced PQ key is authentic (vouched by the peer's\n\t\t// existing key) — existing meshes migrate with no manual re-trust.\n\t\tif (parsed.pqPublicKey && !authPeer.pqPublicKey && info.publicKey) {\n\t\t\tauthPeer.pqPublicKey = parsed.pqPublicKey\n\t\t\tvoid trustPeer(this.config.netDir, info.publicKey).catch(() => {})\n\t\t\tlogger.info(\n\t\t\t\t`Auto-upgraded peer to hybrid PQ: ${info.name} (${parsed.instanceId.substring(0, 8)})`,\n\t\t\t)\n\t\t}\n\n\t\t// Register the peer\n\t\tconst instance: VoleNetInstance = {\n\t\t\tid: parsed.instanceId,\n\t\t\tname: info.name,\n\t\t\tpublicKey: info.publicKey,\n\t\t\tendpoint: info.endpoint ?? '',\n\t\t\tcapabilities: info.capabilities ?? [],\n\t\t\trole: (info.role as VoleNetInstance['role']) ?? 'peer',\n\t\t\tload: 0,\n\t\t\tmaxTasks: 5,\n\t\t\tlastSeen: Date.now(),\n\t\t\tversion: info.version ?? 'unknown',\n\t\t\txPublicKey: info.xPublicKey,\n\t\t\tmlkemPublicKey: info.mlkemPublicKey,\n\t\t}\n\n\t\tthis.instances.set(parsed.instanceId, instance)\n\t\tif (instance.endpoint) {\n\t\t\tthis.transport.addPeer(parsed.instanceId, instance.endpoint)\n\t\t\tthis.hintEndpointDrift(instance)\n\t\t}\n\n\t\tlogger.info(\n\t\t\t`Peer discovered: ${instance.name} (${instance.id.substring(0, 8)}) — ${instance.capabilities.join(', ')}`,\n\t\t)\n\n\t\t// Trigger re-election now that a new peer joined\n\t\tthis.onPeerChanged?.()\n\n\t\t// Send response with our info\n\t\tconst response = createMessage(\n\t\t\t'discover:response',\n\t\t\tthis.config.instanceId,\n\t\t\tparsed.instanceId,\n\t\t\t{\n\t\t\t\tname: this.config.instanceName,\n\t\t\t\tpublicKey: this.config.publicKeyString,\n\t\t\t\tendpoint: this.config.endpoint,\n\t\t\t\tcapabilities: this.config.capabilities,\n\t\t\t\trole: this.config.role,\n\t\t\t\tversion: '3.0.0',\n\t\t\t\txPublicKey: this.config.xPublicKeyB64,\n\t\t\t\tmlkemPublicKey: this.config.mlkemPublicKeyB64,\n\t\t\t},\n\t\t\tthis.config.privateKey,\n\t\t\tthis.config.pqPrivateKey,\n\t\t)\n\t\tthis.transport.sendToPeer(parsed.instanceId, response)\n\n\t\t// Request their tool list\n\t\tconst toolReq = createMessage(\n\t\t\t'tool:list',\n\t\t\tthis.config.instanceId,\n\t\t\tparsed.instanceId,\n\t\t\t{},\n\t\t\tthis.config.privateKey,\n\t\t\tthis.config.pqPrivateKey,\n\t\t)\n\t\tthis.transport.sendToPeer(parsed.instanceId, toolReq)\n\t}\n\n\t/**\n\t * Handle discovery response.\n\t */\n\tprivate handleDiscoverResponse(message: VoleNetMessage): void {\n\t\t// Same as discover but don't send a response back (avoid loop)\n\t\tconst info = message.payload as Partial<VoleNetInstance>\n\t\tif (!info.publicKey || !info.name) return\n\n\t\tconst parsed = parsePublicKey(info.publicKey)\n\t\tif (!parsed) return\n\t\tconst authPeer = this.authorizedPeers.get(parsed.instanceId)\n\t\tif (!authPeer) return\n\n\t\t// Verify the signature before trusting the payload (and before any auto-upgrade).\n\t\tconst result = verifyMessage(message, authPeer.publicKey, authPeer.pqPublicKey)\n\t\tif (!result.valid) {\n\t\t\tlogger.warn(`Discover-response verification failed from ${info.name}: ${result.error}`)\n\t\t\treturn\n\t\t}\n\n\t\t// Auto-upgrade trust to hybrid PQ when this known peer announces an ML-DSA key\n\t\t// (its discover-response was just verified, so the announced PQ key is authentic).\n\t\tif (parsed.pqPublicKey && !authPeer.pqPublicKey) {\n\t\t\tauthPeer.pqPublicKey = parsed.pqPublicKey\n\t\t\tvoid trustPeer(this.config.netDir, info.publicKey).catch(() => {})\n\t\t\tlogger.info(\n\t\t\t\t`Auto-upgraded peer to hybrid PQ: ${info.name} (${parsed.instanceId.substring(0, 8)})`,\n\t\t\t)\n\t\t}\n\n\t\tconst instance: VoleNetInstance = {\n\t\t\tid: parsed.instanceId,\n\t\t\tname: info.name,\n\t\t\tpublicKey: info.publicKey,\n\t\t\tendpoint: info.endpoint ?? '',\n\t\t\tcapabilities: info.capabilities ?? [],\n\t\t\trole: (info.role as VoleNetInstance['role']) ?? 'peer',\n\t\t\tload: 0,\n\t\t\tmaxTasks: 5,\n\t\t\tlastSeen: Date.now(),\n\t\t\tversion: info.version ?? 'unknown',\n\t\t\txPublicKey: info.xPublicKey,\n\t\t\tmlkemPublicKey: info.mlkemPublicKey,\n\t\t}\n\n\t\tthis.instances.set(parsed.instanceId, instance)\n\t\tif (instance.endpoint) {\n\t\t\tthis.transport.addPeer(parsed.instanceId, instance.endpoint)\n\t\t\tthis.hintEndpointDrift(instance)\n\t\t}\n\n\t\tlogger.info(`Peer confirmed: ${instance.name} (${instance.id.substring(0, 8)})`)\n\t\tthis.onPeerChanged?.()\n\n\t\t// Ask for their tools. handleDiscover already does this for peers that discover US —\n\t\t// but the side that INITIATES discovery only ever receives this response, so without\n\t\t// this it never learns what the peer shares. Mutually-configured meshes mask it (both\n\t\t// sides discover each other, so each gets asked); publicJoin does not — the joiner\n\t\t// dials the hub one-way and the hub cannot dial back through NAT. Verified against a\n\t\t// live public hub: unpatched, a joiner receives zero tools, indefinitely.\n\t\tthis.transport.sendToPeer(\n\t\t\tparsed.instanceId,\n\t\t\tcreateMessage(\n\t\t\t\t'tool:list',\n\t\t\t\tthis.config.instanceId,\n\t\t\t\tparsed.instanceId,\n\t\t\t\t{},\n\t\t\t\tthis.config.privateKey,\n\t\t\t\tthis.config.pqPrivateKey,\n\t\t\t),\n\t\t)\n\t}\n\n\tprivate handlePing(message: VoleNetMessage): void {\n\t\tconst instance = this.instances.get(message.from)\n\t\tif (instance) instance.lastSeen = Date.now()\n\n\t\tconst pong = createMessage(\n\t\t\t'pong',\n\t\t\tthis.config.instanceId,\n\t\t\tmessage.from,\n\t\t\t{ timestamp: Date.now() },\n\t\t\tthis.config.privateKey,\n\t\t\tthis.config.pqPrivateKey,\n\t\t)\n\t\tthis.transport.sendToPeer(message.from, pong)\n\t}\n\n\tprivate handlePong(message: VoleNetMessage): void {\n\t\tconst instance = this.instances.get(message.from)\n\t\tif (instance) instance.lastSeen = Date.now()\n\t}\n\n\tprivate handleToolList(message: VoleNetMessage): void {\n\t\tconst tools = message.payload as RemoteToolInfo[]\n\t\tif (Array.isArray(tools)) {\n\t\t\tthis.remoteTools.set(message.from, tools)\n\t\t\tlogger.info(`Received ${tools.length} tools from ${message.from.substring(0, 8)}`)\n\t\t}\n\t}\n\n\t/**\n\t * Health check — ping all peers, remove stale ones.\n\t */\n\t/** Send a signed ping to a peer — fired on WS connect for immediate binding, and by healthCheck. */\n\t/** Warn (once per instance) when a configured peer URL no longer matches what it advertises. */\n\tprivate hintEndpointDrift(instance: VoleNetInstance): void {\n\t\tif (this.driftHinted.has(instance.id)) return\n\t\tconst stale = findEndpointDrift(this.config.configuredPeerUrls, instance.endpoint)\n\t\tif (!stale) return\n\t\tthis.driftHinted.add(instance.id)\n\t\tlogger.warn(\n\t\t\t`Peer \"${instance.name}\" now advertises ${instance.endpoint} — update its url in vole.config.json (currently ${stale}), or reconnects will fail after the old endpoint goes away`,\n\t\t)\n\t}\n\n\tprivate sendPing(peerId: string): void {\n\t\tconst ping = createMessage(\n\t\t\t'ping',\n\t\t\tthis.config.instanceId,\n\t\t\tpeerId,\n\t\t\t{ timestamp: Date.now() },\n\t\t\tthis.config.privateKey,\n\t\t\tthis.config.pqPrivateKey,\n\t\t)\n\t\tthis.transport.sendToPeer(peerId, ping)\n\t}\n\n\tprivate async healthCheck(): Promise<void> {\n\t\tconst now = Date.now()\n\n\t\tfor (const [id, instance] of this.instances) {\n\t\t\tif (now - instance.lastSeen > PEER_TIMEOUT_MS) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Peer timeout: ${instance.name} (${id.substring(0, 8)}) — last seen ${Math.round((now - instance.lastSeen) / 1000)}s ago`,\n\t\t\t\t)\n\t\t\t\tthis.instances.delete(id)\n\t\t\t\tthis.transport.removePeer(id)\n\t\t\t\tthis.remoteTools.delete(id)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Send ping\n\t\t\tconst ping = createMessage(\n\t\t\t\t'ping',\n\t\t\t\tthis.config.instanceId,\n\t\t\t\tid,\n\t\t\t\t{ timestamp: now },\n\t\t\t\tthis.config.privateKey,\n\t\t\t\tthis.config.pqPrivateKey,\n\t\t\t)\n\t\t\tthis.transport.sendToPeer(id, ping)\n\t\t}\n\t}\n\n\t/**\n\t * Get all connected instances.\n\t */\n\tgetInstances(): VoleNetInstance[] {\n\t\treturn Array.from(this.instances.values())\n\t}\n\n\t/**\n\t * Get all remote tools across all peers.\n\t */\n\tgetRemoteTools(): RemoteToolInfo[] {\n\t\tconst tools: RemoteToolInfo[] = []\n\t\tfor (const [, peerTools] of this.remoteTools) {\n\t\t\ttools.push(...peerTools)\n\t\t}\n\t\treturn tools\n\t}\n\n\t/**\n\t * Find which peer has a specific tool.\n\t */\n\tfindToolOwner(toolName: string): { instanceId: string; instance: VoleNetInstance } | null {\n\t\tfor (const [instanceId, tools] of this.remoteTools) {\n\t\t\tif (tools.some((t) => t.name === toolName)) {\n\t\t\t\tconst instance = this.instances.get(instanceId)\n\t\t\t\tif (instance) return { instanceId, instance }\n\t\t\t}\n\t\t}\n\t\treturn null\n\t}\n\n\t/**\n\t * Reload authorized peers from disk.\n\t */\n\tasync reloadAuthorized(): Promise<void> {\n\t\tthis.authorizedPeers = await loadAuthorizedVoles(this.config.netDir)\n\t\tlogger.info(`Reloaded ${this.authorizedPeers.size} authorized peer(s)`)\n\t}\n}\n","/**\n * VoleDrop — E2E-encrypted file transfer over VoleNet (`net.files`).\n *\n * Control/data split: control messages (`file:*`, `relay:blob:*`) ride the ordinary\n * signed message channel (auto-sealed when net.encrypt is on); the bulk bytes stream\n * over `/volenet/blob/*` as chacha20-poly1305 frames (file-crypto.ts). The per-transfer\n * key is ALWAYS sealed with the PQ-hybrid seal — file confidentiality never depends on\n * net.encrypt or TLS.\n *\n * Direction is chosen once by the receiver at accept time:\n * relay-received offer → relay · sender endpoint reachable → pull ·\n * receiver has endpoint → push · common hub → relay · else reject 'no-route'.\n * The hub stores ciphertext only (RelayBlobStore), with per-pair quotas and TTL.\n *\n * Consent: `files.acceptFrom` ('*' | name/id-prefix list) auto-accepts; everything else\n * waits as 'pending' for an explicit acceptFile/rejectFile. Inbox writes are sanitized\n * basenames, atomically renamed only after the sha256 verifies. Never executed.\n */\nimport * as crypto from 'node:crypto'\nimport * as fs from 'node:fs'\nimport * as fsp from 'node:fs/promises'\nimport type * as http from 'node:http'\nimport * as path from 'node:path'\nimport { Transform } from 'node:stream'\nimport { pipeline } from 'node:stream/promises'\nimport { RelayBlobStore } from './blob-store.js'\nimport {\n\tCHUNK_BYTES_DEFAULT,\n\tChunkDecryptStream,\n\tChunkEncryptStream,\n\tFRAME_OVERHEAD,\n\tcipherSizeFor,\n\tscanFrameOffset,\n\ttotalChunksFor,\n} from './file-crypto.js'\nimport type { VoleKeyPair } from './keys.js'\nimport { createLogger } from './logger.js'\nimport { type VoleNetInstance, type VoleNetMessage, createMessage } from './protocol.js'\nimport { type SealedBox, seal, unseal } from './seal.js'\nimport type { VoleNetTransport } from './transport.js'\n\nconst logger = createLogger('volenet-files')\n\nexport interface VoleNetFilesConfig {\n\tenabled?: boolean\n\tinboxDir?: string\n\t/**\n\t * Largest file this node will accept over a direct transfer. Default 2 GiB; `0` means no\n\t * limit. Transfers are chunked, resumable and streamed to disk, so size costs nothing but\n\t * disk — which is exactly why a limit exists at all: it is the only thing standing between a\n\t * peer (or a buggy sender) and a full disk. Relayed transfers are bounded separately by\n\t * `relayMaxBytes`, because there the bytes land on somebody else's disk.\n\t */\n\tmaxBytes?: number\n\tacceptFrom?: '*' | string[]\n\tmaxConcurrent?: number\n\tofferTtlMinutes?: number\n\tchunkBytes?: number\n\t/** Largest single blob this node will host when acting as a relay hub. Default 512 MiB. */\n\trelayMaxBytes?: number\n\trelayQuotaBytes?: number\n\trelayTtlHours?: number\n}\n\n/** 2 GiB — generous enough for video and disk images, bounded enough to be a safety net. */\n// Media moves through this: a gameplay capture is routinely 6-14 GB, and a 2 GiB ceiling refused\n// the exact files people most want to hand between machines. The disk is the real boundary and is\n// already checked before an offer is accepted (see freeSpace), so this is the cruder second guard —\n// generous enough not to be the thing that fails, low enough that an unattended agent does not\n// accept something absurd. Set `net.files.maxBytes` to 0 to lift it entirely.\nconst DEFAULT_MAX_BYTES = 16 * 1024 * 1024 * 1024\n/** A relay hub stores other people's bytes, so it stays capped regardless of endpoint limits. */\nconst DEFAULT_RELAY_MAX_BYTES = 512 * 1024 * 1024\n\n/** Human-readable size for operator-facing messages. */\nfunction fmtBytes(n: number): string {\n\tif (n < 1024) return `${n} B`\n\tconst units = ['KiB', 'MiB', 'GiB', 'TiB']\n\tlet v = n / 1024\n\tlet i = 0\n\twhile (v >= 1024 && i < units.length - 1) {\n\t\tv /= 1024\n\t\ti++\n\t}\n\treturn `${v.toFixed(v >= 10 || i === 0 ? 0 : 1)} ${units[i]}`\n}\n\nexport type TransferState =\n\t| 'offered'\n\t| 'pending'\n\t| 'accepted'\n\t| 'transferring'\n\t| 'verifying'\n\t| 'done'\n\t| 'rejected'\n\t| 'failed'\n\t| 'cancelled'\n\t| 'expired'\n\nexport interface TransferInfo {\n\ttransferId: string\n\tdir: 'send' | 'recv'\n\tpeerId: string\n\tpeerName: string\n\tname: string\n\tsize: number\n\tsha256: string\n\tstate: TransferState\n\tmode?: 'pull' | 'push' | 'relay'\n\tbytesDone: number\n\terror?: string\n\tnote?: string\n\tsavedPath?: string\n\tcreatedAt: number\n\tupdatedAt: number\n}\n\ninterface Transfer extends TransferInfo {\n\tchunkBytes: number\n\ttotalChunks: number\n\trelayed: boolean\n\tviaHub?: string\n\tkey?: Buffer\n\tkeyBox?: SealedBox\n\tsrcPath?: string\n\tpartialPath?: string\n\tpull?: { url: string; token: string }\n\tpush?: { url: string; token: string }\n\thubId?: string\n\tblobId?: string\n\tlastProgressAt?: number\n\tactive?: { destroy: () => void }\n}\n\ninterface TokenEntry {\n\tid: string // transferId (peer transfers) or blobId (hub)\n\tpeerId: string\n\top: 'pull' | 'push' | 'upload' | 'download'\n\texpiresAt: number\n}\n\ninterface ChatEntryLike {\n\tdir: 'in' | 'out'\n\ttext: string\n\tfromName: string\n\ttimestamp: number\n\tmessageId: string\n\trelayed?: boolean\n}\n\nexport interface VoleNetFilesOptions {\n\tconfig: VoleNetFilesConfig\n\trelayEnabled: boolean\n\tprojectRoot: string\n\tnetDir: string\n\tkeyPair: VoleKeyPair\n\ttransport: VoleNetTransport\n\tinstanceName: string\n\tadvertisedEndpoint: string\n\tbus?: { emit: (type: string, event: unknown) => void }\n\tgetInstances: () => VoleNetInstance[]\n\tresolveRelayPeer: (\n\t\tref: string,\n\t) =>\n\t\t| { instanceId: string; name: string; xPublicKey?: string; mlkemPublicKey?: string }\n\t\t| undefined\n\tgetHubForMember: (peerId: string) => string | undefined\n\tsealToMemberViaRelay: (\n\t\tref: string,\n\t\ttype: VoleNetMessage['type'],\n\t\tpayload: Record<string, unknown>,\n\t) => Promise<{ ok: boolean; delivered?: boolean; hubId?: string; error?: string }>\n\tappendChat: (peerId: string, entry: ChatEntryLike) => Promise<void>\n}\n\nconst SWEEP_INTERVAL_MS = 60_000\nconst BLOB_SWEEP_INTERVAL_MS = 60 * 60_000\nconst PROGRESS_THROTTLE_MS = 500\nconst RETRY_DELAYS_MS = [1_000, 5_000, 15_000]\nconst GRANT_TIMEOUT_MS = 15_000\nconst PROBE_TIMEOUT_MS = 4_000\n\nfunction sanitizeFilename(name: string): string {\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: stripping control chars is the point\n\tlet base = path.basename(name || 'file').replace(/[\\x00-\\x1f/\\\\]/g, '')\n\t// The receiver may be NTFS, where <>:\"|?* cannot appear in a filename (a colon names an\n\t// alternate data stream) and a trailing dot or space is silently stripped. The sender's\n\t// filesystem happily allows all of these — macOS will hand us `screen:shot.png` — so they\n\t// are normalized here, on the receiving side. Unlike session ids a filename needs no\n\t// reversibility, only readability, so reserved characters become underscores.\n\tbase = base.replace(/[<>:\"|?*]/g, '_').replace(/[. ]+$/, '')\n\tif (!base || base === '.' || base === '..') base = 'file'\n\t// DOS device names (CON, NUL, COM1…) are reserved even with an extension.\n\tif (/^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\\.|$)/i.test(base)) base = `_${base}`\n\tif (base.length > 200) {\n\t\tconst ext = path.extname(base).slice(0, 20)\n\t\tbase = base.slice(0, 200 - ext.length) + ext\n\t}\n\treturn base\n}\n\nasync function sha256File(filePath: string): Promise<string> {\n\tconst hash = crypto.createHash('sha256')\n\tfor await (const chunk of fs.createReadStream(filePath)) hash.update(chunk as Buffer)\n\treturn hash.digest('hex')\n}\n\n/**\n * Byte-budget guard as a Transform INSIDE the pipeline. Never attach a bare\n * `req.on('data')` counter before pipeline() — that switches the stream to flowing\n * mode, and any await before the pipeline attaches silently drops bytes.\n */\nfunction byteBudget(limit: number): Transform {\n\tlet received = 0\n\treturn new Transform({\n\t\ttransform(chunk: Buffer, _enc, cb) {\n\t\t\treceived += chunk.length\n\t\t\tif (received > limit) cb(new Error(`byte budget exceeded (${received} > ${limit})`))\n\t\t\telse cb(null, chunk)\n\t\t},\n\t})\n}\n\nfunction humanSize(bytes: number): string {\n\tif (bytes < 1024) return `${bytes} B`\n\tif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n\tif (bytes < 1024 ** 3) return `${(bytes / 1024 ** 2).toFixed(1)} MB`\n\treturn `${(bytes / 1024 ** 3).toFixed(2)} GB`\n}\n\nexport class VoleNetFiles {\n\tprivate transfers = new Map<string, Transfer>()\n\t/** sha256(token) → entry. Tokens are never stored or logged in the clear. */\n\tprivate tokens = new Map<string, TokenEntry>()\n\tprivate blobStore: RelayBlobStore | null = null\n\tprivate activeStreams = 0\n\tprivate sweepTimer?: ReturnType<typeof setInterval>\n\tprivate blobSweepTimer?: ReturnType<typeof setInterval>\n\t/** Pending relay:blob grants keyed by transferId (sender) / blobId (receiver fetch). */\n\tprivate grantWaiters = new Map<\n\t\tstring,\n\t\t{ resolve: (g: { blobId: string; token: string; url: string } | { deny: string }) => void }\n\t>()\n\n\tconstructor(private opts: VoleNetFilesOptions) {\n\t\tif (opts.relayEnabled && opts.config.enabled !== false) {\n\t\t\tthis.blobStore = new RelayBlobStore({\n\t\t\t\tdir: path.join(opts.netDir, 'blobs'),\n\t\t\t\tquotaBytes: opts.config.relayQuotaBytes ?? 512 * 1024 * 1024,\n\t\t\t\tttlMs: (opts.config.relayTtlHours ?? 24) * 60 * 60_000,\n\t\t\t\t// Deliberately NOT derived from maxBytes: raising (or removing) what this node\n\t\t\t\t// accepts for itself must not turn its relay into unbounded storage for others.\n\t\t\t\tmaxBlobBytes:\n\t\t\t\t\t(opts.config.relayMaxBytes ?? DEFAULT_RELAY_MAX_BYTES) + 1024 * FRAME_OVERHEAD,\n\t\t\t})\n\t\t\tthis.blobStore.sweep()\n\t\t\tthis.blobSweepTimer = setInterval(() => this.blobStore?.sweep(), BLOB_SWEEP_INTERVAL_MS)\n\t\t}\n\t\tthis.sweepTimer = setInterval(() => this.sweepTransfers(), SWEEP_INTERVAL_MS)\n\t\tvoid this.sweepOutbox()\n\t}\n\n\tstop(): void {\n\t\tif (this.sweepTimer) clearInterval(this.sweepTimer)\n\t\tif (this.blobSweepTimer) clearInterval(this.blobSweepTimer)\n\t\tfor (const t of this.transfers.values()) t.active?.destroy()\n\t\tthis.tokens.clear()\n\t\tthis.grantWaiters.clear()\n\t}\n\n\t// ── Small helpers ─────────────────────────────────────────────────────────\n\n\tprivate get me(): string {\n\t\treturn this.opts.keyPair.instanceId\n\t}\n\n\tprivate get chunkBytes(): number {\n\t\treturn this.opts.config.chunkBytes ?? CHUNK_BYTES_DEFAULT\n\t}\n\n\t/** Configured accept limit. `0` (or negative) means unlimited — checked via `overLimit`. */\n\tprivate get maxBytes(): number {\n\t\treturn this.opts.config.maxBytes ?? DEFAULT_MAX_BYTES\n\t}\n\n\tprivate overLimit(size: number): boolean {\n\t\tconst limit = this.maxBytes\n\t\treturn limit > 0 && size > limit\n\t}\n\n\t/**\n\t * Free bytes on the inbox's filesystem, or null when it can't be determined.\n\t *\n\t * With a generous (or disabled) size limit, the disk becomes the real boundary — and running\n\t * it to zero takes the whole agent down, not just the transfer. Better to decline the offer\n\t * than to accept and die halfway through.\n\t */\n\tprivate async freeSpace(): Promise<number | null> {\n\t\tfor (const dir of [this.inboxDir(), this.opts.netDir]) {\n\t\t\ttry {\n\t\t\t\tconst st = await fsp.statfs(dir)\n\t\t\t\treturn Number(st.bavail) * Number(st.bsize)\n\t\t\t} catch {\n\t\t\t\t// dir may not exist yet — fall through to the next candidate\n\t\t\t}\n\t\t}\n\t\treturn null\n\t}\n\n\tprivate get offerTtlMs(): number {\n\t\treturn (this.opts.config.offerTtlMinutes ?? 60) * 60_000\n\t}\n\n\tprivate inboxDir(): string {\n\t\treturn path.resolve(this.opts.projectRoot, this.opts.config.inboxDir ?? '.openvole/net/inbox')\n\t}\n\n\tprivate outboxDir(): string {\n\t\treturn path.join(this.opts.netDir, 'files', 'outbox')\n\t}\n\n\tprivate touch(t: Transfer, state?: TransferState) {\n\t\tif (state) t.state = state\n\t\tt.updatedAt = Date.now()\n\t}\n\n\tprivate emit(type: string, event: Record<string, unknown>) {\n\t\ttry {\n\t\t\tthis.opts.bus?.emit(type, event)\n\t\t} catch {\n\t\t\t/* listeners must not break transfers */\n\t\t}\n\t}\n\n\tprivate emitProgress(t: Transfer) {\n\t\tconst now = Date.now()\n\t\tif (t.lastProgressAt && now - t.lastProgressAt < PROGRESS_THROTTLE_MS) return\n\t\tt.lastProgressAt = now\n\t\tthis.emit('volenet:file:progress', {\n\t\t\ttransferId: t.transferId,\n\t\t\tdir: t.dir,\n\t\t\tbytes: t.bytesDone,\n\t\t\ttotalBytes: t.size,\n\t\t\tpct: t.size > 0 ? Math.min(100, Math.round((t.bytesDone / t.size) * 100)) : 100,\n\t\t})\n\t}\n\n\tprivate mintToken(id: string, peerId: string, op: TokenEntry['op']): string {\n\t\tconst token = crypto.randomBytes(32).toString('hex')\n\t\tconst hash = crypto.createHash('sha256').update(token).digest('hex')\n\t\tthis.tokens.set(hash, { id, peerId, op, expiresAt: Date.now() + this.offerTtlMs })\n\t\treturn token\n\t}\n\n\tprivate lookupToken(presented: string | undefined): TokenEntry | null {\n\t\tif (!presented || typeof presented !== 'string' || presented.length > 128) return null\n\t\tconst hash = crypto.createHash('sha256').update(presented).digest('hex')\n\t\tconst hashBuf = Buffer.from(hash, 'hex')\n\t\tfor (const [stored, entry] of this.tokens) {\n\t\t\t// Constant-time compare over fixed-length sha256 digests.\n\t\t\tif (crypto.timingSafeEqual(hashBuf, Buffer.from(stored, 'hex'))) {\n\t\t\t\tif (Date.now() > entry.expiresAt) {\n\t\t\t\t\tthis.tokens.delete(stored)\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\treturn entry\n\t\t\t}\n\t\t}\n\t\treturn null\n\t}\n\n\tprivate revokeTokensFor(id: string) {\n\t\tfor (const [hash, entry] of this.tokens) if (entry.id === id) this.tokens.delete(hash)\n\t}\n\n\t/** Send a control message to the transfer's peer over its channel (direct or sealed relay). */\n\tprivate async sendCtl(\n\t\tt: { peerId: string; relayed: boolean },\n\t\ttype: VoleNetMessage['type'],\n\t\tpayload: Record<string, unknown>,\n\t): Promise<boolean> {\n\t\tif (t.relayed) {\n\t\t\tconst r = await this.opts.sealToMemberViaRelay(t.peerId, type, payload)\n\t\t\treturn r.ok && r.delivered !== false\n\t\t}\n\t\tconst msg = createMessage(\n\t\t\ttype,\n\t\t\tthis.me,\n\t\t\tt.peerId,\n\t\t\tpayload,\n\t\t\tthis.opts.keyPair.privateKey,\n\t\t\tthis.opts.keyPair.pqPrivateKey,\n\t\t)\n\t\treturn this.opts.transport.sendToPeer(t.peerId, msg)\n\t}\n\n\tprivate directPeer(ref: string): VoleNetInstance | undefined {\n\t\treturn this.opts\n\t\t\t.getInstances()\n\t\t\t.find((i) => i.id === ref || i.name === ref || i.id.startsWith(ref))\n\t}\n\n\tprivate isAutoAccepted(fromId: string, fromName?: string): boolean {\n\t\tconst policy = this.opts.config.acceptFrom\n\t\tif (policy === '*') return true\n\t\tif (Array.isArray(policy)) {\n\t\t\tfor (const raw of policy) {\n\t\t\t\tconst trimmed = typeof raw === 'string' ? raw.trim() : ''\n\t\t\t\tif (!trimmed) continue\n\t\t\t\tif (trimmed === '*') return true\n\t\t\t\tif (trimmed === fromId || fromId.startsWith(trimmed)) return true\n\t\t\t\tif (fromName && trimmed === fromName) return true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\t// ── Public API ────────────────────────────────────────────────────────────\n\n\tlistTransfers(): TransferInfo[] {\n\t\treturn [...this.transfers.values()]\n\t\t\t.map((t) => this.publicInfo(t))\n\t\t\t.sort((a, b) => b.createdAt - a.createdAt)\n\t}\n\n\tgetTransfer(transferId: string): TransferInfo | undefined {\n\t\tconst t = this.transfers.get(transferId)\n\t\treturn t ? this.publicInfo(t) : undefined\n\t}\n\n\tprivate publicInfo(t: Transfer): TransferInfo {\n\t\treturn {\n\t\t\ttransferId: t.transferId,\n\t\t\tdir: t.dir,\n\t\t\tpeerId: t.peerId,\n\t\t\tpeerName: t.peerName,\n\t\t\tname: t.name,\n\t\t\tsize: t.size,\n\t\t\tsha256: t.sha256,\n\t\t\tstate: t.state,\n\t\t\tmode: t.mode,\n\t\t\tbytesDone: t.bytesDone,\n\t\t\terror: t.error,\n\t\t\tnote: t.note,\n\t\t\tsavedPath: t.savedPath,\n\t\t\tcreatedAt: t.createdAt,\n\t\t\tupdatedAt: t.updatedAt,\n\t\t}\n\t}\n\n\tasync sendFile(\n\t\tpeerRef: string,\n\t\tfilePath: string,\n\t\tnote?: string,\n\t): Promise<{ ok: boolean; transferId?: string; error?: string }> {\n\t\tif (this.opts.config.enabled === false) return { ok: false, error: 'net.files is disabled' }\n\t\tconst abs = path.resolve(this.opts.projectRoot, filePath)\n\t\tlet stat: fs.Stats\n\t\ttry {\n\t\t\tstat = await fsp.stat(abs)\n\t\t} catch {\n\t\t\treturn { ok: false, error: `file not found: ${abs}` }\n\t\t}\n\t\tif (!stat.isFile()) return { ok: false, error: `not a regular file: ${abs}` }\n\n\t\t// Resolve the peer: direct instance first, then hub roster.\n\t\tconst direct = this.directPeer(peerRef)\n\t\tconst relayMember = direct ? undefined : this.opts.resolveRelayPeer(peerRef)\n\t\tconst peer = direct\n\t\t\t? { id: direct.id, name: direct.name, x: direct.xPublicKey, mlkem: direct.mlkemPublicKey }\n\t\t\t: relayMember\n\t\t\t\t? {\n\t\t\t\t\t\tid: relayMember.instanceId,\n\t\t\t\t\t\tname: relayMember.name,\n\t\t\t\t\t\tx: relayMember.xPublicKey,\n\t\t\t\t\t\tmlkem: relayMember.mlkemPublicKey,\n\t\t\t\t\t}\n\t\t\t\t: null\n\t\tif (!peer) return { ok: false, error: `no connected peer: \"${peerRef}\"` }\n\t\tif (!peer.x) return { ok: false, error: `peer \"${peer.name}\" announces no encryption key` }\n\n\t\tconst transferId = crypto.randomUUID()\n\t\tconst key = crypto.randomBytes(32)\n\t\tconst keyBox = seal(peer.x, key, `${this.me}|${peer.id}|file:${transferId}`, peer.mlkem)\n\t\tif (!keyBox) return { ok: false, error: 'failed to seal transfer key' }\n\t\tconst sha256 = await sha256File(abs)\n\n\t\tconst t: Transfer = {\n\t\t\ttransferId,\n\t\t\tdir: 'send',\n\t\t\tpeerId: peer.id,\n\t\t\tpeerName: peer.name,\n\t\t\tname: sanitizeFilename(path.basename(abs)),\n\t\t\tsize: stat.size,\n\t\t\tsha256,\n\t\t\tstate: 'offered',\n\t\t\tbytesDone: 0,\n\t\t\tnote,\n\t\t\tcreatedAt: Date.now(),\n\t\t\tupdatedAt: Date.now(),\n\t\t\tchunkBytes: this.chunkBytes,\n\t\t\ttotalChunks: totalChunksFor(stat.size, this.chunkBytes),\n\t\t\trelayed: !direct,\n\t\t\tkey,\n\t\t\tsrcPath: abs,\n\t\t}\n\n\t\t// Pull credentials whenever we have a reachable-looking endpoint to serve from.\n\t\tlet pull: { url: string; token: string } | undefined\n\t\tif (this.opts.advertisedEndpoint && !t.relayed) {\n\t\t\tpull = {\n\t\t\t\turl: `${this.opts.advertisedEndpoint}/volenet/blob/${transferId}`,\n\t\t\t\ttoken: this.mintToken(transferId, peer.id, 'pull'),\n\t\t\t}\n\t\t\tt.pull = pull\n\t\t}\n\n\t\tthis.transfers.set(transferId, t)\n\t\tconst payload: Record<string, unknown> = {\n\t\t\ttransferId,\n\t\t\tname: t.name,\n\t\t\tsize: t.size,\n\t\t\tsha256,\n\t\t\tchunkBytes: t.chunkBytes,\n\t\t\ttotalChunks: t.totalChunks,\n\t\t\tkeyBox,\n\t\t\tfromName: this.opts.instanceName,\n\t\t\t...(note ? { note } : {}),\n\t\t\t...(pull ? { pull } : {}),\n\t\t}\n\t\tconst sent = await this.sendCtl(t, 'file:offer', payload)\n\t\tif (!sent) {\n\t\t\tthis.transfers.delete(transferId)\n\t\t\tthis.revokeTokensFor(transferId)\n\t\t\treturn { ok: false, error: `could not reach peer \"${peer.name}\"` }\n\t\t}\n\t\tvoid this.opts.appendChat(peer.id, {\n\t\t\tdir: 'out',\n\t\t\ttext: `\\u{1F4CE} ${t.name} (${humanSize(t.size)})${t.note ? ` \\u2014 ${t.note}` : ''}`,\n\t\t\tfromName: this.opts.instanceName,\n\t\t\ttimestamp: Date.now(),\n\t\t\tmessageId: transferId,\n\t\t\trelayed: t.relayed,\n\t\t})\n\t\treturn { ok: true, transferId }\n\t}\n\n\tasync acceptFile(transferId: string): Promise<{ ok: boolean; error?: string }> {\n\t\tconst t = this.transfers.get(transferId)\n\t\tif (!t || t.dir !== 'recv') return { ok: false, error: 'unknown transfer' }\n\t\tif (t.state !== 'pending' && t.state !== 'offered')\n\t\t\treturn { ok: false, error: `transfer is ${t.state}` }\n\t\treturn this.beginReceive(t)\n\t}\n\n\tasync rejectFile(transferId: string, reason = 'denied'): Promise<{ ok: boolean }> {\n\t\tconst t = this.transfers.get(transferId)\n\t\tif (!t || t.dir !== 'recv') return { ok: false }\n\t\tthis.touch(t, 'rejected')\n\t\tthis.revokeTokensFor(transferId)\n\t\tawait this.sendCtl(t, 'file:reject', { transferId, reason })\n\t\treturn { ok: true }\n\t}\n\n\tasync cancelTransfer(transferId: string): Promise<{ ok: boolean }> {\n\t\tconst t = this.transfers.get(transferId)\n\t\tif (!t) return { ok: false }\n\t\tt.active?.destroy()\n\t\tthis.touch(t, 'cancelled')\n\t\tthis.revokeTokensFor(transferId)\n\t\tif (t.partialPath) await fsp.rm(t.partialPath, { force: true }).catch(() => {})\n\t\tawait this.sendCtl(t, 'file:cancel', { transferId }).catch(() => {})\n\t\treturn { ok: true }\n\t}\n\n\t// ── Inbound control messages ──────────────────────────────────────────────\n\n\thandleMessage(msg: VoleNetMessage, ctx: { relayed?: boolean; viaHub?: string }): void {\n\t\tvoid this.handleMessageAsync(msg, ctx).catch((err) => {\n\t\t\tlogger.warn(\n\t\t\t\t`[volenet-files] handler error for ${msg.type}: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t})\n\t}\n\n\tprivate async handleMessageAsync(\n\t\tmsg: VoleNetMessage,\n\t\tctx: { relayed?: boolean; viaHub?: string },\n\t): Promise<void> {\n\t\tswitch (msg.type) {\n\t\t\tcase 'file:offer':\n\t\t\t\treturn this.onOffer(msg, ctx)\n\t\t\tcase 'file:accept':\n\t\t\t\treturn this.onAccept(msg)\n\t\t\tcase 'file:reject':\n\t\t\t\treturn this.onReject(msg)\n\t\t\tcase 'file:relay-ready':\n\t\t\t\treturn this.onRelayReady(msg)\n\t\t\tcase 'file:done':\n\t\t\t\treturn this.onDone(msg)\n\t\t\tcase 'file:error':\n\t\t\t\treturn this.onPeerError(msg)\n\t\t\tcase 'file:cancel':\n\t\t\t\treturn this.onPeerCancel(msg)\n\t\t\tcase 'relay:blob:create':\n\t\t\t\treturn this.hubOnBlobCreate(msg)\n\t\t\tcase 'relay:blob:fetch':\n\t\t\t\treturn this.hubOnBlobFetch(msg)\n\t\t\tcase 'relay:blob:done':\n\t\t\t\treturn this.hubOnBlobDone(msg)\n\t\t\tcase 'relay:blob:grant':\n\t\t\tcase 'relay:blob:deny':\n\t\t\t\treturn this.onBlobGrantOrDeny(msg)\n\t\t\tdefault:\n\t\t\t\treturn\n\t\t}\n\t}\n\n\tprivate async onOffer(msg: VoleNetMessage, ctx: { relayed?: boolean; viaHub?: string }) {\n\t\tconst p = msg.payload as {\n\t\t\ttransferId?: string\n\t\t\tname?: string\n\t\t\tsize?: number\n\t\t\tsha256?: string\n\t\t\tchunkBytes?: number\n\t\t\ttotalChunks?: number\n\t\t\tkeyBox?: SealedBox\n\t\t\tfromName?: string\n\t\t\tnote?: string\n\t\t\tpull?: { url: string; token: string }\n\t\t}\n\t\tif (\n\t\t\t!p?.transferId ||\n\t\t\ttypeof p.name !== 'string' ||\n\t\t\ttypeof p.size !== 'number' ||\n\t\t\tp.size < 0 ||\n\t\t\ttypeof p.sha256 !== 'string' ||\n\t\t\t!p.keyBox ||\n\t\t\ttypeof p.chunkBytes !== 'number' ||\n\t\t\tp.chunkBytes < 1024 ||\n\t\t\tp.chunkBytes > 64 * 1024 * 1024\n\t\t)\n\t\t\treturn\n\t\tif (this.transfers.has(p.transferId)) return // duplicate offer\n\t\tconst fromName = p.fromName || msg.from.substring(0, 8)\n\t\tconst reply = { peerId: msg.from, relayed: !!ctx.relayed }\n\n\t\tif (this.opts.config.enabled === false) {\n\t\t\tawait this.sendCtl(reply, 'file:reject', { transferId: p.transferId, reason: 'disabled' })\n\t\t\treturn\n\t\t}\n\t\tconst free = await this.freeSpace()\n\t\t// 5% headroom: the partial file, its atomic rename, and whatever else the agent is doing.\n\t\tif (free !== null && p.size * 1.05 > free) {\n\t\t\tawait this.sendCtl(reply, 'file:reject', {\n\t\t\t\ttransferId: p.transferId,\n\t\t\t\treason: `no-space: ${fmtBytes(p.size)} offered, ${fmtBytes(free)} free on the inbox filesystem`,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tif (this.overLimit(p.size)) {\n\t\t\t// Say what the limit is. A bare \"too-large\" leaves the sender guessing whether to\n\t\t\t// retry, split the file, or ask the operator to raise net.files.maxBytes.\n\t\t\tawait this.sendCtl(reply, 'file:reject', {\n\t\t\t\ttransferId: p.transferId,\n\t\t\t\treason: `too-large: ${fmtBytes(p.size)} exceeds this node's limit of ${fmtBytes(this.maxBytes)} (net.files.maxBytes)`,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tconst t: Transfer = {\n\t\t\ttransferId: p.transferId,\n\t\t\tdir: 'recv',\n\t\t\tpeerId: msg.from,\n\t\t\tpeerName: fromName,\n\t\t\tname: sanitizeFilename(p.name),\n\t\t\tsize: p.size,\n\t\t\tsha256: p.sha256,\n\t\t\tstate: 'offered',\n\t\t\tbytesDone: 0,\n\t\t\tnote: p.note,\n\t\t\tcreatedAt: Date.now(),\n\t\t\tupdatedAt: Date.now(),\n\t\t\tchunkBytes: p.chunkBytes,\n\t\t\ttotalChunks: p.totalChunks ?? totalChunksFor(p.size, p.chunkBytes),\n\t\t\trelayed: !!ctx.relayed,\n\t\t\tviaHub: ctx.viaHub,\n\t\t\tkeyBox: p.keyBox,\n\t\t\tpull: p.pull,\n\t\t}\n\t\tthis.transfers.set(t.transferId, t)\n\t\tvoid this.opts.appendChat(msg.from, {\n\t\t\tdir: 'in',\n\t\t\ttext: `\\u{1F4CE} ${t.name} (${humanSize(t.size)})${t.note ? ` \\u2014 ${t.note}` : ''}`,\n\t\t\tfromName,\n\t\t\ttimestamp: msg.timestamp,\n\t\t\tmessageId: t.transferId,\n\t\t\trelayed: t.relayed,\n\t\t})\n\n\t\tif (this.isAutoAccepted(msg.from, fromName)) {\n\t\t\tthis.emit('volenet:file:offer', {\n\t\t\t\ttransferId: t.transferId,\n\t\t\t\tfrom: msg.from,\n\t\t\t\tfromName,\n\t\t\t\tname: t.name,\n\t\t\t\tsize: t.size,\n\t\t\t\tnote: t.note,\n\t\t\t\tauto: true,\n\t\t\t})\n\t\t\tawait this.beginReceive(t)\n\t\t} else {\n\t\t\tthis.touch(t, 'pending')\n\t\t\tthis.emit('volenet:file:offer', {\n\t\t\t\ttransferId: t.transferId,\n\t\t\t\tfrom: msg.from,\n\t\t\t\tfromName,\n\t\t\t\tname: t.name,\n\t\t\t\tsize: t.size,\n\t\t\t\tnote: t.note,\n\t\t\t\tauto: false,\n\t\t\t})\n\t\t}\n\t}\n\n\t/** Unseal the key, pick the mode, tell the sender, and start (receiver side). */\n\tprivate async beginReceive(t: Transfer): Promise<{ ok: boolean; error?: string }> {\n\t\tif (!t.keyBox) return { ok: false, error: 'offer carries no key' }\n\t\tif (!this.opts.keyPair.xPrivateKey) {\n\t\t\tthis.touch(t, 'failed')\n\t\t\tt.error = 'this instance has no X25519 key (regenerate with vole net init)'\n\t\t\treturn { ok: false, error: t.error }\n\t\t}\n\t\tconst key = unseal(\n\t\t\tthis.opts.keyPair.xPrivateKey,\n\t\t\tt.keyBox,\n\t\t\t`${t.peerId}|${this.me}|file:${t.transferId}`,\n\t\t\tthis.opts.keyPair.mlkemPrivateKey,\n\t\t)\n\t\tif (!key || key.length !== 32) {\n\t\t\tthis.touch(t, 'failed')\n\t\t\tt.error = 'transfer key failed to unseal'\n\t\t\tawait this.sendCtl(t, 'file:error', { transferId: t.transferId, code: 'io' })\n\t\t\treturn { ok: false, error: t.error }\n\t\t}\n\t\tt.key = key\n\n\t\t// Mode selection (see module docstring).\n\t\tlet mode: 'pull' | 'push' | 'relay' | null = null\n\t\tlet hubId: string | undefined\n\t\tif (t.relayed) {\n\t\t\tmode = 'relay'\n\t\t\thubId = t.viaHub ?? this.opts.getHubForMember(t.peerId)\n\t\t\tif (!hubId) mode = null\n\t\t} else {\n\t\t\tconst sender = this.opts.getInstances().find((i) => i.id === t.peerId)\n\t\t\tif (t.pull && sender?.endpoint && (await this.probe(sender.endpoint))) mode = 'pull'\n\t\t\telse if (this.opts.advertisedEndpoint) mode = 'push'\n\t\t\telse {\n\t\t\t\thubId = this.opts.getHubForMember(t.peerId)\n\t\t\t\tif (hubId) mode = 'relay'\n\t\t\t}\n\t\t}\n\t\tif (!mode) {\n\t\t\tthis.touch(t, 'rejected')\n\t\t\tawait this.sendCtl(t, 'file:reject', { transferId: t.transferId, reason: 'no-route' })\n\t\t\treturn { ok: false, error: 'no route to receive the file' }\n\t\t}\n\n\t\tt.mode = mode\n\t\tt.hubId = hubId\n\t\tthis.touch(t, 'accepted')\n\t\tconst push =\n\t\t\tthis.opts.advertisedEndpoint && mode !== 'relay'\n\t\t\t\t? {\n\t\t\t\t\t\turl: `${this.opts.advertisedEndpoint}/volenet/blob/${t.transferId}`,\n\t\t\t\t\t\ttoken: this.mintToken(t.transferId, t.peerId, 'push'),\n\t\t\t\t\t}\n\t\t\t\t: undefined\n\t\tif (push) t.push = push\n\t\tawait this.sendCtl(t, 'file:accept', {\n\t\t\ttransferId: t.transferId,\n\t\t\tmode,\n\t\t\t...(push ? { push } : {}),\n\t\t\t...(hubId ? { hubId } : {}),\n\t\t})\n\n\t\tif (mode === 'pull') void this.runPull(t)\n\t\t// push: we wait for the sender's POST against our blob endpoint.\n\t\t// relay: we wait for file:relay-ready.\n\t\treturn { ok: true }\n\t}\n\n\tprivate async probe(endpoint: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst res = await fetch(`${endpoint}/health`, {\n\t\t\t\tsignal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n\t\t\t})\n\t\t\treturn res.ok\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tprivate async onAccept(msg: VoleNetMessage) {\n\t\tconst p = msg.payload as {\n\t\t\ttransferId?: string\n\t\t\tmode?: 'pull' | 'push' | 'relay'\n\t\t\tpush?: { url: string; token: string }\n\t\t\thubId?: string\n\t\t}\n\t\tconst t = p?.transferId ? this.transfers.get(p.transferId) : undefined\n\t\tif (!t || t.dir !== 'send' || t.peerId !== msg.from) return\n\t\tif (t.state !== 'offered') return\n\t\tt.mode = p.mode\n\t\tif (p.push) t.push = p.push\n\t\tif (p.hubId) t.hubId = p.hubId\n\t\tthis.touch(t, p.mode === 'pull' ? 'transferring' : 'accepted')\n\t\tif (p.mode === 'push') void this.runPush(t)\n\t\telse if (p.mode === 'relay') void this.runRelayUpload(t)\n\t\t// pull: receiver drives; we serve GETs against our blob endpoint.\n\t}\n\n\tprivate async onReject(msg: VoleNetMessage) {\n\t\tconst p = msg.payload as { transferId?: string; reason?: string }\n\t\tconst t = p?.transferId ? this.transfers.get(p.transferId) : undefined\n\t\tif (!t || t.dir !== 'send' || t.peerId !== msg.from) return\n\t\tthis.touch(t, 'rejected')\n\t\tt.error = p.reason ?? 'denied'\n\t\tthis.revokeTokensFor(t.transferId)\n\t\tthis.emit('volenet:file:rejected', {\n\t\t\ttransferId: t.transferId,\n\t\t\tby: t.peerId,\n\t\t\treason: t.error,\n\t\t})\n\t}\n\n\tprivate async onRelayReady(msg: VoleNetMessage) {\n\t\tconst p = msg.payload as { transferId?: string; blobId?: string; hubId?: string }\n\t\tconst t = p?.transferId ? this.transfers.get(p.transferId) : undefined\n\t\tif (!t || t.dir !== 'recv' || t.peerId !== msg.from || !p.blobId) return\n\t\tif (t.state !== 'accepted') return\n\t\tt.blobId = p.blobId\n\t\tif (p.hubId) t.hubId = p.hubId\n\t\tvoid this.runRelayDownload(t)\n\t}\n\n\tprivate async onDone(msg: VoleNetMessage) {\n\t\tconst p = msg.payload as { transferId?: string; bytes?: number }\n\t\tconst t = p?.transferId ? this.transfers.get(p.transferId) : undefined\n\t\tif (!t || t.dir !== 'send' || t.peerId !== msg.from) return\n\t\tthis.touch(t, 'done')\n\t\tt.bytesDone = t.size\n\t\tthis.revokeTokensFor(t.transferId)\n\t\tawait this.cleanupOutboxSource(t)\n\t\tthis.emit('volenet:file:sent', {\n\t\t\ttransferId: t.transferId,\n\t\t\tto: t.peerId,\n\t\t\ttoName: t.peerName,\n\t\t\tname: t.name,\n\t\t\tsize: t.size,\n\t\t})\n\t}\n\n\tprivate async onPeerError(msg: VoleNetMessage) {\n\t\tconst p = msg.payload as { transferId?: string; code?: string; detail?: string }\n\t\tconst t = p?.transferId ? this.transfers.get(p.transferId) : undefined\n\t\tif (!t || t.peerId !== msg.from) return\n\t\tif (t.state === 'done' || t.state === 'failed') return\n\t\t// Sender-side fallback: the receiver couldn't pull from us — push if we got creds.\n\t\tif (t.dir === 'send' && p.code === 'pull-unreachable' && t.push) {\n\t\t\tt.mode = 'push'\n\t\t\tthis.touch(t, 'accepted')\n\t\t\tvoid this.runPush(t)\n\t\t\treturn\n\t\t}\n\t\tthis.failTransfer(t, p.code ?? 'io', p.detail)\n\t}\n\n\tprivate async onPeerCancel(msg: VoleNetMessage) {\n\t\tconst p = msg.payload as { transferId?: string }\n\t\tconst t = p?.transferId ? this.transfers.get(p.transferId) : undefined\n\t\tif (!t || t.peerId !== msg.from) return\n\t\tt.active?.destroy()\n\t\tthis.touch(t, 'cancelled')\n\t\tthis.revokeTokensFor(t.transferId)\n\t\tif (t.partialPath) await fsp.rm(t.partialPath, { force: true }).catch(() => {})\n\t}\n\n\tprivate failTransfer(t: Transfer, code: string, detail?: string) {\n\t\tt.active?.destroy()\n\t\tthis.touch(t, 'failed')\n\t\tt.error = detail ? `${code}: ${detail}` : code\n\t\tthis.revokeTokensFor(t.transferId)\n\t\tthis.emit('volenet:file:failed', {\n\t\t\ttransferId: t.transferId,\n\t\t\tdir: t.dir,\n\t\t\tcode,\n\t\t\t...(detail ? { detail } : {}),\n\t\t})\n\t}\n\n\t// ── Receiver: pull + shared finalization ──────────────────────────────────\n\n\tprivate async runPull(t: Transfer) {\n\t\tif (!t.pull || !t.key) return\n\t\tthis.touch(t, 'transferring')\n\t\tawait fsp.mkdir(path.join(this.inboxDir(), '.partial'), { recursive: true })\n\t\tt.partialPath = path.join(this.inboxDir(), '.partial', `${t.transferId}.part`)\n\n\t\tlet fromChunk = 0\n\t\tfor (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {\n\t\t\ttry {\n\t\t\t\tconst url = fromChunk > 0 ? `${t.pull.url}?from=${fromChunk}` : t.pull.url\n\t\t\t\tconst res = await fetch(url, {\n\t\t\t\t\theaders: { 'x-vole-blob-token': t.pull.token },\n\t\t\t\t\tsignal: AbortSignal.timeout(this.offerTtlMs),\n\t\t\t\t})\n\t\t\t\tif (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`)\n\t\t\t\tawait this.consumeCipherStream(t, res.body as unknown as NodeJS.ReadableStream, fromChunk)\n\t\t\t\tawait this.finalizeReceive(t)\n\t\t\t\treturn\n\t\t\t} catch (err) {\n\t\t\t\tif ((t.state as TransferState) === 'cancelled') return\n\t\t\t\tconst dec = t.partialPath ? await this.countCompleteChunks(t) : 0\n\t\t\t\tfromChunk = dec\n\t\t\t\tif (attempt < RETRY_DELAYS_MS.length) {\n\t\t\t\t\tawait new Promise((r) => setTimeout(r, RETRY_DELAYS_MS[attempt]))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`[volenet-files] pull failed for ${t.transferId.substring(0, 8)}: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\t)\n\t\t\t\tawait this.sendCtl(t, 'file:error', {\n\t\t\t\t\ttransferId: t.transferId,\n\t\t\t\t\tcode: 'pull-unreachable',\n\t\t\t\t})\n\t\t\t\tthis.failTransfer(t, 'pull-unreachable')\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Pipe a ciphertext stream into the partial file (append from a chunk boundary). */\n\tprivate async consumeCipherStream(\n\t\tt: Transfer,\n\t\tbody: NodeJS.ReadableStream,\n\t\tfromChunk: number,\n\t\tbudget?: number,\n\t): Promise<void> {\n\t\tif (!t.key || !t.partialPath) throw new Error('transfer not initialized')\n\t\tif (fromChunk === 0) await fsp.rm(t.partialPath, { force: true })\n\t\tconst decrypt = new ChunkDecryptStream(t.key, t.transferId, t.chunkBytes, fromChunk)\n\t\tconst out = fs.createWriteStream(t.partialPath, { flags: fromChunk > 0 ? 'a' : 'w' })\n\t\tt.active = {\n\t\t\tdestroy: () => {\n\t\t\t\t;(body as unknown as { destroy?: () => void }).destroy?.()\n\t\t\t\tout.destroy()\n\t\t\t},\n\t\t}\n\t\tt.bytesDone = fromChunk * t.chunkBytes\n\t\tdecrypt.on('data', (chunk: Buffer) => {\n\t\t\tt.bytesDone += chunk.length\n\t\t\tthis.emitProgress(t)\n\t\t})\n\t\ttry {\n\t\t\tif (budget != null) await pipeline(body, byteBudget(budget), decrypt, out)\n\t\t\telse await pipeline(body, decrypt, out)\n\t\t} finally {\n\t\t\tt.active = undefined\n\t\t}\n\t}\n\n\t/** Count fully-written chunks in the partial (the resume cursor after an interrupt). */\n\tprivate async countCompleteChunks(t: Transfer): Promise<number> {\n\t\tif (!t.partialPath) return 0\n\t\ttry {\n\t\t\tconst stat = await fsp.stat(t.partialPath)\n\t\t\tconst whole = Math.floor(stat.size / t.chunkBytes)\n\t\t\t// Truncate any trailing partial chunk so the append restarts on a boundary.\n\t\t\tawait fsp.truncate(t.partialPath, whole * t.chunkBytes)\n\t\t\treturn whole\n\t\t} catch {\n\t\t\treturn 0\n\t\t}\n\t}\n\n\tprivate async finalizeReceive(t: Transfer) {\n\t\tif (!t.partialPath) return\n\t\tthis.touch(t, 'verifying')\n\t\tconst actualSha = await sha256File(t.partialPath)\n\t\tconst stat = await fsp.stat(t.partialPath).catch(() => null)\n\t\tif (actualSha !== t.sha256 || !stat || stat.size !== t.size) {\n\t\t\tawait fsp.rm(t.partialPath, { force: true }).catch(() => {})\n\t\t\tawait this.sendCtl(t, 'file:error', {\n\t\t\t\ttransferId: t.transferId,\n\t\t\t\tcode: 'checksum-mismatch',\n\t\t\t})\n\t\t\tthis.failTransfer(t, 'checksum-mismatch')\n\t\t\treturn\n\t\t}\n\t\t// Collision-suffixed atomic landing.\n\t\tawait fsp.mkdir(this.inboxDir(), { recursive: true })\n\t\tconst ext = path.extname(t.name)\n\t\tconst stem = t.name.slice(0, t.name.length - ext.length)\n\t\tlet target = path.join(this.inboxDir(), t.name)\n\t\tfor (let i = 1; ; i++) {\n\t\t\ttry {\n\t\t\t\tawait fsp.access(target)\n\t\t\t\ttarget = path.join(this.inboxDir(), `${stem} (${i})${ext}`)\n\t\t\t} catch {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tawait fsp.rename(t.partialPath, target)\n\t\tt.savedPath = target\n\t\tt.bytesDone = t.size\n\t\tthis.touch(t, 'done')\n\t\tthis.revokeTokensFor(t.transferId)\n\t\tawait this.sendCtl(t, 'file:done', { transferId: t.transferId, bytes: t.size })\n\t\tif (t.blobId && t.hubId) {\n\t\t\t// Tell the hub it can free the blob now.\n\t\t\tawait this.sendDirect(t.hubId, 'relay:blob:done', { blobId: t.blobId })\n\t\t}\n\t\tthis.emit('volenet:file:received', {\n\t\t\ttransferId: t.transferId,\n\t\t\tfrom: t.peerId,\n\t\t\tfromName: t.peerName,\n\t\t\tname: path.basename(target),\n\t\t\tpath: target,\n\t\t\tsize: t.size,\n\t\t\tsha256: t.sha256,\n\t\t})\n\t}\n\n\t// ── Sender: push + relay upload ───────────────────────────────────────────\n\n\tprivate async runPush(t: Transfer) {\n\t\tif (!t.push || !t.key || !t.srcPath) return\n\t\tthis.touch(t, 'transferring')\n\t\tfor (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {\n\t\t\ttry {\n\t\t\t\tawait this.streamCipherTo(t, t.push.url, t.push.token, { restart: attempt > 0 })\n\t\t\t\t// Receiver verifies + sends file:done; we stay 'transferring' until then.\n\t\t\t\treturn\n\t\t\t} catch (err) {\n\t\t\t\tif ((t.state as TransferState) === 'cancelled') return\n\t\t\t\tif (attempt < RETRY_DELAYS_MS.length) {\n\t\t\t\t\tawait new Promise((r) => setTimeout(r, RETRY_DELAYS_MS[attempt]))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`[volenet-files] push failed for ${t.transferId.substring(0, 8)}: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\t)\n\t\t\t\t// Last resort: relay, when a common hub exists.\n\t\t\t\tconst hubId = this.opts.getHubForMember(t.peerId)\n\t\t\t\tif (hubId) {\n\t\t\t\t\tt.hubId = hubId\n\t\t\t\t\tvoid this.runRelayUpload(t)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tawait this.sendCtl(t, 'file:error', {\n\t\t\t\t\ttransferId: t.transferId,\n\t\t\t\t\tcode: 'push-unreachable',\n\t\t\t\t})\n\t\t\t\tthis.failTransfer(t, 'push-unreachable')\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Stream the source file, encrypted, to a blob endpoint (push or hub upload). */\n\tprivate async streamCipherTo(\n\t\tt: Transfer,\n\t\turl: string,\n\t\ttoken: string,\n\t\topts?: { restart?: boolean },\n\t): Promise<void> {\n\t\tif (!t.key || !t.srcPath) throw new Error('transfer not initialized')\n\t\tconst src = fs.createReadStream(t.srcPath)\n\t\tconst enc = new ChunkEncryptStream(t.key, t.transferId, t.chunkBytes)\n\t\tt.active = { destroy: () => src.destroy() }\n\t\tt.bytesDone = 0\n\t\tsrc.on('data', (chunk) => {\n\t\t\tt.bytesDone += (chunk as Buffer).length\n\t\t\tthis.emitProgress(t)\n\t\t})\n\t\ttry {\n\t\t\tconst res = await fetch(url, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t'x-vole-blob-token': token,\n\t\t\t\t\t'content-type': 'application/octet-stream',\n\t\t\t\t\t...(opts?.restart ? { 'x-vole-restart': '1' } : {}),\n\t\t\t\t},\n\t\t\t\t// Node fetch streams request bodies only with duplex:'half'; the DOM lib\n\t\t\t\t// types don't know either option, hence the cast.\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: see above\n\t\t\t\tbody: src.pipe(enc) as any,\n\t\t\t\tduplex: 'half',\n\t\t\t\tsignal: AbortSignal.timeout(this.offerTtlMs),\n\t\t\t} as RequestInit)\n\t\t\tif (!res.ok) throw new Error(`HTTP ${res.status}`)\n\t\t} finally {\n\t\t\tt.active = undefined\n\t\t}\n\t}\n\n\tprivate async runRelayUpload(t: Transfer) {\n\t\tif (!t.key || !t.srcPath || !t.hubId) return\n\t\tthis.touch(t, 'transferring')\n\t\tconst grant = await this.requestGrant(t.hubId, 'relay:blob:create', {\n\t\t\ttransferId: t.transferId,\n\t\t\tto: t.peerId,\n\t\t\tcipherSize: cipherSizeFor(t.size, t.chunkBytes),\n\t\t})\n\t\tif ('deny' in grant) {\n\t\t\tawait this.sendCtl(t, 'file:error', { transferId: t.transferId, code: 'relay-denied' })\n\t\t\tthis.failTransfer(t, 'relay-denied', grant.deny)\n\t\t\treturn\n\t\t}\n\t\tt.blobId = grant.blobId\n\t\ttry {\n\t\t\tawait this.streamCipherTo(t, grant.url, grant.token, {})\n\t\t} catch (err) {\n\t\t\tthis.failTransfer(t, 'io', err instanceof Error ? err.message : String(err))\n\t\t\treturn\n\t\t}\n\t\tawait this.sendCtl(t, 'file:relay-ready', {\n\t\t\ttransferId: t.transferId,\n\t\t\tblobId: t.blobId,\n\t\t\thubId: t.hubId,\n\t\t})\n\t}\n\n\tprivate async runRelayDownload(t: Transfer) {\n\t\tif (!t.key || !t.blobId || !t.hubId) return\n\t\tthis.touch(t, 'transferring')\n\t\tconst grant = await this.requestGrant(t.hubId, 'relay:blob:fetch', {\n\t\t\ttransferId: t.transferId,\n\t\t\tblobId: t.blobId,\n\t\t})\n\t\tif ('deny' in grant) {\n\t\t\tthis.failTransfer(t, 'relay-denied', grant.deny)\n\t\t\treturn\n\t\t}\n\t\tawait fsp.mkdir(path.join(this.inboxDir(), '.partial'), { recursive: true })\n\t\tt.partialPath = path.join(this.inboxDir(), '.partial', `${t.transferId}.part`)\n\t\tlet fromChunk = 0\n\t\tfor (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {\n\t\t\ttry {\n\t\t\t\tconst url = fromChunk > 0 ? `${grant.url}?from=${fromChunk}` : grant.url\n\t\t\t\tconst res = await fetch(url, {\n\t\t\t\t\theaders: { 'x-vole-blob-token': grant.token },\n\t\t\t\t\tsignal: AbortSignal.timeout(this.offerTtlMs),\n\t\t\t\t})\n\t\t\t\tif (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`)\n\t\t\t\tawait this.consumeCipherStream(t, res.body as unknown as NodeJS.ReadableStream, fromChunk)\n\t\t\t\tawait this.finalizeReceive(t)\n\t\t\t\treturn\n\t\t\t} catch (err) {\n\t\t\t\tif ((t.state as TransferState) === 'cancelled') return\n\t\t\t\tfromChunk = await this.countCompleteChunks(t)\n\t\t\t\tif (attempt < RETRY_DELAYS_MS.length) {\n\t\t\t\t\tawait new Promise((r) => setTimeout(r, RETRY_DELAYS_MS[attempt]))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthis.failTransfer(t, 'io', err instanceof Error ? err.message : String(err))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Send a relay:blob request to the hub and await its grant/deny (direct message). */\n\tprivate async requestGrant(\n\t\thubId: string,\n\t\ttype: 'relay:blob:create' | 'relay:blob:fetch',\n\t\tpayload: Record<string, unknown>,\n\t): Promise<{ blobId: string; token: string; url: string } | { deny: string }> {\n\t\tconst key = String(payload.transferId ?? payload.blobId)\n\t\treturn new Promise((resolve) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.grantWaiters.delete(key)\n\t\t\t\tresolve({ deny: 'grant-timeout' })\n\t\t\t}, GRANT_TIMEOUT_MS)\n\t\t\tthis.grantWaiters.set(key, {\n\t\t\t\tresolve: (g) => {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\tthis.grantWaiters.delete(key)\n\t\t\t\t\tresolve(g)\n\t\t\t\t},\n\t\t\t})\n\t\t\tvoid this.sendDirect(hubId, type, payload).then((sent) => {\n\t\t\t\tif (!sent) {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\tthis.grantWaiters.delete(key)\n\t\t\t\t\tresolve({ deny: 'hub-unreachable' })\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n\n\tprivate async sendDirect(\n\t\tpeerId: string,\n\t\ttype: VoleNetMessage['type'],\n\t\tpayload: Record<string, unknown>,\n\t): Promise<boolean> {\n\t\tconst msg = createMessage(\n\t\t\ttype,\n\t\t\tthis.me,\n\t\t\tpeerId,\n\t\t\tpayload,\n\t\t\tthis.opts.keyPair.privateKey,\n\t\t\tthis.opts.keyPair.pqPrivateKey,\n\t\t)\n\t\treturn this.opts.transport.sendToPeer(peerId, msg)\n\t}\n\n\tprivate onBlobGrantOrDeny(msg: VoleNetMessage) {\n\t\tconst p = msg.payload as {\n\t\t\ttransferId?: string\n\t\t\tblobId?: string\n\t\t\ttoken?: string\n\t\t\turl?: string\n\t\t\treason?: string\n\t\t}\n\t\tconst key = p?.transferId ?? p?.blobId\n\t\tconst waiter = key ? this.grantWaiters.get(String(key)) : undefined\n\t\tif (!waiter) return\n\t\tif (msg.type === 'relay:blob:grant' && p.blobId && p.token && p.url) {\n\t\t\twaiter.resolve({ blobId: p.blobId, token: p.token, url: p.url })\n\t\t} else {\n\t\t\twaiter.resolve({ deny: p.reason ?? 'denied' })\n\t\t}\n\t}\n\n\t// ── Hub: relay-blob policy ────────────────────────────────────────────────\n\n\tprivate hubBlobWindows = new Map<string, number[]>()\n\n\tprivate async hubOnBlobCreate(msg: VoleNetMessage) {\n\t\tconst p = msg.payload as { transferId?: string; to?: string; cipherSize?: number }\n\t\tconst deny = (reason: string) =>\n\t\t\tthis.sendDirect(msg.from, 'relay:blob:deny', {\n\t\t\t\ttransferId: p?.transferId,\n\t\t\t\treason,\n\t\t\t})\n\t\tif (!this.blobStore || this.opts.config.enabled === false) {\n\t\t\tawait deny('disabled')\n\t\t\treturn\n\t\t}\n\t\tif (!p?.transferId || !p.to || typeof p.cipherSize !== 'number' || p.cipherSize < 0) return\n\t\t// The receiver must be a currently-connected member (same rule as relay forwarding).\n\t\tconst bound = this.opts.transport.getPeers().find((x) => x.peerId === p.to && x.connected)\n\t\tif (!bound) {\n\t\t\tawait deny('peer-unknown')\n\t\t\treturn\n\t\t}\n\t\t// Reuse the relay pair-rate shape: 6 creates/min/pair.\n\t\tconst windowKey = `${msg.from}→${p.to}`\n\t\tconst now = Date.now()\n\t\tconst win = (this.hubBlobWindows.get(windowKey) ?? []).filter((ts) => now - ts < 60_000)\n\t\tif (win.length >= 6) {\n\t\t\tawait deny('rate-limited')\n\t\t\treturn\n\t\t}\n\t\twin.push(now)\n\t\tthis.hubBlobWindows.set(windowKey, win)\n\n\t\tconst created = this.blobStore.create(msg.from, p.to, p.transferId, p.cipherSize)\n\t\tif ('error' in created) {\n\t\t\t// Say what the hub's limit is and where the file *can* go. A bare \"too-large\" from a\n\t\t\t// relay reads as \"VoleDrop cannot do this\", when in fact a direct route carries any\n\t\t\t// size — the ceiling exists because a relayed file lands on somebody else's disk.\n\t\t\tawait deny(\n\t\t\t\tcreated.error === 'too-large'\n\t\t\t\t\t? `too-large: this hub relays at most ${fmtBytes(this.opts.config.relayMaxBytes ?? DEFAULT_RELAY_MAX_BYTES)} per file (its net.files.relayMaxBytes). Pair the two nodes so the transfer goes direct — a direct route has no hub limit.`\n\t\t\t\t\t: created.error === 'quota'\n\t\t\t\t\t\t? `quota: this hub's storage budget for this pair is spent (its net.files.relayQuotaBytes). Pair the two nodes to send direct, or retry once earlier transfers expire.`\n\t\t\t\t\t\t: created.error,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tconst token = this.mintToken(created.blobId, msg.from, 'upload')\n\t\tawait this.sendDirect(msg.from, 'relay:blob:grant', {\n\t\t\ttransferId: p.transferId,\n\t\t\tblobId: created.blobId,\n\t\t\ttoken,\n\t\t\turl: `${this.opts.advertisedEndpoint}/volenet/blob/${created.blobId}`,\n\t\t\top: 'upload',\n\t\t})\n\t}\n\n\tprivate async hubOnBlobFetch(msg: VoleNetMessage) {\n\t\tconst p = msg.payload as { transferId?: string; blobId?: string }\n\t\tconst meta = p?.blobId ? this.blobStore?.get(p.blobId) : undefined\n\t\tif (!meta || !meta.complete || meta.to !== msg.from) {\n\t\t\tawait this.sendDirect(msg.from, 'relay:blob:deny', {\n\t\t\t\tblobId: p?.blobId,\n\t\t\t\ttransferId: p?.transferId,\n\t\t\t\treason: 'peer-unknown',\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tconst token = this.mintToken(meta.blobId, msg.from, 'download')\n\t\tawait this.sendDirect(msg.from, 'relay:blob:grant', {\n\t\t\ttransferId: p?.transferId,\n\t\t\tblobId: meta.blobId,\n\t\t\ttoken,\n\t\t\turl: `${this.opts.advertisedEndpoint}/volenet/blob/${meta.blobId}`,\n\t\t\top: 'download',\n\t\t})\n\t}\n\n\tprivate hubOnBlobDone(msg: VoleNetMessage) {\n\t\tconst p = msg.payload as { blobId?: string }\n\t\tconst meta = p?.blobId ? this.blobStore?.get(p.blobId) : undefined\n\t\tif (!meta || meta.to !== msg.from) return\n\t\tthis.revokeTokensFor(meta.blobId)\n\t\tthis.blobStore?.delete(meta.blobId)\n\t}\n\n\t// ── The blob HTTP data plane ──────────────────────────────────────────────\n\n\t/** Returns true when the request was handled (matched + responded). */\n\thandleBlobRequest(\n\t\treq: http.IncomingMessage,\n\t\tres: http.ServerResponse,\n\t\tpathname: string,\n\t): boolean {\n\t\tconst id = pathname.split('/')[3]\n\t\tif (!id || !/^[0-9a-f-]{16,64}$/i.test(id)) return false\n\t\tconst entry = this.lookupToken(req.headers['x-vole-blob-token'] as string | undefined)\n\t\tif (!entry || entry.id !== id) {\n\t\t\tres.writeHead(404)\n\t\t\tres.end()\n\t\t\treturn true\n\t\t}\n\t\tif (this.activeStreams >= (this.opts.config.maxConcurrent ?? 4)) {\n\t\t\tres.writeHead(503)\n\t\t\tres.end()\n\t\t\treturn true\n\t\t}\n\t\tconst done = () => {\n\t\t\tthis.activeStreams = Math.max(0, this.activeStreams - 1)\n\t\t}\n\t\tthis.activeStreams += 1\n\n\t\tconst fail = (status: number) => {\n\t\t\tdone()\n\t\t\tif (!res.headersSent) res.writeHead(status)\n\t\t\tres.end()\n\t\t\treturn true\n\t\t}\n\n\t\ttry {\n\t\t\tif (entry.op === 'pull' && req.method === 'GET') {\n\t\t\t\tvoid this.serveSenderPull(req, res, entry).finally(done)\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif (entry.op === 'push' && req.method === 'POST') {\n\t\t\t\tvoid this.receivePushUpload(req, res, entry).finally(done)\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif (entry.op === 'upload' && req.method === 'POST') {\n\t\t\t\tvoid this.hubReceiveUpload(req, res, entry).finally(done)\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif (entry.op === 'download' && req.method === 'GET') {\n\t\t\t\tvoid this.hubServeDownload(req, res, entry).finally(done)\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn fail(404)\n\t\t} catch {\n\t\t\treturn fail(500)\n\t\t}\n\t}\n\n\t/** Sender serving a receiver's pull: encrypt-on-the-fly from a chunk boundary. */\n\tprivate async serveSenderPull(\n\t\treq: http.IncomingMessage,\n\t\tres: http.ServerResponse,\n\t\tentry: TokenEntry,\n\t) {\n\t\tconst t = this.transfers.get(entry.id)\n\t\tif (!t || t.dir !== 'send' || !t.key || !t.srcPath) {\n\t\t\tres.writeHead(404)\n\t\t\tres.end()\n\t\t\treturn\n\t\t}\n\t\tconst from = Math.max(\n\t\t\t0,\n\t\t\tNumber(new URL(req.url ?? '/', 'http://x').searchParams.get('from') ?? 0) || 0,\n\t\t)\n\t\tif (from > t.totalChunks) {\n\t\t\tres.writeHead(404)\n\t\t\tres.end()\n\t\t\treturn\n\t\t}\n\t\tthis.touch(t, 'transferring')\n\t\tconst remainingPlain = Math.max(0, t.size - from * t.chunkBytes)\n\t\tres.writeHead(200, {\n\t\t\t'content-type': 'application/octet-stream',\n\t\t\t'content-length': String(cipherSizeFor(remainingPlain, t.chunkBytes)),\n\t\t})\n\t\tconst src = fs.createReadStream(t.srcPath, { start: from * t.chunkBytes })\n\t\tconst enc = new ChunkEncryptStream(t.key, t.transferId, t.chunkBytes, from)\n\t\tt.bytesDone = from * t.chunkBytes\n\t\tsrc.on('data', (chunk) => {\n\t\t\tt.bytesDone += (chunk as Buffer).length\n\t\t\tthis.emitProgress(t)\n\t\t})\n\t\tres.on('close', () => src.destroy())\n\t\ttry {\n\t\t\tawait pipeline(src, enc, res)\n\t\t} catch {\n\t\t\t/* client went away — its retry resumes via ?from= */\n\t\t}\n\t}\n\n\t/** Receiver accepting a sender's push POST. */\n\tprivate async receivePushUpload(\n\t\treq: http.IncomingMessage,\n\t\tres: http.ServerResponse,\n\t\tentry: TokenEntry,\n\t) {\n\t\tconst t = this.transfers.get(entry.id)\n\t\tif (!t || t.dir !== 'recv' || !t.key) {\n\t\t\tres.writeHead(404)\n\t\t\tres.end()\n\t\t\treturn\n\t\t}\n\t\tawait fsp.mkdir(path.join(this.inboxDir(), '.partial'), { recursive: true })\n\t\tt.partialPath = path.join(this.inboxDir(), '.partial', `${t.transferId}.part`)\n\t\tthis.touch(t, 'transferring')\n\t\ttry {\n\t\t\tawait this.consumeCipherStream(t, req, 0, cipherSizeFor(t.size, t.chunkBytes) + 4096)\n\t\t} catch (err) {\n\t\t\tawait fsp.rm(t.partialPath, { force: true }).catch(() => {})\n\t\t\tif (!res.headersSent) res.writeHead(400)\n\t\t\tres.end()\n\t\t\tlogger.warn(\n\t\t\t\t`[volenet-files] push upload failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tres.writeHead(201)\n\t\tres.end()\n\t\tawait this.finalizeReceive(t)\n\t}\n\n\t/** Hub storing a sender's relay upload — opaque frames, byte-budgeted. */\n\tprivate async hubReceiveUpload(\n\t\treq: http.IncomingMessage,\n\t\tres: http.ServerResponse,\n\t\tentry: TokenEntry,\n\t) {\n\t\tconst meta = this.blobStore?.get(entry.id)\n\t\tconst out = this.blobStore?.openWrite(entry.id)\n\t\tif (!meta || !out || meta.from !== entry.peerId) {\n\t\t\tres.writeHead(404)\n\t\t\tres.end()\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\tawait pipeline(req, byteBudget(meta.cipherSize + 4096), out)\n\t\t\tif (!this.blobStore?.complete(entry.id)) throw new Error('finalize failed')\n\t\t} catch (err) {\n\t\t\tif (!res.headersSent) res.writeHead(400)\n\t\t\tres.end()\n\t\t\tlogger.warn(\n\t\t\t\t`[volenet-files] hub upload failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tthis.revokeTokensFor(entry.id) // upload token is spent; download gets its own\n\t\tres.writeHead(201)\n\t\tres.end()\n\t}\n\n\t/** Hub serving the receiver's download from the stored blob. */\n\tprivate async hubServeDownload(\n\t\treq: http.IncomingMessage,\n\t\tres: http.ServerResponse,\n\t\tentry: TokenEntry,\n\t) {\n\t\tconst meta = this.blobStore?.get(entry.id)\n\t\tconst storedPath = this.blobStore?.storedPath(entry.id)\n\t\tif (!meta || !storedPath || meta.to !== entry.peerId) {\n\t\t\tres.writeHead(404)\n\t\t\tres.end()\n\t\t\treturn\n\t\t}\n\t\tconst from = Math.max(\n\t\t\t0,\n\t\t\tNumber(new URL(req.url ?? '/', 'http://x').searchParams.get('from') ?? 0) || 0,\n\t\t)\n\t\tlet offset = 0\n\t\ttry {\n\t\t\toffset = await scanFrameOffset(storedPath, from)\n\t\t} catch {\n\t\t\tres.writeHead(404)\n\t\t\tres.end()\n\t\t\treturn\n\t\t}\n\t\tconst stream = this.blobStore?.openRead(entry.id, offset)\n\t\tif (!stream) {\n\t\t\tres.writeHead(404)\n\t\t\tres.end()\n\t\t\treturn\n\t\t}\n\t\tres.writeHead(200, {\n\t\t\t'content-type': 'application/octet-stream',\n\t\t\t'content-length': String(meta.cipherSize - offset),\n\t\t})\n\t\tres.on('close', () => stream.destroy())\n\t\ttry {\n\t\t\tawait pipeline(stream, res)\n\t\t} catch {\n\t\t\t/* receiver retries with ?from= */\n\t\t}\n\t}\n\n\t// ── Housekeeping ──────────────────────────────────────────────────────────\n\n\tprivate sweepTransfers() {\n\t\tconst now = Date.now()\n\t\tfor (const t of this.transfers.values()) {\n\t\t\tconst terminal = ['done', 'rejected', 'failed', 'cancelled', 'expired'].includes(t.state)\n\t\t\tif (!terminal && now - t.createdAt > this.offerTtlMs) {\n\t\t\t\tt.active?.destroy()\n\t\t\t\tthis.touch(t, 'expired')\n\t\t\t\tthis.revokeTokensFor(t.transferId)\n\t\t\t\tif (t.partialPath) void fsp.rm(t.partialPath, { force: true }).catch(() => {})\n\t\t\t\tvoid this.sendCtl(t, 'file:error', {\n\t\t\t\t\ttransferId: t.transferId,\n\t\t\t\t\tcode: 'expired',\n\t\t\t\t}).catch(() => {})\n\t\t\t}\n\t\t\t// Drop terminal records after 24h so the list can't grow forever.\n\t\t\tif (terminal && now - t.updatedAt > 24 * 60 * 60_000) this.transfers.delete(t.transferId)\n\t\t}\n\t}\n\n\t/** Delete a sent file's source ONLY when it lives in our upload spool. */\n\tprivate async cleanupOutboxSource(t: Transfer) {\n\t\tif (!t.srcPath) return\n\t\tconst outbox = this.outboxDir()\n\t\tif (t.srcPath.startsWith(outbox + path.sep)) {\n\t\t\tawait fsp.rm(t.srcPath, { force: true }).catch(() => {})\n\t\t}\n\t}\n\n\t/** TTL sweep of the dashboard-upload spool (orphaned uploads). */\n\tprivate async sweepOutbox() {\n\t\tconst outbox = this.outboxDir()\n\t\ttry {\n\t\t\tconst entries = await fsp.readdir(outbox)\n\t\t\tconst now = Date.now()\n\t\t\tfor (const name of entries) {\n\t\t\t\tconst p = path.join(outbox, name)\n\t\t\t\tconst stat = await fsp.stat(p).catch(() => null)\n\t\t\t\tif (stat?.isFile() && now - stat.mtimeMs > 24 * 60 * 60_000) {\n\t\t\t\t\tawait fsp.rm(p, { force: true }).catch(() => {})\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t/* no outbox yet */\n\t\t}\n\t}\n}\n","/**\n * RelayBlobStore — hub-side ciphertext storage for VoleDrop relay transfers.\n *\n * When two NAT'd relay members exchange a file, the hub stores the encrypted frames\n * between the sender's upload and the receiver's download. The hub NEVER holds keys:\n * blobs are opaque chacha20-poly1305 frames (file-crypto.ts), and the only metadata\n * kept is routing (from, to, transferId, size, age) — the same visibility the hub\n * already has for relayed chat envelopes.\n *\n * Layout: `<dir>/<blobId>` (completed), `<dir>/<blobId>.part` (uploading),\n * `<dir>/<blobId>.meta.json` (sidecar). create() is idempotent per (from, transferId):\n * a retry after a died upload truncates the partial and reuses the same blobId, so a\n * hub restart mid-transfer converges via the sender's ordinary retry path.\n */\nimport * as crypto from 'node:crypto'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { createLogger } from './logger.js'\n\nconst logger = createLogger('volenet-files')\n\nexport interface BlobMeta {\n\tblobId: string\n\tfrom: string\n\tto: string\n\ttransferId: string\n\tcipherSize: number\n\tcreatedAt: number\n\tcomplete: boolean\n}\n\nexport interface BlobStoreOptions {\n\tdir: string\n\t/** Max stored ciphertext bytes per (from,to) pair. */\n\tquotaBytes: number\n\t/** Stored blob lifetime before the sweep deletes it. */\n\tttlMs: number\n\t/** Max single blob size (ciphertext). */\n\tmaxBlobBytes: number\n}\n\nexport class RelayBlobStore {\n\tprivate metas = new Map<string, BlobMeta>()\n\n\tconstructor(private opts: BlobStoreOptions) {\n\t\tfs.mkdirSync(opts.dir, { recursive: true })\n\t\tthis.loadExisting()\n\t}\n\n\t/** Rehydrate metadata sidecars after a restart (sweep prunes stale ones). */\n\tprivate loadExisting(): void {\n\t\tfor (const name of fs.readdirSync(this.opts.dir)) {\n\t\t\tif (!name.endsWith('.meta.json')) continue\n\t\t\ttry {\n\t\t\t\tconst meta = JSON.parse(fs.readFileSync(path.join(this.opts.dir, name), 'utf8')) as BlobMeta\n\t\t\t\tif (meta?.blobId) this.metas.set(meta.blobId, meta)\n\t\t\t} catch {\n\t\t\t\t/* corrupt sidecar — the sweep removes orphans */\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate blobPath(blobId: string, part = false): string {\n\t\treturn path.join(this.opts.dir, part ? `${blobId}.part` : blobId)\n\t}\n\n\tprivate writeMeta(meta: BlobMeta): void {\n\t\tfs.writeFileSync(path.join(this.opts.dir, `${meta.blobId}.meta.json`), JSON.stringify(meta))\n\t}\n\n\t/** Stored bytes attributed to a sender→receiver pair (quota accounting). */\n\tpairUsage(from: string, to: string): number {\n\t\tlet total = 0\n\t\tfor (const m of this.metas.values()) {\n\t\t\tif (m.from === from && m.to === to) total += m.cipherSize\n\t\t}\n\t\treturn total\n\t}\n\n\tcreate(\n\t\tfrom: string,\n\t\tto: string,\n\t\ttransferId: string,\n\t\tcipherSize: number,\n\t): { blobId: string } | { error: 'quota' | 'too-large' } {\n\t\tif (cipherSize > this.opts.maxBlobBytes) return { error: 'too-large' }\n\t\t// Idempotent per (from, transferId): a retried create reuses the allocation.\n\t\tfor (const m of this.metas.values()) {\n\t\t\tif (m.from === from && m.transferId === transferId) {\n\t\t\t\ttry {\n\t\t\t\t\tfs.rmSync(this.blobPath(m.blobId, true), { force: true })\n\t\t\t\t\tfs.rmSync(this.blobPath(m.blobId), { force: true })\n\t\t\t\t} catch {\n\t\t\t\t\t/* best effort */\n\t\t\t\t}\n\t\t\t\tm.complete = false\n\t\t\t\tm.cipherSize = cipherSize\n\t\t\t\tm.createdAt = Date.now()\n\t\t\t\tthis.writeMeta(m)\n\t\t\t\treturn { blobId: m.blobId }\n\t\t\t}\n\t\t}\n\t\tif (this.pairUsage(from, to) + cipherSize > this.opts.quotaBytes) return { error: 'quota' }\n\t\tconst blobId = crypto.randomBytes(16).toString('hex')\n\t\tconst meta: BlobMeta = {\n\t\t\tblobId,\n\t\t\tfrom,\n\t\t\tto,\n\t\t\ttransferId,\n\t\t\tcipherSize,\n\t\t\tcreatedAt: Date.now(),\n\t\t\tcomplete: false,\n\t\t}\n\t\tthis.metas.set(blobId, meta)\n\t\tthis.writeMeta(meta)\n\t\treturn { blobId }\n\t}\n\n\tget(blobId: string): BlobMeta | undefined {\n\t\treturn this.metas.get(blobId)\n\t}\n\n\topenWrite(blobId: string): fs.WriteStream | null {\n\t\tif (!this.metas.has(blobId)) return null\n\t\treturn fs.createWriteStream(this.blobPath(blobId, true))\n\t}\n\n\t/** Mark an upload finished: rename .part into place. */\n\tcomplete(blobId: string): boolean {\n\t\tconst meta = this.metas.get(blobId)\n\t\tif (!meta) return false\n\t\ttry {\n\t\t\tfs.renameSync(this.blobPath(blobId, true), this.blobPath(blobId))\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t\tmeta.complete = true\n\t\tthis.writeMeta(meta)\n\t\treturn true\n\t}\n\n\topenRead(blobId: string, byteOffset = 0): fs.ReadStream | null {\n\t\tconst meta = this.metas.get(blobId)\n\t\tif (!meta?.complete) return null\n\t\ttry {\n\t\t\treturn fs.createReadStream(this.blobPath(blobId), { start: byteOffset })\n\t\t} catch {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tstoredPath(blobId: string): string | null {\n\t\tconst meta = this.metas.get(blobId)\n\t\treturn meta?.complete ? this.blobPath(blobId) : null\n\t}\n\n\tdelete(blobId: string): void {\n\t\tthis.metas.delete(blobId)\n\t\tfor (const p of [\n\t\t\tthis.blobPath(blobId),\n\t\t\tthis.blobPath(blobId, true),\n\t\t\tpath.join(this.opts.dir, `${blobId}.meta.json`),\n\t\t]) {\n\t\t\ttry {\n\t\t\t\tfs.rmSync(p, { force: true })\n\t\t\t} catch {\n\t\t\t\t/* best effort */\n\t\t\t}\n\t\t}\n\t}\n\n\t/** TTL cleanup + orphan `.part`/sidecar removal. Called on start and periodically. */\n\tsweep(now = Date.now()): number {\n\t\tlet removed = 0\n\t\tfor (const meta of [...this.metas.values()]) {\n\t\t\tif (now - meta.createdAt > this.opts.ttlMs) {\n\t\t\t\tthis.delete(meta.blobId)\n\t\t\t\tremoved++\n\t\t\t}\n\t\t}\n\t\t// Orphans on disk with no live meta (crashed uploads, corrupt sidecars).\n\t\tfor (const name of fs.readdirSync(this.opts.dir)) {\n\t\t\tconst id = name.replace(/\\.(part|meta\\.json)$/, '')\n\t\t\tif (!this.metas.has(id)) {\n\t\t\t\ttry {\n\t\t\t\t\tfs.rmSync(path.join(this.opts.dir, name), { force: true })\n\t\t\t\t\tremoved++\n\t\t\t\t} catch {\n\t\t\t\t\t/* best effort */\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (removed > 0) logger.info(`[volenet-files] blob sweep removed ${removed} item(s)`)\n\t\treturn removed\n\t}\n}\n","/**\n * VoleDrop data-plane crypto — chunked authenticated encryption for file transfer.\n *\n * The transfer KEY travels in the control plane: 32 random bytes, sealed once per\n * transfer with the PQ-hybrid seal (seal.ts) under aad `${from}|${to}|file:${transferId}`.\n * This module owns the BULK bytes: the file is split into fixed-size plaintext chunks,\n * each encrypted with chacha20-poly1305 and framed as `[4B BE length][ciphertext ‖ 16B tag]`.\n *\n * Nonce/AAD safety argument: the key is unique per transfer and never reused, so the\n * deterministic counter nonce (4 zero bytes ‖ BE64 chunk index) cannot collide. The AAD\n * `${transferId}|${chunkIndex}` binds every frame to its transfer AND position — frames\n * cannot be reordered, dropped, duplicated, or transplanted across transfers without\n * failing the tag. Determinism also means re-encrypting from a chunk boundary produces\n * byte-identical frames, which is what makes `?from=<chunk>` resume sound.\n */\nimport * as crypto from 'node:crypto'\nimport { Transform } from 'node:stream'\nimport { aead, aeadOpen } from './node-crypto.js'\n\nexport const CHUNK_BYTES_DEFAULT = 4 * 1024 * 1024\nexport const TAG_BYTES = 16\nexport const LEN_BYTES = 4\n/** Per-chunk wire overhead: length prefix + poly1305 tag. */\nexport const FRAME_OVERHEAD = LEN_BYTES + TAG_BYTES\n\nexport function chunkNonce(index: number): Buffer {\n\tconst nonce = Buffer.alloc(12)\n\tnonce.writeBigUInt64BE(BigInt(index), 4)\n\treturn nonce\n}\n\nexport function chunkAad(transferId: string, index: number): Buffer {\n\treturn Buffer.from(`${transferId}|${index}`, 'utf8')\n}\n\n/** Total ciphertext size on the wire for a plaintext of `plainSize` bytes. */\nexport function cipherSizeFor(plainSize: number, chunkBytes = CHUNK_BYTES_DEFAULT): number {\n\tif (plainSize <= 0) return 0\n\tconst chunks = Math.ceil(plainSize / chunkBytes)\n\treturn plainSize + chunks * FRAME_OVERHEAD\n}\n\nexport function totalChunksFor(plainSize: number, chunkBytes = CHUNK_BYTES_DEFAULT): number {\n\tif (plainSize <= 0) return 0\n\treturn Math.ceil(plainSize / chunkBytes)\n}\n\n/** Encrypt one chunk → a complete wire frame `[4B BE len][ct ‖ tag]`. */\nexport function encryptChunk(\n\tkey: Buffer,\n\ttransferId: string,\n\tindex: number,\n\tplain: Buffer,\n): Buffer {\n\tconst cipher = aead(\n\t\tcrypto.createCipheriv('chacha20-poly1305', key, chunkNonce(index), {\n\t\t\tauthTagLength: TAG_BYTES,\n\t\t}),\n\t)\n\tcipher.setAAD(chunkAad(transferId, index))\n\tconst ct = Buffer.concat([cipher.update(plain), cipher.final(), cipher.getAuthTag()])\n\tconst frame = Buffer.alloc(LEN_BYTES + ct.length)\n\tframe.writeUInt32BE(ct.length, 0)\n\tct.copy(frame, LEN_BYTES)\n\treturn frame\n}\n\n/** Decrypt one frame BODY (ct ‖ tag, without the length prefix). Returns null on any failure. */\nexport function decryptChunk(\n\tkey: Buffer,\n\ttransferId: string,\n\tindex: number,\n\tbody: Buffer,\n): Buffer | null {\n\tif (body.length < TAG_BYTES) return null\n\ttry {\n\t\tconst decipher = aeadOpen(\n\t\t\tcrypto.createDecipheriv('chacha20-poly1305', key, chunkNonce(index), {\n\t\t\t\tauthTagLength: TAG_BYTES,\n\t\t\t}),\n\t\t)\n\t\tdecipher.setAAD(chunkAad(transferId, index))\n\t\tdecipher.setAuthTag(body.subarray(body.length - TAG_BYTES))\n\t\treturn Buffer.concat([\n\t\t\tdecipher.update(body.subarray(0, body.length - TAG_BYTES)),\n\t\t\tdecipher.final(),\n\t\t])\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Plaintext in → wire frames out. `startChunk` sets the first chunk index (resume:\n * the caller seeks the source to `startChunk * chunkBytes` and encryption restarts\n * deterministically from there).\n */\nexport class ChunkEncryptStream extends Transform {\n\tprivate buf: Buffer[] = []\n\tprivate buffered = 0\n\tprivate index: number\n\tchunksEmitted = 0\n\n\tconstructor(\n\t\tprivate key: Buffer,\n\t\tprivate transferId: string,\n\t\tprivate chunkBytes = CHUNK_BYTES_DEFAULT,\n\t\tstartChunk = 0,\n\t) {\n\t\tsuper()\n\t\tthis.index = startChunk\n\t}\n\n\tprivate emitChunk(plain: Buffer) {\n\t\tthis.push(encryptChunk(this.key, this.transferId, this.index, plain))\n\t\tthis.index += 1\n\t\tthis.chunksEmitted += 1\n\t}\n\n\toverride _transform(data: Buffer, _enc: string, cb: (err?: Error | null) => void) {\n\t\tthis.buf.push(data)\n\t\tthis.buffered += data.length\n\t\twhile (this.buffered >= this.chunkBytes) {\n\t\t\tconst whole = Buffer.concat(this.buf)\n\t\t\tthis.emitChunk(whole.subarray(0, this.chunkBytes))\n\t\t\tconst rest = whole.subarray(this.chunkBytes)\n\t\t\tthis.buf = rest.length ? [rest] : []\n\t\t\tthis.buffered = rest.length\n\t\t}\n\t\tcb()\n\t}\n\n\toverride _flush(cb: (err?: Error | null) => void) {\n\t\tif (this.buffered > 0) this.emitChunk(Buffer.concat(this.buf))\n\t\tthis.buf = []\n\t\tthis.buffered = 0\n\t\tcb()\n\t}\n}\n\n/**\n * Wire frames in → plaintext out. Strict framing: rejects frames larger than\n * `chunkBytes + TAG_BYTES`, fails on any tag mismatch, and errors on trailing\n * garbage at end of stream. `chunksDone` counts fully decrypted chunks — the\n * receiver's resume cursor.\n */\nexport class ChunkDecryptStream extends Transform {\n\tprivate buf: Buffer[] = []\n\tprivate buffered = 0\n\tprivate index: number\n\tchunksDone = 0\n\n\tconstructor(\n\t\tprivate key: Buffer,\n\t\tprivate transferId: string,\n\t\tprivate chunkBytes = CHUNK_BYTES_DEFAULT,\n\t\tstartChunk = 0,\n\t) {\n\t\tsuper()\n\t\tthis.index = startChunk\n\t}\n\n\toverride _transform(data: Buffer, _enc: string, cb: (err?: Error | null) => void) {\n\t\tthis.buf.push(data)\n\t\tthis.buffered += data.length\n\t\twhile (true) {\n\t\t\tif (this.buffered < LEN_BYTES) break\n\t\t\tconst whole = this.buf.length === 1 ? this.buf[0] : Buffer.concat(this.buf)\n\t\t\tthis.buf = [whole]\n\t\t\tconst bodyLen = whole.readUInt32BE(0)\n\t\t\tif (bodyLen < TAG_BYTES || bodyLen > this.chunkBytes + TAG_BYTES) {\n\t\t\t\tcb(new Error(`file-crypto: invalid frame length ${bodyLen}`))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.buffered < LEN_BYTES + bodyLen) break\n\t\t\tconst body = whole.subarray(LEN_BYTES, LEN_BYTES + bodyLen)\n\t\t\tconst plain = decryptChunk(this.key, this.transferId, this.index, body)\n\t\t\tif (plain === null) {\n\t\t\t\tcb(new Error(`file-crypto: chunk ${this.index} failed authentication`))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.push(plain)\n\t\t\tthis.index += 1\n\t\t\tthis.chunksDone += 1\n\t\t\tconst rest = whole.subarray(LEN_BYTES + bodyLen)\n\t\t\tthis.buf = rest.length ? [rest] : []\n\t\t\tthis.buffered = rest.length\n\t\t}\n\t\tcb()\n\t}\n\n\toverride _flush(cb: (err?: Error | null) => void) {\n\t\tif (this.buffered > 0) {\n\t\t\tcb(new Error(`file-crypto: ${this.buffered} trailing bytes after final frame`))\n\t\t\treturn\n\t\t}\n\t\tcb()\n\t}\n}\n\n/**\n * Byte offset of chunk `fromChunk` inside a stored ciphertext file (hub resume) —\n * walks the length prefixes; no index file needed.\n */\nexport async function scanFrameOffset(\n\tfilePath: string,\n\tfromChunk: number,\n\tchunkBytes = CHUNK_BYTES_DEFAULT,\n): Promise<number> {\n\tif (fromChunk <= 0) return 0\n\tconst fs = await import('node:fs/promises')\n\tconst fd = await fs.open(filePath, 'r')\n\ttry {\n\t\tconst len = Buffer.alloc(LEN_BYTES)\n\t\tlet offset = 0\n\t\tfor (let i = 0; i < fromChunk; i++) {\n\t\t\tconst { bytesRead } = await fd.read(len, 0, LEN_BYTES, offset)\n\t\t\tif (bytesRead < LEN_BYTES) throw new Error(`file-crypto: blob ends before chunk ${fromChunk}`)\n\t\t\tconst bodyLen = len.readUInt32BE(0)\n\t\t\tif (bodyLen < TAG_BYTES || bodyLen > chunkBytes + TAG_BYTES) {\n\t\t\t\tthrow new Error(`file-crypto: invalid stored frame length ${bodyLen}`)\n\t\t\t}\n\t\t\toffset += LEN_BYTES + bodyLen\n\t\t}\n\t\treturn offset\n\t} finally {\n\t\tawait fd.close()\n\t}\n}\n","/**\n * VoleNet Leader Election — ensures only one instance runs heartbeat/schedules.\n *\n * Algorithm: lowest instance ID wins (deterministic, no voting).\n * Leader sends periodic heartbeat. If 3 heartbeats missed (30s), next-lowest takes over.\n */\n\nimport type { KeyObject } from 'node:crypto'\nimport type { VoleNetDiscovery } from './discovery.js'\nimport { createLogger } from './logger.js'\nimport { type VoleNetMessage, createMessage } from './protocol.js'\nimport type { VoleNetTransport } from './transport.js'\n\nconst logger = createLogger('volenet-leader')\n\nconst LEADER_HEARTBEAT_INTERVAL_MS = 10_000 // leader pings every 10s\nconst MAX_MISSED_HEARTBEATS = 3\n\nexport interface LeaderState {\n\tleaderId: string | null\n\tleaderName: string | null\n\tisLeader: boolean\n\tlastHeartbeat: number\n\tmissedHeartbeats: number\n}\n\n/**\n * Manages leader election for heartbeat/schedule ownership.\n */\nexport class VoleNetLeader {\n\tprivate transport: VoleNetTransport\n\tprivate discovery: VoleNetDiscovery\n\tprivate instanceId: string\n\tprivate instanceName: string\n\tprivate privateKey: KeyObject\n\tprivate pqPrivateKey?: KeyObject\n\n\tprivate leaderId: string | null = null\n\tprivate leaderName: string | null = null\n\tprivate lastLeaderHeartbeat = 0\n\tprivate missedHeartbeats = 0\n\tprivate heartbeatTimer: ReturnType<typeof setInterval> | undefined\n\tprivate monitorTimer: ReturnType<typeof setInterval> | undefined\n\n\t// Callback when this instance becomes/loses leader\n\tprivate onBecomeLeader?: () => void\n\tprivate onLoseLeader?: () => void\n\tprivate forcedLeader?: string // if set, this instance name is always leader\n\n\tconstructor(\n\t\ttransport: VoleNetTransport,\n\t\tdiscovery: VoleNetDiscovery,\n\t\tinstanceId: string,\n\t\tinstanceName: string,\n\t\tprivateKey: KeyObject,\n\t\tpqPrivateKey: KeyObject | undefined,\n\t\tforcedLeader?: string,\n\t) {\n\t\tthis.transport = transport\n\t\tthis.discovery = discovery\n\t\tthis.instanceId = instanceId\n\t\tthis.instanceName = instanceName\n\t\tthis.privateKey = privateKey\n\t\tthis.pqPrivateKey = pqPrivateKey\n\t\tthis.forcedLeader = forcedLeader === 'auto' ? undefined : forcedLeader\n\n\t\tthis.transport.onMessage((message) => {\n\t\t\tthis.handleMessage(message)\n\t\t})\n\t}\n\n\t/**\n\t * Start leader election.\n\t */\n\tstart(onBecomeLeader?: () => void, onLoseLeader?: () => void): void {\n\t\tthis.onBecomeLeader = onBecomeLeader\n\t\tthis.onLoseLeader = onLoseLeader\n\n\t\t// Determine initial leader (lowest ID among self + known peers)\n\t\tthis.electLeader()\n\n\t\t// Start monitoring\n\t\tthis.monitorTimer = setInterval(() => this.monitor(), LEADER_HEARTBEAT_INTERVAL_MS)\n\n\t\tlogger.info(\n\t\t\t`Leader election started — current leader: ${this.leaderName ?? 'self'} (${(this.leaderId ?? this.instanceId).substring(0, 8)})`,\n\t\t)\n\t}\n\n\t/**\n\t * Stop leader election.\n\t */\n\tstop(): void {\n\t\tif (this.heartbeatTimer) {\n\t\t\tclearInterval(this.heartbeatTimer)\n\t\t\tthis.heartbeatTimer = undefined\n\t\t}\n\t\tif (this.monitorTimer) {\n\t\t\tclearInterval(this.monitorTimer)\n\t\t\tthis.monitorTimer = undefined\n\t\t}\n\t\tthis.leaderId = null\n\t\tthis.leaderName = null\n\t}\n\n\t/**\n\t * Get current leader state.\n\t */\n\tgetState(): LeaderState {\n\t\treturn {\n\t\t\tleaderId: this.leaderId,\n\t\t\tleaderName: this.leaderName,\n\t\t\tisLeader: this.isLeader(),\n\t\t\tlastHeartbeat: this.lastLeaderHeartbeat,\n\t\t\tmissedHeartbeats: this.missedHeartbeats,\n\t\t}\n\t}\n\n\t/**\n\t * Check if this instance is the leader.\n\t */\n\tisLeader(): boolean {\n\t\treturn this.leaderId === this.instanceId\n\t}\n\n\t/** Trigger re-election (called when peers change) */\n\treelect(): void {\n\t\tthis.electLeader()\n\t}\n\n\t/**\n\t * Elect leader.\n\t * If forcedLeader is set, that instance name is always leader.\n\t * Otherwise, lowest instance ID wins.\n\t */\n\tprivate electLeader(): void {\n\t\tlet newLeaderId: string\n\n\t\tif (this.forcedLeader) {\n\t\t\t// Forced leader by name\n\t\t\tif (this.forcedLeader === this.instanceName) {\n\t\t\t\tnewLeaderId = this.instanceId\n\t\t\t} else {\n\t\t\t\tconst forced = this.discovery.getInstances().find((i) => i.name === this.forcedLeader)\n\t\t\t\tnewLeaderId = forced?.id ?? this.instanceId // fall back to self if forced leader not found\n\t\t\t}\n\t\t} else {\n\t\t\t// Auto: lowest instance ID\n\t\t\tconst allIds = [this.instanceId]\n\t\t\tfor (const instance of this.discovery.getInstances()) {\n\t\t\t\tallIds.push(instance.id)\n\t\t\t}\n\t\t\tallIds.sort()\n\t\t\tnewLeaderId = allIds[0]\n\t\t}\n\n\t\tconst wasLeader = this.isLeader()\n\n\t\tif (newLeaderId !== this.leaderId) {\n\t\t\tthis.leaderId = newLeaderId\n\n\t\t\tif (newLeaderId === this.instanceId) {\n\t\t\t\tthis.leaderName = this.instanceName\n\t\t\t\tlogger.info(`This instance is now the leader`)\n\t\t\t\tthis.startLeaderHeartbeat()\n\t\t\t\tif (!wasLeader) this.onBecomeLeader?.()\n\t\t\t} else {\n\t\t\t\tconst instance = this.discovery.getInstances().find((i) => i.id === newLeaderId)\n\t\t\t\tthis.leaderName = instance?.name ?? newLeaderId.substring(0, 8)\n\t\t\t\tlogger.info(`Leader is: ${this.leaderName} (${newLeaderId.substring(0, 8)})`)\n\t\t\t\tthis.stopLeaderHeartbeat()\n\t\t\t\tif (wasLeader) this.onLoseLeader?.()\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Start sending leader heartbeats.\n\t */\n\tprivate startLeaderHeartbeat(): void {\n\t\tthis.stopLeaderHeartbeat()\n\t\tthis.heartbeatTimer = setInterval(() => {\n\t\t\tconst message = createMessage(\n\t\t\t\t'leader:heartbeat',\n\t\t\t\tthis.instanceId,\n\t\t\t\t'*',\n\t\t\t\t{ timestamp: Date.now() },\n\t\t\t\tthis.privateKey,\n\t\t\t\tthis.pqPrivateKey,\n\t\t\t)\n\t\t\tthis.transport.broadcast(message)\n\t\t}, LEADER_HEARTBEAT_INTERVAL_MS)\n\t}\n\n\t/**\n\t * Stop sending leader heartbeats.\n\t */\n\tprivate stopLeaderHeartbeat(): void {\n\t\tif (this.heartbeatTimer) {\n\t\t\tclearInterval(this.heartbeatTimer)\n\t\t\tthis.heartbeatTimer = undefined\n\t\t}\n\t}\n\n\t/**\n\t * Monitor leader liveness.\n\t */\n\tprivate monitor(): void {\n\t\t// Re-elect if peers changed\n\t\tthis.electLeader()\n\n\t\t// If we're not the leader, check if leader is alive\n\t\tif (!this.isLeader() && this.leaderId) {\n\t\t\tconst timeSinceHeartbeat = Date.now() - this.lastLeaderHeartbeat\n\n\t\t\tif (this.lastLeaderHeartbeat > 0 && timeSinceHeartbeat > LEADER_HEARTBEAT_INTERVAL_MS * 1.5) {\n\t\t\t\tthis.missedHeartbeats++\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Leader heartbeat missed (${this.missedHeartbeats}/${MAX_MISSED_HEARTBEATS}) — last seen ${Math.round(timeSinceHeartbeat / 1000)}s ago`,\n\t\t\t\t)\n\n\t\t\t\tif (this.missedHeartbeats >= MAX_MISSED_HEARTBEATS) {\n\t\t\t\t\tlogger.warn(`Leader ${this.leaderName} appears down — triggering re-election`)\n\t\t\t\t\tthis.missedHeartbeats = 0\n\t\t\t\t\tthis.lastLeaderHeartbeat = 0\n\t\t\t\t\t// Remove dead leader from consideration and re-elect\n\t\t\t\t\tthis.electLeader()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Handle incoming leader-related messages.\n\t */\n\tprivate handleMessage(message: VoleNetMessage): void {\n\t\tswitch (message.type) {\n\t\t\tcase 'leader:heartbeat':\n\t\t\t\tif (message.from === this.leaderId) {\n\t\t\t\t\tthis.lastLeaderHeartbeat = Date.now()\n\t\t\t\t\tthis.missedHeartbeats = 0\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase 'leader:claim':\n\t\t\t\t// Another instance is claiming leadership\n\t\t\t\t// Accept if their ID is lower than ours\n\t\t\t\tif (message.from < this.instanceId) {\n\t\t\t\t\tlogger.info(`Accepted leader claim from ${message.from.substring(0, 8)}`)\n\t\t\t\t\tif (this.isLeader()) {\n\t\t\t\t\t\tthis.stopLeaderHeartbeat()\n\t\t\t\t\t\tthis.onLoseLeader?.()\n\t\t\t\t\t}\n\t\t\t\t\tthis.leaderId = message.from\n\t\t\t\t\tconst instance = this.discovery.getInstances().find((i) => i.id === message.from)\n\t\t\t\t\tthis.leaderName = instance?.name ?? message.from.substring(0, 8)\n\t\t\t\t\tthis.lastLeaderHeartbeat = Date.now()\n\t\t\t\t\tthis.missedHeartbeats = 0\n\n\t\t\t\t\t// Acknowledge\n\t\t\t\t\tconst ack = createMessage(\n\t\t\t\t\t\t'leader:ack',\n\t\t\t\t\t\tthis.instanceId,\n\t\t\t\t\t\tmessage.from,\n\t\t\t\t\t\t{ accepted: true },\n\t\t\t\t\t\tthis.privateKey,\n\t\t\t\t\t\tthis.pqPrivateKey,\n\t\t\t\t\t)\n\t\t\t\t\tthis.transport.sendToPeer(message.from, ack)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase 'leader:ack':\n\t\t\t\t// Our claim was acknowledged\n\t\t\t\tbreak\n\t\t}\n\t}\n}\n","/**\n * VoleNet Remote Task — delegate tasks to remote peers, execute remote tools.\n * Transparent to the Brain — remote tools appear in the tool registry like local ones.\n */\n\nimport * as crypto from 'node:crypto'\nimport type { KeyObject } from 'node:crypto'\nimport type { VoleNetDiscovery } from './discovery.js'\nimport { createLogger } from './logger.js'\nimport { type VoleNetMessage, createMessage } from './protocol.js'\nimport type { VoleNetTransport } from './transport.js'\n\nconst logger = createLogger('volenet-remote')\n\nexport interface RemoteTaskRequest {\n\ttaskId: string\n\tinput: string\n\tmaxIterations?: number\n\tagentProfile?: string\n\tcontext?: string\n\t/** Set by net_message — the sender's instance name. Marks this as a chat message\n\t * (framed as a peer message + run in a per-peer session) rather than a one-shot task. */\n\tfromName?: string\n}\n\nexport interface RemoteTaskResult {\n\ttaskId: string\n\tstatus: 'queued' | 'running' | 'completed' | 'failed' | 'timeout'\n\tresult?: string\n\terror?: string\n\tdurationMs?: number\n}\n\nexport interface RemoteToolCallRequest {\n\tcallId: string\n\ttoolName: string\n\tparams: unknown\n}\n\nexport interface RemoteToolCallResult {\n\tcallId: string\n\tsuccess: boolean\n\toutput?: unknown\n\terror?: string\n}\n\n/**\n * Manages remote task delegation and tool execution across VoleNet peers.\n */\nexport class RemoteTaskManager {\n\tprivate transport: VoleNetTransport\n\tprivate discovery: VoleNetDiscovery\n\tprivate instanceId: string\n\tprivate privateKey: KeyObject\n\tprivate pqPrivateKey?: KeyObject\n\tprivate pendingTasks = new Map<\n\t\tstring,\n\t\t{\n\t\t\tresolve: (result: RemoteTaskResult) => void\n\t\t\treject: (error: Error) => void\n\t\t\ttimer: ReturnType<typeof setTimeout>\n\t\t}\n\t>()\n\tprivate pendingToolCalls = new Map<\n\t\tstring,\n\t\t{\n\t\t\tresolve: (result: RemoteToolCallResult) => void\n\t\t\treject: (error: Error) => void\n\t\t\ttimer: ReturnType<typeof setTimeout>\n\t\t}\n\t>()\n\tprivate routing: Record<string, string>\n\n\tconstructor(\n\t\ttransport: VoleNetTransport,\n\t\tdiscovery: VoleNetDiscovery,\n\t\tinstanceId: string,\n\t\tprivateKey: KeyObject,\n\t\tpqPrivateKey: KeyObject | undefined,\n\t\trouting?: Record<string, string>,\n\t) {\n\t\tthis.transport = transport\n\t\tthis.discovery = discovery\n\t\tthis.instanceId = instanceId\n\t\tthis.privateKey = privateKey\n\t\tthis.pqPrivateKey = pqPrivateKey\n\t\tthis.routing = routing ?? {}\n\n\t\t// Register message handlers\n\t\tthis.transport.onMessage((message) => {\n\t\t\tthis.handleMessage(message)\n\t\t})\n\t}\n\n\t/**\n\t * Delegate a task to a remote peer.\n\t * Returns when the remote task completes or times out.\n\t */\n\tasync delegateTask(\n\t\ttargetInstanceId: string,\n\t\trequest: RemoteTaskRequest,\n\t\ttimeoutMs = 300_000,\n\t): Promise<RemoteTaskResult> {\n\t\tconst taskId = request.taskId || crypto.randomUUID()\n\n\t\tconst message = createMessage(\n\t\t\t'task:delegate',\n\t\t\tthis.instanceId,\n\t\t\ttargetInstanceId,\n\t\t\t{ ...request, taskId },\n\t\t\tthis.privateKey,\n\t\t\tthis.pqPrivateKey,\n\t\t)\n\n\t\tconst sent = await this.transport.sendToPeer(targetInstanceId, message)\n\t\tif (!sent) {\n\t\t\treturn { taskId, status: 'failed', error: 'Failed to reach peer' }\n\t\t}\n\n\t\tlogger.info(\n\t\t\t`Delegated task ${taskId.substring(0, 8)} to ${targetInstanceId.substring(0, 8)}: \"${request.input.substring(0, 80)}\"`,\n\t\t)\n\n\t\t// Wait for result\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.pendingTasks.delete(taskId)\n\t\t\t\tresolve({ taskId, status: 'timeout', error: `Task timed out after ${timeoutMs}ms` })\n\t\t\t}, timeoutMs)\n\n\t\t\tthis.pendingTasks.set(taskId, { resolve, reject, timer })\n\t\t})\n\t}\n\n\t/**\n\t * Execute a tool on a remote peer.\n\t * Used by the tool registry when a tool is remote.\n\t */\n\tasync executeRemoteTool(\n\t\ttargetInstanceId: string,\n\t\ttoolName: string,\n\t\tparams: unknown,\n\t\ttimeoutMs = 60_000,\n\t): Promise<RemoteToolCallResult> {\n\t\tconst callId = crypto.randomUUID()\n\n\t\tconst message = createMessage(\n\t\t\t'tool:call',\n\t\t\tthis.instanceId,\n\t\t\ttargetInstanceId,\n\t\t\t{ callId, toolName, params } satisfies RemoteToolCallRequest,\n\t\t\tthis.privateKey,\n\t\t\tthis.pqPrivateKey,\n\t\t)\n\n\t\tconst sent = await this.transport.sendToPeer(targetInstanceId, message)\n\t\tif (!sent) {\n\t\t\treturn { callId, success: false, error: 'Failed to reach peer' }\n\t\t}\n\n\t\tlogger.info(`Remote tool call ${toolName} → ${targetInstanceId.substring(0, 8)}`)\n\n\t\treturn new Promise((resolve) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.pendingToolCalls.delete(callId)\n\t\t\t\tresolve({\n\t\t\t\t\tcallId,\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `Remote tool call timed out after ${timeoutMs}ms`,\n\t\t\t\t})\n\t\t\t}, timeoutMs)\n\n\t\t\tthis.pendingToolCalls.set(callId, { resolve, reject: () => {}, timer })\n\t\t})\n\t}\n\n\t/**\n\t * Resolve which peer should handle a tool call.\n\t * Checks routing config first, then falls back to discovery.\n\t */\n\tresolveToolTarget(toolName: string): string | null {\n\t\t// Check explicit routing rules (pattern matching)\n\t\tfor (const [pattern, targetName] of Object.entries(this.routing)) {\n\t\t\tif (matchPattern(pattern, toolName)) {\n\t\t\t\t// Find instance by name\n\t\t\t\tconst instances = this.discovery.getInstances()\n\t\t\t\tconst target = instances.find((i) => i.name === targetName)\n\t\t\t\tif (target) return target.id\n\t\t\t}\n\t\t}\n\n\t\t// Check if any peer has this tool\n\t\tconst owner = this.discovery.findToolOwner(toolName)\n\t\treturn owner?.instanceId ?? null\n\t}\n\n\t/**\n\t * Handle incoming messages related to tasks and tools.\n\t */\n\tprivate handleMessage(message: VoleNetMessage): void {\n\t\tswitch (message.type) {\n\t\t\tcase 'task:result':\n\t\t\t\tthis.handleTaskResult(message)\n\t\t\t\tbreak\n\t\t\tcase 'task:status':\n\t\t\t\tthis.handleTaskStatus(message)\n\t\t\t\tbreak\n\t\t\tcase 'tool:result':\n\t\t\t\tthis.handleToolResult(message)\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\tprivate handleTaskResult(message: VoleNetMessage): void {\n\t\tconst result = message.payload as RemoteTaskResult\n\t\tif (!result?.taskId) return\n\n\t\tconst pending = this.pendingTasks.get(result.taskId)\n\t\tif (pending) {\n\t\t\tclearTimeout(pending.timer)\n\t\t\tthis.pendingTasks.delete(result.taskId)\n\t\t\tpending.resolve(result)\n\t\t\tlogger.info(`Remote task ${result.taskId.substring(0, 8)} completed: ${result.status}`)\n\t\t}\n\t}\n\n\tprivate handleTaskStatus(message: VoleNetMessage): void {\n\t\tconst status = message.payload as { taskId: string; status: string }\n\t\tif (!status?.taskId) return\n\t\tlogger.info(`Remote task ${status.taskId.substring(0, 8)} status: ${status.status}`)\n\t}\n\n\tprivate handleToolResult(message: VoleNetMessage): void {\n\t\tconst result = message.payload as RemoteToolCallResult\n\t\tif (!result?.callId) return\n\n\t\tconst pending = this.pendingToolCalls.get(result.callId)\n\t\tif (pending) {\n\t\t\tclearTimeout(pending.timer)\n\t\t\tthis.pendingToolCalls.delete(result.callId)\n\t\t\tif (result.success) {\n\t\t\t\tconst preview =\n\t\t\t\t\ttypeof result.output === 'string'\n\t\t\t\t\t\t? result.output.substring(0, 200)\n\t\t\t\t\t\t: JSON.stringify(result.output).substring(0, 200)\n\t\t\t\tlogger.info(\n\t\t\t\t\t`Remote tool result received from ${message.from.substring(0, 8)}: success — ${preview}`,\n\t\t\t\t)\n\t\t\t} else {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Remote tool result received from ${message.from.substring(0, 8)}: failed — ${result.error}`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tpending.resolve(result)\n\t\t}\n\t}\n\n\t/**\n\t * Cleanup pending requests.\n\t */\n\tdispose(): void {\n\t\tfor (const [, pending] of this.pendingTasks) {\n\t\t\tclearTimeout(pending.timer)\n\t\t\tpending.resolve({ taskId: '', status: 'failed', error: 'VoleNet shutting down' })\n\t\t}\n\t\tthis.pendingTasks.clear()\n\n\t\tfor (const [, pending] of this.pendingToolCalls) {\n\t\t\tclearTimeout(pending.timer)\n\t\t\tpending.resolve({ callId: '', success: false, error: 'VoleNet shutting down' })\n\t\t}\n\t\tthis.pendingToolCalls.clear()\n\t}\n}\n\n/**\n * Match a glob-like pattern against a tool name.\n * Supports: \"image_*\" matches \"image_resize\", \"image_crop\", etc.\n */\nfunction matchPattern(pattern: string, name: string): boolean {\n\tif (pattern === name) return true\n\tif (pattern.endsWith('*')) {\n\t\treturn name.startsWith(pattern.slice(0, -1))\n\t}\n\treturn false\n}\n","/**\n * Answers that could not be delivered because the peer had gone.\n *\n * A `task:delegate` runs our brain, which takes as long as a model takes. A peer that asked\n * from a phone may well be closed by the time the answer is ready — and unlike chat, nobody\n * else is holding a copy: the asker has the question, we have the only answer. Writing it to\n * a socket that is no longer there lost it outright, and the asker was left with a question\n * that never came back.\n *\n * So an undelivered answer waits here, on the machine that produced it, and goes out when\n * that peer next speaks to us. The payload is stored rather than the signed message, because\n * a message carries a timestamp and freshness is enforced on receipt: it is re-signed at\n * delivery, exactly as the chat outbox re-signs what it held.\n *\n * Bounded on purpose — a peer that never comes back must not grow a file forever.\n */\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\n\n/** One answer waiting on the peer that asked for it. */\nexport interface PendingResult {\n\t/** Who asked. */\n\tpeerId: string\n\t/** Their id for the task — what the answer must carry to be recognised. */\n\ttaskId: string\n\tstatus: string\n\tresult?: string\n\terror?: string\n\t/** When the answer was ready. */\n\tat: number\n\tattempts: number\n}\n\nexport const DEFAULT_RESULT_TTL_MS = 7 * 24 * 60 * 60 * 1000\n/** Ceiling per peer, oldest dropped first, so one absent asker cannot fill the disk. */\nexport const MAX_RESULTS_PER_PEER = 50\n\nexport class ResultOutbox {\n\tprivate entries: PendingResult[] = []\n\n\tconstructor(\n\t\tprivate readonly file: string,\n\t\tprivate readonly ttlMs = DEFAULT_RESULT_TTL_MS,\n\t) {}\n\n\tasync load(now = Date.now()): Promise<void> {\n\t\ttry {\n\t\t\tconst raw = JSON.parse(await fs.readFile(this.file, 'utf-8')) as unknown\n\t\t\tthis.entries = Array.isArray(raw)\n\t\t\t\t? (raw as PendingResult[]).filter(\n\t\t\t\t\t\t(e) =>\n\t\t\t\t\t\t\te &&\n\t\t\t\t\t\t\ttypeof e.peerId === 'string' &&\n\t\t\t\t\t\t\ttypeof e.taskId === 'string' &&\n\t\t\t\t\t\t\ttypeof e.at === 'number',\n\t\t\t\t\t)\n\t\t\t\t: []\n\t\t} catch {\n\t\t\tthis.entries = []\n\t\t}\n\t\tawait this.sweep(now)\n\t}\n\n\tlist(): PendingResult[] {\n\t\treturn [...this.entries]\n\t}\n\n\tforPeer(peerId: string): PendingResult[] {\n\t\treturn this.entries.filter((e) => e.peerId === peerId)\n\t}\n\n\thas(peerId: string): boolean {\n\t\treturn this.entries.some((e) => e.peerId === peerId)\n\t}\n\n\tget size(): number {\n\t\treturn this.entries.length\n\t}\n\n\t/** Hold an answer. Re-asking the same task replaces the old answer rather than stacking. */\n\tasync add(entry: Omit<PendingResult, 'attempts'>): Promise<PendingResult> {\n\t\tthis.entries = this.entries.filter(\n\t\t\t(e) => !(e.peerId === entry.peerId && e.taskId === entry.taskId),\n\t\t)\n\t\tconst full: PendingResult = { ...entry, attempts: 0 }\n\t\tthis.entries.push(full)\n\t\tconst mine = this.entries.filter((e) => e.peerId === entry.peerId)\n\t\tif (mine.length > MAX_RESULTS_PER_PEER) {\n\t\t\tconst drop = new Set(mine.slice(0, mine.length - MAX_RESULTS_PER_PEER))\n\t\t\tthis.entries = this.entries.filter((e) => !drop.has(e))\n\t\t}\n\t\tawait this.persist()\n\t\treturn full\n\t}\n\n\tasync remove(peerId: string, taskId: string): Promise<boolean> {\n\t\tconst before = this.entries.length\n\t\tthis.entries = this.entries.filter((e) => !(e.peerId === peerId && e.taskId === taskId))\n\t\tif (this.entries.length === before) return false\n\t\tawait this.persist()\n\t\treturn true\n\t}\n\n\tasync noteAttempt(peerId: string, taskId: string): Promise<void> {\n\t\tconst e = this.entries.find((x) => x.peerId === peerId && x.taskId === taskId)\n\t\tif (!e) return\n\t\te.attempts++\n\t\tawait this.persist()\n\t}\n\n\t/** Drop answers older than the TTL. Returns what was dropped so the owner can say so. */\n\tasync sweep(now = Date.now()): Promise<PendingResult[]> {\n\t\tconst expired = this.entries.filter((e) => now - e.at > this.ttlMs)\n\t\tif (expired.length === 0) return []\n\t\tthis.entries = this.entries.filter((e) => now - e.at <= this.ttlMs)\n\t\tawait this.persist()\n\t\treturn expired\n\t}\n\n\tprivate async persist(): Promise<void> {\n\t\tawait fs.mkdir(path.dirname(this.file), { recursive: true })\n\t\tawait fs.writeFile(this.file, JSON.stringify(this.entries, null, 2), 'utf-8')\n\t}\n}\n","/**\n * VoleNet Sync — memory and session synchronization across peers.\n *\n * Memory sync: write propagation + remote search + result merging.\n * Session sync: transcript replication for cross-device continuity.\n *\n * Conflict resolution: last-write-wins with instance attribution.\n * Consistency model: eventual (async propagation, no blocking).\n */\n\nimport type { KeyObject } from 'node:crypto'\nimport type { VoleNetDiscovery } from './discovery.js'\nimport { createLogger } from './logger.js'\nimport { type VoleNetMessage, createMessage } from './protocol.js'\nimport type { VoleNetTransport } from './transport.js'\n\nconst logger = createLogger('volenet-sync')\n\n/** Memory write event for propagation */\nexport interface MemorySyncEntry {\n\tfile: string\n\tsource: string\n\tcontent: string\n\tmode: 'overwrite' | 'append'\n\ttimestamp: number\n\tinstanceId: string\n\tversion: number\n}\n\n/** Remote memory search request */\nexport interface MemorySearchRequest {\n\tquery: string\n\tsource?: string\n\tlimit?: number\n\trequestId: string\n}\n\n/** Remote memory search result */\nexport interface MemorySearchResult {\n\trequestId: string\n\tinstanceId: string\n\tinstanceName: string\n\tresults: Array<{\n\t\tfile: string\n\t\tsource: string\n\t\tscore: number\n\t\tsnippet: string\n\t}>\n}\n\n/** Session sync entry */\nexport interface SessionSyncEntry {\n\tsessionId: string\n\trole: string\n\tcontent: string\n\ttimestamp: number\n\tinstanceId: string\n}\n\nexport interface SyncConfig {\n\tmemory: boolean\n\tsession: boolean\n}\n\n/**\n * Manages memory and session synchronization across VoleNet peers.\n */\nexport class VoleNetSync {\n\tprivate transport: VoleNetTransport\n\tprivate discovery: VoleNetDiscovery\n\tprivate instanceId: string\n\tprivate instanceName: string\n\tprivate privateKey: KeyObject\n\tprivate pqPrivateKey?: KeyObject\n\tprivate config: SyncConfig\n\n\t// Callbacks for local memory/session operations\n\tprivate onMemoryWrite?: (entry: MemorySyncEntry) => Promise<void>\n\tprivate onMemorySearch?: (request: MemorySearchRequest) => Promise<MemorySearchResult['results']>\n\tprivate onSessionWrite?: (entry: SessionSyncEntry) => Promise<void>\n\n\t// Pending remote search requests\n\tprivate pendingSearches = new Map<\n\t\tstring,\n\t\t{\n\t\t\tresults: MemorySearchResult[]\n\t\t\ttimer: ReturnType<typeof setTimeout>\n\t\t\tresolve: (results: MemorySearchResult[]) => void\n\t\t\texpectedPeers: number\n\t\t}\n\t>()\n\n\t// Dedup: track recently synced writes to avoid echo loops\n\tprivate recentSyncs = new Set<string>()\n\n\tconstructor(\n\t\ttransport: VoleNetTransport,\n\t\tdiscovery: VoleNetDiscovery,\n\t\tinstanceId: string,\n\t\tinstanceName: string,\n\t\tprivateKey: KeyObject,\n\t\tpqPrivateKey: KeyObject | undefined,\n\t\tconfig: SyncConfig,\n\t) {\n\t\tthis.transport = transport\n\t\tthis.discovery = discovery\n\t\tthis.instanceId = instanceId\n\t\tthis.instanceName = instanceName\n\t\tthis.privateKey = privateKey\n\t\tthis.pqPrivateKey = pqPrivateKey\n\t\tthis.config = config\n\n\t\t// Register message handlers\n\t\tthis.transport.onMessage((message) => {\n\t\t\tthis.handleMessage(message)\n\t\t})\n\t}\n\n\t/**\n\t * Register callback for when a remote memory write arrives.\n\t * The callback should apply the write to the local memory store.\n\t */\n\tsetMemoryWriteHandler(handler: (entry: MemorySyncEntry) => Promise<void>): void {\n\t\tthis.onMemoryWrite = handler\n\t}\n\n\t/**\n\t * Register callback for handling remote memory search requests.\n\t * The callback should search the local store and return results.\n\t */\n\tsetMemorySearchHandler(\n\t\thandler: (request: MemorySearchRequest) => Promise<MemorySearchResult['results']>,\n\t): void {\n\t\tthis.onMemorySearch = handler\n\t}\n\n\t/**\n\t * Register callback for when a remote session write arrives.\n\t */\n\tsetSessionWriteHandler(handler: (entry: SessionSyncEntry) => Promise<void>): void {\n\t\tthis.onSessionWrite = handler\n\t}\n\n\t/**\n\t * Propagate a local memory write to all peers.\n\t * Called by paw-memory after a successful local write.\n\t */\n\tasync propagateMemoryWrite(entry: MemorySyncEntry): Promise<void> {\n\t\tif (!this.config.memory) return\n\n\t\t// Dedup — don't re-propagate writes we received from peers\n\t\tconst syncKey = `${entry.file}:${entry.timestamp}:${entry.instanceId}`\n\t\tif (this.recentSyncs.has(syncKey)) return\n\t\tthis.recentSyncs.add(syncKey)\n\t\t// Clean old entries (keep last 5 minutes)\n\t\tsetTimeout(() => this.recentSyncs.delete(syncKey), 300_000)\n\n\t\tconst message = createMessage(\n\t\t\t'memory:sync',\n\t\t\tthis.instanceId,\n\t\t\t'*',\n\t\t\tentry,\n\t\t\tthis.privateKey,\n\t\t\tthis.pqPrivateKey,\n\t\t)\n\n\t\tconst sent = await this.transport.broadcast(message)\n\t\tif (sent > 0) {\n\t\t\tlogger.debug(`Memory write propagated to ${sent} peer(s): ${entry.file}`)\n\t\t}\n\t}\n\n\t/**\n\t * Search memory across all peers + local.\n\t * Returns merged results from all sources, re-ranked by score.\n\t */\n\tasync searchRemoteMemory(\n\t\tquery: string,\n\t\toptions?: { source?: string; limit?: number; timeoutMs?: number },\n\t): Promise<MemorySearchResult[]> {\n\t\tif (!this.config.memory) return []\n\n\t\tconst instances = this.discovery.getInstances()\n\t\tif (instances.length === 0) return []\n\n\t\tconst requestId = `search-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`\n\t\tconst timeoutMs = options?.timeoutMs ?? 5000\n\t\tconst limit = options?.limit ?? 10\n\n\t\tconst message = createMessage(\n\t\t\t'memory:search',\n\t\t\tthis.instanceId,\n\t\t\t'*',\n\t\t\t{\n\t\t\t\tquery,\n\t\t\t\tsource: options?.source,\n\t\t\t\tlimit,\n\t\t\t\trequestId,\n\t\t\t} satisfies MemorySearchRequest,\n\t\t\tthis.privateKey,\n\t\t\tthis.pqPrivateKey,\n\t\t)\n\n\t\t// Set up result collector\n\t\treturn new Promise((resolve) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tconst pending = this.pendingSearches.get(requestId)\n\t\t\t\tthis.pendingSearches.delete(requestId)\n\t\t\t\tresolve(pending?.results ?? [])\n\t\t\t}, timeoutMs)\n\n\t\t\tthis.pendingSearches.set(requestId, {\n\t\t\t\tresults: [],\n\t\t\t\ttimer,\n\t\t\t\tresolve,\n\t\t\t\texpectedPeers: instances.length,\n\t\t\t})\n\n\t\t\tthis.transport.broadcast(message)\n\t\t})\n\t}\n\n\t/**\n\t * Propagate a session message to peers.\n\t */\n\tasync propagateSessionWrite(entry: SessionSyncEntry): Promise<void> {\n\t\tif (!this.config.session) return\n\n\t\tconst syncKey = `${entry.sessionId}:${entry.timestamp}:${entry.instanceId}`\n\t\tif (this.recentSyncs.has(syncKey)) return\n\t\tthis.recentSyncs.add(syncKey)\n\t\tsetTimeout(() => this.recentSyncs.delete(syncKey), 300_000)\n\n\t\tconst message = createMessage(\n\t\t\t'session:sync',\n\t\t\tthis.instanceId,\n\t\t\t'*',\n\t\t\tentry,\n\t\t\tthis.privateKey,\n\t\t\tthis.pqPrivateKey,\n\t\t)\n\n\t\tawait this.transport.broadcast(message)\n\t}\n\n\t/**\n\t * Handle incoming sync messages.\n\t */\n\tprivate handleMessage(message: VoleNetMessage): void {\n\t\tswitch (message.type) {\n\t\t\tcase 'memory:sync':\n\t\t\t\tthis.handleMemorySync(message)\n\t\t\t\tbreak\n\t\t\tcase 'memory:search':\n\t\t\t\tthis.handleMemorySearchRequest(message)\n\t\t\t\tbreak\n\t\t\tcase 'memory:results':\n\t\t\t\tthis.handleMemorySearchResults(message)\n\t\t\t\tbreak\n\t\t\tcase 'session:sync':\n\t\t\t\tthis.handleSessionSync(message)\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t/**\n\t * Handle incoming memory write from a peer.\n\t */\n\tprivate async handleMemorySync(message: VoleNetMessage): Promise<void> {\n\t\tconst entry = message.payload as MemorySyncEntry\n\t\tif (!entry?.file || !entry?.content) return\n\n\t\t// Dedup\n\t\tconst syncKey = `${entry.file}:${entry.timestamp}:${entry.instanceId}`\n\t\tif (this.recentSyncs.has(syncKey)) return\n\t\tthis.recentSyncs.add(syncKey)\n\t\tsetTimeout(() => this.recentSyncs.delete(syncKey), 300_000)\n\n\t\tlogger.debug(`Memory sync from ${message.from.substring(0, 8)}: ${entry.file} (${entry.mode})`)\n\n\t\tif (this.onMemoryWrite) {\n\t\t\ttry {\n\t\t\t\tawait this.onMemoryWrite(entry)\n\t\t\t} catch (err) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to apply memory sync: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Handle remote memory search request — search local store and reply.\n\t */\n\tprivate async handleMemorySearchRequest(message: VoleNetMessage): Promise<void> {\n\t\tconst request = message.payload as MemorySearchRequest\n\t\tif (!request?.query || !request?.requestId) return\n\n\t\tif (!this.onMemorySearch) return\n\n\t\ttry {\n\t\t\tconst results = await this.onMemorySearch(request)\n\t\t\tconst response = createMessage(\n\t\t\t\t'memory:results',\n\t\t\t\tthis.instanceId,\n\t\t\t\tmessage.from,\n\t\t\t\t{\n\t\t\t\t\trequestId: request.requestId,\n\t\t\t\t\tinstanceId: this.instanceId,\n\t\t\t\t\tinstanceName: this.instanceName,\n\t\t\t\t\tresults,\n\t\t\t\t} satisfies MemorySearchResult,\n\t\t\t\tthis.privateKey,\n\t\t\t\tthis.pqPrivateKey,\n\t\t\t)\n\t\t\tawait this.transport.sendToPeer(message.from, response)\n\t\t} catch (err) {\n\t\t\tlogger.warn(\n\t\t\t\t`Failed to handle memory search: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Handle memory search results from a peer.\n\t */\n\tprivate handleMemorySearchResults(message: VoleNetMessage): void {\n\t\tconst result = message.payload as MemorySearchResult\n\t\tif (!result?.requestId) return\n\n\t\tconst pending = this.pendingSearches.get(result.requestId)\n\t\tif (!pending) return\n\n\t\tpending.results.push(result)\n\n\t\t// If all peers responded, resolve early\n\t\tif (pending.results.length >= pending.expectedPeers) {\n\t\t\tclearTimeout(pending.timer)\n\t\t\tthis.pendingSearches.delete(result.requestId)\n\t\t\tpending.resolve(pending.results)\n\t\t}\n\t}\n\n\t/**\n\t * Handle session sync from a peer.\n\t */\n\tprivate async handleSessionSync(message: VoleNetMessage): Promise<void> {\n\t\tconst entry = message.payload as SessionSyncEntry\n\t\tif (!entry?.sessionId || !entry?.content) return\n\n\t\tconst syncKey = `${entry.sessionId}:${entry.timestamp}:${entry.instanceId}`\n\t\tif (this.recentSyncs.has(syncKey)) return\n\t\tthis.recentSyncs.add(syncKey)\n\t\tsetTimeout(() => this.recentSyncs.delete(syncKey), 300_000)\n\n\t\tlogger.debug(`Session sync from ${message.from.substring(0, 8)}: ${entry.sessionId}`)\n\n\t\tif (this.onSessionWrite) {\n\t\t\ttry {\n\t\t\t\tawait this.onSessionWrite(entry)\n\t\t\t} catch (err) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to apply session sync: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Cleanup.\n\t */\n\tdispose(): void {\n\t\tfor (const [, pending] of this.pendingSearches) {\n\t\t\tclearTimeout(pending.timer)\n\t\t\tpending.resolve([])\n\t\t}\n\t\tthis.pendingSearches.clear()\n\t\tthis.recentSyncs.clear()\n\t}\n}\n","/**\n * The only thing this library needs to know about an agent's tools.\n *\n * VoleNet can lend a node's tools to a peer it trusts, which means it has to be able to list them\n * and run one. That is the whole contract — two methods and five fields — so it is stated here\n * structurally rather than imported from the agent framework. Anything that satisfies this shape\n * works, including a host with no notion of paws at all.\n */\n\n/** One tool, as the network needs to see it. */\nexport interface SharedToolEntry {\n\tname: string\n\tdescription: string\n\t/** Which plugin provides it. Used to decide what may be shared, so it is required. */\n\tpawName: string\n\t/**\n\t * The host's own context type is opaque here, and deliberately `any`: typed as `unknown` this\n\t * contract would be satisfiable by nothing, since a function taking a specific context is not\n\t * assignable to one taking anything at all.\n\t */\n\t// biome-ignore lint/suspicious/noExplicitAny: see above\n\texecute: (params: unknown, ctx?: any) => Promise<unknown>\n}\n\n/** A tool as the host defines it, when the network registers a peer's tools locally. */\nexport interface SharedToolDefinition {\n\tname: string\n\tdescription: string\n\t/** The host's own parameter schema type (a Zod schema in OpenVole). Opaque here. */\n\tparameters: unknown\n\t/**\n\t * The host's own context type is opaque here, and deliberately `any`: typed as `unknown` this\n\t * contract would be satisfiable by nothing, since a function taking a specific context is not\n\t * assignable to one taking anything at all.\n\t */\n\t// biome-ignore lint/suspicious/noExplicitAny: see above\n\texecute: (params: unknown, ctx?: any) => Promise<unknown>\n}\n\n/** Whatever holds this node's tools. `ToolRegistry` in OpenVole; anything with this shape here. */\nexport interface ToolProvider {\n\tget(toolName: string): SharedToolEntry | undefined\n\tlist(): SharedToolEntry[]\n\t/** Publish a peer's tools locally, so the host can call them as if they were its own. */\n\tregister(pawName: string, tools: SharedToolDefinition[], inProcess: boolean): void\n}\n\n/**\n * Plugins whose tools are backed by the host's control plane, and must never be shared over the\n * mesh.\n *\n * They read as ordinary tools but execute against the local server — managing siblings, or\n * speaking as this agent to one. A shared tool runs *on its owner*, so lending one of these lends\n * the owner's authority with it: a peer that cannot manage agents itself could drive the owner's\n * `agent_submit` and have every permission check pass, because by then the call really is the\n * owner's. Excluded by **source** rather than by name — a name pattern would have to be kept in\n * step with every tool ever added, and would quietly miss the first one somebody forgets.\n *\n * This lives with the network rather than with the tools because the network is what enforces it.\n */\nexport const CONTROL_PLANE_PAWS = ['__orchestrate__', '__agent_chat__'] as const\n\nexport function isControlPlanePaw(pawName: string): boolean {\n\treturn (CONTROL_PLANE_PAWS as readonly string[]).includes(pawName)\n}\n","/**\n * VoleNet Transport — HTTP server + WebSocket for peer communication.\n *\n * Two modes:\n * - HTTP POST: for initial auth and one-shot messages (fallback)\n * - WebSocket: for persistent bidirectional messaging (preferred)\n *\n * WebSocket enables NAT traversal — the peer behind NAT connects out,\n * and the other side pushes through the open connection.\n * Auto-reconnect with exponential backoff on disconnect.\n */\n\nimport * as fs from 'node:fs'\nimport * as http from 'node:http'\nimport * as https from 'node:https'\nimport { WebSocket, WebSocketServer } from 'ws'\nimport { createLogger } from './logger.js'\nimport { type VoleNetMessage, deserialize, serialize } from './protocol.js'\n\nconst logger = createLogger('volenet-transport')\n\nconst WS_RECONNECT_INTERVAL_MS = 5_000\nconst WS_RECONNECT_MAX_RETRIES = 10\nconst WS_PING_INTERVAL_MS = 20_000 // liveness: a socket that misses one pong is dropped\nconst MAX_MESSAGES_PER_MINUTE = 1200 // per source (IP / WS connection); generous — normal mesh traffic is well below\nconst MAX_MESSAGE_BYTES = 1_000_000 // cap inbound HTTP message bodies (1 MB)\nconst DEFAULT_MAX_CONNECTIONS = 1000 // cap concurrent inbound WebSocket connections (DoS)\nconst DEFAULT_AUTH_TIMEOUT_MS = 10_000 // close inbound sockets that never send a verified message (DoS)\nconst DEFAULT_MAX_MESSAGES_PER_SECOND = 5000 // global inbound message ceiling across all sources (load shed)\nconst REPLAY_WINDOW_MS = 65_000 // remember accepted (from,id) a bit beyond the 60s freshness window\nconst REPLAY_MAX_ENTRIES = 20_000 // sweep the replay cache once it grows past this\n\nexport interface TransportConfig {\n\tport: number\n\ttls?: {\n\t\tcert: string\n\t\tkey: string\n\t}\n\t/** Max inbound messages per minute per source (IP / WS connection). Default 1200. */\n\tmaxMessagesPerMinute?: number\n\t/** Max concurrent inbound WebSocket connections (DoS). Default 1000. */\n\tmaxConnections?: number\n\t/** Close an inbound WS that doesn't send a verified message within this window, ms (DoS). Default 10000. */\n\tauthTimeoutMs?: number\n\t/** Global inbound message ceiling per second across all sources (load shed). Default 5000. */\n\tmaxMessagesPerSecond?: number\n\t/** Publish peer display names in /volenet/info (off by default — names are enumeration surface). */\n\tpublishNames?: boolean\n\t/** Liveness: ping every socket this often (ms) and drop one that misses a pong. Default 20000; 0 disables. */\n\tpingIntervalMs?: number\n}\n\nexport type MessageHandler = (message: VoleNetMessage, peerId: string) => void\nexport type JoinHandler = (body: unknown, ip: string) => Promise<{ status: number; json: unknown }>\n\ninterface PeerConnection {\n\tpeerId: string\n\tendpoint: string\n\tconnected: boolean\n\tlastSeen: number\n\tws: WebSocket | null\n\treconnectTimer: ReturnType<typeof setTimeout> | null\n\treconnectAttempts: number\n\tconnecting: boolean\n}\n\n/**\n * VoleNet Transport layer.\n * HTTP server for initial connections + WebSocket for persistent messaging.\n */\nexport class VoleNetTransport {\n\tprivate server: http.Server | https.Server | null = null\n\tprivate wss: WebSocketServer | null = null\n\tprivate peers = new Map<string, PeerConnection>()\n\tprivate messageHandlers: MessageHandler[] = []\n\t/** Optional outbound transform — wraps a message in a sealed:direct envelope before sending. */\n\tprivate sealer: ((peerId: string, message: VoleNetMessage) => VoleNetMessage) | null = null\n\t/** Resolves a full peerId to its announced display name (for /volenet/info when publishNames). */\n\tprivate nameResolver: ((peerId: string) => string | undefined) | null = null\n\tprivate joinHandler: JoinHandler | null = null\n\tprivate pairHandler: JoinHandler | null = null\n\tprivate identityProvider:\n\t\t| (() => { publicKey: string; name?: string; instanceId?: string })\n\t\t| null = null\n\t/** VoleDrop data plane — handles /volenet/blob/* streamed requests. */\n\tprivate blobHandler:\n\t\t| ((req: http.IncomingMessage, res: http.ServerResponse, pathname: string) => boolean)\n\t\t| null = null\n\tprivate msgWindow = new Map<string, number[]>()\n\tprivate wsConnSeq = 0\n\tprivate config: TransportConfig\n\tprivate started = false\n\tprivate wsConnections = 0\n\tprivate globalWindow: number[] = []\n\tprivate verifyFn?: (message: VoleNetMessage) => boolean\n\tprivate responder?: (message: VoleNetMessage) => VoleNetMessage | null\n\tprivate onConnectCbs: Array<(peerId: string) => void> = []\n\tprivate onDisconnectCbs: Array<(peerId: string) => void> = []\n\tprivate seenMsgs = new Map<string, number>()\n\t/** Every socket this transport owns, either direction — so stop() closes orphans too. */\n\tprivate sockets = new Set<WebSocket>()\n\tprivate alive = new WeakMap<WebSocket, boolean>()\n\tprivate pingTimer: ReturnType<typeof setInterval> | null = null\n\n\tconstructor(config: TransportConfig) {\n\t\tthis.config = config\n\t}\n\n\t/** Register a handler for public self-join requests (HTTP POST /volenet/join). */\n\tsetJoinHandler(handler: JoinHandler): void {\n\t\tthis.joinHandler = handler\n\t}\n\n\t/** Register a handler for consent-based pairing requests (HTTP POST /volenet/pair). */\n\tsetPairHandler(handler: JoinHandler): void {\n\t\tthis.pairHandler = handler\n\t}\n\n\t/**\n\t * Provide this node's own public identity for /volenet/info — the key `vole net pair`\n\t * fetches (and fingerprints) before asking the operator here for consent. Public keys\n\t * are announced to every peer anyway; exposing one here is not an enumeration surface.\n\t */\n\tsetIdentityProvider(fn: () => { publicKey: string; name?: string; instanceId?: string }): void {\n\t\tthis.identityProvider = fn\n\t}\n\n\t/**\n\t * Register the VoleDrop blob handler for /volenet/blob/* — the file-transfer data\n\t * plane. Returns true when it handled the request. Auth (per-transfer tokens), byte\n\t * budgets, and concurrency caps are the handler's responsibility: these routes carry\n\t * large streamed bodies and deliberately bypass the JSON message pipeline.\n\t */\n\tsetBlobHandler(\n\t\tfn: (req: http.IncomingMessage, res: http.ServerResponse, pathname: string) => boolean,\n\t): void {\n\t\tthis.blobHandler = fn\n\t}\n\n\t/**\n\t * Inject a signature verifier. An inbound WebSocket is bound to a peer id ONLY after a\n\t * message from it verifies — so an attacker can't claim a victim's id and hijack its\n\t * downstream traffic.\n\t */\n\tsetVerifier(fn: (message: VoleNetMessage) => boolean): void {\n\t\tthis.verifyFn = fn\n\t}\n\n\t/**\n\t * Inject a request responder for request/response messages (e.g. `discover` over HTTP).\n\t * The returned message is delivered inline in the HTTP response body, so a peer behind NAT\n\t * learns the responder's identity without the responder having to dial it back.\n\t */\n\tsetResponder(fn: (message: VoleNetMessage) => VoleNetMessage | null): void {\n\t\tthis.responder = fn\n\t}\n\n\t/**\n\t * Called when an outbound WebSocket to a peer opens. Lets the owner push a signed message\n\t * immediately so the remote side binds this socket without waiting for the next heartbeat —\n\t * which is what makes reverse delivery (hub→NAT'd-follower) consistent right after (re)connect.\n\t */\n\tsetOnConnect(fn: (peerId: string) => void): void {\n\t\tthis.onConnectCbs.push(fn)\n\t}\n\n\tsetOnDisconnect(fn: (peerId: string) => void): void {\n\t\tthis.onDisconnectCbs.push(fn)\n\t}\n\n\t/** Global sliding-window message ceiling across all sources (load shed). False when over. */\n\tprivate globalRateAllow(): boolean {\n\t\tconst now = Date.now()\n\t\tconst max = this.config.maxMessagesPerSecond ?? DEFAULT_MAX_MESSAGES_PER_SECOND\n\t\tthis.globalWindow = this.globalWindow.filter((t) => now - t < 1000)\n\t\tif (this.globalWindow.length >= max) return false\n\t\tthis.globalWindow.push(now)\n\t\treturn true\n\t}\n\n\t/**\n\t * Central inbound gate. Every message must (a) verify — valid signature from an authorized\n\t * peer — and (b) not be a replay of a recently-accepted (from,id), before it reaches ANY\n\t * handler. Fails closed if no verifier is wired. This makes verification a single chokepoint\n\t * so an individual handler can never forget to check.\n\t */\n\tprivate verifyAndAccept(message: VoleNetMessage): boolean {\n\t\tif (!this.verifyFn || !this.verifyFn(message)) return false\n\t\tconst key = `${message.from}:${message.id}`\n\t\tconst now = Date.now()\n\t\tif (this.seenMsgs.has(key)) return false // replay within the freshness window\n\t\tthis.seenMsgs.set(key, now)\n\t\tif (this.seenMsgs.size > REPLAY_MAX_ENTRIES) {\n\t\t\tfor (const [k, t] of this.seenMsgs) if (now - t > REPLAY_WINDOW_MS) this.seenMsgs.delete(k)\n\t\t}\n\t\treturn true\n\t}\n\n\t/** Sliding-window rate limit per source (IP or WS connection). Returns false when over. */\n\tprivate rateAllow(key: string): boolean {\n\t\tconst max = this.config.maxMessagesPerMinute ?? MAX_MESSAGES_PER_MINUTE\n\t\tconst now = Date.now()\n\t\t// Prune stale per-source windows so the map can't grow unbounded under IP/connection spray.\n\t\tif (this.msgWindow.size > 4096) {\n\t\t\tfor (const [k, ts] of this.msgWindow) {\n\t\t\t\tif (ts.length === 0 || now - ts[ts.length - 1] > 60_000) this.msgWindow.delete(k)\n\t\t\t}\n\t\t}\n\t\tconst win = (this.msgWindow.get(key) ?? []).filter((t) => now - t < 60_000)\n\t\tif (win.length >= max) {\n\t\t\tthis.msgWindow.set(key, win)\n\t\t\treturn false\n\t\t}\n\t\twin.push(now)\n\t\tthis.msgWindow.set(key, win)\n\t\treturn true\n\t}\n\n\t/**\n\t * Start the transport server (HTTP + WebSocket).\n\t */\n\tasync start(): Promise<void> {\n\t\tif (this.started) return\n\n\t\tconst requestHandler = (req: http.IncomingMessage, res: http.ServerResponse) => {\n\t\t\tif (req.url === '/health' && req.method === 'GET') {\n\t\t\t\tres.writeHead(200, { 'Content-Type': 'application/json' })\n\t\t\t\tres.end(JSON.stringify({ status: 'ok', protocol: 'volenet', version: 1 }))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (req.url === '/volenet/message' && req.method === 'POST') {\n\t\t\t\tconst ip = req.socket.remoteAddress ?? 'unknown'\n\t\t\t\tif (!this.rateAllow(`ip:${ip}`) || !this.globalRateAllow()) {\n\t\t\t\t\tres.writeHead(429, { 'Content-Type': 'application/json' })\n\t\t\t\t\tres.end(JSON.stringify({ error: 'rate limited' }))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tlet body = ''\n\t\t\t\treq.on('data', (chunk: Buffer) => {\n\t\t\t\t\tbody += chunk\n\t\t\t\t\tif (body.length > MAX_MESSAGE_BYTES) req.destroy()\n\t\t\t\t})\n\t\t\t\treq.on('end', () => {\n\t\t\t\t\tconst message = deserialize(body)\n\t\t\t\t\tif (!message) {\n\t\t\t\t\t\tres.writeHead(400, { 'Content-Type': 'application/json' })\n\t\t\t\t\t\tres.end(JSON.stringify({ error: 'Invalid message' }))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\t// Verify signature + authorization + replay BEFORE any handler runs.\n\t\t\t\t\tif (!this.verifyAndAccept(message)) {\n\t\t\t\t\t\tres.writeHead(401, { 'Content-Type': 'application/json' })\n\t\t\t\t\t\tres.end(JSON.stringify({ error: 'unverified' }))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tfor (const handler of this.messageHandlers) {\n\t\t\t\t\t\thandler(message, message.from)\n\t\t\t\t\t}\n\t\t\t\t\t// Inline reply (e.g. discover:response) so a NAT'd sender learns our identity\n\t\t\t\t\t// from its own request — we don't have to dial it back.\n\t\t\t\t\tconst reply = this.responder ? this.responder(message) : null\n\t\t\t\t\tres.writeHead(200, { 'Content-Type': 'application/json' })\n\t\t\t\t\tres.end(JSON.stringify(reply ? { ok: true, response: reply } : { ok: true }))\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (req.url === '/volenet/info' && req.method === 'GET') {\n\t\t\t\tconst peerList = this.getPeers()\n\t\t\t\t// Display names are opt-in: they're an enumeration surface, so only exposed when the\n\t\t\t\t// operator sets net.publishNames (e.g. a public hub whose members are meant to be seen).\n\t\t\t\tconst withNames = this.config.publishNames && this.nameResolver\n\t\t\t\tres.writeHead(200, { 'Content-Type': 'application/json' })\n\t\t\t\tconst identity = this.identityProvider?.()\n\t\t\t\tres.end(\n\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\tok: true,\n\t\t\t\t\t\tprotocol: 'volenet',\n\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\t// Own public key (always safe — it's announced to every peer) + name when\n\t\t\t\t\t\t// publishNames allows it. `vole net pair` fingerprints this before trusting.\n\t\t\t\t\t\t...(identity?.publicKey ? { publicKey: identity.publicKey } : {}),\n\t\t\t\t\t\t...(identity?.name ? { name: identity.name } : {}),\n\t\t\t\t\t\tpeers: peerList.map((p) => ({\n\t\t\t\t\t\t\tid: p.peerId.substring(0, 8),\n\t\t\t\t\t\t\t...(withNames ? { name: this.nameResolver?.(p.peerId) } : {}),\n\t\t\t\t\t\t\tendpoint: p.endpoint,\n\t\t\t\t\t\t\tconnected: p.connected,\n\t\t\t\t\t\t\ttransport: p.transport,\n\t\t\t\t\t\t\tlastSeen: p.lastSeen,\n\t\t\t\t\t\t})),\n\t\t\t\t\t\tpeerCount: peerList.length,\n\t\t\t\t\t\twebsocketConnections: peerList.filter((p) => p.transport === 'websocket').length,\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (req.url === '/volenet/join' && req.method === 'POST') {\n\t\t\t\tlet body = ''\n\t\t\t\treq.on('data', (chunk: Buffer) => {\n\t\t\t\t\tbody += chunk\n\t\t\t\t\tif (body.length > 8192) req.destroy()\n\t\t\t\t})\n\t\t\t\treq.on('end', () => {\n\t\t\t\t\tif (!this.joinHandler) {\n\t\t\t\t\t\tres.writeHead(404)\n\t\t\t\t\t\tres.end()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tlet parsed: unknown\n\t\t\t\t\ttry {\n\t\t\t\t\t\tparsed = JSON.parse(body)\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tres.writeHead(400, { 'Content-Type': 'application/json' })\n\t\t\t\t\t\tres.end(JSON.stringify({ error: 'invalid json' }))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tconst ip = req.socket.remoteAddress ?? 'unknown'\n\t\t\t\t\tthis.joinHandler(parsed, ip)\n\t\t\t\t\t\t.then(({ status, json }) => {\n\t\t\t\t\t\t\tres.writeHead(status, { 'Content-Type': 'application/json' })\n\t\t\t\t\t\t\tres.end(JSON.stringify(json))\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\t\tres.writeHead(500, { 'Content-Type': 'application/json' })\n\t\t\t\t\t\t\tres.end(JSON.stringify({ error: e instanceof Error ? e.message : 'join failed' }))\n\t\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Consent-based pairing: an unknown node introduces itself with its public key.\n\t\t\t// Nothing is trusted here — the request is queued for the OPERATOR to accept\n\t\t\t// (vole net pair / dashboard). Same shape and caps as /volenet/join.\n\t\t\tif (req.url === '/volenet/pair' && req.method === 'POST') {\n\t\t\t\tlet body = ''\n\t\t\t\treq.on('data', (chunk: Buffer) => {\n\t\t\t\t\tbody += chunk\n\t\t\t\t\tif (body.length > 8192) req.destroy()\n\t\t\t\t})\n\t\t\t\treq.on('end', () => {\n\t\t\t\t\tif (!this.pairHandler) {\n\t\t\t\t\t\tres.writeHead(404)\n\t\t\t\t\t\tres.end()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tlet parsed: unknown\n\t\t\t\t\ttry {\n\t\t\t\t\t\tparsed = JSON.parse(body)\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tres.writeHead(400, { 'Content-Type': 'application/json' })\n\t\t\t\t\t\tres.end(JSON.stringify({ error: 'invalid json' }))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tconst ip = req.socket.remoteAddress ?? 'unknown'\n\t\t\t\t\tthis.pairHandler(parsed, ip)\n\t\t\t\t\t\t.then(({ status, json }) => {\n\t\t\t\t\t\t\tres.writeHead(status, { 'Content-Type': 'application/json' })\n\t\t\t\t\t\t\tres.end(JSON.stringify(json))\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\t\tres.writeHead(500, { 'Content-Type': 'application/json' })\n\t\t\t\t\t\t\tres.end(JSON.stringify({ error: e instanceof Error ? e.message : 'pair failed' }))\n\t\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// VoleDrop data plane: /volenet/blob/<id> — streamed, token-authenticated inside\n\t\t\t// the handler. Matched on the parsed pathname (never raw req.url) so query strings\n\t\t\t// (?from=N resume) and encoded traversal can't dodge or confuse the route.\n\t\t\tif (this.blobHandler && req.url) {\n\t\t\t\tlet pathname: string\n\t\t\t\ttry {\n\t\t\t\t\tpathname = new URL(req.url, 'http://localhost').pathname\n\t\t\t\t} catch {\n\t\t\t\t\tpathname = ''\n\t\t\t\t}\n\t\t\t\tif (pathname.startsWith('/volenet/blob/')) {\n\t\t\t\t\tconst ip = req.socket.remoteAddress ?? 'unknown'\n\t\t\t\t\tif (!this.rateAllow(`blob:${ip}`) || !this.globalRateAllow()) {\n\t\t\t\t\t\tres.writeHead(429)\n\t\t\t\t\t\tres.end()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif (this.blobHandler(req, res, pathname)) return\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tres.writeHead(404)\n\t\t\tres.end()\n\t\t}\n\n\t\tif (this.config.tls) {\n\t\t\tthis.server = https.createServer(\n\t\t\t\t{\n\t\t\t\t\tcert: fs.readFileSync(this.config.tls.cert),\n\t\t\t\t\tkey: fs.readFileSync(this.config.tls.key),\n\t\t\t\t},\n\t\t\t\trequestHandler,\n\t\t\t)\n\t\t} else {\n\t\t\tthis.server = http.createServer(requestHandler)\n\t\t}\n\n\t\t// Attach WebSocket server\n\t\tthis.wss = new WebSocketServer({ server: this.server, maxPayload: MAX_MESSAGE_BYTES })\n\n\t\tthis.wss.on('error', (err) => {\n\t\t\tlogger.warn(`VoleNet WebSocket server error: ${err.message}`)\n\t\t})\n\n\t\tthis.wss.on('connection', (ws) => {\n\t\t\t// Attach an error listener immediately — a socket erroring during the cap/auth-timeout\n\t\t\t// close below (before the main handler is wired) would otherwise crash the process.\n\t\t\tws.on('error', () => {})\n\t\t\t// Connection cap (DoS): refuse new sockets past the limit.\n\t\t\tconst maxConns = this.config.maxConnections ?? DEFAULT_MAX_CONNECTIONS\n\t\t\tif (this.wsConnections >= maxConns) {\n\t\t\t\tws.close(1013, 'too many connections')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.wsConnections++\n\t\t\tthis.track(ws)\n\n\t\t\tconst connKey = `ws:${++this.wsConnSeq}`\n\t\t\tlet authedPeerId: string | null = null\n\n\t\t\t// Auth timeout (DoS): drop sockets that never send a verified message.\n\t\t\tconst authTimer = setTimeout(() => {\n\t\t\t\tif (!authedPeerId) {\n\t\t\t\t\tlogger.warn('Closing unauthenticated WebSocket (auth timeout)')\n\t\t\t\t\tws.close(1008, 'auth timeout')\n\t\t\t\t}\n\t\t\t}, this.config.authTimeoutMs ?? DEFAULT_AUTH_TIMEOUT_MS)\n\n\t\t\tws.on('message', (data) => {\n\t\t\t\tif (!this.rateAllow(connKey)) return // per-connection rate limit\n\t\t\t\tif (!this.globalRateAllow()) return // global load shed\n\t\t\t\tconst message = deserialize(data.toString())\n\t\t\t\tif (!message) return\n\t\t\t\t// Verify signature + authorization + replay for EVERY message before dispatch.\n\t\t\t\tif (!this.verifyAndAccept(message)) return\n\n\t\t\t\tif (!authedPeerId) {\n\t\t\t\t\t// First verified message binds this socket to that peer id (the binding is what\n\t\t\t\t\t// sendToPeer routes over). An attacker can't bind a victim's id — verifyAndAccept\n\t\t\t\t\t// already proved the signature.\n\t\t\t\t\tauthedPeerId = message.from\n\t\t\t\t\tclearTimeout(authTimer)\n\t\t\t\t\tconst existing = this.peers.get(message.from)\n\t\t\t\t\tlet bound = true\n\t\t\t\t\tif (existing) {\n\t\t\t\t\t\tbound = this.adopt(existing, ws, 'in')\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.peers.set(message.from, {\n\t\t\t\t\t\t\tpeerId: message.from,\n\t\t\t\t\t\t\tendpoint: '',\n\t\t\t\t\t\t\tconnected: true,\n\t\t\t\t\t\t\tlastSeen: Date.now(),\n\t\t\t\t\t\t\tws,\n\t\t\t\t\t\t\treconnectTimer: null,\n\t\t\t\t\t\t\treconnectAttempts: 0,\n\t\t\t\t\t\t\tconnecting: false,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tif (bound) {\n\t\t\t\t\t\tlogger.info(`WebSocket authenticated + bound to peer ${message.from.substring(0, 8)}`)\n\t\t\t\t\t\tfor (const cb of this.onConnectCbs) cb(message.from)\n\t\t\t\t\t}\n\t\t\t\t} else if (message.from !== authedPeerId) {\n\t\t\t\t\t// A socket authenticated as one peer cannot later speak for another.\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tfor (const handler of this.messageHandlers) {\n\t\t\t\t\thandler(message, message.from)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tws.on('close', () => {\n\t\t\t\tthis.wsConnections--\n\t\t\t\tclearTimeout(authTimer)\n\t\t\t\tthis.msgWindow.delete(connKey)\n\t\t\t\tfor (const [peerId, peer] of this.peers) {\n\t\t\t\t\tif (peer.ws === ws) {\n\t\t\t\t\t\tpeer.ws = null\n\t\t\t\t\t\tpeer.connected = false\n\t\t\t\t\t\tlogger.info(`WebSocket disconnected: ${peerId.substring(0, 8)}`)\n\t\t\t\t\t\tfor (const cb of this.onDisconnectCbs) cb(peerId)\n\t\t\t\t\t\tthis.scheduleReconnect(peerId)\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tws.on('error', (err) => {\n\t\t\t\tlogger.warn(`WebSocket error: ${err.message}`)\n\t\t\t})\n\t\t})\n\n\t\tawait this.listen()\n\n\t\t// Bound successfully — downgrade the error handler so a transient runtime\n\t\t// socket error is logged instead of crashing the process (unhandled 'error').\n\t\tthis.server!.on('error', (err) => {\n\t\t\tlogger.warn(`VoleNet server error: ${(err as Error).message}`)\n\t\t})\n\n\t\tthis.started = true\n\t\tthis.startPinging()\n\t}\n\n\t/** Bind the listening port, retrying briefly on EADDRINUSE (covers restart races). */\n\tprivate async listen(): Promise<void> {\n\t\tconst maxAttempts = 5\n\t\tconst retryDelayMs = 300\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\tconst server = this.server!\n\t\t\t\t\tconst onError = (err: Error) => {\n\t\t\t\t\t\tserver.removeListener('listening', onListening)\n\t\t\t\t\t\treject(err)\n\t\t\t\t\t}\n\t\t\t\t\tconst onListening = () => {\n\t\t\t\t\t\tserver.removeListener('error', onError)\n\t\t\t\t\t\tconst scheme = this.config.tls ? 'wss' : 'ws'\n\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t`VoleNet server listening on ${scheme}://0.0.0.0:${this.config.port} (HTTP + WebSocket)`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tresolve()\n\t\t\t\t\t}\n\t\t\t\t\tserver.once('error', onError)\n\t\t\t\t\tserver.once('listening', onListening)\n\t\t\t\t\tserver.listen(this.config.port)\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t} catch (err) {\n\t\t\t\tconst e = err as NodeJS.ErrnoException\n\t\t\t\tif (e.code === 'EADDRINUSE' && attempt < maxAttempts) {\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`VoleNet port ${this.config.port} in use — retrying (${attempt}/${maxAttempts - 1}) in ${retryDelayMs}ms`,\n\t\t\t\t\t)\n\t\t\t\t\tawait new Promise((r) => setTimeout(r, retryDelayMs))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthrow new Error(\n\t\t\t\t\te.code === 'EADDRINUSE'\n\t\t\t\t\t\t? `VoleNet could not bind port ${this.config.port}: still in use after ${maxAttempts} attempts — another instance may be running on this port.`\n\t\t\t\t\t\t: `VoleNet failed to start on port ${this.config.port}: ${e.message}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Stop the transport.\n\t */\n\tasync stop(): Promise<void> {\n\t\tif (!this.started) return\n\t\tthis.started = false\n\t\tif (this.pingTimer) {\n\t\t\tclearInterval(this.pingTimer)\n\t\t\tthis.pingTimer = null\n\t\t}\n\n\t\tfor (const [, peer] of this.peers) {\n\t\t\tif (peer.ws) peer.ws.close()\n\t\t\tif (peer.reconnectTimer) clearTimeout(peer.reconnectTimer)\n\t\t}\n\t\t// Including any socket no peer entry references any more — see adopt().\n\t\tfor (const ws of this.sockets) {\n\t\t\ttry {\n\t\t\t\tws.terminate()\n\t\t\t} catch {\n\t\t\t\t/* already gone */\n\t\t\t}\n\t\t}\n\t\tthis.sockets.clear()\n\t\tthis.peers.clear()\n\t\tthis.seenMsgs.clear()\n\t\tthis.msgWindow.clear()\n\t\tthis.globalWindow = []\n\n\t\t// Close WS clients, then force-drop any lingering keep-alive / upgraded sockets\n\t\t// so the listening port is released promptly. Otherwise server.close() waits on\n\t\t// open connections and a restart races the old listener (EADDRINUSE).\n\t\tif (this.wss) {\n\t\t\t// wss.close() waits for every client socket; reconnect churn can leave inbound\n\t\t\t// sockets that no peer entry references (a rebind replaces entry.ws, orphaning the\n\t\t\t// old one). Terminate them all or close() never calls back and stop() hangs.\n\t\t\tfor (const client of this.wss.clients) client.terminate()\n\t\t\tawait new Promise<void>((resolve) => this.wss!.close(() => resolve()))\n\t\t\tthis.wss = null\n\t\t}\n\n\t\tif (this.server) {\n\t\t\tthis.server.closeAllConnections?.()\n\t\t\tawait new Promise<void>((resolve) => this.server!.close(() => resolve()))\n\t\t\tthis.server = null\n\t\t}\n\n\t\tlogger.info('VoleNet server stopped')\n\t}\n\n\tonMessage(handler: MessageHandler): void {\n\t\tthis.messageHandlers.push(handler)\n\t}\n\n\t/** Install the outbound seal transform (direct end-to-end encryption). */\n\tsetSealer(fn: (peerId: string, message: VoleNetMessage) => VoleNetMessage): void {\n\t\tthis.sealer = fn\n\t}\n\n\t/** Install the display-name resolver used by /volenet/info when publishNames is enabled. */\n\tsetNameResolver(fn: (peerId: string) => string | undefined): void {\n\t\tthis.nameResolver = fn\n\t}\n\n\t/**\n\t * Feed a message into the normal receive pipeline as if it arrived over the wire. Used to\n\t * re-dispatch the inner message recovered from a sealed:direct envelope, so every handler\n\t * (tool calls, sync, chat) processes it — with full signature/replay/authorization checks —\n\t * exactly as an unencrypted direct message.\n\t */\n\tinjectMessage(message: VoleNetMessage): boolean {\n\t\tif (!this.verifyAndAccept(message)) return false\n\t\tfor (const handler of this.messageHandlers) handler(message, message.from)\n\t\treturn true\n\t}\n\n\t/**\n\t * Send a message to a peer.\n\t * Prefers WebSocket (instant, bidirectional), falls back to HTTP POST.\n\t */\n\tasync sendToPeer(peerId: string, message: VoleNetMessage): Promise<boolean> {\n\t\tconst peer = this.peers.get(peerId)\n\t\tif (!peer) {\n\t\t\tlogger.warn(`Peer not found: ${peerId.substring(0, 8)}`)\n\t\t\treturn false\n\t\t}\n\n\t\t// Direct-seal hook: the manager may wrap this into a sealed:direct envelope (transparent\n\t\t// end-to-end encryption to a capable peer). Relay/bootstrap types pass through unchanged.\n\t\tconst outbound = this.sealer ? this.sealer(peerId, message) : message\n\n\t\t// Try WebSocket first\n\t\tif (peer.ws && peer.ws.readyState === WebSocket.OPEN) {\n\t\t\ttry {\n\t\t\t\tpeer.ws.send(serialize(outbound))\n\t\t\t\tpeer.lastSeen = Date.now()\n\t\t\t\t// A successful push proves the channel is live — heal `connected` in case a prior\n\t\t\t\t// HTTP-fallback failure (e.g. to a NAT'd peer's unreachable advertised endpoint)\n\t\t\t\t// latched it false. Only the WS bind/open path sets it true, so without this a\n\t\t\t\t// stuck-false peer would never recover even while its socket works.\n\t\t\t\tpeer.connected = true\n\t\t\t\treturn true\n\t\t\t} catch {\n\t\t\t\t// Fall through to HTTP\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to HTTP POST\n\t\ttry {\n\t\t\tconst response = await fetch(`${peer.endpoint}/volenet/message`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\tbody: serialize(outbound),\n\t\t\t\t// Short fallback: when there's no live socket (e.g. a NAT'd peer mid-reconnect),\n\t\t\t\t// fail fast instead of hanging — the WS is the real delivery path.\n\t\t\t\tsignal: AbortSignal.timeout(5000),\n\t\t\t})\n\t\t\tpeer.lastSeen = Date.now()\n\t\t\treturn response.ok\n\t\t} catch (err) {\n\t\t\tlogger.warn(\n\t\t\t\t`Send failed for ${peerId.substring(0, 8)}: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t\t// Do NOT latch `connected = false` here. This one-shot HTTP POST fails routinely for\n\t\t\t// NAT'd peers whose advertised endpoint (a LAN/private IP) the hub can't reach — but\n\t\t\t// their real delivery path is the inbound WebSocket they hold open to us. Connectivity\n\t\t\t// is owned by the WS bind (true) / close (false) lifecycle; a failed best-effort\n\t\t\t// fallback must not mark a peer with a live socket offline.\n\t\t\treturn false\n\t\t}\n\t}\n\n\tasync broadcast(message: VoleNetMessage): Promise<number> {\n\t\tlet sent = 0\n\t\tfor (const [peerId] of this.peers) {\n\t\t\tif (await this.sendToPeer(peerId, message)) sent++\n\t\t}\n\t\treturn sent\n\t}\n\n\t/**\n\t * Register a peer and initiate WebSocket connection.\n\t */\n\taddPeer(peerId: string, endpoint: string): void {\n\t\tconst existing = this.peers.get(peerId)\n\t\tif (existing) {\n\t\t\t// Peer re-announced (e.g. after a restart). Refresh endpoint, reset backoff, and\n\t\t\t// re-open the WebSocket if it has dropped — this is what heals the send path so a\n\t\t\t// reconnect/restart doesn't leave the peer un-sendable.\n\t\t\tif (endpoint) existing.endpoint = endpoint\n\t\t\texisting.reconnectAttempts = 0\n\t\t\tif (!existing.connecting && existing.ws?.readyState !== WebSocket.OPEN) {\n\t\t\t\tthis.connectWebSocket(peerId)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tthis.peers.set(peerId, {\n\t\t\tpeerId,\n\t\t\tendpoint,\n\t\t\tconnected: false,\n\t\t\tlastSeen: Date.now(),\n\t\t\tws: null,\n\t\t\treconnectTimer: null,\n\t\t\treconnectAttempts: 0,\n\t\t\tconnecting: false,\n\t\t})\n\n\t\tthis.connectWebSocket(peerId)\n\t\tlogger.info(`Peer added: ${peerId.substring(0, 8)} (${endpoint})`)\n\t}\n\n\tremovePeer(peerId: string): void {\n\t\tconst peer = this.peers.get(peerId)\n\t\tif (peer) {\n\t\t\tif (peer.ws) peer.ws.close()\n\t\t\tif (peer.reconnectTimer) clearTimeout(peer.reconnectTimer)\n\t\t}\n\t\tthis.peers.delete(peerId)\n\t\tlogger.info(`Peer removed: ${peerId.substring(0, 8)}`)\n\t}\n\n\t/**\n\t * One socket per pair.\n\t *\n\t * Two nodes that can both dial end up with two sockets between them, and each side binds the\n\t * OTHER's dial as the peer's socket while its own stays open, unreferenced. A stop() closes\n\t * what it references and leaves the orphan; the far side then keeps believing the peer is\n\t * connected, on a socket nothing will ever close, and forwards into it.\n\t *\n\t * When a second socket appears for a peer, the node with the smaller id counts as the dialer\n\t * and both sides keep that one — so they agree — and close the other. Only a genuine double\n\t * triggers this: a node that cannot be dialed keeps whatever socket it has. Without a known\n\t * self id, the newest wins.\n\t *\n\t * @returns false when the new socket was the one let go.\n\t */\n\tprivate adopt(peer: PeerConnection, ws: WebSocket, dir: 'in' | 'out'): boolean {\n\t\tconst old = peer.ws\n\t\tconst oldLive =\n\t\t\told !== null &&\n\t\t\told !== ws &&\n\t\t\t(old.readyState === WebSocket.OPEN || old.readyState === WebSocket.CONNECTING)\n\t\tif (oldLive) {\n\t\t\tconst self = this.identityProvider?.()?.instanceId\n\t\t\tconst iDial = self !== undefined && self < peer.peerId\n\t\t\tconst keepOld = self !== undefined && ((iDial && dir === 'in') || (!iDial && dir === 'out'))\n\t\t\tif (keepOld) {\n\t\t\t\tthis.drop(ws)\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tpeer.ws = null\n\t\t\tthis.drop(old)\n\t\t}\n\t\tpeer.ws = ws\n\t\tpeer.connected = true\n\t\treturn true\n\t}\n\n\t/** Close a socket we are done with. Its close handler finds no peer referencing it and does nothing. */\n\tprivate drop(ws: WebSocket): void {\n\t\ttry {\n\t\t\tws.close()\n\t\t} catch {\n\t\t\t/* already closing */\n\t\t}\n\t}\n\n\tprivate track(ws: WebSocket): void {\n\t\tthis.sockets.add(ws)\n\t\tthis.alive.set(ws, true)\n\t\tws.on('pong', () => this.alive.set(ws, true))\n\t\tws.on('close', () => this.sockets.delete(ws))\n\t}\n\n\t/**\n\t * Liveness. A phone that walks out of Wi-Fi sends no close frame; without this its socket\n\t * would stay OPEN here for as long as TCP takes to notice, and everything sent to it is lost.\n\t * The ws library answers pings on its own, so the far side needs nothing.\n\t */\n\tprivate startPinging(): void {\n\t\tconst every = this.config.pingIntervalMs ?? WS_PING_INTERVAL_MS\n\t\tif (every <= 0) return\n\t\tthis.pingTimer = setInterval(() => {\n\t\t\tfor (const ws of this.sockets) {\n\t\t\t\tif (ws.readyState !== WebSocket.OPEN) continue\n\t\t\t\tif (this.alive.get(ws) === false) {\n\t\t\t\t\tws.terminate()\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthis.alive.set(ws, false)\n\t\t\t\ttry {\n\t\t\t\t\tws.ping()\n\t\t\t\t} catch {\n\t\t\t\t\t/* closing */\n\t\t\t\t}\n\t\t\t}\n\t\t}, every)\n\t\tthis.pingTimer.unref?.()\n\t}\n\n\tgetPeers(): Array<{\n\t\tpeerId: string\n\t\tendpoint: string\n\t\tconnected: boolean\n\t\tlastSeen: number\n\t\ttransport: 'websocket' | 'http' | 'disconnected'\n\t}> {\n\t\treturn Array.from(this.peers.values()).map((p) => ({\n\t\t\tpeerId: p.peerId,\n\t\t\tendpoint: p.endpoint,\n\t\t\tconnected: p.connected,\n\t\t\tlastSeen: p.lastSeen,\n\t\t\ttransport:\n\t\t\t\tp.ws?.readyState === WebSocket.OPEN\n\t\t\t\t\t? ('websocket' as const)\n\t\t\t\t\t: p.connected\n\t\t\t\t\t\t? ('http' as const)\n\t\t\t\t\t\t: ('disconnected' as const),\n\t\t}))\n\t}\n\n\tisPeerConnected(peerId: string): boolean {\n\t\treturn this.peers.get(peerId)?.connected ?? false\n\t}\n\n\tasync pingPeer(endpoint: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await fetch(`${endpoint}/health`, {\n\t\t\t\tsignal: AbortSignal.timeout(5000),\n\t\t\t})\n\t\t\treturn response.ok\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Connect to a peer via WebSocket.\n\t */\n\tprivate connectWebSocket(peerId: string): void {\n\t\tif (!this.started) return // no fresh dials during/after shutdown\n\t\tconst peer = this.peers.get(peerId)\n\t\tif (!peer) return\n\t\t// Don't open a second socket while one is connecting or already open.\n\t\tif (peer.connecting || peer.ws?.readyState === WebSocket.OPEN) return\n\t\tif (!peer.endpoint) return\n\n\t\tconst scheme = peer.endpoint.startsWith('https') ? 'wss' : 'ws'\n\t\tconst wsUrl = peer.endpoint.replace(/^https?/, scheme)\n\n\t\tpeer.connecting = true\n\t\ttry {\n\t\t\tconst ws = new WebSocket(wsUrl)\n\t\t\tthis.track(ws)\n\n\t\t\tws.on('open', () => {\n\t\t\t\tpeer.connecting = false\n\t\t\t\tpeer.reconnectAttempts = 0\n\t\t\t\tif (!this.adopt(peer, ws, 'out')) return // the pair's socket is the inbound one\n\t\t\t\tlogger.info(`WebSocket connected to ${peerId.substring(0, 8)}`)\n\t\t\t\t// Push a signed message now so the remote binds this socket immediately\n\t\t\t\t// (don't wait up to a heartbeat interval) — keeps reverse delivery consistent.\n\t\t\t\tfor (const cb of this.onConnectCbs) cb(peerId)\n\t\t\t})\n\n\t\t\tws.on('message', (data) => {\n\t\t\t\tif (!this.globalRateAllow()) return // global load shed\n\t\t\t\tconst message = deserialize(data.toString())\n\t\t\t\tif (!message) return\n\t\t\t\t// Verify signature + authorization + replay before dispatch (same gate as inbound).\n\t\t\t\tif (!this.verifyAndAccept(message)) return\n\t\t\t\tfor (const handler of this.messageHandlers) {\n\t\t\t\t\thandler(message, message.from)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tws.on('close', () => {\n\t\t\t\tpeer.connecting = false\n\t\t\t\tif (peer.ws === ws) {\n\t\t\t\t\tpeer.ws = null\n\t\t\t\t\tpeer.connected = false\n\t\t\t\t\t// The bound socket went away — say so, exactly as the inbound path does. This\n\t\t\t\t\t// only ever told the inbound side; a peer bound over OUR dial could vanish and\n\t\t\t\t\t// nothing upstream (roster, notices) would hear about it.\n\t\t\t\t\tlogger.info(`WebSocket to ${peerId.substring(0, 8)} closed`)\n\t\t\t\t\tfor (const cb of this.onDisconnectCbs) cb(peerId)\n\t\t\t\t}\n\t\t\t\t// Reschedule on any close, including a failed initial connect (peer.ws never set).\n\t\t\t\tthis.scheduleReconnect(peerId)\n\t\t\t})\n\n\t\t\tws.on('error', () => {\n\t\t\t\t// close event fires after error — reconnect handled there\n\t\t\t})\n\t\t} catch {\n\t\t\tpeer.connecting = false\n\t\t\tthis.scheduleReconnect(peerId)\n\t\t}\n\t}\n\n\t/**\n\t * Schedule WebSocket reconnect with exponential backoff.\n\t */\n\tprivate scheduleReconnect(peerId: string): void {\n\t\tconst peer = this.peers.get(peerId)\n\t\tif (!peer || peer.reconnectTimer) return\n\t\tif (peer.reconnectAttempts >= WS_RECONNECT_MAX_RETRIES) {\n\t\t\tlogger.warn(`Max reconnect attempts for ${peerId.substring(0, 8)} — HTTP fallback only`)\n\t\t\treturn\n\t\t}\n\n\t\tconst delay = WS_RECONNECT_INTERVAL_MS * Math.pow(1.5, peer.reconnectAttempts)\n\t\tpeer.reconnectAttempts++\n\n\t\tpeer.reconnectTimer = setTimeout(() => {\n\t\t\tpeer.reconnectTimer = null\n\t\t\tif (this.peers.has(peerId) && !peer.ws) {\n\t\t\t\tthis.connectWebSocket(peerId)\n\t\t\t}\n\t\t}, delay)\n\t}\n}\n"],"mappings":";AAMA,SAAS,cAAAA,mBAAkB;AAC3B,YAAYC,SAAQ;AACpB,YAAY,QAAQ;AACpB,YAAYC,WAAU;;;ACWtB,YAAY,YAAY;AACxB,YAAY,QAAQ;AACpB,YAAY,UAAU;AA8Bf,IAAM,wBAAwB,IAAI,KAAK,KAAK,KAAK;AACjD,IAAM,wBAAwB,IAAI,KAAK,KAAK,KAAK;AAEjD,IAAM,qBAAqB;AAElC,eAAe,SAAY,MAAc,UAAyB;AACjE,MAAI;AACH,WAAO,KAAK,MAAM,MAAS,YAAS,MAAM,OAAO,CAAC;AAAA,EACnD,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAEA,eAAe,UAAU,MAAc,OAA+B;AACrE,QAAS,SAAW,aAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACtD,QAAM,MAAM,GAAG,IAAI,IAAI,QAAQ,GAAG;AAClC,QAAS,aAAU,KAAK,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,OAAO;AAC/D,QAAS,UAAO,KAAK,IAAI;AAC1B;AAEO,IAAM,aAAN,MAAiB;AAAA,EAGvB,YACkB,MACA,QAAQ,uBACxB;AAFgB;AACA;AAAA,EACf;AAAA,EAFe;AAAA,EACA;AAAA,EAJV,UAAyB,CAAC;AAAA,EAOlC,MAAM,KAAK,MAAM,KAAK,IAAI,GAAkB;AAC3C,UAAM,MAAM,MAAM,SAAkB,KAAK,MAAM,CAAC,CAAC;AACjD,SAAK,UAAU,MAAM,QAAQ,GAAG,IAC5B,IAAsB;AAAA,MACvB,CAAC,MACA,KACA,OAAO,EAAE,QAAQ,YACjB,OAAO,EAAE,OAAO,YAChB,OAAO,EAAE,SAAS,YAClB,OAAO,EAAE,WAAW;AAAA,IACtB,IACC,CAAC;AACJ,UAAM,KAAK,MAAM,GAAG;AAAA,EACrB;AAAA,EAEA,OAAsB;AACrB,WAAO,CAAC,GAAG,KAAK,OAAO;AAAA,EACxB;AAAA,EAEA,QAAQ,IAA2B;AAClC,WAAO,KAAK,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AAAA,EAC9C;AAAA,EAEA,IAAI,OAAe;AAClB,WAAO,KAAK,QAAQ;AAAA,EACrB;AAAA,EAEA,MAAM,IAAI,OAAuF;AAChG,UAAM,OAAoB,EAAE,GAAG,OAAO,KAAK,MAAM,OAAc,kBAAW,GAAG,UAAU,EAAE;AACzF,SAAK,QAAQ,KAAK,IAAI;AACtB,UAAM,KAAK,QAAQ;AACnB,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,OAAO,KAA+B;AAC3C,UAAM,SAAS,KAAK,QAAQ;AAC5B,SAAK,UAAU,KAAK,QAAQ,OAAO,CAAC,MAAM,EAAE,QAAQ,GAAG;AACvD,QAAI,KAAK,QAAQ,WAAW,OAAQ,QAAO;AAC3C,UAAM,KAAK,QAAQ;AACnB,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,YAAY,KAAa,OAA+B;AAC7D,UAAM,IAAI,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG;AAChD,QAAI,CAAC,EAAG;AACR,MAAE;AACF,MAAE,YAAY;AACd,UAAM,KAAK,QAAQ;AAAA,EACpB;AAAA;AAAA,EAGA,MAAM,MAAM,MAAM,KAAK,IAAI,GAA2B;AACrD,UAAM,UAAU,KAAK,QAAQ,OAAO,CAAC,MAAM,MAAM,EAAE,SAAS,KAAK,KAAK;AACtE,QAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAClC,SAAK,UAAU,KAAK,QAAQ,OAAO,CAAC,MAAM,MAAM,EAAE,UAAU,KAAK,KAAK;AACtE,UAAM,KAAK,QAAQ;AACnB,WAAO;AAAA,EACR;AAAA,EAEA,MAAc,UAAyB;AACtC,UAAM,UAAU,KAAK,MAAM,KAAK,OAAO;AAAA,EACxC;AACD;AAEO,IAAM,eAAN,MAAmB;AAAA,EAIzB,YACkB,MACA,QAAQ,uBACxB;AAFgB;AACA;AAAA,EACf;AAAA,EAFe;AAAA,EACA;AAAA;AAAA,EAJV,WAAW,oBAAI,IAAsC;AAAA,EAO7D,MAAM,KAAK,MAAM,KAAK,IAAI,GAAkB;AAC3C,UAAM,MAAM,MAAM,SAAwC,KAAK,MAAM,CAAC,CAAC;AACvE,SAAK,WAAW,oBAAI,IAAI;AACxB,eAAW,CAAC,IAAI,IAAI,KAAK,OAAO,QAAQ,OAAO,CAAC,CAAC,GAAG;AACnD,UAAI,CAAC,MAAM,QAAQ,IAAI,EAAG;AAC1B,YAAM,IAAI,oBAAI,IAAyB;AACvC,iBAAW,KAAK,MAAM;AACrB,YAAI,KAAK,OAAO,EAAE,SAAS,YAAY,OAAO,EAAE,SAAS,SAAU,GAAE,IAAI,EAAE,MAAM,CAAC;AAAA,MACnF;AACA,UAAI,EAAE,KAAM,MAAK,SAAS,IAAI,IAAI,CAAC;AAAA,IACpC;AACA,UAAM,KAAK,MAAM,GAAG;AAAA,EACrB;AAAA;AAAA,EAGA,MAAM,OAAO,IAAY,MAAc,UAAkB,MAAM,KAAK,IAAI,GAAkB;AACzF,QAAI,IAAI,KAAK,SAAS,IAAI,EAAE;AAC5B,QAAI,CAAC,GAAG;AACP,UAAI,oBAAI,IAAI;AACZ,WAAK,SAAS,IAAI,IAAI,CAAC;AAAA,IACxB;AACA,UAAM,WAAW,EAAE,IAAI,IAAI;AAC3B,QAAI,UAAU;AACb,eAAS;AACT,eAAS,OAAO;AAChB,eAAS,WAAW,YAAY,SAAS;AAAA,IAC1C,OAAO;AACN,UAAI,EAAE,QAAQ,oBAAoB;AAEjC,YAAI;AACJ,mBAAW,MAAM,EAAG,KAAI,CAAC,UAAU,GAAG,CAAC,EAAE,OAAO,OAAO,CAAC,EAAE,KAAM,UAAS;AACzE,YAAI,OAAQ,GAAE,OAAO,OAAO,CAAC,CAAC;AAAA,MAC/B;AACA,QAAE,IAAI,MAAM,EAAE,MAAM,UAAU,OAAO,GAAG,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,IAChE;AACA,UAAM,KAAK,QAAQ;AAAA,EACpB;AAAA;AAAA,EAGA,MAAM,KAAK,IAAY,MAAM,KAAK,IAAI,GAA2B;AAChE,UAAM,KAAK,MAAM,GAAG;AACpB,UAAM,IAAI,KAAK,SAAS,IAAI,EAAE;AAC9B,QAAI,CAAC,KAAK,EAAE,SAAS,EAAG,QAAO,CAAC;AAChC,SAAK,SAAS,OAAO,EAAE;AACvB,UAAM,KAAK,QAAQ;AACnB,WAAO,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI;AAAA,EACtD;AAAA;AAAA,EAGA,MAAM,QAAQ,IAAY,SAAuC;AAChE,QAAI,QAAQ,WAAW,EAAG;AAC1B,QAAI,IAAI,KAAK,SAAS,IAAI,EAAE;AAC5B,QAAI,CAAC,GAAG;AACP,UAAI,oBAAI,IAAI;AACZ,WAAK,SAAS,IAAI,IAAI,CAAC;AAAA,IACxB;AACA,eAAW,KAAK,SAAS;AACxB,YAAM,KAAK,EAAE,IAAI,EAAE,IAAI;AACvB,UAAI,IAAI;AACP,WAAG,SAAS,EAAE;AACd,WAAG,QAAQ,KAAK,IAAI,GAAG,OAAO,EAAE,KAAK;AACrC,WAAG,OAAO,KAAK,IAAI,GAAG,MAAM,EAAE,IAAI;AAAA,MACnC,MAAO,GAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAAA,IAC9B;AACA,UAAM,KAAK,QAAQ;AAAA,EACpB;AAAA,EAEA,KAAK,IAA2B;AAC/B,WAAO,CAAC,GAAI,KAAK,SAAS,IAAI,EAAE,GAAG,OAAO,KAAK,CAAC,CAAE;AAAA,EACnD;AAAA;AAAA,EAGA,IAAI,OAAe;AAClB,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA,EAEA,MAAM,MAAM,MAAM,KAAK,IAAI,GAAkB;AAC5C,QAAI,UAAU;AACd,eAAW,CAAC,IAAI,CAAC,KAAK,KAAK,UAAU;AACpC,iBAAW,CAAC,MAAM,CAAC,KAAK,GAAG;AAC1B,YAAI,MAAM,EAAE,OAAO,KAAK,OAAO;AAC9B,YAAE,OAAO,IAAI;AACb,oBAAU;AAAA,QACX;AAAA,MACD;AACA,UAAI,EAAE,SAAS,EAAG,MAAK,SAAS,OAAO,EAAE;AAAA,IAC1C;AACA,QAAI,QAAS,OAAM,KAAK,QAAQ;AAAA,EACjC;AAAA,EAEA,MAAc,UAAyB;AACtC,UAAM,MAAqC,CAAC;AAC5C,eAAW,CAAC,IAAI,CAAC,KAAK,KAAK,SAAU,KAAI,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC;AAC7D,UAAM,UAAU,KAAK,MAAM,GAAG;AAAA,EAC/B;AACD;;;AC7OA,YAAYC,aAAY;AACxB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;;;ACLtB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAYtB,IAAM,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,EAAE;AAGhE,IAAI;AACJ,IAAI;AAEJ,IAAM,SAAS,oBAAI,IAA8C;AAEjE,SAAS,eAAuB;AAC/B,QAAM,SAAS,QAAQ,IAAI,kBAAkB,QAAQ,YAAY;AACjE,SAAO,OAAO,KAAK,KAAK,OAAO;AAChC;AAEA,SAAS,aAAyC;AACjD,MAAI,OAAQ,QAAO;AACnB,QAAM,OAAO,QAAQ,IAAI;AACzB,MAAI,CAAC,KAAM,QAAO;AAClB,EAAG,cAAe,cAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACpD,WAAY,sBAAkB,MAAM,EAAE,OAAO,IAAI,CAAC;AAClD,SAAO;AACR;AAEA,SAAS,MAAM,OAAc,QAAgB,KAAa,MAAuB;AAChF,QAAM,MAAM,WAAW;AACvB,MAAI,CAAC,OAAO,OAAO,KAAK,IAAI,aAAa,EAAG;AAC5C,QAAM,QAAQ,KAAK,SAChB,IAAI,KAAK,IAAI,CAAC,MAAO,OAAO,MAAM,WAAW,IAAI,KAAK,UAAU,CAAC,CAAE,EAAE,KAAK,GAAG,CAAC,KAC9E;AACH,MAAI,MAAM,IAAG,oBAAI,KAAK,GAAE,YAAY,CAAC,KAAK,MAAM,YAAY,CAAC,KAAK,MAAM,IAAI,GAAG,GAAG,KAAK;AAAA,CAAI;AAC5F;AAEA,SAAS,WAAW,KAAqB;AACxC,QAAM,SAAS,IAAI,GAAG;AACtB,QAAM,KACL,CAAC,UACD,CAAC,QAAgB,SAChB,MAAM,OAAO,QAAQ,KAAK,IAAI;AAChC,SAAO;AAAA,IACN,OAAO,GAAG,OAAO;AAAA,IACjB,MAAM,GAAG,MAAM;AAAA,IACf,MAAM,GAAG,MAAM;AAAA,IACf,OAAO,GAAG,OAAO;AAAA,IACjB,OAAO,GAAG,OAAO;AAAA,EAClB;AACD;AAMO,SAAS,iBAAiB,MAA2B;AAC3D,YAAU;AACV,aAAW,SAAS,OAAO,OAAO,EAAG,OAAM,UAAU,KAAK,MAAM,GAAG;AACpE;AAEO,SAAS,aAAa,KAAqB;AACjD,QAAM,QAAQ,EAAE,KAAK,SAAS,UAAU,QAAQ,GAAG,IAAI,WAAW,GAAG,EAAE;AACvE,SAAO,IAAI,KAAK,KAAK;AAErB,QAAM,KACL,CAAC,UACD,CAAC,QAAgB,SAChB,MAAM,QAAQ,KAAK,EAAE,KAAK,GAAG,IAAI;AACnC,SAAO;AAAA,IACN,OAAO,GAAG,OAAO;AAAA,IACjB,MAAM,GAAG,MAAM;AAAA,IACf,MAAM,GAAG,MAAM;AAAA,IACf,OAAO,GAAG,OAAO;AAAA,IACjB,OAAO,GAAG,OAAO;AAAA,EAClB;AACD;AAGO,SAAS,cAAoB;AACnC,UAAQ,IAAI;AACZ,WAAS;AACV;;;AC5EA,YAAYC,aAAY;;;ACejB,IAAM,QAAQ,CAAC,QAAiC;AAEhD,IAAM,OAAO,CAAC,WAAgC;AAC9C,IAAM,WAAW,CAAC,aAAoC;;;ADJ7D,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AACtB,IAAM,cAAc;AACpB,IAAM,YAAY;AAOX,SAAS,uBAA6D;AAC5E,MAAI;AAEH,WAAc,4BAAoB,YAAwB;AAAA,EAC3D,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAEO,SAAS,cAAc,KAA+B;AAC5D,SAAO,OAAO,KAAK,IAAI,OAAO,EAAE,MAAM,QAAQ,QAAQ,MAAM,CAAC,CAAC,EAAE,SAAS,QAAQ;AAClF;AAEO,SAAS,aAAa,KAA2C;AACvE,MAAI;AACH,WAAc,wBAAgB,EAAE,KAAK,OAAO,KAAK,KAAK,QAAQ,GAAG,QAAQ,OAAO,MAAM,OAAO,CAAC;AAAA,EAC/F,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAEO,SAAS,iBAAiB,KAA2C;AAC3E,MAAI;AACH,WAAc,wBAAgB,EAAE,KAAK,OAAO,KAAK,KAAK,QAAQ,GAAG,QAAQ,OAAO,MAAM,OAAO,CAAC;AAAA,EAC/F,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAEA,SAAS,UAAU,KAAa,QAAgB,KAAqB;AACpE,SAAO,OAAO,KAAY,iBAAS,UAAU,KAAK,OAAO,MAAM,CAAC,GAAG,GAAG,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC;AAC3F;AAMO,SAAS,KACf,kBACA,WACA,KACA,sBACmB;AACnB,QAAM,YAAY,aAAa,gBAAgB;AAC/C,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,MAAa,4BAAoB,QAAQ;AAC/C,QAAM,MAAa,sBAAc,EAAE,YAAY,IAAI,YAAY,WAAW,UAAU,CAAC;AAErF,MAAI,MAAM;AACV,MAAI,SAAS;AACb,MAAI;AACJ,MAAI,sBAAsB;AACzB,UAAM,WAAW,iBAAiB,oBAAoB;AACtD,QAAI,UAAU;AACb,UAAI;AACH,cAAM,EAAE,WAAW,WAAW,IAAI,MAAMC,OAAM,EAAE,YAAY,QAAQ;AACpE,cAAM,OAAO,OAAO,CAAC,KAAK,OAAO,KAAK,SAAS,CAAC,CAAC;AACjD,iBAAS;AACT,cAAM,OAAO,KAAK,UAAU,EAAE,SAAS,QAAQ;AAAA,MAChD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA,EACD;AAEA,QAAM,MAAM,UAAU,KAAK,QAAQ,GAAG;AACtC,QAAM,QAAe,oBAAY,WAAW;AAC5C,QAAM,SAAS;AAAA,IACP,uBAAe,qBAAqB,KAAK,OAAO,EAAE,eAAe,UAAU,CAAC;AAAA,EACpF;AACA,SAAO,OAAO,OAAO,KAAK,KAAK,MAAM,CAAC;AACtC,QAAM,KAAK,OAAO,OAAO,CAAC,OAAO,OAAO,SAAS,GAAG,OAAO,MAAM,GAAG,OAAO,WAAW,CAAC,CAAC;AACxF,SAAO;AAAA,IACN,KAAK,cAAc,IAAI,SAAS;AAAA,IAChC,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,IACrB,GAAG,MAAM,SAAS,QAAQ;AAAA,IAC1B,GAAG,GAAG,SAAS,QAAQ;AAAA,EACxB;AACD;AAOO,SAAS,OACf,aACA,KACA,KACA,iBACgB;AAChB,MAAI;AACH,UAAM,MAAM,aAAa,IAAI,GAAG;AAChC,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,MAAa,sBAAc,EAAE,YAAY,aAAa,WAAW,IAAI,CAAC;AAE5E,QAAI,MAAM;AACV,QAAI,SAAS;AACb,QAAI,IAAI,KAAK;AACZ,UAAI,CAAC,gBAAiB,QAAO;AAC7B,YAAM,OAAO,MAAMA,OAAM,EAAE,YAAY,iBAAiB,OAAO,KAAK,IAAI,KAAK,QAAQ,CAAC;AACtF,YAAM,OAAO,OAAO,CAAC,KAAK,OAAO,KAAK,IAAI,CAAC,CAAC;AAC5C,eAAS;AAAA,IACV;AAEA,UAAM,MAAM,UAAU,KAAK,QAAQ,GAAG;AACtC,UAAM,QAAQ,OAAO,KAAK,IAAI,GAAG,QAAQ;AACzC,UAAM,KAAK,OAAO,KAAK,IAAI,GAAG,QAAQ;AACtC,QAAI,MAAM,WAAW,eAAe,GAAG,UAAU,UAAW,QAAO;AACnE,UAAM,WAAW;AAAA,MACT,yBAAiB,qBAAqB,KAAK,OAAO,EAAE,eAAe,UAAU,CAAC;AAAA,IACtF;AACA,aAAS,OAAO,OAAO,KAAK,KAAK,MAAM,CAAC;AACxC,aAAS,WAAW,GAAG,SAAS,GAAG,SAAS,SAAS,CAAC;AACtD,WAAO,OAAO,OAAO,CAAC,SAAS,OAAO,GAAG,SAAS,GAAG,GAAG,SAAS,SAAS,CAAC,GAAG,SAAS,MAAM,CAAC,CAAC;AAAA,EAChG,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAGO,SAAS,eACf,SACA,MACA,IACA,cACU;AACV,QAAM,MAAM,GAAG,IAAI,IAAI,EAAE;AACzB,QAAM,MAAM,KAAK,IAAI;AACrB,MAAI,QAAQ,OAAO,MAAM;AACxB,eAAW,CAAC,GAAG,EAAE,KAAK;AACrB,UAAI,GAAG,WAAW,KAAK,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,IAAQ,SAAQ,OAAO,CAAC;AAAA,EAC3E;AACA,QAAM,OAAO,QAAQ,IAAI,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,MAAM,IAAI,GAAM;AACnE,MAAI,IAAI,UAAU,cAAc;AAC/B,YAAQ,IAAI,KAAK,GAAG;AACpB,WAAO;AAAA,EACR;AACA,MAAI,KAAK,GAAG;AACZ,UAAQ,IAAI,KAAK,GAAG;AACpB,SAAO;AACR;;;AFzKA,IAAM,SAAS,aAAa,cAAc;AAuB1C,SAAS,gBAAsD;AAC9D,MAAI;AAEH,WAAc,4BAAoB,WAAwB;AAAA,EAC3D,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAGA,SAAS,iBAAiB,KAA2C;AACpE,MAAI;AACH,WAAc,wBAAgB,EAAE,KAAK,OAAO,KAAK,KAAK,QAAQ,GAAG,QAAQ,OAAO,MAAM,OAAO,CAAC;AAAA,EAC/F,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAMA,eAAsB,gBAAgB,QAAgB,cAA4C;AACjG,QAAS,UAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AAE1C,QAAM,EAAE,WAAW,WAAW,IAAW,4BAAoB,SAAS;AAGtE,QAAM,aAAa,WAAW,OAAO,EAAE,MAAM,SAAS,QAAQ,MAAM,CAAC;AACrE,QAAM,YAAY,UAAU,OAAO,EAAE,MAAM,QAAQ,QAAQ,MAAM,CAAC;AAClE,QAAM,YAAY,OAAO,KAAK,SAAS,EAAE,SAAS,QAAQ;AAE1D,QAAM,KAAK,cAAc;AACzB,MAAI,QAAQ;AACZ,MAAI,IAAI;AACP,YAAQ,OAAO,KAAK,GAAG,UAAU,OAAO,EAAE,MAAM,QAAQ,QAAQ,MAAM,CAAC,CAAC,EAAE,SAAS,QAAQ;AAC3F,UAAS;AAAA,MACH,WAAK,QAAQ,aAAa;AAAA,MAC/B,GAAG,WAAW,OAAO,EAAE,MAAM,SAAS,QAAQ,MAAM,CAAC;AAAA,MACrD,EAAE,MAAM,IAAM;AAAA,IACf;AAAA,EACD;AACA,QAAM,kBAAkB,KACrB,gBAAgB,SAAS,IAAI,YAAY,IAAI,KAAK,KAClD,gBAAgB,SAAS,IAAI,YAAY;AAG5C,QAAM,iBAAsB,WAAK,QAAQ,UAAU;AACnD,QAAS,cAAU,gBAAgB,YAAY,EAAE,MAAM,IAAM,CAAC;AAG9D,QAAM,gBAAqB,WAAK,QAAQ,cAAc;AACtD,QAAS,cAAU,eAAe,kBAAkB,MAAM,OAAO;AAGjE,QAAM,iBAAsB,WAAK,QAAQ,kBAAkB;AAC3D,MAAI;AACH,UAAS,WAAO,cAAc;AAAA,EAC/B,QAAQ;AACP,UAAS,cAAU,gBAAgB,IAAI,OAAO;AAAA,EAC/C;AAIA,QAAM,IAAW,4BAAoB,QAAQ;AAC7C,QAAS;AAAA,IACH,WAAK,QAAQ,iBAAiB;AAAA,IACnC,EAAE,WAAW,OAAO,EAAE,MAAM,SAAS,QAAQ,MAAM,CAAC;AAAA,IACpD,EAAE,MAAM,IAAM;AAAA,EACf;AACA,QAAM,gBAAgB,OAAO,KAAK,EAAE,UAAU,OAAO,EAAE,MAAM,QAAQ,QAAQ,MAAM,CAAC,CAAC,EAAE;AAAA,IACtF;AAAA,EACD;AACA,QAAS,cAAe,WAAK,QAAQ,qBAAqB,GAAG,GAAG,aAAa;AAAA,GAAM,OAAO;AAG1F,QAAMC,SAAQ,qBAAqB;AACnC,MAAI;AACJ,MAAIA,QAAO;AACV,UAAS;AAAA,MACH,WAAK,QAAQ,gBAAgB;AAAA,MAClCA,OAAM,WAAW,OAAO,EAAE,MAAM,SAAS,QAAQ,MAAM,CAAC;AAAA,MACxD,EAAE,MAAM,IAAM;AAAA,IACf;AACA,wBAAoB,OAAO;AAAA,MAC1BA,OAAM,UAAU,OAAO,EAAE,MAAM,QAAQ,QAAQ,MAAM,CAAC;AAAA,IACvD,EAAE,SAAS,QAAQ;AACnB,UAAS,cAAe,WAAK,QAAQ,oBAAoB,GAAG,GAAG,iBAAiB;AAAA,GAAM,OAAO;AAAA,EAC9F;AAEA,QAAM,aAAa,iBAAiB,SAAS;AAC7C,SAAO;AAAA,IACN,yCAAoC,UAAU,GAAG,KAAK,+BAA+B,YAAY;AAAA,EAClG;AAEA,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,IAAI;AAAA,IACjB,cAAc,IAAI;AAAA,IAClB,YAAY,EAAE;AAAA,IACd,aAAa,EAAE;AAAA,IACf;AAAA,IACA,gBAAgBA,QAAO;AAAA,IACvB,iBAAiBA,QAAO;AAAA,IACxB;AAAA,EACD;AACD;AAKA,eAAsB,YAAY,QAA6C;AAC9E,QAAM,iBAAsB,WAAK,QAAQ,UAAU;AACnD,QAAM,gBAAqB,WAAK,QAAQ,cAAc;AAEtD,MAAI;AACH,UAAM,aAAa,MAAS,aAAS,gBAAgB,OAAO;AAC5D,UAAM,mBAAmB,MAAS,aAAS,eAAe,OAAO,GAAG,KAAK;AAEzE,UAAM,aAAoB,yBAAiB,UAAU;AACrD,UAAM,YAAmB,wBAAgB,UAAU;AAEnD,UAAM,QAAQ,gBAAgB,MAAM,GAAG;AACvC,QAAI,MAAM,CAAC,MAAM,kBAAkB,MAAM,SAAS,GAAG;AACpD,aAAO,MAAM,2BAA2B;AACxC,aAAO;AAAA,IACR;AAEA,UAAM,aAAa,iBAAiB,MAAM,CAAC,CAAC;AAG5C,QAAI;AACJ,QAAI;AACJ,QAAI,uBAAuB;AAC3B,QAAI;AACH,YAAM,QAAQ,MAAS,aAAc,WAAK,QAAQ,aAAa,GAAG,OAAO;AACzE,qBAAsB,yBAAiB,KAAK;AAC5C,oBAAqB,wBAAgB,YAAY;AAAA,IAClD,QAAQ;AACP,YAAM,KAAK,cAAc;AACzB,UAAI,IAAI;AACP,uBAAe,GAAG;AAClB,sBAAc,GAAG;AACjB,cAAS;AAAA,UACH,WAAK,QAAQ,aAAa;AAAA,UAC/B,GAAG,WAAW,OAAO,EAAE,MAAM,SAAS,QAAQ,MAAM,CAAC;AAAA,UACrD,EAAE,MAAM,IAAM;AAAA,QACf;AACA,YAAI,MAAM,SAAS,GAAG;AACrB,gBAAM,QAAQ,OAAO,KAAK,GAAG,UAAU,OAAO,EAAE,MAAM,QAAQ,QAAQ,MAAM,CAAC,CAAC,EAAE;AAAA,YAC/E;AAAA,UACD;AACA,iCAAuB,gBAAgB,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,KAAK,WAAW,UAAU,GAAG,CAAC,CAAC,IAAI,KAAK;AAClG,gBAAS;AAAA,YACH,WAAK,QAAQ,cAAc;AAAA,YAChC,uBAAuB;AAAA,YACvB;AAAA,UACD;AACA,iBAAO,KAAK,mDAAmD;AAAA,QAChE;AAAA,MACD;AAAA,IACD;AAGA,QAAI;AACJ,QAAI;AACJ,QAAI;AACH,YAAM,OAAO,MAAS,aAAc,WAAK,QAAQ,iBAAiB,GAAG,OAAO;AAC5E,oBAAqB,yBAAiB,IAAI;AAC1C,mBAAoB,wBAAgB,WAAW;AAAA,IAChD,QAAQ;AACP,YAAM,IAAW,4BAAoB,QAAQ;AAC7C,oBAAc,EAAE;AAChB,mBAAa,EAAE;AACf,YAAS;AAAA,QACH,WAAK,QAAQ,iBAAiB;AAAA,QACnC,EAAE,WAAW,OAAO,EAAE,MAAM,SAAS,QAAQ,MAAM,CAAC;AAAA,QACpD,EAAE,MAAM,IAAM;AAAA,MACf;AACA,aAAO,KAAK,uDAAuD;AAAA,IACpE;AACA,UAAM,gBAAgB,OAAO,KAAK,WAAW,OAAO,EAAE,MAAM,QAAQ,QAAQ,MAAM,CAAC,CAAC,EAAE;AAAA,MACrF;AAAA,IACD;AACA,UAAS,cAAe,WAAK,QAAQ,qBAAqB,GAAG,GAAG,aAAa;AAAA,GAAM,OAAO;AAI1F,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AACH,YAAM,WAAW,MAAS,aAAc,WAAK,QAAQ,gBAAgB,GAAG,OAAO;AAC/E,wBAAyB,yBAAiB,QAAQ;AAClD,uBAAwB,wBAAgB,eAAe;AAAA,IACxD,QAAQ;AACP,YAAMA,SAAQ,qBAAqB;AACnC,UAAIA,QAAO;AACV,0BAAkBA,OAAM;AACxB,yBAAiBA,OAAM;AACvB,cAAS;AAAA,UACH,WAAK,QAAQ,gBAAgB;AAAA,UAClCA,OAAM,WAAW,OAAO,EAAE,MAAM,SAAS,QAAQ,MAAM,CAAC;AAAA,UACxD,EAAE,MAAM,IAAM;AAAA,QACf;AACA,eAAO,KAAK,6DAA6D;AAAA,MAC1E;AAAA,IACD;AACA,QAAI,gBAAgB;AACnB,0BAAoB,OAAO;AAAA,QAC1B,eAAe,OAAO,EAAE,MAAM,QAAQ,QAAQ,MAAM,CAAC;AAAA,MACtD,EAAE,SAAS,QAAQ;AACnB,YAAS,cAAe,WAAK,QAAQ,oBAAoB,GAAG,GAAG,iBAAiB;AAAA,GAAM,OAAO;AAAA,IAC9F;AAEA,WAAO;AAAA,MACN;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACD,SAAS,KAAK;AACb,QAAK,IAA8B,SAAS,UAAU;AACrD,aAAO;AAAA,IACR;AACA,WAAO,MAAM,2BAA2B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC1F,WAAO;AAAA,EACR;AACD;AAMO,SAASC,MAAK,YAA8B,MAAsB;AACxE,QAAM,YAAmB,aAAK,MAAM,OAAO,KAAK,IAAI,GAAG,UAAU;AACjE,SAAO,UAAU,SAAS,QAAQ;AACnC;AAKO,SAASC,QAAO,WAA6B,MAAc,WAA4B;AAC7F,MAAI;AACH,WAAc,eAAO,MAAM,OAAO,KAAK,IAAI,GAAG,WAAW,OAAO,KAAK,WAAW,QAAQ,CAAC;AAAA,EAC1F,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAKO,SAAS,eAAe,WAKtB;AACR,QAAM,QAAQ,UAAU,KAAK,EAAE,MAAM,GAAG;AACxC,MAAI,MAAM,CAAC,MAAM,kBAAkB,MAAM,SAAS,EAAG,QAAO;AAE5D,MAAI;AACH,UAAM,MAAM,OAAO,KAAK,MAAM,CAAC,GAAG,QAAQ;AAC1C,UAAM,YAAmB,wBAAgB,EAAE,KAAK,KAAK,QAAQ,OAAO,MAAM,OAAO,CAAC;AAClF,UAAM,aAAa,iBAAiB,MAAM,CAAC,CAAC;AAC5C,UAAM,OAAO,MAAM,CAAC,KAAK,WAAW,UAAU,GAAG,CAAC;AAElD,UAAM,cAAc,MAAM,CAAC,IAAI,iBAAiB,MAAM,CAAC,CAAC,IAAI;AAC5D,WAAO,EAAE,WAAW,YAAY,MAAM,YAAY;AAAA,EACnD,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAKA,eAAsB,oBAAoB,QASxC;AACD,QAAM,iBAAsB,WAAK,QAAQ,kBAAkB;AAC3D,QAAM,UAAU,oBAAI,IAGlB;AAEF,MAAI;AACH,UAAM,UAAU,MAAS,aAAS,gBAAgB,OAAO;AACzD,eAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACvC,YAAM,UAAU,KAAK,KAAK;AAC1B,UAAI,CAAC,WAAW,QAAQ,WAAW,GAAG,EAAG;AAEzC,YAAM,SAAS,eAAe,OAAO;AACrC,UAAI,QAAQ;AACX,gBAAQ,IAAI,OAAO,YAAY;AAAA,UAC9B,WAAW,OAAO;AAAA,UAClB,MAAM,OAAO;AAAA,UACb,aAAa,OAAO;AAAA,QACrB,CAAC;AAAA,MACF;AAAA,IACD;AAAA,EACD,QAAQ;AAAA,EAER;AAEA,SAAO;AACR;AAKA,eAAsB,UACrB,QACA,iBACA,MACkB;AAClB,QAAM,SAAS,eAAe,eAAe;AAC7C,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,mEAAmE;AAEhG,QAAM,iBAAsB,WAAK,QAAQ,kBAAkB;AAG3D,QAAM,WAAW,MAAM,oBAAoB,MAAM;AACjD,QAAM,UAAU,SAAS,IAAI,OAAO,UAAU;AAC9C,MAAI,SAAS;AAIZ,UAAM,eAAe,MAAM,iBAAiB;AAC5C,QAAI,gBAAgB,CAAC,QAAQ,eAAe,OAAO,aAAa;AAC/D,YAAM,sBAAsB,QAAQ,OAAO,YAAY,gBAAgB,KAAK,CAAC;AAC7E,aAAO;AAAA,QACN,+BAA+B,OAAO,IAAI,KAAK,OAAO,WAAW,UAAU,GAAG,CAAC,CAAC;AAAA,MACjF;AAAA,IACD;AACA,WAAO,OAAO;AAAA,EACf;AAGA,QAAS,eAAW,gBAAgB,gBAAgB,KAAK,IAAI,MAAM,OAAO;AAC1E,SAAO,KAAK,iBAAiB,OAAO,IAAI,KAAK,OAAO,WAAW,UAAU,GAAG,CAAC,CAAC,GAAG;AAEjF,SAAO,OAAO;AACf;AAGA,eAAe,sBACd,QACA,YACA,SACgB;AAChB,QAAM,iBAAsB,WAAK,QAAQ,kBAAkB;AAC3D,QAAM,UAAU,MAAS,aAAS,gBAAgB,OAAO,EAAE,MAAM,MAAM,EAAE;AACzE,MAAI,WAAW;AACf,QAAM,MAAM,QAAQ,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS;AAC7C,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,WAAW,QAAQ,WAAW,GAAG,EAAG,QAAO;AAChD,UAAM,SAAS,eAAe,OAAO;AACrC,QAAI,UAAU,OAAO,eAAe,YAAY;AAC/C,iBAAW;AACX,aAAO;AAAA,IACR;AACA,WAAO;AAAA,EACR,CAAC;AACD,MAAI,CAAC,SAAU,KAAI,KAAK,OAAO;AAC/B,QAAS,cAAU,gBAAgB,IAAI,KAAK,IAAI,GAAG,OAAO;AAC3D;AAKA,eAAsB,WAAW,QAAgB,iBAA2C;AAC3F,QAAM,iBAAsB,WAAK,QAAQ,kBAAkB;AAE3D,MAAI;AACH,UAAM,UAAU,MAAS,aAAS,gBAAgB,OAAO;AACzD,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,UAAM,WAAW,MAAM,OAAO,CAAC,SAAS;AACvC,YAAM,UAAU,KAAK,KAAK;AAC1B,UAAI,CAAC,WAAW,QAAQ,WAAW,GAAG,EAAG,QAAO;AAChD,YAAM,SAAS,eAAe,OAAO;AACrC,UAAI,CAAC,OAAQ,QAAO;AACpB,aAAO,OAAO,eAAe,mBAAmB,CAAC,QAAQ,SAAS,eAAe;AAAA,IAClF,CAAC;AAED,QAAI,SAAS,WAAW,MAAM,OAAQ,QAAO;AAE7C,UAAS,cAAU,gBAAgB,SAAS,KAAK,IAAI,GAAG,OAAO;AAC/D,WAAO,KAAK,iBAAiB,gBAAgB,UAAU,GAAG,CAAC,CAAC,EAAE;AAC9D,WAAO;AAAA,EACR,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAQA,eAAsB,iBACrB,QAGC;AACD,QAAM,cAAmB,WAAK,QAAQ,eAAe;AACrD,QAAM,UAAU,oBAAI,IAGlB;AACF,MAAI;AACH,UAAM,UAAU,MAAS,aAAS,aAAa,OAAO;AACtD,eAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACvC,YAAM,UAAU,KAAK,KAAK;AAC1B,UAAI,CAAC,WAAW,QAAQ,WAAW,GAAG,EAAG;AACzC,YAAM,SAAS,eAAe,OAAO;AACrC,UAAI,QAAQ;AACX,gBAAQ,IAAI,OAAO,YAAY;AAAA,UAC9B,WAAW,OAAO;AAAA,UAClB,MAAM,OAAO;AAAA,UACb,aAAa,OAAO;AAAA,QACrB,CAAC;AAAA,MACF;AAAA,IACD;AAAA,EACD,QAAQ;AAAA,EAER;AACA,SAAO;AACR;AAGA,eAAsB,eAAe,QAAgB,iBAA0C;AAC9F,QAAM,SAAS,eAAe,eAAe;AAC7C,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,mEAAmE;AAChG,QAAM,cAAmB,WAAK,QAAQ,eAAe;AACrD,QAAM,WAAW,MAAM,iBAAiB,MAAM;AAC9C,MAAI,SAAS,IAAI,OAAO,UAAU,EAAG,QAAO,OAAO;AACnD,QAAS,eAAW,aAAa,gBAAgB,KAAK,IAAI,MAAM,OAAO;AACvE,SAAO,KAAK,2BAA2B,OAAO,IAAI,KAAK,OAAO,WAAW,UAAU,GAAG,CAAC,CAAC,GAAG;AAC3F,SAAO,OAAO;AACf;AAGA,eAAsB,kBAAkB,QAAgB,iBAA2C;AAClG,QAAM,cAAmB,WAAK,QAAQ,eAAe;AACrD,MAAI;AACH,UAAM,UAAU,MAAS,aAAS,aAAa,OAAO;AACtD,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,UAAM,WAAW,MAAM,OAAO,CAAC,SAAS;AACvC,YAAM,UAAU,KAAK,KAAK;AAC1B,UAAI,CAAC,WAAW,QAAQ,WAAW,GAAG,EAAG,QAAO;AAChD,YAAM,SAAS,eAAe,OAAO;AACrC,UAAI,CAAC,OAAQ,QAAO;AACpB,aAAO,OAAO,eAAe,mBAAmB,CAAC,QAAQ,SAAS,eAAe;AAAA,IAClF,CAAC;AACD,QAAI,SAAS,WAAW,MAAM,OAAQ,QAAO;AAC7C,UAAS,cAAU,aAAa,SAAS,KAAK,IAAI,GAAG,OAAO;AAC5D,WAAO,KAAK,2BAA2B,gBAAgB,UAAU,GAAG,CAAC,CAAC,EAAE;AACxE,WAAO;AAAA,EACR,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAaA,SAAS,iBAAiB,cAA8B;AACvD,SAAc,mBAAW,QAAQ,EAAE,OAAO,YAAY,EAAE,OAAO,KAAK,EAAE,UAAU,GAAG,EAAE;AACtF;;;AIphBA,YAAYC,aAAY;AAIjB,IAAM,kBAAkB;AACxB,IAAM,qBAAqB;AAwG3B,SAAS,cACf,MACA,MACA,IACA,SACA,YACA,cACiB;AACjB,QAAM,KAAY,mBAAW;AAC7B,QAAM,YAAY,KAAK,IAAI;AAG3B,QAAM,aAAa,aAAa,IAAI,MAAM,MAAM,IAAI,WAAW,OAAO;AACtE,QAAM,YAAYC,MAAK,YAAY,UAAU;AAC7C,QAAM,QAAQ,eAAeA,MAAK,cAAc,UAAU,IAAI;AAE9D,SAAO;AAAA,IACN,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,IACzB;AAAA,EACD;AACD;AAKO,SAAS,cACf,SACA,WACA,aACqC;AAErC,MAAI,QAAQ,YAAY,iBAAiB;AACxC,WAAO,EAAE,OAAO,OAAO,OAAO,iCAAiC,QAAQ,OAAO,GAAG;AAAA,EAClF;AAIA,MAAI,OAAO,QAAQ,cAAc,YAAY,CAAC,OAAO,SAAS,QAAQ,SAAS,GAAG;AACjF,WAAO,EAAE,OAAO,OAAO,OAAO,+BAA+B;AAAA,EAC9D;AACA,QAAM,MAAM,KAAK,IAAI,IAAI,QAAQ;AACjC,MAAI,MAAM,oBAAoB;AAC7B,WAAO,EAAE,OAAO,OAAO,OAAO,oBAAoB,GAAG,YAAY,kBAAkB,MAAM;AAAA,EAC1F;AACA,MAAI,MAAM,MAAO;AAChB,WAAO,EAAE,OAAO,OAAO,OAAO,4BAA4B,CAAC,GAAG,WAAW;AAAA,EAC1E;AAGA,QAAM,aAAa;AAAA,IAClB,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACT;AACA,QAAM,QAAQC,QAAO,WAAW,YAAY,QAAQ,SAAS;AAC7D,MAAI,CAAC,OAAO;AACX,WAAO,EAAE,OAAO,OAAO,OAAO,oBAAoB;AAAA,EACnD;AAIA,MAAI,aAAa;AAChB,QAAI,CAAC,QAAQ,OAAO;AACnB,aAAO,EAAE,OAAO,OAAO,OAAO,iCAAiC;AAAA,IAChE;AACA,QAAI,CAACA,QAAO,aAAa,YAAY,QAAQ,KAAK,GAAG;AACpD,aAAO,EAAE,OAAO,OAAO,OAAO,iCAAiC;AAAA,IAChE;AAAA,EACD;AAEA,SAAO,EAAE,OAAO,KAAK;AACtB;AAKO,SAAS,UAAU,SAAiC;AAC1D,SAAO,KAAK,UAAU,OAAO;AAC9B;AAKO,SAAS,YAAY,MAAqC;AAChE,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,QAAI,CAAC,OAAO,WAAW,CAAC,OAAO,QAAQ,CAAC,OAAO,QAAQ,CAAC,OAAO,IAAI;AAClE,aAAO;AAAA,IACR;AACA,WAAO;AAAA,EACR,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAOA,SAAS,cAAc,OAAwB;AAC9C,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO,KAAK,UAAU,KAAK,KAAK;AACjF,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,IAAI,MAAM,IAAI,aAAa,EAAE,KAAK,GAAG,CAAC;AACvE,QAAM,MAAM;AAIZ,SAAO,IAAI,OAAO,KAAK,GAAG,EACxB,KAAK,EACL,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,MAAS,EAClC,IAAI,CAAC,MAAM,GAAG,KAAK,UAAU,CAAC,CAAC,IAAI,cAAc,IAAI,CAAC,CAAC,CAAC,EAAE,EAC1D,KAAK,GAAG,CAAC;AACZ;AAEA,SAAS,aACR,IACA,MACA,MACA,IACA,WACA,SACS;AAGT,QAAM,aAAa,YAAY,UAAa,YAAY,OAAO,cAAc,OAAO,IAAI;AACxF,SAAO,GAAG,eAAe,IAAI,EAAE,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,IAAI,SAAS,IAAI,UAAU;AACjF;;;AC1OA,IAAMC,UAAS,aAAa,mBAAmB;AAE/C,IAAM,qBAAqB;AAC3B,IAAM,kBAAkB;AA4BjB,SAAS,kBACf,gBACA,YACgB;AAChB,MAAI,CAAC,gBAAgB,UAAU,CAAC,WAAY,QAAO;AACnD,QAAM,OAAO,CAAC,MAAc,EAAE,KAAK,EAAE,QAAQ,QAAQ,EAAE;AACvD,QAAM,SAAS,CAAC,MAA6B;AAC5C,QAAI;AACH,aAAO,IAAI,IAAI,KAAK,CAAC,CAAC,EAAE,YAAY;AAAA,IACrC,QAAQ;AACP,aAAO;AAAA,IACR;AAAA,EACD;AACA,QAAM,MAAM,KAAK,UAAU;AAC3B,QAAM,UAAU,OAAO,GAAG;AAC1B,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,OAAO,eAAe,IAAI,IAAI;AACpC,MAAI,KAAK,SAAS,GAAG,EAAG,QAAO;AAC/B,SAAO,KAAK,KAAK,CAAC,MAAM,OAAO,CAAC,MAAM,OAAO,KAAK;AACnD;AAEO,IAAM,mBAAN,MAAuB;AAAA,EACrB,YAAY,oBAAI,IAA6B;AAAA,EAC7C,cAAc,oBAAI,IAA8B;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB,oBAAI,IAG5B;AAAA,EACM;AAAA;AAAA,EAEA,cAAc,oBAAI,IAAY;AAAA,EAEtC,YAAY,WAA6B,QAAyB;AACjE,SAAK,YAAY;AACjB,SAAK,SAAS;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,iBAAiB,SAA2B;AAC3C,SAAK,gBAAgB;AAAA,EACtB;AAAA,EAEA,MAAM,QAAuB;AAE5B,SAAK,kBAAkB,MAAM,oBAAoB,KAAK,OAAO,MAAM;AACnE,IAAAA,QAAO,KAAK,UAAU,KAAK,gBAAgB,IAAI,qBAAqB;AAGpE,SAAK,UAAU,UAAU,CAAC,SAAS,WAAW;AAC7C,WAAK,cAAc,SAAS,MAAM;AAAA,IACnC,CAAC;AAID,SAAK,UAAU,aAAa,CAAC,WAAW,KAAK,SAAS,MAAM,CAAC;AAG7D,SAAK,cAAc,YAAY,MAAM,KAAK,YAAY,GAAG,kBAAkB;AAE3E,IAAAA,QAAO;AAAA,MACN,sCAAiC,KAAK,OAAO,YAAY,KAAK,KAAK,OAAO,WAAW,UAAU,GAAG,CAAC,CAAC;AAAA,IACrG;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,OAAa;AACZ,QAAI,KAAK,aAAa;AACrB,oBAAc,KAAK,WAAW;AAC9B,WAAK,cAAc;AAAA,IACpB;AACA,SAAK,UAAU,MAAM;AACrB,SAAK,YAAY,MAAM;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,UAA0C;AAE7D,UAAM,YAAY,MAAM,KAAK,UAAU,SAAS,QAAQ;AACxD,QAAI,CAAC,WAAW;AACf,MAAAA,QAAO,KAAK,qBAAqB,QAAQ,EAAE;AAC3C,aAAO;AAAA,IACR;AAGA,UAAM,UAAU;AAAA,MACf;AAAA,MACA,KAAK,OAAO;AAAA,MACZ;AAAA,MACA;AAAA,QACC,MAAM,KAAK,OAAO;AAAA,QAClB,WAAW,KAAK,OAAO;AAAA,QACvB,UAAU,KAAK,OAAO;AAAA,QACtB,cAAc,KAAK,OAAO;AAAA,QAC1B,MAAM,KAAK,OAAO;AAAA,QAClB,SAAS;AAAA,QACT,YAAY,KAAK,OAAO;AAAA,QACxB,gBAAgB,KAAK,OAAO;AAAA,MAC7B;AAAA,MACA,KAAK,OAAO;AAAA,MACZ,KAAK,OAAO;AAAA,IACb;AAEA,QAAI;AACH,YAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,oBAAoB;AAAA,QAC3D,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B,QAAQ,YAAY,QAAQ,GAAK;AAAA,MAClC,CAAC;AAED,UAAI,SAAS,IAAI;AAChB,QAAAA,QAAO,KAAK,qBAAqB,QAAQ,EAAE;AAG3C,YAAI;AACH,gBAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,cAAI,MAAM,UAAU,SAAS,qBAAqB;AACjD,iBAAK,cAAc,KAAK,UAAU,KAAK,SAAS,IAAI;AAAA,UACrD;AAAA,QACD,QAAQ;AAAA,QAER;AACA,eAAO,QAAQ;AAAA,MAChB;AAAA,IACD,SAAS,KAAK;AACb,MAAAA,QAAO;AAAA,QACN,wBAAwB,QAAQ,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACtF;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,SAAyB,SAAuB;AACrE,YAAQ,QAAQ,MAAM;AAAA,MACrB,KAAK;AACJ,aAAK,eAAe,OAAO;AAC3B;AAAA,MACD,KAAK;AACJ,aAAK,uBAAuB,OAAO;AACnC;AAAA,MACD,KAAK;AACJ,aAAK,WAAW,OAAO;AACvB;AAAA,MACD,KAAK;AACJ,aAAK,WAAW,OAAO;AACvB;AAAA,MACD,KAAK;AACJ,aAAK,eAAe,OAAO;AAC3B;AAAA,IACF;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,kBAAkB,SAAkC;AACnD,UAAM,WAAW,KAAK,gBAAgB,IAAI,QAAQ,IAAI;AACtD,QAAI,CAAC,SAAU,QAAO;AACtB,WAAO,cAAc,SAAS,SAAS,WAAW,SAAS,WAAW,EAAE;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,sBAAsB,SAAgD;AACrE,QAAI,QAAQ,SAAS,WAAY,QAAO;AACxC,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAK,aAAa,CAAC,KAAK,KAAM,QAAO;AAC1C,UAAM,SAAS,eAAe,KAAK,SAAS;AAC5C,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,WAAW,KAAK,gBAAgB,IAAI,OAAO,UAAU;AAC3D,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,SAAS,cAAc,SAAS,SAAS,WAAW,SAAS,WAAW;AAC9E,QAAI,CAAC,OAAO,MAAO,QAAO;AAC1B,WAAO;AAAA,MACN;AAAA,MACA,KAAK,OAAO;AAAA,MACZ,OAAO;AAAA,MACP;AAAA,QACC,MAAM,KAAK,OAAO;AAAA,QAClB,WAAW,KAAK,OAAO;AAAA,QACvB,UAAU,KAAK,OAAO;AAAA,QACtB,cAAc,KAAK,OAAO;AAAA,QAC1B,MAAM,KAAK,OAAO;AAAA,QAClB,SAAS;AAAA,QACT,YAAY,KAAK,OAAO;AAAA,QACxB,gBAAgB,KAAK,OAAO;AAAA,MAC7B;AAAA,MACA,KAAK,OAAO;AAAA,MACZ,KAAK,OAAO;AAAA,IACb;AAAA,EACD;AAAA,EAEQ,eAAe,SAA+B;AACrD,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAK,aAAa,CAAC,KAAK,KAAM;AAGnC,UAAM,SAAS,eAAe,KAAK,SAAS;AAC5C,QAAI,CAAC,QAAQ;AACZ,MAAAA,QAAO,KAAK,iCAAiC,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,EAAE;AAC3E;AAAA,IACD;AAEA,QAAI,CAAC,KAAK,gBAAgB,IAAI,OAAO,UAAU,GAAG;AACjD,MAAAA,QAAO;AAAA,QACN,2CAA2C,KAAK,IAAI,KAAK,OAAO,WAAW,UAAU,GAAG,CAAC,CAAC;AAAA,MAC3F;AACA;AAAA,IACD;AAGA,UAAM,WAAW,KAAK,gBAAgB,IAAI,OAAO,UAAU;AAC3D,UAAM,SAAS,cAAc,SAAS,SAAS,WAAW,SAAS,WAAW;AAC9E,QAAI,CAAC,OAAO,OAAO;AAClB,MAAAA,QAAO,KAAK,oCAAoC,KAAK,IAAI,KAAK,OAAO,KAAK,EAAE;AAC5E;AAAA,IACD;AAKA,QAAI,OAAO,eAAe,CAAC,SAAS,eAAe,KAAK,WAAW;AAClE,eAAS,cAAc,OAAO;AAC9B,WAAK,UAAU,KAAK,OAAO,QAAQ,KAAK,SAAS,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AACjE,MAAAA,QAAO;AAAA,QACN,oCAAoC,KAAK,IAAI,KAAK,OAAO,WAAW,UAAU,GAAG,CAAC,CAAC;AAAA,MACpF;AAAA,IACD;AAGA,UAAM,WAA4B;AAAA,MACjC,IAAI,OAAO;AAAA,MACX,MAAM,KAAK;AAAA,MACX,WAAW,KAAK;AAAA,MAChB,UAAU,KAAK,YAAY;AAAA,MAC3B,cAAc,KAAK,gBAAgB,CAAC;AAAA,MACpC,MAAO,KAAK,QAAoC;AAAA,MAChD,MAAM;AAAA,MACN,UAAU;AAAA,MACV,UAAU,KAAK,IAAI;AAAA,MACnB,SAAS,KAAK,WAAW;AAAA,MACzB,YAAY,KAAK;AAAA,MACjB,gBAAgB,KAAK;AAAA,IACtB;AAEA,SAAK,UAAU,IAAI,OAAO,YAAY,QAAQ;AAC9C,QAAI,SAAS,UAAU;AACtB,WAAK,UAAU,QAAQ,OAAO,YAAY,SAAS,QAAQ;AAC3D,WAAK,kBAAkB,QAAQ;AAAA,IAChC;AAEA,IAAAA,QAAO;AAAA,MACN,oBAAoB,SAAS,IAAI,KAAK,SAAS,GAAG,UAAU,GAAG,CAAC,CAAC,YAAO,SAAS,aAAa,KAAK,IAAI,CAAC;AAAA,IACzG;AAGA,SAAK,gBAAgB;AAGrB,UAAM,WAAW;AAAA,MAChB;AAAA,MACA,KAAK,OAAO;AAAA,MACZ,OAAO;AAAA,MACP;AAAA,QACC,MAAM,KAAK,OAAO;AAAA,QAClB,WAAW,KAAK,OAAO;AAAA,QACvB,UAAU,KAAK,OAAO;AAAA,QACtB,cAAc,KAAK,OAAO;AAAA,QAC1B,MAAM,KAAK,OAAO;AAAA,QAClB,SAAS;AAAA,QACT,YAAY,KAAK,OAAO;AAAA,QACxB,gBAAgB,KAAK,OAAO;AAAA,MAC7B;AAAA,MACA,KAAK,OAAO;AAAA,MACZ,KAAK,OAAO;AAAA,IACb;AACA,SAAK,UAAU,WAAW,OAAO,YAAY,QAAQ;AAGrD,UAAM,UAAU;AAAA,MACf;AAAA,MACA,KAAK,OAAO;AAAA,MACZ,OAAO;AAAA,MACP,CAAC;AAAA,MACD,KAAK,OAAO;AAAA,MACZ,KAAK,OAAO;AAAA,IACb;AACA,SAAK,UAAU,WAAW,OAAO,YAAY,OAAO;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAKQ,uBAAuB,SAA+B;AAE7D,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAK,aAAa,CAAC,KAAK,KAAM;AAEnC,UAAM,SAAS,eAAe,KAAK,SAAS;AAC5C,QAAI,CAAC,OAAQ;AACb,UAAM,WAAW,KAAK,gBAAgB,IAAI,OAAO,UAAU;AAC3D,QAAI,CAAC,SAAU;AAGf,UAAM,SAAS,cAAc,SAAS,SAAS,WAAW,SAAS,WAAW;AAC9E,QAAI,CAAC,OAAO,OAAO;AAClB,MAAAA,QAAO,KAAK,8CAA8C,KAAK,IAAI,KAAK,OAAO,KAAK,EAAE;AACtF;AAAA,IACD;AAIA,QAAI,OAAO,eAAe,CAAC,SAAS,aAAa;AAChD,eAAS,cAAc,OAAO;AAC9B,WAAK,UAAU,KAAK,OAAO,QAAQ,KAAK,SAAS,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AACjE,MAAAA,QAAO;AAAA,QACN,oCAAoC,KAAK,IAAI,KAAK,OAAO,WAAW,UAAU,GAAG,CAAC,CAAC;AAAA,MACpF;AAAA,IACD;AAEA,UAAM,WAA4B;AAAA,MACjC,IAAI,OAAO;AAAA,MACX,MAAM,KAAK;AAAA,MACX,WAAW,KAAK;AAAA,MAChB,UAAU,KAAK,YAAY;AAAA,MAC3B,cAAc,KAAK,gBAAgB,CAAC;AAAA,MACpC,MAAO,KAAK,QAAoC;AAAA,MAChD,MAAM;AAAA,MACN,UAAU;AAAA,MACV,UAAU,KAAK,IAAI;AAAA,MACnB,SAAS,KAAK,WAAW;AAAA,MACzB,YAAY,KAAK;AAAA,MACjB,gBAAgB,KAAK;AAAA,IACtB;AAEA,SAAK,UAAU,IAAI,OAAO,YAAY,QAAQ;AAC9C,QAAI,SAAS,UAAU;AACtB,WAAK,UAAU,QAAQ,OAAO,YAAY,SAAS,QAAQ;AAC3D,WAAK,kBAAkB,QAAQ;AAAA,IAChC;AAEA,IAAAA,QAAO,KAAK,mBAAmB,SAAS,IAAI,KAAK,SAAS,GAAG,UAAU,GAAG,CAAC,CAAC,GAAG;AAC/E,SAAK,gBAAgB;AAQrB,SAAK,UAAU;AAAA,MACd,OAAO;AAAA,MACP;AAAA,QACC;AAAA,QACA,KAAK,OAAO;AAAA,QACZ,OAAO;AAAA,QACP,CAAC;AAAA,QACD,KAAK,OAAO;AAAA,QACZ,KAAK,OAAO;AAAA,MACb;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,WAAW,SAA+B;AACjD,UAAM,WAAW,KAAK,UAAU,IAAI,QAAQ,IAAI;AAChD,QAAI,SAAU,UAAS,WAAW,KAAK,IAAI;AAE3C,UAAM,OAAO;AAAA,MACZ;AAAA,MACA,KAAK,OAAO;AAAA,MACZ,QAAQ;AAAA,MACR,EAAE,WAAW,KAAK,IAAI,EAAE;AAAA,MACxB,KAAK,OAAO;AAAA,MACZ,KAAK,OAAO;AAAA,IACb;AACA,SAAK,UAAU,WAAW,QAAQ,MAAM,IAAI;AAAA,EAC7C;AAAA,EAEQ,WAAW,SAA+B;AACjD,UAAM,WAAW,KAAK,UAAU,IAAI,QAAQ,IAAI;AAChD,QAAI,SAAU,UAAS,WAAW,KAAK,IAAI;AAAA,EAC5C;AAAA,EAEQ,eAAe,SAA+B;AACrD,UAAM,QAAQ,QAAQ;AACtB,QAAI,MAAM,QAAQ,KAAK,GAAG;AACzB,WAAK,YAAY,IAAI,QAAQ,MAAM,KAAK;AACxC,MAAAA,QAAO,KAAK,YAAY,MAAM,MAAM,eAAe,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,EAAE;AAAA,IAClF;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,kBAAkB,UAAiC;AAC1D,QAAI,KAAK,YAAY,IAAI,SAAS,EAAE,EAAG;AACvC,UAAM,QAAQ,kBAAkB,KAAK,OAAO,oBAAoB,SAAS,QAAQ;AACjF,QAAI,CAAC,MAAO;AACZ,SAAK,YAAY,IAAI,SAAS,EAAE;AAChC,IAAAA,QAAO;AAAA,MACN,SAAS,SAAS,IAAI,oBAAoB,SAAS,QAAQ,yDAAoD,KAAK;AAAA,IACrH;AAAA,EACD;AAAA,EAEQ,SAAS,QAAsB;AACtC,UAAM,OAAO;AAAA,MACZ;AAAA,MACA,KAAK,OAAO;AAAA,MACZ;AAAA,MACA,EAAE,WAAW,KAAK,IAAI,EAAE;AAAA,MACxB,KAAK,OAAO;AAAA,MACZ,KAAK,OAAO;AAAA,IACb;AACA,SAAK,UAAU,WAAW,QAAQ,IAAI;AAAA,EACvC;AAAA,EAEA,MAAc,cAA6B;AAC1C,UAAM,MAAM,KAAK,IAAI;AAErB,eAAW,CAAC,IAAI,QAAQ,KAAK,KAAK,WAAW;AAC5C,UAAI,MAAM,SAAS,WAAW,iBAAiB;AAC9C,QAAAA,QAAO;AAAA,UACN,iBAAiB,SAAS,IAAI,KAAK,GAAG,UAAU,GAAG,CAAC,CAAC,sBAAiB,KAAK,OAAO,MAAM,SAAS,YAAY,GAAI,CAAC;AAAA,QACnH;AACA,aAAK,UAAU,OAAO,EAAE;AACxB,aAAK,UAAU,WAAW,EAAE;AAC5B,aAAK,YAAY,OAAO,EAAE;AAC1B;AAAA,MACD;AAGA,YAAM,OAAO;AAAA,QACZ;AAAA,QACA,KAAK,OAAO;AAAA,QACZ;AAAA,QACA,EAAE,WAAW,IAAI;AAAA,QACjB,KAAK,OAAO;AAAA,QACZ,KAAK,OAAO;AAAA,MACb;AACA,WAAK,UAAU,WAAW,IAAI,IAAI;AAAA,IACnC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,eAAkC;AACjC,WAAO,MAAM,KAAK,KAAK,UAAU,OAAO,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAmC;AAClC,UAAM,QAA0B,CAAC;AACjC,eAAW,CAAC,EAAE,SAAS,KAAK,KAAK,aAAa;AAC7C,YAAM,KAAK,GAAG,SAAS;AAAA,IACxB;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,UAA4E;AACzF,eAAW,CAAC,YAAY,KAAK,KAAK,KAAK,aAAa;AACnD,UAAI,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ,GAAG;AAC3C,cAAM,WAAW,KAAK,UAAU,IAAI,UAAU;AAC9C,YAAI,SAAU,QAAO,EAAE,YAAY,SAAS;AAAA,MAC7C;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBAAkC;AACvC,SAAK,kBAAkB,MAAM,oBAAoB,KAAK,OAAO,MAAM;AACnE,IAAAA,QAAO,KAAK,YAAY,KAAK,gBAAgB,IAAI,qBAAqB;AAAA,EACvE;AACD;;;ACthBA,YAAYC,aAAY;AACxB,YAAYC,SAAQ;AACpB,YAAY,SAAS;AAErB,YAAYC,WAAU;AACtB,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,gBAAgB;;;ACVzB,YAAYC,aAAY;AACxB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAGtB,IAAMC,UAAS,aAAa,eAAe;AAsBpC,IAAM,iBAAN,MAAqB;AAAA,EAG3B,YAAoB,MAAwB;AAAxB;AACnB,IAAG,cAAU,KAAK,KAAK,EAAE,WAAW,KAAK,CAAC;AAC1C,SAAK,aAAa;AAAA,EACnB;AAAA,EAHoB;AAAA,EAFZ,QAAQ,oBAAI,IAAsB;AAAA;AAAA,EAQlC,eAAqB;AAC5B,eAAW,QAAW,gBAAY,KAAK,KAAK,GAAG,GAAG;AACjD,UAAI,CAAC,KAAK,SAAS,YAAY,EAAG;AAClC,UAAI;AACH,cAAM,OAAO,KAAK,MAAS,iBAAkB,WAAK,KAAK,KAAK,KAAK,IAAI,GAAG,MAAM,CAAC;AAC/E,YAAI,MAAM,OAAQ,MAAK,MAAM,IAAI,KAAK,QAAQ,IAAI;AAAA,MACnD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,SAAS,QAAgB,OAAO,OAAe;AACtD,WAAY,WAAK,KAAK,KAAK,KAAK,OAAO,GAAG,MAAM,UAAU,MAAM;AAAA,EACjE;AAAA,EAEQ,UAAU,MAAsB;AACvC,IAAG,kBAAmB,WAAK,KAAK,KAAK,KAAK,GAAG,KAAK,MAAM,YAAY,GAAG,KAAK,UAAU,IAAI,CAAC;AAAA,EAC5F;AAAA;AAAA,EAGA,UAAU,MAAc,IAAoB;AAC3C,QAAI,QAAQ;AACZ,eAAW,KAAK,KAAK,MAAM,OAAO,GAAG;AACpC,UAAI,EAAE,SAAS,QAAQ,EAAE,OAAO,GAAI,UAAS,EAAE;AAAA,IAChD;AACA,WAAO;AAAA,EACR;AAAA,EAEA,OACC,MACA,IACA,YACA,YACwD;AACxD,QAAI,aAAa,KAAK,KAAK,aAAc,QAAO,EAAE,OAAO,YAAY;AAErE,eAAW,KAAK,KAAK,MAAM,OAAO,GAAG;AACpC,UAAI,EAAE,SAAS,QAAQ,EAAE,eAAe,YAAY;AACnD,YAAI;AACH,UAAG,WAAO,KAAK,SAAS,EAAE,QAAQ,IAAI,GAAG,EAAE,OAAO,KAAK,CAAC;AACxD,UAAG,WAAO,KAAK,SAAS,EAAE,MAAM,GAAG,EAAE,OAAO,KAAK,CAAC;AAAA,QACnD,QAAQ;AAAA,QAER;AACA,UAAE,WAAW;AACb,UAAE,aAAa;AACf,UAAE,YAAY,KAAK,IAAI;AACvB,aAAK,UAAU,CAAC;AAChB,eAAO,EAAE,QAAQ,EAAE,OAAO;AAAA,MAC3B;AAAA,IACD;AACA,QAAI,KAAK,UAAU,MAAM,EAAE,IAAI,aAAa,KAAK,KAAK,WAAY,QAAO,EAAE,OAAO,QAAQ;AAC1F,UAAM,SAAgB,oBAAY,EAAE,EAAE,SAAS,KAAK;AACpD,UAAM,OAAiB;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,MACpB,UAAU;AAAA,IACX;AACA,SAAK,MAAM,IAAI,QAAQ,IAAI;AAC3B,SAAK,UAAU,IAAI;AACnB,WAAO,EAAE,OAAO;AAAA,EACjB;AAAA,EAEA,IAAI,QAAsC;AACzC,WAAO,KAAK,MAAM,IAAI,MAAM;AAAA,EAC7B;AAAA,EAEA,UAAU,QAAuC;AAChD,QAAI,CAAC,KAAK,MAAM,IAAI,MAAM,EAAG,QAAO;AACpC,WAAU,sBAAkB,KAAK,SAAS,QAAQ,IAAI,CAAC;AAAA,EACxD;AAAA;AAAA,EAGA,SAAS,QAAyB;AACjC,UAAM,OAAO,KAAK,MAAM,IAAI,MAAM;AAClC,QAAI,CAAC,KAAM,QAAO;AAClB,QAAI;AACH,MAAG,eAAW,KAAK,SAAS,QAAQ,IAAI,GAAG,KAAK,SAAS,MAAM,CAAC;AAAA,IACjE,QAAQ;AACP,aAAO;AAAA,IACR;AACA,SAAK,WAAW;AAChB,SAAK,UAAU,IAAI;AACnB,WAAO;AAAA,EACR;AAAA,EAEA,SAAS,QAAgB,aAAa,GAAyB;AAC9D,UAAM,OAAO,KAAK,MAAM,IAAI,MAAM;AAClC,QAAI,CAAC,MAAM,SAAU,QAAO;AAC5B,QAAI;AACH,aAAU,qBAAiB,KAAK,SAAS,MAAM,GAAG,EAAE,OAAO,WAAW,CAAC;AAAA,IACxE,QAAQ;AACP,aAAO;AAAA,IACR;AAAA,EACD;AAAA,EAEA,WAAW,QAA+B;AACzC,UAAM,OAAO,KAAK,MAAM,IAAI,MAAM;AAClC,WAAO,MAAM,WAAW,KAAK,SAAS,MAAM,IAAI;AAAA,EACjD;AAAA,EAEA,OAAO,QAAsB;AAC5B,SAAK,MAAM,OAAO,MAAM;AACxB,eAAW,KAAK;AAAA,MACf,KAAK,SAAS,MAAM;AAAA,MACpB,KAAK,SAAS,QAAQ,IAAI;AAAA,MACrB,WAAK,KAAK,KAAK,KAAK,GAAG,MAAM,YAAY;AAAA,IAC/C,GAAG;AACF,UAAI;AACH,QAAG,WAAO,GAAG,EAAE,OAAO,KAAK,CAAC;AAAA,MAC7B,QAAQ;AAAA,MAER;AAAA,IACD;AAAA,EACD;AAAA;AAAA,EAGA,MAAM,MAAM,KAAK,IAAI,GAAW;AAC/B,QAAI,UAAU;AACd,eAAW,QAAQ,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,GAAG;AAC5C,UAAI,MAAM,KAAK,YAAY,KAAK,KAAK,OAAO;AAC3C,aAAK,OAAO,KAAK,MAAM;AACvB;AAAA,MACD;AAAA,IACD;AAEA,eAAW,QAAW,gBAAY,KAAK,KAAK,GAAG,GAAG;AACjD,YAAM,KAAK,KAAK,QAAQ,wBAAwB,EAAE;AAClD,UAAI,CAAC,KAAK,MAAM,IAAI,EAAE,GAAG;AACxB,YAAI;AACH,UAAG,WAAY,WAAK,KAAK,KAAK,KAAK,IAAI,GAAG,EAAE,OAAO,KAAK,CAAC;AACzD;AAAA,QACD,QAAQ;AAAA,QAER;AAAA,MACD;AAAA,IACD;AACA,QAAI,UAAU,EAAG,CAAAA,QAAO,KAAK,sCAAsC,OAAO,UAAU;AACpF,WAAO;AAAA,EACR;AACD;;;ACpLA,YAAYC,aAAY;AACxB,SAAS,iBAAiB;AAGnB,IAAM,sBAAsB,IAAI,OAAO;AACvC,IAAMC,aAAY;AAClB,IAAM,YAAY;AAElB,IAAM,iBAAiB,YAAYA;AAEnC,SAAS,WAAW,OAAuB;AACjD,QAAM,QAAQ,OAAO,MAAM,EAAE;AAC7B,QAAM,iBAAiB,OAAO,KAAK,GAAG,CAAC;AACvC,SAAO;AACR;AAEO,SAAS,SAAS,YAAoB,OAAuB;AACnE,SAAO,OAAO,KAAK,GAAG,UAAU,IAAI,KAAK,IAAI,MAAM;AACpD;AAGO,SAAS,cAAc,WAAmB,aAAa,qBAA6B;AAC1F,MAAI,aAAa,EAAG,QAAO;AAC3B,QAAM,SAAS,KAAK,KAAK,YAAY,UAAU;AAC/C,SAAO,YAAY,SAAS;AAC7B;AAEO,SAAS,eAAe,WAAmB,aAAa,qBAA6B;AAC3F,MAAI,aAAa,EAAG,QAAO;AAC3B,SAAO,KAAK,KAAK,YAAY,UAAU;AACxC;AAGO,SAAS,aACf,KACA,YACA,OACA,OACS;AACT,QAAM,SAAS;AAAA,IACP,uBAAe,qBAAqB,KAAK,WAAW,KAAK,GAAG;AAAA,MAClE,eAAeA;AAAA,IAChB,CAAC;AAAA,EACF;AACA,SAAO,OAAO,SAAS,YAAY,KAAK,CAAC;AACzC,QAAM,KAAK,OAAO,OAAO,CAAC,OAAO,OAAO,KAAK,GAAG,OAAO,MAAM,GAAG,OAAO,WAAW,CAAC,CAAC;AACpF,QAAM,QAAQ,OAAO,MAAM,YAAY,GAAG,MAAM;AAChD,QAAM,cAAc,GAAG,QAAQ,CAAC;AAChC,KAAG,KAAK,OAAO,SAAS;AACxB,SAAO;AACR;AAGO,SAAS,aACf,KACA,YACA,OACA,MACgB;AAChB,MAAI,KAAK,SAASA,WAAW,QAAO;AACpC,MAAI;AACH,UAAM,WAAW;AAAA,MACT,yBAAiB,qBAAqB,KAAK,WAAW,KAAK,GAAG;AAAA,QACpE,eAAeA;AAAA,MAChB,CAAC;AAAA,IACF;AACA,aAAS,OAAO,SAAS,YAAY,KAAK,CAAC;AAC3C,aAAS,WAAW,KAAK,SAAS,KAAK,SAASA,UAAS,CAAC;AAC1D,WAAO,OAAO,OAAO;AAAA,MACpB,SAAS,OAAO,KAAK,SAAS,GAAG,KAAK,SAASA,UAAS,CAAC;AAAA,MACzD,SAAS,MAAM;AAAA,IAChB,CAAC;AAAA,EACF,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAOO,IAAM,qBAAN,cAAiC,UAAU;AAAA,EAMjD,YACS,KACA,YACA,aAAa,qBACrB,aAAa,GACZ;AACD,UAAM;AALE;AACA;AACA;AAIR,SAAK,QAAQ;AAAA,EACd;AAAA,EAPS;AAAA,EACA;AAAA,EACA;AAAA,EARD,MAAgB,CAAC;AAAA,EACjB,WAAW;AAAA,EACX;AAAA,EACR,gBAAgB;AAAA,EAYR,UAAU,OAAe;AAChC,SAAK,KAAK,aAAa,KAAK,KAAK,KAAK,YAAY,KAAK,OAAO,KAAK,CAAC;AACpE,SAAK,SAAS;AACd,SAAK,iBAAiB;AAAA,EACvB;AAAA,EAES,WAAW,MAAc,MAAc,IAAkC;AACjF,SAAK,IAAI,KAAK,IAAI;AAClB,SAAK,YAAY,KAAK;AACtB,WAAO,KAAK,YAAY,KAAK,YAAY;AACxC,YAAM,QAAQ,OAAO,OAAO,KAAK,GAAG;AACpC,WAAK,UAAU,MAAM,SAAS,GAAG,KAAK,UAAU,CAAC;AACjD,YAAM,OAAO,MAAM,SAAS,KAAK,UAAU;AAC3C,WAAK,MAAM,KAAK,SAAS,CAAC,IAAI,IAAI,CAAC;AACnC,WAAK,WAAW,KAAK;AAAA,IACtB;AACA,OAAG;AAAA,EACJ;AAAA,EAES,OAAO,IAAkC;AACjD,QAAI,KAAK,WAAW,EAAG,MAAK,UAAU,OAAO,OAAO,KAAK,GAAG,CAAC;AAC7D,SAAK,MAAM,CAAC;AACZ,SAAK,WAAW;AAChB,OAAG;AAAA,EACJ;AACD;AAQO,IAAM,qBAAN,cAAiC,UAAU;AAAA,EAMjD,YACS,KACA,YACA,aAAa,qBACrB,aAAa,GACZ;AACD,UAAM;AALE;AACA;AACA;AAIR,SAAK,QAAQ;AAAA,EACd;AAAA,EAPS;AAAA,EACA;AAAA,EACA;AAAA,EARD,MAAgB,CAAC;AAAA,EACjB,WAAW;AAAA,EACX;AAAA,EACR,aAAa;AAAA,EAYJ,WAAW,MAAc,MAAc,IAAkC;AACjF,SAAK,IAAI,KAAK,IAAI;AAClB,SAAK,YAAY,KAAK;AACtB,WAAO,MAAM;AACZ,UAAI,KAAK,WAAW,UAAW;AAC/B,YAAM,QAAQ,KAAK,IAAI,WAAW,IAAI,KAAK,IAAI,CAAC,IAAI,OAAO,OAAO,KAAK,GAAG;AAC1E,WAAK,MAAM,CAAC,KAAK;AACjB,YAAM,UAAU,MAAM,aAAa,CAAC;AACpC,UAAI,UAAUA,cAAa,UAAU,KAAK,aAAaA,YAAW;AACjE,WAAG,IAAI,MAAM,qCAAqC,OAAO,EAAE,CAAC;AAC5D;AAAA,MACD;AACA,UAAI,KAAK,WAAW,YAAY,QAAS;AACzC,YAAM,OAAO,MAAM,SAAS,WAAW,YAAY,OAAO;AAC1D,YAAM,QAAQ,aAAa,KAAK,KAAK,KAAK,YAAY,KAAK,OAAO,IAAI;AACtE,UAAI,UAAU,MAAM;AACnB,WAAG,IAAI,MAAM,sBAAsB,KAAK,KAAK,wBAAwB,CAAC;AACtE;AAAA,MACD;AACA,WAAK,KAAK,KAAK;AACf,WAAK,SAAS;AACd,WAAK,cAAc;AACnB,YAAM,OAAO,MAAM,SAAS,YAAY,OAAO;AAC/C,WAAK,MAAM,KAAK,SAAS,CAAC,IAAI,IAAI,CAAC;AACnC,WAAK,WAAW,KAAK;AAAA,IACtB;AACA,OAAG;AAAA,EACJ;AAAA,EAES,OAAO,IAAkC;AACjD,QAAI,KAAK,WAAW,GAAG;AACtB,SAAG,IAAI,MAAM,gBAAgB,KAAK,QAAQ,mCAAmC,CAAC;AAC9E;AAAA,IACD;AACA,OAAG;AAAA,EACJ;AACD;AAMA,eAAsB,gBACrB,UACA,WACA,aAAa,qBACK;AAClB,MAAI,aAAa,EAAG,QAAO;AAC3B,QAAMC,MAAK,MAAM,OAAO,aAAkB;AAC1C,QAAM,KAAK,MAAMA,IAAG,KAAK,UAAU,GAAG;AACtC,MAAI;AACH,UAAM,MAAM,OAAO,MAAM,SAAS;AAClC,QAAI,SAAS;AACb,aAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AACnC,YAAM,EAAE,UAAU,IAAI,MAAM,GAAG,KAAK,KAAK,GAAG,WAAW,MAAM;AAC7D,UAAI,YAAY,UAAW,OAAM,IAAI,MAAM,uCAAuC,SAAS,EAAE;AAC7F,YAAM,UAAU,IAAI,aAAa,CAAC;AAClC,UAAI,UAAUD,cAAa,UAAU,aAAaA,YAAW;AAC5D,cAAM,IAAI,MAAM,4CAA4C,OAAO,EAAE;AAAA,MACtE;AACA,gBAAU,YAAY;AAAA,IACvB;AACA,WAAO;AAAA,EACR,UAAE;AACD,UAAM,GAAG,MAAM;AAAA,EAChB;AACD;;;AF3LA,IAAME,UAAS,aAAa,eAAe;AA6B3C,IAAM,oBAAoB,KAAK,OAAO,OAAO;AAE7C,IAAM,0BAA0B,MAAM,OAAO;AAG7C,SAAS,SAAS,GAAmB;AACpC,MAAI,IAAI,KAAM,QAAO,GAAG,CAAC;AACzB,QAAM,QAAQ,CAAC,OAAO,OAAO,OAAO,KAAK;AACzC,MAAI,IAAI,IAAI;AACZ,MAAI,IAAI;AACR,SAAO,KAAK,QAAQ,IAAI,MAAM,SAAS,GAAG;AACzC,SAAK;AACL;AAAA,EACD;AACA,SAAO,GAAG,EAAE,QAAQ,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAC5D;AA0FA,IAAM,oBAAoB;AAC1B,IAAM,yBAAyB,KAAK;AACpC,IAAM,uBAAuB;AAC7B,IAAM,kBAAkB,CAAC,KAAO,KAAO,IAAM;AAC7C,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAEzB,SAAS,iBAAiB,MAAsB;AAE/C,MAAI,OAAY,eAAS,QAAQ,MAAM,EAAE,QAAQ,mBAAmB,EAAE;AAMtE,SAAO,KAAK,QAAQ,cAAc,GAAG,EAAE,QAAQ,UAAU,EAAE;AAC3D,MAAI,CAAC,QAAQ,SAAS,OAAO,SAAS,KAAM,QAAO;AAEnD,MAAI,8CAA8C,KAAK,IAAI,EAAG,QAAO,IAAI,IAAI;AAC7E,MAAI,KAAK,SAAS,KAAK;AACtB,UAAM,MAAW,cAAQ,IAAI,EAAE,MAAM,GAAG,EAAE;AAC1C,WAAO,KAAK,MAAM,GAAG,MAAM,IAAI,MAAM,IAAI;AAAA,EAC1C;AACA,SAAO;AACR;AAEA,eAAe,WAAW,UAAmC;AAC5D,QAAM,OAAc,mBAAW,QAAQ;AACvC,mBAAiB,SAAY,qBAAiB,QAAQ,EAAG,MAAK,OAAO,KAAe;AACpF,SAAO,KAAK,OAAO,KAAK;AACzB;AAOA,SAAS,WAAW,OAA0B;AAC7C,MAAI,WAAW;AACf,SAAO,IAAIC,WAAU;AAAA,IACpB,UAAU,OAAe,MAAM,IAAI;AAClC,kBAAY,MAAM;AAClB,UAAI,WAAW,MAAO,IAAG,IAAI,MAAM,yBAAyB,QAAQ,MAAM,KAAK,GAAG,CAAC;AAAA,UAC9E,IAAG,MAAM,KAAK;AAAA,IACpB;AAAA,EACD,CAAC;AACF;AAEA,SAAS,UAAU,OAAuB;AACzC,MAAI,QAAQ,KAAM,QAAO,GAAG,KAAK;AACjC,MAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC;AAC5D,MAAI,QAAQ,QAAQ,EAAG,QAAO,IAAI,QAAQ,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAC/D,SAAO,IAAI,QAAQ,QAAQ,GAAG,QAAQ,CAAC,CAAC;AACzC;AAEO,IAAM,eAAN,MAAmB;AAAA,EAczB,YAAoB,MAA2B;AAA3B;AACnB,QAAI,KAAK,gBAAgB,KAAK,OAAO,YAAY,OAAO;AACvD,WAAK,YAAY,IAAI,eAAe;AAAA,QACnC,KAAU,WAAK,KAAK,QAAQ,OAAO;AAAA,QACnC,YAAY,KAAK,OAAO,mBAAmB,MAAM,OAAO;AAAA,QACxD,QAAQ,KAAK,OAAO,iBAAiB,MAAM,KAAK;AAAA;AAAA;AAAA,QAGhD,eACE,KAAK,OAAO,iBAAiB,2BAA2B,OAAO;AAAA,MAClE,CAAC;AACD,WAAK,UAAU,MAAM;AACrB,WAAK,iBAAiB,YAAY,MAAM,KAAK,WAAW,MAAM,GAAG,sBAAsB;AAAA,IACxF;AACA,SAAK,aAAa,YAAY,MAAM,KAAK,eAAe,GAAG,iBAAiB;AAC5E,SAAK,KAAK,YAAY;AAAA,EACvB;AAAA,EAhBoB;AAAA,EAbZ,YAAY,oBAAI,IAAsB;AAAA;AAAA,EAEtC,SAAS,oBAAI,IAAwB;AAAA,EACrC,YAAmC;AAAA,EACnC,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA;AAAA,EAEA,eAAe,oBAAI,IAGzB;AAAA,EAoBF,OAAa;AACZ,QAAI,KAAK,WAAY,eAAc,KAAK,UAAU;AAClD,QAAI,KAAK,eAAgB,eAAc,KAAK,cAAc;AAC1D,eAAW,KAAK,KAAK,UAAU,OAAO,EAAG,GAAE,QAAQ,QAAQ;AAC3D,SAAK,OAAO,MAAM;AAClB,SAAK,aAAa,MAAM;AAAA,EACzB;AAAA;AAAA,EAIA,IAAY,KAAa;AACxB,WAAO,KAAK,KAAK,QAAQ;AAAA,EAC1B;AAAA,EAEA,IAAY,aAAqB;AAChC,WAAO,KAAK,KAAK,OAAO,cAAc;AAAA,EACvC;AAAA;AAAA,EAGA,IAAY,WAAmB;AAC9B,WAAO,KAAK,KAAK,OAAO,YAAY;AAAA,EACrC;AAAA,EAEQ,UAAU,MAAuB;AACxC,UAAM,QAAQ,KAAK;AACnB,WAAO,QAAQ,KAAK,OAAO;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,YAAoC;AACjD,eAAW,OAAO,CAAC,KAAK,SAAS,GAAG,KAAK,KAAK,MAAM,GAAG;AACtD,UAAI;AACH,cAAM,KAAK,MAAU,WAAO,GAAG;AAC/B,eAAO,OAAO,GAAG,MAAM,IAAI,OAAO,GAAG,KAAK;AAAA,MAC3C,QAAQ;AAAA,MAER;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA,EAEA,IAAY,aAAqB;AAChC,YAAQ,KAAK,KAAK,OAAO,mBAAmB,MAAM;AAAA,EACnD;AAAA,EAEQ,WAAmB;AAC1B,WAAY,cAAQ,KAAK,KAAK,aAAa,KAAK,KAAK,OAAO,YAAY,qBAAqB;AAAA,EAC9F;AAAA,EAEQ,YAAoB;AAC3B,WAAY,WAAK,KAAK,KAAK,QAAQ,SAAS,QAAQ;AAAA,EACrD;AAAA,EAEQ,MAAM,GAAa,OAAuB;AACjD,QAAI,MAAO,GAAE,QAAQ;AACrB,MAAE,YAAY,KAAK,IAAI;AAAA,EACxB;AAAA,EAEQ,KAAK,MAAc,OAAgC;AAC1D,QAAI;AACH,WAAK,KAAK,KAAK,KAAK,MAAM,KAAK;AAAA,IAChC,QAAQ;AAAA,IAER;AAAA,EACD;AAAA,EAEQ,aAAa,GAAa;AACjC,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,EAAE,kBAAkB,MAAM,EAAE,iBAAiB,qBAAsB;AACvE,MAAE,iBAAiB;AACnB,SAAK,KAAK,yBAAyB;AAAA,MAClC,YAAY,EAAE;AAAA,MACd,KAAK,EAAE;AAAA,MACP,OAAO,EAAE;AAAA,MACT,YAAY,EAAE;AAAA,MACd,KAAK,EAAE,OAAO,IAAI,KAAK,IAAI,KAAK,KAAK,MAAO,EAAE,YAAY,EAAE,OAAQ,GAAG,CAAC,IAAI;AAAA,IAC7E,CAAC;AAAA,EACF;AAAA,EAEQ,UAAU,IAAY,QAAgB,IAA8B;AAC3E,UAAM,QAAe,oBAAY,EAAE,EAAE,SAAS,KAAK;AACnD,UAAM,OAAc,mBAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACnE,SAAK,OAAO,IAAI,MAAM,EAAE,IAAI,QAAQ,IAAI,WAAW,KAAK,IAAI,IAAI,KAAK,WAAW,CAAC;AACjF,WAAO;AAAA,EACR;AAAA,EAEQ,YAAY,WAAkD;AACrE,QAAI,CAAC,aAAa,OAAO,cAAc,YAAY,UAAU,SAAS,IAAK,QAAO;AAClF,UAAM,OAAc,mBAAW,QAAQ,EAAE,OAAO,SAAS,EAAE,OAAO,KAAK;AACvE,UAAM,UAAU,OAAO,KAAK,MAAM,KAAK;AACvC,eAAW,CAAC,QAAQ,KAAK,KAAK,KAAK,QAAQ;AAE1C,UAAW,wBAAgB,SAAS,OAAO,KAAK,QAAQ,KAAK,CAAC,GAAG;AAChE,YAAI,KAAK,IAAI,IAAI,MAAM,WAAW;AACjC,eAAK,OAAO,OAAO,MAAM;AACzB,iBAAO;AAAA,QACR;AACA,eAAO;AAAA,MACR;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA,EAEQ,gBAAgB,IAAY;AACnC,eAAW,CAAC,MAAM,KAAK,KAAK,KAAK,OAAQ,KAAI,MAAM,OAAO,GAAI,MAAK,OAAO,OAAO,IAAI;AAAA,EACtF;AAAA;AAAA,EAGA,MAAc,QACb,GACA,MACA,SACmB;AACnB,QAAI,EAAE,SAAS;AACd,YAAM,IAAI,MAAM,KAAK,KAAK,qBAAqB,EAAE,QAAQ,MAAM,OAAO;AACtE,aAAO,EAAE,MAAM,EAAE,cAAc;AAAA,IAChC;AACA,UAAM,MAAM;AAAA,MACX;AAAA,MACA,KAAK;AAAA,MACL,EAAE;AAAA,MACF;AAAA,MACA,KAAK,KAAK,QAAQ;AAAA,MAClB,KAAK,KAAK,QAAQ;AAAA,IACnB;AACA,WAAO,KAAK,KAAK,UAAU,WAAW,EAAE,QAAQ,GAAG;AAAA,EACpD;AAAA,EAEQ,WAAW,KAA0C;AAC5D,WAAO,KAAK,KACV,aAAa,EACb,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,WAAW,GAAG,CAAC;AAAA,EACrE;AAAA,EAEQ,eAAe,QAAgB,UAA4B;AAClE,UAAM,SAAS,KAAK,KAAK,OAAO;AAChC,QAAI,WAAW,IAAK,QAAO;AAC3B,QAAI,MAAM,QAAQ,MAAM,GAAG;AAC1B,iBAAW,OAAO,QAAQ;AACzB,cAAM,UAAU,OAAO,QAAQ,WAAW,IAAI,KAAK,IAAI;AACvD,YAAI,CAAC,QAAS;AACd,YAAI,YAAY,IAAK,QAAO;AAC5B,YAAI,YAAY,UAAU,OAAO,WAAW,OAAO,EAAG,QAAO;AAC7D,YAAI,YAAY,YAAY,SAAU,QAAO;AAAA,MAC9C;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA;AAAA,EAIA,gBAAgC;AAC/B,WAAO,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,EAChC,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,EAC7B,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAAA,EAC3C;AAAA,EAEA,YAAY,YAA8C;AACzD,UAAM,IAAI,KAAK,UAAU,IAAI,UAAU;AACvC,WAAO,IAAI,KAAK,WAAW,CAAC,IAAI;AAAA,EACjC;AAAA,EAEQ,WAAW,GAA2B;AAC7C,WAAO;AAAA,MACN,YAAY,EAAE;AAAA,MACd,KAAK,EAAE;AAAA,MACP,QAAQ,EAAE;AAAA,MACV,UAAU,EAAE;AAAA,MACZ,MAAM,EAAE;AAAA,MACR,MAAM,EAAE;AAAA,MACR,QAAQ,EAAE;AAAA,MACV,OAAO,EAAE;AAAA,MACT,MAAM,EAAE;AAAA,MACR,WAAW,EAAE;AAAA,MACb,OAAO,EAAE;AAAA,MACT,MAAM,EAAE;AAAA,MACR,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,IACd;AAAA,EACD;AAAA,EAEA,MAAM,SACL,SACA,UACA,MACgE;AAChE,QAAI,KAAK,KAAK,OAAO,YAAY,MAAO,QAAO,EAAE,IAAI,OAAO,OAAO,wBAAwB;AAC3F,UAAM,MAAW,cAAQ,KAAK,KAAK,aAAa,QAAQ;AACxD,QAAIC;AACJ,QAAI;AACH,MAAAA,QAAO,MAAU,SAAK,GAAG;AAAA,IAC1B,QAAQ;AACP,aAAO,EAAE,IAAI,OAAO,OAAO,mBAAmB,GAAG,GAAG;AAAA,IACrD;AACA,QAAI,CAACA,MAAK,OAAO,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO,uBAAuB,GAAG,GAAG;AAG5E,UAAM,SAAS,KAAK,WAAW,OAAO;AACtC,UAAM,cAAc,SAAS,SAAY,KAAK,KAAK,iBAAiB,OAAO;AAC3E,UAAM,OAAO,SACV,EAAE,IAAI,OAAO,IAAI,MAAM,OAAO,MAAM,GAAG,OAAO,YAAY,OAAO,OAAO,eAAe,IACvF,cACC;AAAA,MACA,IAAI,YAAY;AAAA,MAChB,MAAM,YAAY;AAAA,MAClB,GAAG,YAAY;AAAA,MACf,OAAO,YAAY;AAAA,IACpB,IACC;AACJ,QAAI,CAAC,KAAM,QAAO,EAAE,IAAI,OAAO,OAAO,uBAAuB,OAAO,IAAI;AACxE,QAAI,CAAC,KAAK,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO,SAAS,KAAK,IAAI,gCAAgC;AAE1F,UAAM,aAAoB,mBAAW;AACrC,UAAM,MAAa,oBAAY,EAAE;AACjC,UAAM,SAAS,KAAK,KAAK,GAAG,KAAK,GAAG,KAAK,EAAE,IAAI,KAAK,EAAE,SAAS,UAAU,IAAI,KAAK,KAAK;AACvF,QAAI,CAAC,OAAQ,QAAO,EAAE,IAAI,OAAO,OAAO,8BAA8B;AACtE,UAAM,SAAS,MAAM,WAAW,GAAG;AAEnC,UAAM,IAAc;AAAA,MACnB;AAAA,MACA,KAAK;AAAA,MACL,QAAQ,KAAK;AAAA,MACb,UAAU,KAAK;AAAA,MACf,MAAM,iBAAsB,eAAS,GAAG,CAAC;AAAA,MACzC,MAAMA,MAAK;AAAA,MACX;AAAA,MACA,OAAO;AAAA,MACP,WAAW;AAAA,MACX;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,MACpB,WAAW,KAAK,IAAI;AAAA,MACpB,YAAY,KAAK;AAAA,MACjB,aAAa,eAAeA,MAAK,MAAM,KAAK,UAAU;AAAA,MACtD,SAAS,CAAC;AAAA,MACV;AAAA,MACA,SAAS;AAAA,IACV;AAGA,QAAI;AACJ,QAAI,KAAK,KAAK,sBAAsB,CAAC,EAAE,SAAS;AAC/C,aAAO;AAAA,QACN,KAAK,GAAG,KAAK,KAAK,kBAAkB,iBAAiB,UAAU;AAAA,QAC/D,OAAO,KAAK,UAAU,YAAY,KAAK,IAAI,MAAM;AAAA,MAClD;AACA,QAAE,OAAO;AAAA,IACV;AAEA,SAAK,UAAU,IAAI,YAAY,CAAC;AAChC,UAAM,UAAmC;AAAA,MACxC;AAAA,MACA,MAAM,EAAE;AAAA,MACR,MAAM,EAAE;AAAA,MACR;AAAA,MACA,YAAY,EAAE;AAAA,MACd,aAAa,EAAE;AAAA,MACf;AAAA,MACA,UAAU,KAAK,KAAK;AAAA,MACpB,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,MACvB,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,IACxB;AACA,UAAM,OAAO,MAAM,KAAK,QAAQ,GAAG,cAAc,OAAO;AACxD,QAAI,CAAC,MAAM;AACV,WAAK,UAAU,OAAO,UAAU;AAChC,WAAK,gBAAgB,UAAU;AAC/B,aAAO,EAAE,IAAI,OAAO,OAAO,yBAAyB,KAAK,IAAI,IAAI;AAAA,IAClE;AACA,SAAK,KAAK,KAAK,WAAW,KAAK,IAAI;AAAA,MAClC,KAAK;AAAA,MACL,MAAM,aAAa,EAAE,IAAI,KAAK,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,WAAW,EAAE,IAAI,KAAK,EAAE;AAAA,MACpF,UAAU,KAAK,KAAK;AAAA,MACpB,WAAW,KAAK,IAAI;AAAA,MACpB,WAAW;AAAA,MACX,SAAS,EAAE;AAAA,IACZ,CAAC;AACD,WAAO,EAAE,IAAI,MAAM,WAAW;AAAA,EAC/B;AAAA,EAEA,MAAM,WAAW,YAA8D;AAC9E,UAAM,IAAI,KAAK,UAAU,IAAI,UAAU;AACvC,QAAI,CAAC,KAAK,EAAE,QAAQ,OAAQ,QAAO,EAAE,IAAI,OAAO,OAAO,mBAAmB;AAC1E,QAAI,EAAE,UAAU,aAAa,EAAE,UAAU;AACxC,aAAO,EAAE,IAAI,OAAO,OAAO,eAAe,EAAE,KAAK,GAAG;AACrD,WAAO,KAAK,aAAa,CAAC;AAAA,EAC3B;AAAA,EAEA,MAAM,WAAW,YAAoB,SAAS,UAAoC;AACjF,UAAM,IAAI,KAAK,UAAU,IAAI,UAAU;AACvC,QAAI,CAAC,KAAK,EAAE,QAAQ,OAAQ,QAAO,EAAE,IAAI,MAAM;AAC/C,SAAK,MAAM,GAAG,UAAU;AACxB,SAAK,gBAAgB,UAAU;AAC/B,UAAM,KAAK,QAAQ,GAAG,eAAe,EAAE,YAAY,OAAO,CAAC;AAC3D,WAAO,EAAE,IAAI,KAAK;AAAA,EACnB;AAAA,EAEA,MAAM,eAAe,YAA8C;AAClE,UAAM,IAAI,KAAK,UAAU,IAAI,UAAU;AACvC,QAAI,CAAC,EAAG,QAAO,EAAE,IAAI,MAAM;AAC3B,MAAE,QAAQ,QAAQ;AAClB,SAAK,MAAM,GAAG,WAAW;AACzB,SAAK,gBAAgB,UAAU;AAC/B,QAAI,EAAE,YAAa,OAAU,OAAG,EAAE,aAAa,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9E,UAAM,KAAK,QAAQ,GAAG,eAAe,EAAE,WAAW,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACnE,WAAO,EAAE,IAAI,KAAK;AAAA,EACnB;AAAA;AAAA,EAIA,cAAc,KAAqB,KAAmD;AACrF,SAAK,KAAK,mBAAmB,KAAK,GAAG,EAAE,MAAM,CAAC,QAAQ;AACrD,MAAAF,QAAO;AAAA,QACN,qCAAqC,IAAI,IAAI,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACnG;AAAA,IACD,CAAC;AAAA,EACF;AAAA,EAEA,MAAc,mBACb,KACA,KACgB;AAChB,YAAQ,IAAI,MAAM;AAAA,MACjB,KAAK;AACJ,eAAO,KAAK,QAAQ,KAAK,GAAG;AAAA,MAC7B,KAAK;AACJ,eAAO,KAAK,SAAS,GAAG;AAAA,MACzB,KAAK;AACJ,eAAO,KAAK,SAAS,GAAG;AAAA,MACzB,KAAK;AACJ,eAAO,KAAK,aAAa,GAAG;AAAA,MAC7B,KAAK;AACJ,eAAO,KAAK,OAAO,GAAG;AAAA,MACvB,KAAK;AACJ,eAAO,KAAK,YAAY,GAAG;AAAA,MAC5B,KAAK;AACJ,eAAO,KAAK,aAAa,GAAG;AAAA,MAC7B,KAAK;AACJ,eAAO,KAAK,gBAAgB,GAAG;AAAA,MAChC,KAAK;AACJ,eAAO,KAAK,eAAe,GAAG;AAAA,MAC/B,KAAK;AACJ,eAAO,KAAK,cAAc,GAAG;AAAA,MAC9B,KAAK;AAAA,MACL,KAAK;AACJ,eAAO,KAAK,kBAAkB,GAAG;AAAA,MAClC;AACC;AAAA,IACF;AAAA,EACD;AAAA,EAEA,MAAc,QAAQ,KAAqB,KAA6C;AACvF,UAAM,IAAI,IAAI;AAYd,QACC,CAAC,GAAG,cACJ,OAAO,EAAE,SAAS,YAClB,OAAO,EAAE,SAAS,YAClB,EAAE,OAAO,KACT,OAAO,EAAE,WAAW,YACpB,CAAC,EAAE,UACH,OAAO,EAAE,eAAe,YACxB,EAAE,aAAa,QACf,EAAE,aAAa,KAAK,OAAO;AAE3B;AACD,QAAI,KAAK,UAAU,IAAI,EAAE,UAAU,EAAG;AACtC,UAAM,WAAW,EAAE,YAAY,IAAI,KAAK,UAAU,GAAG,CAAC;AACtD,UAAM,QAAQ,EAAE,QAAQ,IAAI,MAAM,SAAS,CAAC,CAAC,IAAI,QAAQ;AAEzD,QAAI,KAAK,KAAK,OAAO,YAAY,OAAO;AACvC,YAAM,KAAK,QAAQ,OAAO,eAAe,EAAE,YAAY,EAAE,YAAY,QAAQ,WAAW,CAAC;AACzF;AAAA,IACD;AACA,UAAM,OAAO,MAAM,KAAK,UAAU;AAElC,QAAI,SAAS,QAAQ,EAAE,OAAO,OAAO,MAAM;AAC1C,YAAM,KAAK,QAAQ,OAAO,eAAe;AAAA,QACxC,YAAY,EAAE;AAAA,QACd,QAAQ,aAAa,SAAS,EAAE,IAAI,CAAC,aAAa,SAAS,IAAI,CAAC;AAAA,MACjE,CAAC;AACD;AAAA,IACD;AACA,QAAI,KAAK,UAAU,EAAE,IAAI,GAAG;AAG3B,YAAM,KAAK,QAAQ,OAAO,eAAe;AAAA,QACxC,YAAY,EAAE;AAAA,QACd,QAAQ,cAAc,SAAS,EAAE,IAAI,CAAC,iCAAiC,SAAS,KAAK,QAAQ,CAAC;AAAA,MAC/F,CAAC;AACD;AAAA,IACD;AAEA,UAAM,IAAc;AAAA,MACnB,YAAY,EAAE;AAAA,MACd,KAAK;AAAA,MACL,QAAQ,IAAI;AAAA,MACZ,UAAU;AAAA,MACV,MAAM,iBAAiB,EAAE,IAAI;AAAA,MAC7B,MAAM,EAAE;AAAA,MACR,QAAQ,EAAE;AAAA,MACV,OAAO;AAAA,MACP,WAAW;AAAA,MACX,MAAM,EAAE;AAAA,MACR,WAAW,KAAK,IAAI;AAAA,MACpB,WAAW,KAAK,IAAI;AAAA,MACpB,YAAY,EAAE;AAAA,MACd,aAAa,EAAE,eAAe,eAAe,EAAE,MAAM,EAAE,UAAU;AAAA,MACjE,SAAS,CAAC,CAAC,IAAI;AAAA,MACf,QAAQ,IAAI;AAAA,MACZ,QAAQ,EAAE;AAAA,MACV,MAAM,EAAE;AAAA,IACT;AACA,SAAK,UAAU,IAAI,EAAE,YAAY,CAAC;AAClC,SAAK,KAAK,KAAK,WAAW,IAAI,MAAM;AAAA,MACnC,KAAK;AAAA,MACL,MAAM,aAAa,EAAE,IAAI,KAAK,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,WAAW,EAAE,IAAI,KAAK,EAAE;AAAA,MACpF;AAAA,MACA,WAAW,IAAI;AAAA,MACf,WAAW,EAAE;AAAA,MACb,SAAS,EAAE;AAAA,IACZ,CAAC;AAED,QAAI,KAAK,eAAe,IAAI,MAAM,QAAQ,GAAG;AAC5C,WAAK,KAAK,sBAAsB;AAAA,QAC/B,YAAY,EAAE;AAAA,QACd,MAAM,IAAI;AAAA,QACV;AAAA,QACA,MAAM,EAAE;AAAA,QACR,MAAM,EAAE;AAAA,QACR,MAAM,EAAE;AAAA,QACR,MAAM;AAAA,MACP,CAAC;AACD,YAAM,KAAK,aAAa,CAAC;AAAA,IAC1B,OAAO;AACN,WAAK,MAAM,GAAG,SAAS;AACvB,WAAK,KAAK,sBAAsB;AAAA,QAC/B,YAAY,EAAE;AAAA,QACd,MAAM,IAAI;AAAA,QACV;AAAA,QACA,MAAM,EAAE;AAAA,QACR,MAAM,EAAE;AAAA,QACR,MAAM,EAAE;AAAA,QACR,MAAM;AAAA,MACP,CAAC;AAAA,IACF;AAAA,EACD;AAAA;AAAA,EAGA,MAAc,aAAa,GAAuD;AACjF,QAAI,CAAC,EAAE,OAAQ,QAAO,EAAE,IAAI,OAAO,OAAO,uBAAuB;AACjE,QAAI,CAAC,KAAK,KAAK,QAAQ,aAAa;AACnC,WAAK,MAAM,GAAG,QAAQ;AACtB,QAAE,QAAQ;AACV,aAAO,EAAE,IAAI,OAAO,OAAO,EAAE,MAAM;AAAA,IACpC;AACA,UAAM,MAAM;AAAA,MACX,KAAK,KAAK,QAAQ;AAAA,MAClB,EAAE;AAAA,MACF,GAAG,EAAE,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,UAAU;AAAA,MAC3C,KAAK,KAAK,QAAQ;AAAA,IACnB;AACA,QAAI,CAAC,OAAO,IAAI,WAAW,IAAI;AAC9B,WAAK,MAAM,GAAG,QAAQ;AACtB,QAAE,QAAQ;AACV,YAAM,KAAK,QAAQ,GAAG,cAAc,EAAE,YAAY,EAAE,YAAY,MAAM,KAAK,CAAC;AAC5E,aAAO,EAAE,IAAI,OAAO,OAAO,EAAE,MAAM;AAAA,IACpC;AACA,MAAE,MAAM;AAGR,QAAI,OAAyC;AAC7C,QAAI;AACJ,QAAI,EAAE,SAAS;AACd,aAAO;AACP,cAAQ,EAAE,UAAU,KAAK,KAAK,gBAAgB,EAAE,MAAM;AACtD,UAAI,CAAC,MAAO,QAAO;AAAA,IACpB,OAAO;AACN,YAAM,SAAS,KAAK,KAAK,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM;AACrE,UAAI,EAAE,QAAQ,QAAQ,YAAa,MAAM,KAAK,MAAM,OAAO,QAAQ,EAAI,QAAO;AAAA,eACrE,KAAK,KAAK,mBAAoB,QAAO;AAAA,WACzC;AACJ,gBAAQ,KAAK,KAAK,gBAAgB,EAAE,MAAM;AAC1C,YAAI,MAAO,QAAO;AAAA,MACnB;AAAA,IACD;AACA,QAAI,CAAC,MAAM;AACV,WAAK,MAAM,GAAG,UAAU;AACxB,YAAM,KAAK,QAAQ,GAAG,eAAe,EAAE,YAAY,EAAE,YAAY,QAAQ,WAAW,CAAC;AACrF,aAAO,EAAE,IAAI,OAAO,OAAO,+BAA+B;AAAA,IAC3D;AAEA,MAAE,OAAO;AACT,MAAE,QAAQ;AACV,SAAK,MAAM,GAAG,UAAU;AACxB,UAAM,OACL,KAAK,KAAK,sBAAsB,SAAS,UACtC;AAAA,MACA,KAAK,GAAG,KAAK,KAAK,kBAAkB,iBAAiB,EAAE,UAAU;AAAA,MACjE,OAAO,KAAK,UAAU,EAAE,YAAY,EAAE,QAAQ,MAAM;AAAA,IACrD,IACC;AACJ,QAAI,KAAM,GAAE,OAAO;AACnB,UAAM,KAAK,QAAQ,GAAG,eAAe;AAAA,MACpC,YAAY,EAAE;AAAA,MACd;AAAA,MACA,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,MACvB,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,IAC1B,CAAC;AAED,QAAI,SAAS,OAAQ,MAAK,KAAK,QAAQ,CAAC;AAGxC,WAAO,EAAE,IAAI,KAAK;AAAA,EACnB;AAAA,EAEA,MAAc,MAAM,UAAoC;AACvD,QAAI;AACH,YAAM,MAAM,MAAM,MAAM,GAAG,QAAQ,WAAW;AAAA,QAC7C,QAAQ,YAAY,QAAQ,gBAAgB;AAAA,MAC7C,CAAC;AACD,aAAO,IAAI;AAAA,IACZ,QAAQ;AACP,aAAO;AAAA,IACR;AAAA,EACD;AAAA,EAEA,MAAc,SAAS,KAAqB;AAC3C,UAAM,IAAI,IAAI;AAMd,UAAM,IAAI,GAAG,aAAa,KAAK,UAAU,IAAI,EAAE,UAAU,IAAI;AAC7D,QAAI,CAAC,KAAK,EAAE,QAAQ,UAAU,EAAE,WAAW,IAAI,KAAM;AACrD,QAAI,EAAE,UAAU,UAAW;AAC3B,MAAE,OAAO,EAAE;AACX,QAAI,EAAE,KAAM,GAAE,OAAO,EAAE;AACvB,QAAI,EAAE,MAAO,GAAE,QAAQ,EAAE;AACzB,SAAK,MAAM,GAAG,EAAE,SAAS,SAAS,iBAAiB,UAAU;AAC7D,QAAI,EAAE,SAAS,OAAQ,MAAK,KAAK,QAAQ,CAAC;AAAA,aACjC,EAAE,SAAS,QAAS,MAAK,KAAK,eAAe,CAAC;AAAA,EAExD;AAAA,EAEA,MAAc,SAAS,KAAqB;AAC3C,UAAM,IAAI,IAAI;AACd,UAAM,IAAI,GAAG,aAAa,KAAK,UAAU,IAAI,EAAE,UAAU,IAAI;AAC7D,QAAI,CAAC,KAAK,EAAE,QAAQ,UAAU,EAAE,WAAW,IAAI,KAAM;AACrD,SAAK,MAAM,GAAG,UAAU;AACxB,MAAE,QAAQ,EAAE,UAAU;AACtB,SAAK,gBAAgB,EAAE,UAAU;AACjC,SAAK,KAAK,yBAAyB;AAAA,MAClC,YAAY,EAAE;AAAA,MACd,IAAI,EAAE;AAAA,MACN,QAAQ,EAAE;AAAA,IACX,CAAC;AAAA,EACF;AAAA,EAEA,MAAc,aAAa,KAAqB;AAC/C,UAAM,IAAI,IAAI;AACd,UAAM,IAAI,GAAG,aAAa,KAAK,UAAU,IAAI,EAAE,UAAU,IAAI;AAC7D,QAAI,CAAC,KAAK,EAAE,QAAQ,UAAU,EAAE,WAAW,IAAI,QAAQ,CAAC,EAAE,OAAQ;AAClE,QAAI,EAAE,UAAU,WAAY;AAC5B,MAAE,SAAS,EAAE;AACb,QAAI,EAAE,MAAO,GAAE,QAAQ,EAAE;AACzB,SAAK,KAAK,iBAAiB,CAAC;AAAA,EAC7B;AAAA,EAEA,MAAc,OAAO,KAAqB;AACzC,UAAM,IAAI,IAAI;AACd,UAAM,IAAI,GAAG,aAAa,KAAK,UAAU,IAAI,EAAE,UAAU,IAAI;AAC7D,QAAI,CAAC,KAAK,EAAE,QAAQ,UAAU,EAAE,WAAW,IAAI,KAAM;AACrD,SAAK,MAAM,GAAG,MAAM;AACpB,MAAE,YAAY,EAAE;AAChB,SAAK,gBAAgB,EAAE,UAAU;AACjC,UAAM,KAAK,oBAAoB,CAAC;AAChC,SAAK,KAAK,qBAAqB;AAAA,MAC9B,YAAY,EAAE;AAAA,MACd,IAAI,EAAE;AAAA,MACN,QAAQ,EAAE;AAAA,MACV,MAAM,EAAE;AAAA,MACR,MAAM,EAAE;AAAA,IACT,CAAC;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,KAAqB;AAC9C,UAAM,IAAI,IAAI;AACd,UAAM,IAAI,GAAG,aAAa,KAAK,UAAU,IAAI,EAAE,UAAU,IAAI;AAC7D,QAAI,CAAC,KAAK,EAAE,WAAW,IAAI,KAAM;AACjC,QAAI,EAAE,UAAU,UAAU,EAAE,UAAU,SAAU;AAEhD,QAAI,EAAE,QAAQ,UAAU,EAAE,SAAS,sBAAsB,EAAE,MAAM;AAChE,QAAE,OAAO;AACT,WAAK,MAAM,GAAG,UAAU;AACxB,WAAK,KAAK,QAAQ,CAAC;AACnB;AAAA,IACD;AACA,SAAK,aAAa,GAAG,EAAE,QAAQ,MAAM,EAAE,MAAM;AAAA,EAC9C;AAAA,EAEA,MAAc,aAAa,KAAqB;AAC/C,UAAM,IAAI,IAAI;AACd,UAAM,IAAI,GAAG,aAAa,KAAK,UAAU,IAAI,EAAE,UAAU,IAAI;AAC7D,QAAI,CAAC,KAAK,EAAE,WAAW,IAAI,KAAM;AACjC,MAAE,QAAQ,QAAQ;AAClB,SAAK,MAAM,GAAG,WAAW;AACzB,SAAK,gBAAgB,EAAE,UAAU;AACjC,QAAI,EAAE,YAAa,OAAU,OAAG,EAAE,aAAa,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAC/E;AAAA,EAEQ,aAAa,GAAa,MAAc,QAAiB;AAChE,MAAE,QAAQ,QAAQ;AAClB,SAAK,MAAM,GAAG,QAAQ;AACtB,MAAE,QAAQ,SAAS,GAAG,IAAI,KAAK,MAAM,KAAK;AAC1C,SAAK,gBAAgB,EAAE,UAAU;AACjC,SAAK,KAAK,uBAAuB;AAAA,MAChC,YAAY,EAAE;AAAA,MACd,KAAK,EAAE;AAAA,MACP;AAAA,MACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,IAC5B,CAAC;AAAA,EACF;AAAA;AAAA,EAIA,MAAc,QAAQ,GAAa;AAClC,QAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,IAAK;AACvB,SAAK,MAAM,GAAG,cAAc;AAC5B,UAAU,UAAW,WAAK,KAAK,SAAS,GAAG,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3E,MAAE,cAAmB,WAAK,KAAK,SAAS,GAAG,YAAY,GAAG,EAAE,UAAU,OAAO;AAE7E,QAAI,YAAY;AAChB,aAAS,UAAU,GAAG,WAAW,gBAAgB,QAAQ,WAAW;AACnE,UAAI;AACH,cAAM,MAAM,YAAY,IAAI,GAAG,EAAE,KAAK,GAAG,SAAS,SAAS,KAAK,EAAE,KAAK;AACvE,cAAM,MAAM,MAAM,MAAM,KAAK;AAAA,UAC5B,SAAS,EAAE,qBAAqB,EAAE,KAAK,MAAM;AAAA,UAC7C,QAAQ,YAAY,QAAQ,KAAK,UAAU;AAAA,QAC5C,CAAC;AACD,YAAI,CAAC,IAAI,MAAM,CAAC,IAAI,KAAM,OAAM,IAAI,MAAM,QAAQ,IAAI,MAAM,EAAE;AAC9D,cAAM,KAAK,oBAAoB,GAAG,IAAI,MAA0C,SAAS;AACzF,cAAM,KAAK,gBAAgB,CAAC;AAC5B;AAAA,MACD,SAAS,KAAK;AACb,YAAK,EAAE,UAA4B,YAAa;AAChD,cAAM,MAAM,EAAE,cAAc,MAAM,KAAK,oBAAoB,CAAC,IAAI;AAChE,oBAAY;AACZ,YAAI,UAAU,gBAAgB,QAAQ;AACrC,gBAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,gBAAgB,OAAO,CAAC,CAAC;AAChE;AAAA,QACD;AACA,QAAAA,QAAO;AAAA,UACN,mCAAmC,EAAE,WAAW,UAAU,GAAG,CAAC,CAAC,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QACrH;AACA,cAAM,KAAK,QAAQ,GAAG,cAAc;AAAA,UACnC,YAAY,EAAE;AAAA,UACd,MAAM;AAAA,QACP,CAAC;AACD,aAAK,aAAa,GAAG,kBAAkB;AACvC;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA;AAAA,EAGA,MAAc,oBACb,GACA,MACA,WACA,QACgB;AAChB,QAAI,CAAC,EAAE,OAAO,CAAC,EAAE,YAAa,OAAM,IAAI,MAAM,0BAA0B;AACxE,QAAI,cAAc,EAAG,OAAU,OAAG,EAAE,aAAa,EAAE,OAAO,KAAK,CAAC;AAChE,UAAM,UAAU,IAAI,mBAAmB,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,SAAS;AACnF,UAAM,MAAS,sBAAkB,EAAE,aAAa,EAAE,OAAO,YAAY,IAAI,MAAM,IAAI,CAAC;AACpF,MAAE,SAAS;AAAA,MACV,SAAS,MAAM;AACd;AAAC,QAAC,KAA6C,UAAU;AACzD,YAAI,QAAQ;AAAA,MACb;AAAA,IACD;AACA,MAAE,YAAY,YAAY,EAAE;AAC5B,YAAQ,GAAG,QAAQ,CAAC,UAAkB;AACrC,QAAE,aAAa,MAAM;AACrB,WAAK,aAAa,CAAC;AAAA,IACpB,CAAC;AACD,QAAI;AACH,UAAI,UAAU,KAAM,OAAM,SAAS,MAAM,WAAW,MAAM,GAAG,SAAS,GAAG;AAAA,UACpE,OAAM,SAAS,MAAM,SAAS,GAAG;AAAA,IACvC,UAAE;AACD,QAAE,SAAS;AAAA,IACZ;AAAA,EACD;AAAA;AAAA,EAGA,MAAc,oBAAoB,GAA8B;AAC/D,QAAI,CAAC,EAAE,YAAa,QAAO;AAC3B,QAAI;AACH,YAAME,QAAO,MAAU,SAAK,EAAE,WAAW;AACzC,YAAM,QAAQ,KAAK,MAAMA,MAAK,OAAO,EAAE,UAAU;AAEjD,YAAU,aAAS,EAAE,aAAa,QAAQ,EAAE,UAAU;AACtD,aAAO;AAAA,IACR,QAAQ;AACP,aAAO;AAAA,IACR;AAAA,EACD;AAAA,EAEA,MAAc,gBAAgB,GAAa;AAC1C,QAAI,CAAC,EAAE,YAAa;AACpB,SAAK,MAAM,GAAG,WAAW;AACzB,UAAM,YAAY,MAAM,WAAW,EAAE,WAAW;AAChD,UAAMA,QAAO,MAAU,SAAK,EAAE,WAAW,EAAE,MAAM,MAAM,IAAI;AAC3D,QAAI,cAAc,EAAE,UAAU,CAACA,SAAQA,MAAK,SAAS,EAAE,MAAM;AAC5D,YAAU,OAAG,EAAE,aAAa,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAC3D,YAAM,KAAK,QAAQ,GAAG,cAAc;AAAA,QACnC,YAAY,EAAE;AAAA,QACd,MAAM;AAAA,MACP,CAAC;AACD,WAAK,aAAa,GAAG,mBAAmB;AACxC;AAAA,IACD;AAEA,UAAU,UAAM,KAAK,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AACpD,UAAM,MAAW,cAAQ,EAAE,IAAI;AAC/B,UAAM,OAAO,EAAE,KAAK,MAAM,GAAG,EAAE,KAAK,SAAS,IAAI,MAAM;AACvD,QAAI,SAAc,WAAK,KAAK,SAAS,GAAG,EAAE,IAAI;AAC9C,aAAS,IAAI,KAAK,KAAK;AACtB,UAAI;AACH,cAAU,WAAO,MAAM;AACvB,iBAAc,WAAK,KAAK,SAAS,GAAG,GAAG,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE;AAAA,MAC3D,QAAQ;AACP;AAAA,MACD;AAAA,IACD;AACA,UAAU,WAAO,EAAE,aAAa,MAAM;AACtC,MAAE,YAAY;AACd,MAAE,YAAY,EAAE;AAChB,SAAK,MAAM,GAAG,MAAM;AACpB,SAAK,gBAAgB,EAAE,UAAU;AACjC,UAAM,KAAK,QAAQ,GAAG,aAAa,EAAE,YAAY,EAAE,YAAY,OAAO,EAAE,KAAK,CAAC;AAC9E,QAAI,EAAE,UAAU,EAAE,OAAO;AAExB,YAAM,KAAK,WAAW,EAAE,OAAO,mBAAmB,EAAE,QAAQ,EAAE,OAAO,CAAC;AAAA,IACvE;AACA,SAAK,KAAK,yBAAyB;AAAA,MAClC,YAAY,EAAE;AAAA,MACd,MAAM,EAAE;AAAA,MACR,UAAU,EAAE;AAAA,MACZ,MAAW,eAAS,MAAM;AAAA,MAC1B,MAAM;AAAA,MACN,MAAM,EAAE;AAAA,MACR,QAAQ,EAAE;AAAA,IACX,CAAC;AAAA,EACF;AAAA;AAAA,EAIA,MAAc,QAAQ,GAAa;AAClC,QAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,EAAE,QAAS;AACrC,SAAK,MAAM,GAAG,cAAc;AAC5B,aAAS,UAAU,GAAG,WAAW,gBAAgB,QAAQ,WAAW;AACnE,UAAI;AACH,cAAM,KAAK,eAAe,GAAG,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;AAE/E;AAAA,MACD,SAAS,KAAK;AACb,YAAK,EAAE,UAA4B,YAAa;AAChD,YAAI,UAAU,gBAAgB,QAAQ;AACrC,gBAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,gBAAgB,OAAO,CAAC,CAAC;AAChE;AAAA,QACD;AACA,QAAAF,QAAO;AAAA,UACN,mCAAmC,EAAE,WAAW,UAAU,GAAG,CAAC,CAAC,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QACrH;AAEA,cAAM,QAAQ,KAAK,KAAK,gBAAgB,EAAE,MAAM;AAChD,YAAI,OAAO;AACV,YAAE,QAAQ;AACV,eAAK,KAAK,eAAe,CAAC;AAC1B;AAAA,QACD;AACA,cAAM,KAAK,QAAQ,GAAG,cAAc;AAAA,UACnC,YAAY,EAAE;AAAA,UACd,MAAM;AAAA,QACP,CAAC;AACD,aAAK,aAAa,GAAG,kBAAkB;AACvC;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA;AAAA,EAGA,MAAc,eACb,GACA,KACA,OACA,MACgB;AAChB,QAAI,CAAC,EAAE,OAAO,CAAC,EAAE,QAAS,OAAM,IAAI,MAAM,0BAA0B;AACpE,UAAM,MAAS,qBAAiB,EAAE,OAAO;AACzC,UAAM,MAAM,IAAI,mBAAmB,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU;AACpE,MAAE,SAAS,EAAE,SAAS,MAAM,IAAI,QAAQ,EAAE;AAC1C,MAAE,YAAY;AACd,QAAI,GAAG,QAAQ,CAAC,UAAU;AACzB,QAAE,aAAc,MAAiB;AACjC,WAAK,aAAa,CAAC;AAAA,IACpB,CAAC;AACD,QAAI;AACH,YAAM,MAAM,MAAM,MAAM,KAAK;AAAA,QAC5B,QAAQ;AAAA,QACR,SAAS;AAAA,UACR,qBAAqB;AAAA,UACrB,gBAAgB;AAAA,UAChB,GAAI,MAAM,UAAU,EAAE,kBAAkB,IAAI,IAAI,CAAC;AAAA,QAClD;AAAA;AAAA;AAAA;AAAA,QAIA,MAAM,IAAI,KAAK,GAAG;AAAA,QAClB,QAAQ;AAAA,QACR,QAAQ,YAAY,QAAQ,KAAK,UAAU;AAAA,MAC5C,CAAgB;AAChB,UAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,QAAQ,IAAI,MAAM,EAAE;AAAA,IAClD,UAAE;AACD,QAAE,SAAS;AAAA,IACZ;AAAA,EACD;AAAA,EAEA,MAAc,eAAe,GAAa;AACzC,QAAI,CAAC,EAAE,OAAO,CAAC,EAAE,WAAW,CAAC,EAAE,MAAO;AACtC,SAAK,MAAM,GAAG,cAAc;AAC5B,UAAM,QAAQ,MAAM,KAAK,aAAa,EAAE,OAAO,qBAAqB;AAAA,MACnE,YAAY,EAAE;AAAA,MACd,IAAI,EAAE;AAAA,MACN,YAAY,cAAc,EAAE,MAAM,EAAE,UAAU;AAAA,IAC/C,CAAC;AACD,QAAI,UAAU,OAAO;AACpB,YAAM,KAAK,QAAQ,GAAG,cAAc,EAAE,YAAY,EAAE,YAAY,MAAM,eAAe,CAAC;AACtF,WAAK,aAAa,GAAG,gBAAgB,MAAM,IAAI;AAC/C;AAAA,IACD;AACA,MAAE,SAAS,MAAM;AACjB,QAAI;AACH,YAAM,KAAK,eAAe,GAAG,MAAM,KAAK,MAAM,OAAO,CAAC,CAAC;AAAA,IACxD,SAAS,KAAK;AACb,WAAK,aAAa,GAAG,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC3E;AAAA,IACD;AACA,UAAM,KAAK,QAAQ,GAAG,oBAAoB;AAAA,MACzC,YAAY,EAAE;AAAA,MACd,QAAQ,EAAE;AAAA,MACV,OAAO,EAAE;AAAA,IACV,CAAC;AAAA,EACF;AAAA,EAEA,MAAc,iBAAiB,GAAa;AAC3C,QAAI,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,EAAE,MAAO;AACrC,SAAK,MAAM,GAAG,cAAc;AAC5B,UAAM,QAAQ,MAAM,KAAK,aAAa,EAAE,OAAO,oBAAoB;AAAA,MAClE,YAAY,EAAE;AAAA,MACd,QAAQ,EAAE;AAAA,IACX,CAAC;AACD,QAAI,UAAU,OAAO;AACpB,WAAK,aAAa,GAAG,gBAAgB,MAAM,IAAI;AAC/C;AAAA,IACD;AACA,UAAU,UAAW,WAAK,KAAK,SAAS,GAAG,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3E,MAAE,cAAmB,WAAK,KAAK,SAAS,GAAG,YAAY,GAAG,EAAE,UAAU,OAAO;AAC7E,QAAI,YAAY;AAChB,aAAS,UAAU,GAAG,WAAW,gBAAgB,QAAQ,WAAW;AACnE,UAAI;AACH,cAAM,MAAM,YAAY,IAAI,GAAG,MAAM,GAAG,SAAS,SAAS,KAAK,MAAM;AACrE,cAAM,MAAM,MAAM,MAAM,KAAK;AAAA,UAC5B,SAAS,EAAE,qBAAqB,MAAM,MAAM;AAAA,UAC5C,QAAQ,YAAY,QAAQ,KAAK,UAAU;AAAA,QAC5C,CAAC;AACD,YAAI,CAAC,IAAI,MAAM,CAAC,IAAI,KAAM,OAAM,IAAI,MAAM,QAAQ,IAAI,MAAM,EAAE;AAC9D,cAAM,KAAK,oBAAoB,GAAG,IAAI,MAA0C,SAAS;AACzF,cAAM,KAAK,gBAAgB,CAAC;AAC5B;AAAA,MACD,SAAS,KAAK;AACb,YAAK,EAAE,UAA4B,YAAa;AAChD,oBAAY,MAAM,KAAK,oBAAoB,CAAC;AAC5C,YAAI,UAAU,gBAAgB,QAAQ;AACrC,gBAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,gBAAgB,OAAO,CAAC,CAAC;AAChE;AAAA,QACD;AACA,aAAK,aAAa,GAAG,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC3E;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA;AAAA,EAGA,MAAc,aACb,OACA,MACA,SAC6E;AAC7E,UAAM,MAAM,OAAO,QAAQ,cAAc,QAAQ,MAAM;AACvD,WAAO,IAAI,QAAQ,CAACG,aAAY;AAC/B,YAAM,QAAQ,WAAW,MAAM;AAC9B,aAAK,aAAa,OAAO,GAAG;AAC5B,QAAAA,SAAQ,EAAE,MAAM,gBAAgB,CAAC;AAAA,MAClC,GAAG,gBAAgB;AACnB,WAAK,aAAa,IAAI,KAAK;AAAA,QAC1B,SAAS,CAAC,MAAM;AACf,uBAAa,KAAK;AAClB,eAAK,aAAa,OAAO,GAAG;AAC5B,UAAAA,SAAQ,CAAC;AAAA,QACV;AAAA,MACD,CAAC;AACD,WAAK,KAAK,WAAW,OAAO,MAAM,OAAO,EAAE,KAAK,CAAC,SAAS;AACzD,YAAI,CAAC,MAAM;AACV,uBAAa,KAAK;AAClB,eAAK,aAAa,OAAO,GAAG;AAC5B,UAAAA,SAAQ,EAAE,MAAM,kBAAkB,CAAC;AAAA,QACpC;AAAA,MACD,CAAC;AAAA,IACF,CAAC;AAAA,EACF;AAAA,EAEA,MAAc,WACb,QACA,MACA,SACmB;AACnB,UAAM,MAAM;AAAA,MACX;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,KAAK,KAAK,QAAQ;AAAA,MAClB,KAAK,KAAK,QAAQ;AAAA,IACnB;AACA,WAAO,KAAK,KAAK,UAAU,WAAW,QAAQ,GAAG;AAAA,EAClD;AAAA,EAEQ,kBAAkB,KAAqB;AAC9C,UAAM,IAAI,IAAI;AAOd,UAAM,MAAM,GAAG,cAAc,GAAG;AAChC,UAAM,SAAS,MAAM,KAAK,aAAa,IAAI,OAAO,GAAG,CAAC,IAAI;AAC1D,QAAI,CAAC,OAAQ;AACb,QAAI,IAAI,SAAS,sBAAsB,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK;AACpE,aAAO,QAAQ,EAAE,QAAQ,EAAE,QAAQ,OAAO,EAAE,OAAO,KAAK,EAAE,IAAI,CAAC;AAAA,IAChE,OAAO;AACN,aAAO,QAAQ,EAAE,MAAM,EAAE,UAAU,SAAS,CAAC;AAAA,IAC9C;AAAA,EACD;AAAA;AAAA,EAIQ,iBAAiB,oBAAI,IAAsB;AAAA,EAEnD,MAAc,gBAAgB,KAAqB;AAClD,UAAM,IAAI,IAAI;AACd,UAAM,OAAO,CAAC,WACb,KAAK,WAAW,IAAI,MAAM,mBAAmB;AAAA,MAC5C,YAAY,GAAG;AAAA,MACf;AAAA,IACD,CAAC;AACF,QAAI,CAAC,KAAK,aAAa,KAAK,KAAK,OAAO,YAAY,OAAO;AAC1D,YAAM,KAAK,UAAU;AACrB;AAAA,IACD;AACA,QAAI,CAAC,GAAG,cAAc,CAAC,EAAE,MAAM,OAAO,EAAE,eAAe,YAAY,EAAE,aAAa,EAAG;AAErF,UAAM,QAAQ,KAAK,KAAK,UAAU,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS;AACzF,QAAI,CAAC,OAAO;AACX,YAAM,KAAK,cAAc;AACzB;AAAA,IACD;AAEA,UAAM,YAAY,GAAG,IAAI,IAAI,SAAI,EAAE,EAAE;AACrC,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,OAAO,KAAK,eAAe,IAAI,SAAS,KAAK,CAAC,GAAG,OAAO,CAAC,OAAO,MAAM,KAAK,GAAM;AACvF,QAAI,IAAI,UAAU,GAAG;AACpB,YAAM,KAAK,cAAc;AACzB;AAAA,IACD;AACA,QAAI,KAAK,GAAG;AACZ,SAAK,eAAe,IAAI,WAAW,GAAG;AAEtC,UAAM,UAAU,KAAK,UAAU,OAAO,IAAI,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU;AAChF,QAAI,WAAW,SAAS;AAIvB,YAAM;AAAA,QACL,QAAQ,UAAU,cACf,sCAAsC,SAAS,KAAK,KAAK,OAAO,iBAAiB,uBAAuB,CAAC,oIACzG,QAAQ,UAAU,UACjB,wKACA,QAAQ;AAAA,MACb;AACA;AAAA,IACD;AACA,UAAM,QAAQ,KAAK,UAAU,QAAQ,QAAQ,IAAI,MAAM,QAAQ;AAC/D,UAAM,KAAK,WAAW,IAAI,MAAM,oBAAoB;AAAA,MACnD,YAAY,EAAE;AAAA,MACd,QAAQ,QAAQ;AAAA,MAChB;AAAA,MACA,KAAK,GAAG,KAAK,KAAK,kBAAkB,iBAAiB,QAAQ,MAAM;AAAA,MACnE,IAAI;AAAA,IACL,CAAC;AAAA,EACF;AAAA,EAEA,MAAc,eAAe,KAAqB;AACjD,UAAM,IAAI,IAAI;AACd,UAAM,OAAO,GAAG,SAAS,KAAK,WAAW,IAAI,EAAE,MAAM,IAAI;AACzD,QAAI,CAAC,QAAQ,CAAC,KAAK,YAAY,KAAK,OAAO,IAAI,MAAM;AACpD,YAAM,KAAK,WAAW,IAAI,MAAM,mBAAmB;AAAA,QAClD,QAAQ,GAAG;AAAA,QACX,YAAY,GAAG;AAAA,QACf,QAAQ;AAAA,MACT,CAAC;AACD;AAAA,IACD;AACA,UAAM,QAAQ,KAAK,UAAU,KAAK,QAAQ,IAAI,MAAM,UAAU;AAC9D,UAAM,KAAK,WAAW,IAAI,MAAM,oBAAoB;AAAA,MACnD,YAAY,GAAG;AAAA,MACf,QAAQ,KAAK;AAAA,MACb;AAAA,MACA,KAAK,GAAG,KAAK,KAAK,kBAAkB,iBAAiB,KAAK,MAAM;AAAA,MAChE,IAAI;AAAA,IACL,CAAC;AAAA,EACF;AAAA,EAEQ,cAAc,KAAqB;AAC1C,UAAM,IAAI,IAAI;AACd,UAAM,OAAO,GAAG,SAAS,KAAK,WAAW,IAAI,EAAE,MAAM,IAAI;AACzD,QAAI,CAAC,QAAQ,KAAK,OAAO,IAAI,KAAM;AACnC,SAAK,gBAAgB,KAAK,MAAM;AAChC,SAAK,WAAW,OAAO,KAAK,MAAM;AAAA,EACnC;AAAA;AAAA;AAAA,EAKA,kBACC,KACA,KACA,UACU;AACV,UAAM,KAAK,SAAS,MAAM,GAAG,EAAE,CAAC;AAChC,QAAI,CAAC,MAAM,CAAC,sBAAsB,KAAK,EAAE,EAAG,QAAO;AACnD,UAAM,QAAQ,KAAK,YAAY,IAAI,QAAQ,mBAAmB,CAAuB;AACrF,QAAI,CAAC,SAAS,MAAM,OAAO,IAAI;AAC9B,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI;AACR,aAAO;AAAA,IACR;AACA,QAAI,KAAK,kBAAkB,KAAK,KAAK,OAAO,iBAAiB,IAAI;AAChE,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI;AACR,aAAO;AAAA,IACR;AACA,UAAM,OAAO,MAAM;AAClB,WAAK,gBAAgB,KAAK,IAAI,GAAG,KAAK,gBAAgB,CAAC;AAAA,IACxD;AACA,SAAK,iBAAiB;AAEtB,UAAM,OAAO,CAAC,WAAmB;AAChC,WAAK;AACL,UAAI,CAAC,IAAI,YAAa,KAAI,UAAU,MAAM;AAC1C,UAAI,IAAI;AACR,aAAO;AAAA,IACR;AAEA,QAAI;AACH,UAAI,MAAM,OAAO,UAAU,IAAI,WAAW,OAAO;AAChD,aAAK,KAAK,gBAAgB,KAAK,KAAK,KAAK,EAAE,QAAQ,IAAI;AACvD,eAAO;AAAA,MACR;AACA,UAAI,MAAM,OAAO,UAAU,IAAI,WAAW,QAAQ;AACjD,aAAK,KAAK,kBAAkB,KAAK,KAAK,KAAK,EAAE,QAAQ,IAAI;AACzD,eAAO;AAAA,MACR;AACA,UAAI,MAAM,OAAO,YAAY,IAAI,WAAW,QAAQ;AACnD,aAAK,KAAK,iBAAiB,KAAK,KAAK,KAAK,EAAE,QAAQ,IAAI;AACxD,eAAO;AAAA,MACR;AACA,UAAI,MAAM,OAAO,cAAc,IAAI,WAAW,OAAO;AACpD,aAAK,KAAK,iBAAiB,KAAK,KAAK,KAAK,EAAE,QAAQ,IAAI;AACxD,eAAO;AAAA,MACR;AACA,aAAO,KAAK,GAAG;AAAA,IAChB,QAAQ;AACP,aAAO,KAAK,GAAG;AAAA,IAChB;AAAA,EACD;AAAA;AAAA,EAGA,MAAc,gBACb,KACA,KACA,OACC;AACD,UAAM,IAAI,KAAK,UAAU,IAAI,MAAM,EAAE;AACrC,QAAI,CAAC,KAAK,EAAE,QAAQ,UAAU,CAAC,EAAE,OAAO,CAAC,EAAE,SAAS;AACnD,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI;AACR;AAAA,IACD;AACA,UAAM,OAAO,KAAK;AAAA,MACjB;AAAA,MACA,OAAO,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,EAAE,aAAa,IAAI,MAAM,KAAK,CAAC,KAAK;AAAA,IAC9E;AACA,QAAI,OAAO,EAAE,aAAa;AACzB,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI;AACR;AAAA,IACD;AACA,SAAK,MAAM,GAAG,cAAc;AAC5B,UAAM,iBAAiB,KAAK,IAAI,GAAG,EAAE,OAAO,OAAO,EAAE,UAAU;AAC/D,QAAI,UAAU,KAAK;AAAA,MAClB,gBAAgB;AAAA,MAChB,kBAAkB,OAAO,cAAc,gBAAgB,EAAE,UAAU,CAAC;AAAA,IACrE,CAAC;AACD,UAAM,MAAS,qBAAiB,EAAE,SAAS,EAAE,OAAO,OAAO,EAAE,WAAW,CAAC;AACzE,UAAM,MAAM,IAAI,mBAAmB,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,IAAI;AAC1E,MAAE,YAAY,OAAO,EAAE;AACvB,QAAI,GAAG,QAAQ,CAAC,UAAU;AACzB,QAAE,aAAc,MAAiB;AACjC,WAAK,aAAa,CAAC;AAAA,IACpB,CAAC;AACD,QAAI,GAAG,SAAS,MAAM,IAAI,QAAQ,CAAC;AACnC,QAAI;AACH,YAAM,SAAS,KAAK,KAAK,GAAG;AAAA,IAC7B,QAAQ;AAAA,IAER;AAAA,EACD;AAAA;AAAA,EAGA,MAAc,kBACb,KACA,KACA,OACC;AACD,UAAM,IAAI,KAAK,UAAU,IAAI,MAAM,EAAE;AACrC,QAAI,CAAC,KAAK,EAAE,QAAQ,UAAU,CAAC,EAAE,KAAK;AACrC,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI;AACR;AAAA,IACD;AACA,UAAU,UAAW,WAAK,KAAK,SAAS,GAAG,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3E,MAAE,cAAmB,WAAK,KAAK,SAAS,GAAG,YAAY,GAAG,EAAE,UAAU,OAAO;AAC7E,SAAK,MAAM,GAAG,cAAc;AAC5B,QAAI;AACH,YAAM,KAAK,oBAAoB,GAAG,KAAK,GAAG,cAAc,EAAE,MAAM,EAAE,UAAU,IAAI,IAAI;AAAA,IACrF,SAAS,KAAK;AACb,YAAU,OAAG,EAAE,aAAa,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAC3D,UAAI,CAAC,IAAI,YAAa,KAAI,UAAU,GAAG;AACvC,UAAI,IAAI;AACR,MAAAH,QAAO;AAAA,QACN,uCAAuC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACxF;AACA;AAAA,IACD;AACA,QAAI,UAAU,GAAG;AACjB,QAAI,IAAI;AACR,UAAM,KAAK,gBAAgB,CAAC;AAAA,EAC7B;AAAA;AAAA,EAGA,MAAc,iBACb,KACA,KACA,OACC;AACD,UAAM,OAAO,KAAK,WAAW,IAAI,MAAM,EAAE;AACzC,UAAM,MAAM,KAAK,WAAW,UAAU,MAAM,EAAE;AAC9C,QAAI,CAAC,QAAQ,CAAC,OAAO,KAAK,SAAS,MAAM,QAAQ;AAChD,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI;AACR;AAAA,IACD;AACA,QAAI;AACH,YAAM,SAAS,KAAK,WAAW,KAAK,aAAa,IAAI,GAAG,GAAG;AAC3D,UAAI,CAAC,KAAK,WAAW,SAAS,MAAM,EAAE,EAAG,OAAM,IAAI,MAAM,iBAAiB;AAAA,IAC3E,SAAS,KAAK;AACb,UAAI,CAAC,IAAI,YAAa,KAAI,UAAU,GAAG;AACvC,UAAI,IAAI;AACR,MAAAA,QAAO;AAAA,QACN,sCAAsC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACvF;AACA;AAAA,IACD;AACA,SAAK,gBAAgB,MAAM,EAAE;AAC7B,QAAI,UAAU,GAAG;AACjB,QAAI,IAAI;AAAA,EACT;AAAA;AAAA,EAGA,MAAc,iBACb,KACA,KACA,OACC;AACD,UAAM,OAAO,KAAK,WAAW,IAAI,MAAM,EAAE;AACzC,UAAM,aAAa,KAAK,WAAW,WAAW,MAAM,EAAE;AACtD,QAAI,CAAC,QAAQ,CAAC,cAAc,KAAK,OAAO,MAAM,QAAQ;AACrD,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI;AACR;AAAA,IACD;AACA,UAAM,OAAO,KAAK;AAAA,MACjB;AAAA,MACA,OAAO,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,EAAE,aAAa,IAAI,MAAM,KAAK,CAAC,KAAK;AAAA,IAC9E;AACA,QAAI,SAAS;AACb,QAAI;AACH,eAAS,MAAM,gBAAgB,YAAY,IAAI;AAAA,IAChD,QAAQ;AACP,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI;AACR;AAAA,IACD;AACA,UAAMI,UAAS,KAAK,WAAW,SAAS,MAAM,IAAI,MAAM;AACxD,QAAI,CAACA,SAAQ;AACZ,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI;AACR;AAAA,IACD;AACA,QAAI,UAAU,KAAK;AAAA,MAClB,gBAAgB;AAAA,MAChB,kBAAkB,OAAO,KAAK,aAAa,MAAM;AAAA,IAClD,CAAC;AACD,QAAI,GAAG,SAAS,MAAMA,QAAO,QAAQ,CAAC;AACtC,QAAI;AACH,YAAM,SAASA,SAAQ,GAAG;AAAA,IAC3B,QAAQ;AAAA,IAER;AAAA,EACD;AAAA;AAAA,EAIQ,iBAAiB;AACxB,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,KAAK,KAAK,UAAU,OAAO,GAAG;AACxC,YAAM,WAAW,CAAC,QAAQ,YAAY,UAAU,aAAa,SAAS,EAAE,SAAS,EAAE,KAAK;AACxF,UAAI,CAAC,YAAY,MAAM,EAAE,YAAY,KAAK,YAAY;AACrD,UAAE,QAAQ,QAAQ;AAClB,aAAK,MAAM,GAAG,SAAS;AACvB,aAAK,gBAAgB,EAAE,UAAU;AACjC,YAAI,EAAE,YAAa,MAAS,OAAG,EAAE,aAAa,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAC7E,aAAK,KAAK,QAAQ,GAAG,cAAc;AAAA,UAClC,YAAY,EAAE;AAAA,UACd,MAAM;AAAA,QACP,CAAC,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAClB;AAEA,UAAI,YAAY,MAAM,EAAE,YAAY,KAAK,KAAK,IAAQ,MAAK,UAAU,OAAO,EAAE,UAAU;AAAA,IACzF;AAAA,EACD;AAAA;AAAA,EAGA,MAAc,oBAAoB,GAAa;AAC9C,QAAI,CAAC,EAAE,QAAS;AAChB,UAAM,SAAS,KAAK,UAAU;AAC9B,QAAI,EAAE,QAAQ,WAAW,SAAc,SAAG,GAAG;AAC5C,YAAU,OAAG,EAAE,SAAS,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACxD;AAAA,EACD;AAAA;AAAA,EAGA,MAAc,cAAc;AAC3B,UAAM,SAAS,KAAK,UAAU;AAC9B,QAAI;AACH,YAAM,UAAU,MAAU,YAAQ,MAAM;AACxC,YAAM,MAAM,KAAK,IAAI;AACrB,iBAAW,QAAQ,SAAS;AAC3B,cAAM,IAAS,WAAK,QAAQ,IAAI;AAChC,cAAMF,QAAO,MAAU,SAAK,CAAC,EAAE,MAAM,MAAM,IAAI;AAC/C,YAAIA,OAAM,OAAO,KAAK,MAAMA,MAAK,UAAU,KAAK,KAAK,KAAQ;AAC5D,gBAAU,OAAG,GAAG,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,UAAC,CAAC;AAAA,QAChD;AAAA,MACD;AAAA,IACD,QAAQ;AAAA,IAER;AAAA,EACD;AACD;;;AGrhDA,IAAMG,UAAS,aAAa,gBAAgB;AAE5C,IAAM,+BAA+B;AACrC,IAAM,wBAAwB;AAavB,IAAM,gBAAN,MAAoB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,WAA0B;AAAA,EAC1B,aAA4B;AAAA,EAC5B,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAER,YACC,WACA,WACA,YACA,cACA,YACA,cACA,cACC;AACD,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,SAAK,aAAa;AAClB,SAAK,eAAe;AACpB,SAAK,aAAa;AAClB,SAAK,eAAe;AACpB,SAAK,eAAe,iBAAiB,SAAS,SAAY;AAE1D,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,WAAK,cAAc,OAAO;AAAA,IAC3B,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAA6B,cAAiC;AACnE,SAAK,iBAAiB;AACtB,SAAK,eAAe;AAGpB,SAAK,YAAY;AAGjB,SAAK,eAAe,YAAY,MAAM,KAAK,QAAQ,GAAG,4BAA4B;AAElF,IAAAA,QAAO;AAAA,MACN,kDAA6C,KAAK,cAAc,MAAM,MAAM,KAAK,YAAY,KAAK,YAAY,UAAU,GAAG,CAAC,CAAC;AAAA,IAC9H;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,OAAa;AACZ,QAAI,KAAK,gBAAgB;AACxB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IACvB;AACA,QAAI,KAAK,cAAc;AACtB,oBAAc,KAAK,YAAY;AAC/B,WAAK,eAAe;AAAA,IACrB;AACA,SAAK,WAAW;AAChB,SAAK,aAAa;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,WAAwB;AACvB,WAAO;AAAA,MACN,UAAU,KAAK;AAAA,MACf,YAAY,KAAK;AAAA,MACjB,UAAU,KAAK,SAAS;AAAA,MACxB,eAAe,KAAK;AAAA,MACpB,kBAAkB,KAAK;AAAA,IACxB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,WAAoB;AACnB,WAAO,KAAK,aAAa,KAAK;AAAA,EAC/B;AAAA;AAAA,EAGA,UAAgB;AACf,SAAK,YAAY;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,cAAoB;AAC3B,QAAI;AAEJ,QAAI,KAAK,cAAc;AAEtB,UAAI,KAAK,iBAAiB,KAAK,cAAc;AAC5C,sBAAc,KAAK;AAAA,MACpB,OAAO;AACN,cAAM,SAAS,KAAK,UAAU,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,YAAY;AACrF,sBAAc,QAAQ,MAAM,KAAK;AAAA,MAClC;AAAA,IACD,OAAO;AAEN,YAAM,SAAS,CAAC,KAAK,UAAU;AAC/B,iBAAW,YAAY,KAAK,UAAU,aAAa,GAAG;AACrD,eAAO,KAAK,SAAS,EAAE;AAAA,MACxB;AACA,aAAO,KAAK;AACZ,oBAAc,OAAO,CAAC;AAAA,IACvB;AAEA,UAAM,YAAY,KAAK,SAAS;AAEhC,QAAI,gBAAgB,KAAK,UAAU;AAClC,WAAK,WAAW;AAEhB,UAAI,gBAAgB,KAAK,YAAY;AACpC,aAAK,aAAa,KAAK;AACvB,QAAAA,QAAO,KAAK,iCAAiC;AAC7C,aAAK,qBAAqB;AAC1B,YAAI,CAAC,UAAW,MAAK,iBAAiB;AAAA,MACvC,OAAO;AACN,cAAM,WAAW,KAAK,UAAU,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,WAAW;AAC/E,aAAK,aAAa,UAAU,QAAQ,YAAY,UAAU,GAAG,CAAC;AAC9D,QAAAA,QAAO,KAAK,cAAc,KAAK,UAAU,KAAK,YAAY,UAAU,GAAG,CAAC,CAAC,GAAG;AAC5E,aAAK,oBAAoB;AACzB,YAAI,UAAW,MAAK,eAAe;AAAA,MACpC;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKQ,uBAA6B;AACpC,SAAK,oBAAoB;AACzB,SAAK,iBAAiB,YAAY,MAAM;AACvC,YAAM,UAAU;AAAA,QACf;AAAA,QACA,KAAK;AAAA,QACL;AAAA,QACA,EAAE,WAAW,KAAK,IAAI,EAAE;AAAA,QACxB,KAAK;AAAA,QACL,KAAK;AAAA,MACN;AACA,WAAK,UAAU,UAAU,OAAO;AAAA,IACjC,GAAG,4BAA4B;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAA4B;AACnC,QAAI,KAAK,gBAAgB;AACxB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IACvB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAgB;AAEvB,SAAK,YAAY;AAGjB,QAAI,CAAC,KAAK,SAAS,KAAK,KAAK,UAAU;AACtC,YAAM,qBAAqB,KAAK,IAAI,IAAI,KAAK;AAE7C,UAAI,KAAK,sBAAsB,KAAK,qBAAqB,+BAA+B,KAAK;AAC5F,aAAK;AACL,QAAAA,QAAO;AAAA,UACN,4BAA4B,KAAK,gBAAgB,IAAI,qBAAqB,sBAAiB,KAAK,MAAM,qBAAqB,GAAI,CAAC;AAAA,QACjI;AAEA,YAAI,KAAK,oBAAoB,uBAAuB;AACnD,UAAAA,QAAO,KAAK,UAAU,KAAK,UAAU,6CAAwC;AAC7E,eAAK,mBAAmB;AACxB,eAAK,sBAAsB;AAE3B,eAAK,YAAY;AAAA,QAClB;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,SAA+B;AACpD,YAAQ,QAAQ,MAAM;AAAA,MACrB,KAAK;AACJ,YAAI,QAAQ,SAAS,KAAK,UAAU;AACnC,eAAK,sBAAsB,KAAK,IAAI;AACpC,eAAK,mBAAmB;AAAA,QACzB;AACA;AAAA,MAED,KAAK;AAGJ,YAAI,QAAQ,OAAO,KAAK,YAAY;AACnC,UAAAA,QAAO,KAAK,8BAA8B,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,EAAE;AACxE,cAAI,KAAK,SAAS,GAAG;AACpB,iBAAK,oBAAoB;AACzB,iBAAK,eAAe;AAAA,UACrB;AACA,eAAK,WAAW,QAAQ;AACxB,gBAAM,WAAW,KAAK,UAAU,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ,IAAI;AAChF,eAAK,aAAa,UAAU,QAAQ,QAAQ,KAAK,UAAU,GAAG,CAAC;AAC/D,eAAK,sBAAsB,KAAK,IAAI;AACpC,eAAK,mBAAmB;AAGxB,gBAAM,MAAM;AAAA,YACX;AAAA,YACA,KAAK;AAAA,YACL,QAAQ;AAAA,YACR,EAAE,UAAU,KAAK;AAAA,YACjB,KAAK;AAAA,YACL,KAAK;AAAA,UACN;AACA,eAAK,UAAU,WAAW,QAAQ,MAAM,GAAG;AAAA,QAC5C;AACA;AAAA,MAED,KAAK;AAEJ;AAAA,IACF;AAAA,EACD;AACD;;;AChRA,YAAYC,aAAY;AAOxB,IAAMC,UAAS,aAAa,gBAAgB;AAqCrC,IAAM,oBAAN,MAAwB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe,oBAAI,IAOzB;AAAA,EACM,mBAAmB,oBAAI,IAO7B;AAAA,EACM;AAAA,EAER,YACC,WACA,WACA,YACA,YACA,cACA,SACC;AACD,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,SAAK,aAAa;AAClB,SAAK,aAAa;AAClB,SAAK,eAAe;AACpB,SAAK,UAAU,WAAW,CAAC;AAG3B,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,WAAK,cAAc,OAAO;AAAA,IAC3B,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aACL,kBACA,SACA,YAAY,KACgB;AAC5B,UAAM,SAAS,QAAQ,UAAiB,mBAAW;AAEnD,UAAM,UAAU;AAAA,MACf;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA,EAAE,GAAG,SAAS,OAAO;AAAA,MACrB,KAAK;AAAA,MACL,KAAK;AAAA,IACN;AAEA,UAAM,OAAO,MAAM,KAAK,UAAU,WAAW,kBAAkB,OAAO;AACtE,QAAI,CAAC,MAAM;AACV,aAAO,EAAE,QAAQ,QAAQ,UAAU,OAAO,uBAAuB;AAAA,IAClE;AAEA,IAAAA,QAAO;AAAA,MACN,kBAAkB,OAAO,UAAU,GAAG,CAAC,CAAC,OAAO,iBAAiB,UAAU,GAAG,CAAC,CAAC,MAAM,QAAQ,MAAM,UAAU,GAAG,EAAE,CAAC;AAAA,IACpH;AAGA,WAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACvC,YAAM,QAAQ,WAAW,MAAM;AAC9B,aAAK,aAAa,OAAO,MAAM;AAC/B,QAAAA,SAAQ,EAAE,QAAQ,QAAQ,WAAW,OAAO,wBAAwB,SAAS,KAAK,CAAC;AAAA,MACpF,GAAG,SAAS;AAEZ,WAAK,aAAa,IAAI,QAAQ,EAAE,SAAAA,UAAS,QAAQ,MAAM,CAAC;AAAA,IACzD,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,kBACL,kBACA,UACA,QACA,YAAY,KACoB;AAChC,UAAM,SAAgB,mBAAW;AAEjC,UAAM,UAAU;AAAA,MACf;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA,EAAE,QAAQ,UAAU,OAAO;AAAA,MAC3B,KAAK;AAAA,MACL,KAAK;AAAA,IACN;AAEA,UAAM,OAAO,MAAM,KAAK,UAAU,WAAW,kBAAkB,OAAO;AACtE,QAAI,CAAC,MAAM;AACV,aAAO,EAAE,QAAQ,SAAS,OAAO,OAAO,uBAAuB;AAAA,IAChE;AAEA,IAAAD,QAAO,KAAK,oBAAoB,QAAQ,WAAM,iBAAiB,UAAU,GAAG,CAAC,CAAC,EAAE;AAEhF,WAAO,IAAI,QAAQ,CAACC,aAAY;AAC/B,YAAM,QAAQ,WAAW,MAAM;AAC9B,aAAK,iBAAiB,OAAO,MAAM;AACnC,QAAAA,SAAQ;AAAA,UACP;AAAA,UACA,SAAS;AAAA,UACT,OAAO,oCAAoC,SAAS;AAAA,QACrD,CAAC;AAAA,MACF,GAAG,SAAS;AAEZ,WAAK,iBAAiB,IAAI,QAAQ,EAAE,SAAAA,UAAS,QAAQ,MAAM;AAAA,MAAC,GAAG,MAAM,CAAC;AAAA,IACvE,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,kBAAkB,UAAiC;AAElD,eAAW,CAAC,SAAS,UAAU,KAAK,OAAO,QAAQ,KAAK,OAAO,GAAG;AACjE,UAAI,aAAa,SAAS,QAAQ,GAAG;AAEpC,cAAM,YAAY,KAAK,UAAU,aAAa;AAC9C,cAAM,SAAS,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU;AAC1D,YAAI,OAAQ,QAAO,OAAO;AAAA,MAC3B;AAAA,IACD;AAGA,UAAM,QAAQ,KAAK,UAAU,cAAc,QAAQ;AACnD,WAAO,OAAO,cAAc;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,SAA+B;AACpD,YAAQ,QAAQ,MAAM;AAAA,MACrB,KAAK;AACJ,aAAK,iBAAiB,OAAO;AAC7B;AAAA,MACD,KAAK;AACJ,aAAK,iBAAiB,OAAO;AAC7B;AAAA,MACD,KAAK;AACJ,aAAK,iBAAiB,OAAO;AAC7B;AAAA,IACF;AAAA,EACD;AAAA,EAEQ,iBAAiB,SAA+B;AACvD,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,QAAQ,OAAQ;AAErB,UAAM,UAAU,KAAK,aAAa,IAAI,OAAO,MAAM;AACnD,QAAI,SAAS;AACZ,mBAAa,QAAQ,KAAK;AAC1B,WAAK,aAAa,OAAO,OAAO,MAAM;AACtC,cAAQ,QAAQ,MAAM;AACtB,MAAAD,QAAO,KAAK,eAAe,OAAO,OAAO,UAAU,GAAG,CAAC,CAAC,eAAe,OAAO,MAAM,EAAE;AAAA,IACvF;AAAA,EACD;AAAA,EAEQ,iBAAiB,SAA+B;AACvD,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,QAAQ,OAAQ;AACrB,IAAAA,QAAO,KAAK,eAAe,OAAO,OAAO,UAAU,GAAG,CAAC,CAAC,YAAY,OAAO,MAAM,EAAE;AAAA,EACpF;AAAA,EAEQ,iBAAiB,SAA+B;AACvD,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,QAAQ,OAAQ;AAErB,UAAM,UAAU,KAAK,iBAAiB,IAAI,OAAO,MAAM;AACvD,QAAI,SAAS;AACZ,mBAAa,QAAQ,KAAK;AAC1B,WAAK,iBAAiB,OAAO,OAAO,MAAM;AAC1C,UAAI,OAAO,SAAS;AACnB,cAAM,UACL,OAAO,OAAO,WAAW,WACtB,OAAO,OAAO,UAAU,GAAG,GAAG,IAC9B,KAAK,UAAU,OAAO,MAAM,EAAE,UAAU,GAAG,GAAG;AAClD,QAAAA,QAAO;AAAA,UACN,oCAAoC,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,oBAAe,OAAO;AAAA,QACvF;AAAA,MACD,OAAO;AACN,QAAAA,QAAO;AAAA,UACN,oCAAoC,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,mBAAc,OAAO,KAAK;AAAA,QAC3F;AAAA,MACD;AACA,cAAQ,QAAQ,MAAM;AAAA,IACvB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACf,eAAW,CAAC,EAAE,OAAO,KAAK,KAAK,cAAc;AAC5C,mBAAa,QAAQ,KAAK;AAC1B,cAAQ,QAAQ,EAAE,QAAQ,IAAI,QAAQ,UAAU,OAAO,wBAAwB,CAAC;AAAA,IACjF;AACA,SAAK,aAAa,MAAM;AAExB,eAAW,CAAC,EAAE,OAAO,KAAK,KAAK,kBAAkB;AAChD,mBAAa,QAAQ,KAAK;AAC1B,cAAQ,QAAQ,EAAE,QAAQ,IAAI,SAAS,OAAO,OAAO,wBAAwB,CAAC;AAAA,IAC/E;AACA,SAAK,iBAAiB,MAAM;AAAA,EAC7B;AACD;AAMA,SAAS,aAAa,SAAiB,MAAuB;AAC7D,MAAI,YAAY,KAAM,QAAO;AAC7B,MAAI,QAAQ,SAAS,GAAG,GAAG;AAC1B,WAAO,KAAK,WAAW,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,EAC5C;AACA,SAAO;AACR;;;AC7QA,YAAYE,SAAQ;AACpB,YAAYC,WAAU;AAgBf,IAAM,wBAAwB,IAAI,KAAK,KAAK,KAAK;AAEjD,IAAM,uBAAuB;AAE7B,IAAM,eAAN,MAAmB;AAAA,EAGzB,YACkB,MACA,QAAQ,uBACxB;AAFgB;AACA;AAAA,EACf;AAAA,EAFe;AAAA,EACA;AAAA,EAJV,UAA2B,CAAC;AAAA,EAOpC,MAAM,KAAK,MAAM,KAAK,IAAI,GAAkB;AAC3C,QAAI;AACH,YAAM,MAAM,KAAK,MAAM,MAAS,aAAS,KAAK,MAAM,OAAO,CAAC;AAC5D,WAAK,UAAU,MAAM,QAAQ,GAAG,IAC5B,IAAwB;AAAA,QACzB,CAAC,MACA,KACA,OAAO,EAAE,WAAW,YACpB,OAAO,EAAE,WAAW,YACpB,OAAO,EAAE,OAAO;AAAA,MAClB,IACC,CAAC;AAAA,IACL,QAAQ;AACP,WAAK,UAAU,CAAC;AAAA,IACjB;AACA,UAAM,KAAK,MAAM,GAAG;AAAA,EACrB;AAAA,EAEA,OAAwB;AACvB,WAAO,CAAC,GAAG,KAAK,OAAO;AAAA,EACxB;AAAA,EAEA,QAAQ,QAAiC;AACxC,WAAO,KAAK,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM;AAAA,EACtD;AAAA,EAEA,IAAI,QAAyB;AAC5B,WAAO,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,MAAM;AAAA,EACpD;AAAA,EAEA,IAAI,OAAe;AAClB,WAAO,KAAK,QAAQ;AAAA,EACrB;AAAA;AAAA,EAGA,MAAM,IAAI,OAAgE;AACzE,SAAK,UAAU,KAAK,QAAQ;AAAA,MAC3B,CAAC,MAAM,EAAE,EAAE,WAAW,MAAM,UAAU,EAAE,WAAW,MAAM;AAAA,IAC1D;AACA,UAAM,OAAsB,EAAE,GAAG,OAAO,UAAU,EAAE;AACpD,SAAK,QAAQ,KAAK,IAAI;AACtB,UAAM,OAAO,KAAK,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,MAAM;AACjE,QAAI,KAAK,SAAS,sBAAsB;AACvC,YAAM,OAAO,IAAI,IAAI,KAAK,MAAM,GAAG,KAAK,SAAS,oBAAoB,CAAC;AACtE,WAAK,UAAU,KAAK,QAAQ,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;AAAA,IACvD;AACA,UAAM,KAAK,QAAQ;AACnB,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,OAAO,QAAgB,QAAkC;AAC9D,UAAM,SAAS,KAAK,QAAQ;AAC5B,SAAK,UAAU,KAAK,QAAQ,OAAO,CAAC,MAAM,EAAE,EAAE,WAAW,UAAU,EAAE,WAAW,OAAO;AACvF,QAAI,KAAK,QAAQ,WAAW,OAAQ,QAAO;AAC3C,UAAM,KAAK,QAAQ;AACnB,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,YAAY,QAAgB,QAA+B;AAChE,UAAM,IAAI,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,UAAU,EAAE,WAAW,MAAM;AAC7E,QAAI,CAAC,EAAG;AACR,MAAE;AACF,UAAM,KAAK,QAAQ;AAAA,EACpB;AAAA;AAAA,EAGA,MAAM,MAAM,MAAM,KAAK,IAAI,GAA6B;AACvD,UAAM,UAAU,KAAK,QAAQ,OAAO,CAAC,MAAM,MAAM,EAAE,KAAK,KAAK,KAAK;AAClE,QAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAClC,SAAK,UAAU,KAAK,QAAQ,OAAO,CAAC,MAAM,MAAM,EAAE,MAAM,KAAK,KAAK;AAClE,UAAM,KAAK,QAAQ;AACnB,WAAO;AAAA,EACR;AAAA,EAEA,MAAc,UAAyB;AACtC,UAAS,UAAW,cAAQ,KAAK,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3D,UAAS,cAAU,KAAK,MAAM,KAAK,UAAU,KAAK,SAAS,MAAM,CAAC,GAAG,OAAO;AAAA,EAC7E;AACD;;;AC3GA,IAAMC,UAAS,aAAa,cAAc;AAmDnC,IAAM,cAAN,MAAkB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA,kBAAkB,oBAAI,IAQ5B;AAAA;AAAA,EAGM,cAAc,oBAAI,IAAY;AAAA,EAEtC,YACC,WACA,WACA,YACA,cACA,YACA,cACA,QACC;AACD,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,SAAK,aAAa;AAClB,SAAK,eAAe;AACpB,SAAK,aAAa;AAClB,SAAK,eAAe;AACpB,SAAK,SAAS;AAGd,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,WAAK,cAAc,OAAO;AAAA,IAC3B,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,SAA0D;AAC/E,SAAK,gBAAgB;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,uBACC,SACO;AACP,SAAK,iBAAiB;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,uBAAuB,SAA2D;AACjF,SAAK,iBAAiB;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,qBAAqB,OAAuC;AACjE,QAAI,CAAC,KAAK,OAAO,OAAQ;AAGzB,UAAM,UAAU,GAAG,MAAM,IAAI,IAAI,MAAM,SAAS,IAAI,MAAM,UAAU;AACpE,QAAI,KAAK,YAAY,IAAI,OAAO,EAAG;AACnC,SAAK,YAAY,IAAI,OAAO;AAE5B,eAAW,MAAM,KAAK,YAAY,OAAO,OAAO,GAAG,GAAO;AAE1D,UAAM,UAAU;AAAA,MACf;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,IACN;AAEA,UAAM,OAAO,MAAM,KAAK,UAAU,UAAU,OAAO;AACnD,QAAI,OAAO,GAAG;AACb,MAAAA,QAAO,MAAM,8BAA8B,IAAI,aAAa,MAAM,IAAI,EAAE;AAAA,IACzE;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,mBACL,OACA,SACgC;AAChC,QAAI,CAAC,KAAK,OAAO,OAAQ,QAAO,CAAC;AAEjC,UAAM,YAAY,KAAK,UAAU,aAAa;AAC9C,QAAI,UAAU,WAAW,EAAG,QAAO,CAAC;AAEpC,UAAM,YAAY,UAAU,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,CAAC;AACpF,UAAM,YAAY,SAAS,aAAa;AACxC,UAAM,QAAQ,SAAS,SAAS;AAEhC,UAAM,UAAU;AAAA,MACf;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA;AAAA,QACC;AAAA,QACA,QAAQ,SAAS;AAAA,QACjB;AAAA,QACA;AAAA,MACD;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,IACN;AAGA,WAAO,IAAI,QAAQ,CAACC,aAAY;AAC/B,YAAM,QAAQ,WAAW,MAAM;AAC9B,cAAM,UAAU,KAAK,gBAAgB,IAAI,SAAS;AAClD,aAAK,gBAAgB,OAAO,SAAS;AACrC,QAAAA,SAAQ,SAAS,WAAW,CAAC,CAAC;AAAA,MAC/B,GAAG,SAAS;AAEZ,WAAK,gBAAgB,IAAI,WAAW;AAAA,QACnC,SAAS,CAAC;AAAA,QACV;AAAA,QACA,SAAAA;AAAA,QACA,eAAe,UAAU;AAAA,MAC1B,CAAC;AAED,WAAK,UAAU,UAAU,OAAO;AAAA,IACjC,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBAAsB,OAAwC;AACnE,QAAI,CAAC,KAAK,OAAO,QAAS;AAE1B,UAAM,UAAU,GAAG,MAAM,SAAS,IAAI,MAAM,SAAS,IAAI,MAAM,UAAU;AACzE,QAAI,KAAK,YAAY,IAAI,OAAO,EAAG;AACnC,SAAK,YAAY,IAAI,OAAO;AAC5B,eAAW,MAAM,KAAK,YAAY,OAAO,OAAO,GAAG,GAAO;AAE1D,UAAM,UAAU;AAAA,MACf;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,IACN;AAEA,UAAM,KAAK,UAAU,UAAU,OAAO;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,SAA+B;AACpD,YAAQ,QAAQ,MAAM;AAAA,MACrB,KAAK;AACJ,aAAK,iBAAiB,OAAO;AAC7B;AAAA,MACD,KAAK;AACJ,aAAK,0BAA0B,OAAO;AACtC;AAAA,MACD,KAAK;AACJ,aAAK,0BAA0B,OAAO;AACtC;AAAA,MACD,KAAK;AACJ,aAAK,kBAAkB,OAAO;AAC9B;AAAA,IACF;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBAAiB,SAAwC;AACtE,UAAM,QAAQ,QAAQ;AACtB,QAAI,CAAC,OAAO,QAAQ,CAAC,OAAO,QAAS;AAGrC,UAAM,UAAU,GAAG,MAAM,IAAI,IAAI,MAAM,SAAS,IAAI,MAAM,UAAU;AACpE,QAAI,KAAK,YAAY,IAAI,OAAO,EAAG;AACnC,SAAK,YAAY,IAAI,OAAO;AAC5B,eAAW,MAAM,KAAK,YAAY,OAAO,OAAO,GAAG,GAAO;AAE1D,IAAAD,QAAO,MAAM,oBAAoB,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,KAAK,MAAM,IAAI,KAAK,MAAM,IAAI,GAAG;AAE9F,QAAI,KAAK,eAAe;AACvB,UAAI;AACH,cAAM,KAAK,cAAc,KAAK;AAAA,MAC/B,SAAS,KAAK;AACb,QAAAA,QAAO;AAAA,UACN,gCAAgC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QACjF;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,0BAA0B,SAAwC;AAC/E,UAAM,UAAU,QAAQ;AACxB,QAAI,CAAC,SAAS,SAAS,CAAC,SAAS,UAAW;AAE5C,QAAI,CAAC,KAAK,eAAgB;AAE1B,QAAI;AACH,YAAM,UAAU,MAAM,KAAK,eAAe,OAAO;AACjD,YAAM,WAAW;AAAA,QAChB;AAAA,QACA,KAAK;AAAA,QACL,QAAQ;AAAA,QACR;AAAA,UACC,WAAW,QAAQ;AAAA,UACnB,YAAY,KAAK;AAAA,UACjB,cAAc,KAAK;AAAA,UACnB;AAAA,QACD;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA,MACN;AACA,YAAM,KAAK,UAAU,WAAW,QAAQ,MAAM,QAAQ;AAAA,IACvD,SAAS,KAAK;AACb,MAAAA,QAAO;AAAA,QACN,mCAAmC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACpF;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKQ,0BAA0B,SAA+B;AAChE,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,QAAQ,UAAW;AAExB,UAAM,UAAU,KAAK,gBAAgB,IAAI,OAAO,SAAS;AACzD,QAAI,CAAC,QAAS;AAEd,YAAQ,QAAQ,KAAK,MAAM;AAG3B,QAAI,QAAQ,QAAQ,UAAU,QAAQ,eAAe;AACpD,mBAAa,QAAQ,KAAK;AAC1B,WAAK,gBAAgB,OAAO,OAAO,SAAS;AAC5C,cAAQ,QAAQ,QAAQ,OAAO;AAAA,IAChC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,kBAAkB,SAAwC;AACvE,UAAM,QAAQ,QAAQ;AACtB,QAAI,CAAC,OAAO,aAAa,CAAC,OAAO,QAAS;AAE1C,UAAM,UAAU,GAAG,MAAM,SAAS,IAAI,MAAM,SAAS,IAAI,MAAM,UAAU;AACzE,QAAI,KAAK,YAAY,IAAI,OAAO,EAAG;AACnC,SAAK,YAAY,IAAI,OAAO;AAC5B,eAAW,MAAM,KAAK,YAAY,OAAO,OAAO,GAAG,GAAO;AAE1D,IAAAA,QAAO,MAAM,qBAAqB,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,KAAK,MAAM,SAAS,EAAE;AAEpF,QAAI,KAAK,gBAAgB;AACxB,UAAI;AACH,cAAM,KAAK,eAAe,KAAK;AAAA,MAChC,SAAS,KAAK;AACb,QAAAA,QAAO;AAAA,UACN,iCAAiC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QAClF;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACf,eAAW,CAAC,EAAE,OAAO,KAAK,KAAK,iBAAiB;AAC/C,mBAAa,QAAQ,KAAK;AAC1B,cAAQ,QAAQ,CAAC,CAAC;AAAA,IACnB;AACA,SAAK,gBAAgB,MAAM;AAC3B,SAAK,YAAY,MAAM;AAAA,EACxB;AACD;;;AC/TO,IAAM,qBAAqB,CAAC,mBAAmB,gBAAgB;AAE/D,SAAS,kBAAkB,SAA0B;AAC3D,SAAQ,mBAAyC,SAAS,OAAO;AAClE;;;ACpDA,YAAYE,SAAQ;AACpB,YAAY,UAAU;AACtB,YAAY,WAAW;AACvB,SAAS,WAAW,uBAAuB;AAI3C,IAAMC,UAAS,aAAa,mBAAmB;AAE/C,IAAM,2BAA2B;AACjC,IAAM,2BAA2B;AACjC,IAAM,sBAAsB;AAC5B,IAAM,0BAA0B;AAChC,IAAM,oBAAoB;AAC1B,IAAM,0BAA0B;AAChC,IAAM,0BAA0B;AAChC,IAAM,kCAAkC;AACxC,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAwCpB,IAAM,mBAAN,MAAuB;AAAA,EACrB,SAA4C;AAAA,EAC5C,MAA8B;AAAA,EAC9B,QAAQ,oBAAI,IAA4B;AAAA,EACxC,kBAAoC,CAAC;AAAA;AAAA,EAErC,SAA+E;AAAA;AAAA,EAE/E,eAAgE;AAAA,EAChE,cAAkC;AAAA,EAClC,cAAkC;AAAA,EAClC,mBAEE;AAAA;AAAA,EAEF,cAEE;AAAA,EACF,YAAY,oBAAI,IAAsB;AAAA,EACtC,YAAY;AAAA,EACZ;AAAA,EACA,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,eAAyB,CAAC;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,eAAgD,CAAC;AAAA,EACjD,kBAAmD,CAAC;AAAA,EACpD,WAAW,oBAAI,IAAoB;AAAA;AAAA,EAEnC,UAAU,oBAAI,IAAe;AAAA,EAC7B,QAAQ,oBAAI,QAA4B;AAAA,EACxC,YAAmD;AAAA,EAE3D,YAAY,QAAyB;AACpC,SAAK,SAAS;AAAA,EACf;AAAA;AAAA,EAGA,eAAe,SAA4B;AAC1C,SAAK,cAAc;AAAA,EACpB;AAAA;AAAA,EAGA,eAAe,SAA4B;AAC1C,SAAK,cAAc;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,oBAAoB,IAA2E;AAC9F,SAAK,mBAAmB;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eACC,IACO;AACP,SAAK,cAAc;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY,IAAgD;AAC3D,SAAK,WAAW;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,IAA8D;AAC1E,SAAK,YAAY;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,IAAoC;AAChD,SAAK,aAAa,KAAK,EAAE;AAAA,EAC1B;AAAA,EAEA,gBAAgB,IAAoC;AACnD,SAAK,gBAAgB,KAAK,EAAE;AAAA,EAC7B;AAAA;AAAA,EAGQ,kBAA2B;AAClC,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,MAAM,KAAK,OAAO,wBAAwB;AAChD,SAAK,eAAe,KAAK,aAAa,OAAO,CAAC,MAAM,MAAM,IAAI,GAAI;AAClE,QAAI,KAAK,aAAa,UAAU,IAAK,QAAO;AAC5C,SAAK,aAAa,KAAK,GAAG;AAC1B,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAgB,SAAkC;AACzD,QAAI,CAAC,KAAK,YAAY,CAAC,KAAK,SAAS,OAAO,EAAG,QAAO;AACtD,UAAM,MAAM,GAAG,QAAQ,IAAI,IAAI,QAAQ,EAAE;AACzC,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,KAAK,SAAS,IAAI,GAAG,EAAG,QAAO;AACnC,SAAK,SAAS,IAAI,KAAK,GAAG;AAC1B,QAAI,KAAK,SAAS,OAAO,oBAAoB;AAC5C,iBAAW,CAAC,GAAG,CAAC,KAAK,KAAK,SAAU,KAAI,MAAM,IAAI,iBAAkB,MAAK,SAAS,OAAO,CAAC;AAAA,IAC3F;AACA,WAAO;AAAA,EACR;AAAA;AAAA,EAGQ,UAAU,KAAsB;AACvC,UAAM,MAAM,KAAK,OAAO,wBAAwB;AAChD,UAAM,MAAM,KAAK,IAAI;AAErB,QAAI,KAAK,UAAU,OAAO,MAAM;AAC/B,iBAAW,CAAC,GAAG,EAAE,KAAK,KAAK,WAAW;AACrC,YAAI,GAAG,WAAW,KAAK,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,IAAQ,MAAK,UAAU,OAAO,CAAC;AAAA,MACjF;AAAA,IACD;AACA,UAAM,OAAO,KAAK,UAAU,IAAI,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,MAAM,IAAI,GAAM;AAC1E,QAAI,IAAI,UAAU,KAAK;AACtB,WAAK,UAAU,IAAI,KAAK,GAAG;AAC3B,aAAO;AAAA,IACR;AACA,QAAI,KAAK,GAAG;AACZ,SAAK,UAAU,IAAI,KAAK,GAAG;AAC3B,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC5B,QAAI,KAAK,QAAS;AAElB,UAAM,iBAAiB,CAAC,KAA2B,QAA6B;AAC/E,UAAI,IAAI,QAAQ,aAAa,IAAI,WAAW,OAAO;AAClD,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI,IAAI,KAAK,UAAU,EAAE,QAAQ,MAAM,UAAU,WAAW,SAAS,EAAE,CAAC,CAAC;AACzE;AAAA,MACD;AAEA,UAAI,IAAI,QAAQ,sBAAsB,IAAI,WAAW,QAAQ;AAC5D,cAAM,KAAK,IAAI,OAAO,iBAAiB;AACvC,YAAI,CAAC,KAAK,UAAU,MAAM,EAAE,EAAE,KAAK,CAAC,KAAK,gBAAgB,GAAG;AAC3D,cAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,cAAI,IAAI,KAAK,UAAU,EAAE,OAAO,eAAe,CAAC,CAAC;AACjD;AAAA,QACD;AACA,YAAI,OAAO;AACX,YAAI,GAAG,QAAQ,CAAC,UAAkB;AACjC,kBAAQ;AACR,cAAI,KAAK,SAAS,kBAAmB,KAAI,QAAQ;AAAA,QAClD,CAAC;AACD,YAAI,GAAG,OAAO,MAAM;AACnB,gBAAM,UAAU,YAAY,IAAI;AAChC,cAAI,CAAC,SAAS;AACb,gBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,gBAAI,IAAI,KAAK,UAAU,EAAE,OAAO,kBAAkB,CAAC,CAAC;AACpD;AAAA,UACD;AAEA,cAAI,CAAC,KAAK,gBAAgB,OAAO,GAAG;AACnC,gBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,gBAAI,IAAI,KAAK,UAAU,EAAE,OAAO,aAAa,CAAC,CAAC;AAC/C;AAAA,UACD;AACA,qBAAW,WAAW,KAAK,iBAAiB;AAC3C,oBAAQ,SAAS,QAAQ,IAAI;AAAA,UAC9B;AAGA,gBAAM,QAAQ,KAAK,YAAY,KAAK,UAAU,OAAO,IAAI;AACzD,cAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,cAAI,IAAI,KAAK,UAAU,QAAQ,EAAE,IAAI,MAAM,UAAU,MAAM,IAAI,EAAE,IAAI,KAAK,CAAC,CAAC;AAAA,QAC7E,CAAC;AACD;AAAA,MACD;AAEA,UAAI,IAAI,QAAQ,mBAAmB,IAAI,WAAW,OAAO;AACxD,cAAM,WAAW,KAAK,SAAS;AAG/B,cAAM,YAAY,KAAK,OAAO,gBAAgB,KAAK;AACnD,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,cAAM,WAAW,KAAK,mBAAmB;AACzC,YAAI;AAAA,UACH,KAAK,UAAU;AAAA,YACd,IAAI;AAAA,YACJ,UAAU;AAAA,YACV,SAAS;AAAA;AAAA;AAAA,YAGT,GAAI,UAAU,YAAY,EAAE,WAAW,SAAS,UAAU,IAAI,CAAC;AAAA,YAC/D,GAAI,UAAU,OAAO,EAAE,MAAM,SAAS,KAAK,IAAI,CAAC;AAAA,YAChD,OAAO,SAAS,IAAI,CAAC,OAAO;AAAA,cAC3B,IAAI,EAAE,OAAO,UAAU,GAAG,CAAC;AAAA,cAC3B,GAAI,YAAY,EAAE,MAAM,KAAK,eAAe,EAAE,MAAM,EAAE,IAAI,CAAC;AAAA,cAC3D,UAAU,EAAE;AAAA,cACZ,WAAW,EAAE;AAAA,cACb,WAAW,EAAE;AAAA,cACb,UAAU,EAAE;AAAA,YACb,EAAE;AAAA,YACF,WAAW,SAAS;AAAA,YACpB,sBAAsB,SAAS,OAAO,CAAC,MAAM,EAAE,cAAc,WAAW,EAAE;AAAA,UAC3E,CAAC;AAAA,QACF;AACA;AAAA,MACD;AAEA,UAAI,IAAI,QAAQ,mBAAmB,IAAI,WAAW,QAAQ;AACzD,YAAI,OAAO;AACX,YAAI,GAAG,QAAQ,CAAC,UAAkB;AACjC,kBAAQ;AACR,cAAI,KAAK,SAAS,KAAM,KAAI,QAAQ;AAAA,QACrC,CAAC;AACD,YAAI,GAAG,OAAO,MAAM;AACnB,cAAI,CAAC,KAAK,aAAa;AACtB,gBAAI,UAAU,GAAG;AACjB,gBAAI,IAAI;AACR;AAAA,UACD;AACA,cAAI;AACJ,cAAI;AACH,qBAAS,KAAK,MAAM,IAAI;AAAA,UACzB,QAAQ;AACP,gBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,gBAAI,IAAI,KAAK,UAAU,EAAE,OAAO,eAAe,CAAC,CAAC;AACjD;AAAA,UACD;AACA,gBAAM,KAAK,IAAI,OAAO,iBAAiB;AACvC,eAAK,YAAY,QAAQ,EAAE,EACzB,KAAK,CAAC,EAAE,QAAQ,KAAK,MAAM;AAC3B,gBAAI,UAAU,QAAQ,EAAE,gBAAgB,mBAAmB,CAAC;AAC5D,gBAAI,IAAI,KAAK,UAAU,IAAI,CAAC;AAAA,UAC7B,CAAC,EACA,MAAM,CAAC,MAAM;AACb,gBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,gBAAI,IAAI,KAAK,UAAU,EAAE,OAAO,aAAa,QAAQ,EAAE,UAAU,cAAc,CAAC,CAAC;AAAA,UAClF,CAAC;AAAA,QACH,CAAC;AACD;AAAA,MACD;AAKA,UAAI,IAAI,QAAQ,mBAAmB,IAAI,WAAW,QAAQ;AACzD,YAAI,OAAO;AACX,YAAI,GAAG,QAAQ,CAAC,UAAkB;AACjC,kBAAQ;AACR,cAAI,KAAK,SAAS,KAAM,KAAI,QAAQ;AAAA,QACrC,CAAC;AACD,YAAI,GAAG,OAAO,MAAM;AACnB,cAAI,CAAC,KAAK,aAAa;AACtB,gBAAI,UAAU,GAAG;AACjB,gBAAI,IAAI;AACR;AAAA,UACD;AACA,cAAI;AACJ,cAAI;AACH,qBAAS,KAAK,MAAM,IAAI;AAAA,UACzB,QAAQ;AACP,gBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,gBAAI,IAAI,KAAK,UAAU,EAAE,OAAO,eAAe,CAAC,CAAC;AACjD;AAAA,UACD;AACA,gBAAM,KAAK,IAAI,OAAO,iBAAiB;AACvC,eAAK,YAAY,QAAQ,EAAE,EACzB,KAAK,CAAC,EAAE,QAAQ,KAAK,MAAM;AAC3B,gBAAI,UAAU,QAAQ,EAAE,gBAAgB,mBAAmB,CAAC;AAC5D,gBAAI,IAAI,KAAK,UAAU,IAAI,CAAC;AAAA,UAC7B,CAAC,EACA,MAAM,CAAC,MAAM;AACb,gBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,gBAAI,IAAI,KAAK,UAAU,EAAE,OAAO,aAAa,QAAQ,EAAE,UAAU,cAAc,CAAC,CAAC;AAAA,UAClF,CAAC;AAAA,QACH,CAAC;AACD;AAAA,MACD;AAKA,UAAI,KAAK,eAAe,IAAI,KAAK;AAChC,YAAI;AACJ,YAAI;AACH,qBAAW,IAAI,IAAI,IAAI,KAAK,kBAAkB,EAAE;AAAA,QACjD,QAAQ;AACP,qBAAW;AAAA,QACZ;AACA,YAAI,SAAS,WAAW,gBAAgB,GAAG;AAC1C,gBAAM,KAAK,IAAI,OAAO,iBAAiB;AACvC,cAAI,CAAC,KAAK,UAAU,QAAQ,EAAE,EAAE,KAAK,CAAC,KAAK,gBAAgB,GAAG;AAC7D,gBAAI,UAAU,GAAG;AACjB,gBAAI,IAAI;AACR;AAAA,UACD;AACA,cAAI,KAAK,YAAY,KAAK,KAAK,QAAQ,EAAG;AAAA,QAC3C;AAAA,MACD;AAEA,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI;AAAA,IACT;AAEA,QAAI,KAAK,OAAO,KAAK;AACpB,WAAK,SAAe;AAAA,QACnB;AAAA,UACC,MAAS,iBAAa,KAAK,OAAO,IAAI,IAAI;AAAA,UAC1C,KAAQ,iBAAa,KAAK,OAAO,IAAI,GAAG;AAAA,QACzC;AAAA,QACA;AAAA,MACD;AAAA,IACD,OAAO;AACN,WAAK,SAAc,kBAAa,cAAc;AAAA,IAC/C;AAGA,SAAK,MAAM,IAAI,gBAAgB,EAAE,QAAQ,KAAK,QAAQ,YAAY,kBAAkB,CAAC;AAErF,SAAK,IAAI,GAAG,SAAS,CAAC,QAAQ;AAC7B,MAAAA,QAAO,KAAK,mCAAmC,IAAI,OAAO,EAAE;AAAA,IAC7D,CAAC;AAED,SAAK,IAAI,GAAG,cAAc,CAAC,OAAO;AAGjC,SAAG,GAAG,SAAS,MAAM;AAAA,MAAC,CAAC;AAEvB,YAAM,WAAW,KAAK,OAAO,kBAAkB;AAC/C,UAAI,KAAK,iBAAiB,UAAU;AACnC,WAAG,MAAM,MAAM,sBAAsB;AACrC;AAAA,MACD;AACA,WAAK;AACL,WAAK,MAAM,EAAE;AAEb,YAAM,UAAU,MAAM,EAAE,KAAK,SAAS;AACtC,UAAI,eAA8B;AAGlC,YAAM,YAAY,WAAW,MAAM;AAClC,YAAI,CAAC,cAAc;AAClB,UAAAA,QAAO,KAAK,kDAAkD;AAC9D,aAAG,MAAM,MAAM,cAAc;AAAA,QAC9B;AAAA,MACD,GAAG,KAAK,OAAO,iBAAiB,uBAAuB;AAEvD,SAAG,GAAG,WAAW,CAAC,SAAS;AAC1B,YAAI,CAAC,KAAK,UAAU,OAAO,EAAG;AAC9B,YAAI,CAAC,KAAK,gBAAgB,EAAG;AAC7B,cAAM,UAAU,YAAY,KAAK,SAAS,CAAC;AAC3C,YAAI,CAAC,QAAS;AAEd,YAAI,CAAC,KAAK,gBAAgB,OAAO,EAAG;AAEpC,YAAI,CAAC,cAAc;AAIlB,yBAAe,QAAQ;AACvB,uBAAa,SAAS;AACtB,gBAAM,WAAW,KAAK,MAAM,IAAI,QAAQ,IAAI;AAC5C,cAAI,QAAQ;AACZ,cAAI,UAAU;AACb,oBAAQ,KAAK,MAAM,UAAU,IAAI,IAAI;AAAA,UACtC,OAAO;AACN,iBAAK,MAAM,IAAI,QAAQ,MAAM;AAAA,cAC5B,QAAQ,QAAQ;AAAA,cAChB,UAAU;AAAA,cACV,WAAW;AAAA,cACX,UAAU,KAAK,IAAI;AAAA,cACnB;AAAA,cACA,gBAAgB;AAAA,cAChB,mBAAmB;AAAA,cACnB,YAAY;AAAA,YACb,CAAC;AAAA,UACF;AACA,cAAI,OAAO;AACV,YAAAA,QAAO,KAAK,2CAA2C,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,EAAE;AACrF,uBAAW,MAAM,KAAK,aAAc,IAAG,QAAQ,IAAI;AAAA,UACpD;AAAA,QACD,WAAW,QAAQ,SAAS,cAAc;AAEzC;AAAA,QACD;AAEA,mBAAW,WAAW,KAAK,iBAAiB;AAC3C,kBAAQ,SAAS,QAAQ,IAAI;AAAA,QAC9B;AAAA,MACD,CAAC;AAED,SAAG,GAAG,SAAS,MAAM;AACpB,aAAK;AACL,qBAAa,SAAS;AACtB,aAAK,UAAU,OAAO,OAAO;AAC7B,mBAAW,CAAC,QAAQ,IAAI,KAAK,KAAK,OAAO;AACxC,cAAI,KAAK,OAAO,IAAI;AACnB,iBAAK,KAAK;AACV,iBAAK,YAAY;AACjB,YAAAA,QAAO,KAAK,2BAA2B,OAAO,UAAU,GAAG,CAAC,CAAC,EAAE;AAC/D,uBAAW,MAAM,KAAK,gBAAiB,IAAG,MAAM;AAChD,iBAAK,kBAAkB,MAAM;AAC7B;AAAA,UACD;AAAA,QACD;AAAA,MACD,CAAC;AAED,SAAG,GAAG,SAAS,CAAC,QAAQ;AACvB,QAAAA,QAAO,KAAK,oBAAoB,IAAI,OAAO,EAAE;AAAA,MAC9C,CAAC;AAAA,IACF,CAAC;AAED,UAAM,KAAK,OAAO;AAIlB,SAAK,OAAQ,GAAG,SAAS,CAAC,QAAQ;AACjC,MAAAA,QAAO,KAAK,yBAA0B,IAAc,OAAO,EAAE;AAAA,IAC9D,CAAC;AAED,SAAK,UAAU;AACf,SAAK,aAAa;AAAA,EACnB;AAAA;AAAA,EAGA,MAAc,SAAwB;AACrC,UAAM,cAAc;AACpB,UAAM,eAAe;AACrB,aAAS,UAAU,GAAG,WAAW,aAAa,WAAW;AACxD,UAAI;AACH,cAAM,IAAI,QAAc,CAACC,UAAS,WAAW;AAC5C,gBAAM,SAAS,KAAK;AACpB,gBAAM,UAAU,CAAC,QAAe;AAC/B,mBAAO,eAAe,aAAa,WAAW;AAC9C,mBAAO,GAAG;AAAA,UACX;AACA,gBAAM,cAAc,MAAM;AACzB,mBAAO,eAAe,SAAS,OAAO;AACtC,kBAAM,SAAS,KAAK,OAAO,MAAM,QAAQ;AACzC,YAAAD,QAAO;AAAA,cACN,+BAA+B,MAAM,cAAc,KAAK,OAAO,IAAI;AAAA,YACpE;AACA,YAAAC,SAAQ;AAAA,UACT;AACA,iBAAO,KAAK,SAAS,OAAO;AAC5B,iBAAO,KAAK,aAAa,WAAW;AACpC,iBAAO,OAAO,KAAK,OAAO,IAAI;AAAA,QAC/B,CAAC;AACD;AAAA,MACD,SAAS,KAAK;AACb,cAAM,IAAI;AACV,YAAI,EAAE,SAAS,gBAAgB,UAAU,aAAa;AACrD,UAAAD,QAAO;AAAA,YACN,gBAAgB,KAAK,OAAO,IAAI,4BAAuB,OAAO,IAAI,cAAc,CAAC,QAAQ,YAAY;AAAA,UACtG;AACA,gBAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC;AACpD;AAAA,QACD;AACA,cAAM,IAAI;AAAA,UACT,EAAE,SAAS,eACR,+BAA+B,KAAK,OAAO,IAAI,wBAAwB,WAAW,mEAClF,mCAAmC,KAAK,OAAO,IAAI,KAAK,EAAE,OAAO;AAAA,QACrE;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAsB;AAC3B,QAAI,CAAC,KAAK,QAAS;AACnB,SAAK,UAAU;AACf,QAAI,KAAK,WAAW;AACnB,oBAAc,KAAK,SAAS;AAC5B,WAAK,YAAY;AAAA,IAClB;AAEA,eAAW,CAAC,EAAE,IAAI,KAAK,KAAK,OAAO;AAClC,UAAI,KAAK,GAAI,MAAK,GAAG,MAAM;AAC3B,UAAI,KAAK,eAAgB,cAAa,KAAK,cAAc;AAAA,IAC1D;AAEA,eAAW,MAAM,KAAK,SAAS;AAC9B,UAAI;AACH,WAAG,UAAU;AAAA,MACd,QAAQ;AAAA,MAER;AAAA,IACD;AACA,SAAK,QAAQ,MAAM;AACnB,SAAK,MAAM,MAAM;AACjB,SAAK,SAAS,MAAM;AACpB,SAAK,UAAU,MAAM;AACrB,SAAK,eAAe,CAAC;AAKrB,QAAI,KAAK,KAAK;AAIb,iBAAW,UAAU,KAAK,IAAI,QAAS,QAAO,UAAU;AACxD,YAAM,IAAI,QAAc,CAACC,aAAY,KAAK,IAAK,MAAM,MAAMA,SAAQ,CAAC,CAAC;AACrE,WAAK,MAAM;AAAA,IACZ;AAEA,QAAI,KAAK,QAAQ;AAChB,WAAK,OAAO,sBAAsB;AAClC,YAAM,IAAI,QAAc,CAACA,aAAY,KAAK,OAAQ,MAAM,MAAMA,SAAQ,CAAC,CAAC;AACxE,WAAK,SAAS;AAAA,IACf;AAEA,IAAAD,QAAO,KAAK,wBAAwB;AAAA,EACrC;AAAA,EAEA,UAAU,SAA+B;AACxC,SAAK,gBAAgB,KAAK,OAAO;AAAA,EAClC;AAAA;AAAA,EAGA,UAAU,IAAuE;AAChF,SAAK,SAAS;AAAA,EACf;AAAA;AAAA,EAGA,gBAAgB,IAAkD;AACjE,SAAK,eAAe;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,cAAc,SAAkC;AAC/C,QAAI,CAAC,KAAK,gBAAgB,OAAO,EAAG,QAAO;AAC3C,eAAW,WAAW,KAAK,gBAAiB,SAAQ,SAAS,QAAQ,IAAI;AACzE,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAAW,QAAgB,SAA2C;AAC3E,UAAM,OAAO,KAAK,MAAM,IAAI,MAAM;AAClC,QAAI,CAAC,MAAM;AACV,MAAAA,QAAO,KAAK,mBAAmB,OAAO,UAAU,GAAG,CAAC,CAAC,EAAE;AACvD,aAAO;AAAA,IACR;AAIA,UAAM,WAAW,KAAK,SAAS,KAAK,OAAO,QAAQ,OAAO,IAAI;AAG9D,QAAI,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,MAAM;AACrD,UAAI;AACH,aAAK,GAAG,KAAK,UAAU,QAAQ,CAAC;AAChC,aAAK,WAAW,KAAK,IAAI;AAKzB,aAAK,YAAY;AACjB,eAAO;AAAA,MACR,QAAQ;AAAA,MAER;AAAA,IACD;AAGA,QAAI;AACH,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,QAAQ,oBAAoB;AAAA,QAChE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,UAAU,QAAQ;AAAA;AAAA;AAAA,QAGxB,QAAQ,YAAY,QAAQ,GAAI;AAAA,MACjC,CAAC;AACD,WAAK,WAAW,KAAK,IAAI;AACzB,aAAO,SAAS;AAAA,IACjB,SAAS,KAAK;AACb,MAAAA,QAAO;AAAA,QACN,mBAAmB,OAAO,UAAU,GAAG,CAAC,CAAC,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC/F;AAMA,aAAO;AAAA,IACR;AAAA,EACD;AAAA,EAEA,MAAM,UAAU,SAA0C;AACzD,QAAI,OAAO;AACX,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO;AAClC,UAAI,MAAM,KAAK,WAAW,QAAQ,OAAO,EAAG;AAAA,IAC7C;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,QAAgB,UAAwB;AAC/C,UAAM,WAAW,KAAK,MAAM,IAAI,MAAM;AACtC,QAAI,UAAU;AAIb,UAAI,SAAU,UAAS,WAAW;AAClC,eAAS,oBAAoB;AAC7B,UAAI,CAAC,SAAS,cAAc,SAAS,IAAI,eAAe,UAAU,MAAM;AACvE,aAAK,iBAAiB,MAAM;AAAA,MAC7B;AACA;AAAA,IACD;AAEA,SAAK,MAAM,IAAI,QAAQ;AAAA,MACtB;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX,UAAU,KAAK,IAAI;AAAA,MACnB,IAAI;AAAA,MACJ,gBAAgB;AAAA,MAChB,mBAAmB;AAAA,MACnB,YAAY;AAAA,IACb,CAAC;AAED,SAAK,iBAAiB,MAAM;AAC5B,IAAAA,QAAO,KAAK,eAAe,OAAO,UAAU,GAAG,CAAC,CAAC,KAAK,QAAQ,GAAG;AAAA,EAClE;AAAA,EAEA,WAAW,QAAsB;AAChC,UAAM,OAAO,KAAK,MAAM,IAAI,MAAM;AAClC,QAAI,MAAM;AACT,UAAI,KAAK,GAAI,MAAK,GAAG,MAAM;AAC3B,UAAI,KAAK,eAAgB,cAAa,KAAK,cAAc;AAAA,IAC1D;AACA,SAAK,MAAM,OAAO,MAAM;AACxB,IAAAA,QAAO,KAAK,iBAAiB,OAAO,UAAU,GAAG,CAAC,CAAC,EAAE;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBQ,MAAM,MAAsB,IAAe,KAA4B;AAC9E,UAAM,MAAM,KAAK;AACjB,UAAM,UACL,QAAQ,QACR,QAAQ,OACP,IAAI,eAAe,UAAU,QAAQ,IAAI,eAAe,UAAU;AACpE,QAAI,SAAS;AACZ,YAAM,OAAO,KAAK,mBAAmB,GAAG;AACxC,YAAM,QAAQ,SAAS,UAAa,OAAO,KAAK;AAChD,YAAM,UAAU,SAAS,WAAe,SAAS,QAAQ,QAAU,CAAC,SAAS,QAAQ;AACrF,UAAI,SAAS;AACZ,aAAK,KAAK,EAAE;AACZ,eAAO;AAAA,MACR;AACA,WAAK,KAAK;AACV,WAAK,KAAK,GAAG;AAAA,IACd;AACA,SAAK,KAAK;AACV,SAAK,YAAY;AACjB,WAAO;AAAA,EACR;AAAA;AAAA,EAGQ,KAAK,IAAqB;AACjC,QAAI;AACH,SAAG,MAAM;AAAA,IACV,QAAQ;AAAA,IAER;AAAA,EACD;AAAA,EAEQ,MAAM,IAAqB;AAClC,SAAK,QAAQ,IAAI,EAAE;AACnB,SAAK,MAAM,IAAI,IAAI,IAAI;AACvB,OAAG,GAAG,QAAQ,MAAM,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC;AAC5C,OAAG,GAAG,SAAS,MAAM,KAAK,QAAQ,OAAO,EAAE,CAAC;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,eAAqB;AAC5B,UAAM,QAAQ,KAAK,OAAO,kBAAkB;AAC5C,QAAI,SAAS,EAAG;AAChB,SAAK,YAAY,YAAY,MAAM;AAClC,iBAAW,MAAM,KAAK,SAAS;AAC9B,YAAI,GAAG,eAAe,UAAU,KAAM;AACtC,YAAI,KAAK,MAAM,IAAI,EAAE,MAAM,OAAO;AACjC,aAAG,UAAU;AACb;AAAA,QACD;AACA,aAAK,MAAM,IAAI,IAAI,KAAK;AACxB,YAAI;AACH,aAAG,KAAK;AAAA,QACT,QAAQ;AAAA,QAER;AAAA,MACD;AAAA,IACD,GAAG,KAAK;AACR,SAAK,UAAU,QAAQ;AAAA,EACxB;AAAA,EAEA,WAMG;AACF,WAAO,MAAM,KAAK,KAAK,MAAM,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO;AAAA,MAClD,QAAQ,EAAE;AAAA,MACV,UAAU,EAAE;AAAA,MACZ,WAAW,EAAE;AAAA,MACb,UAAU,EAAE;AAAA,MACZ,WACC,EAAE,IAAI,eAAe,UAAU,OAC3B,cACD,EAAE,YACA,SACA;AAAA,IACP,EAAE;AAAA,EACH;AAAA,EAEA,gBAAgB,QAAyB;AACxC,WAAO,KAAK,MAAM,IAAI,MAAM,GAAG,aAAa;AAAA,EAC7C;AAAA,EAEA,MAAM,SAAS,UAAoC;AAClD,QAAI;AACH,YAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,WAAW;AAAA,QAClD,QAAQ,YAAY,QAAQ,GAAI;AAAA,MACjC,CAAC;AACD,aAAO,SAAS;AAAA,IACjB,QAAQ;AACP,aAAO;AAAA,IACR;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,QAAsB;AAC9C,QAAI,CAAC,KAAK,QAAS;AACnB,UAAM,OAAO,KAAK,MAAM,IAAI,MAAM;AAClC,QAAI,CAAC,KAAM;AAEX,QAAI,KAAK,cAAc,KAAK,IAAI,eAAe,UAAU,KAAM;AAC/D,QAAI,CAAC,KAAK,SAAU;AAEpB,UAAM,SAAS,KAAK,SAAS,WAAW,OAAO,IAAI,QAAQ;AAC3D,UAAM,QAAQ,KAAK,SAAS,QAAQ,WAAW,MAAM;AAErD,SAAK,aAAa;AAClB,QAAI;AACH,YAAM,KAAK,IAAI,UAAU,KAAK;AAC9B,WAAK,MAAM,EAAE;AAEb,SAAG,GAAG,QAAQ,MAAM;AACnB,aAAK,aAAa;AAClB,aAAK,oBAAoB;AACzB,YAAI,CAAC,KAAK,MAAM,MAAM,IAAI,KAAK,EAAG;AAClC,QAAAA,QAAO,KAAK,0BAA0B,OAAO,UAAU,GAAG,CAAC,CAAC,EAAE;AAG9D,mBAAW,MAAM,KAAK,aAAc,IAAG,MAAM;AAAA,MAC9C,CAAC;AAED,SAAG,GAAG,WAAW,CAAC,SAAS;AAC1B,YAAI,CAAC,KAAK,gBAAgB,EAAG;AAC7B,cAAM,UAAU,YAAY,KAAK,SAAS,CAAC;AAC3C,YAAI,CAAC,QAAS;AAEd,YAAI,CAAC,KAAK,gBAAgB,OAAO,EAAG;AACpC,mBAAW,WAAW,KAAK,iBAAiB;AAC3C,kBAAQ,SAAS,QAAQ,IAAI;AAAA,QAC9B;AAAA,MACD,CAAC;AAED,SAAG,GAAG,SAAS,MAAM;AACpB,aAAK,aAAa;AAClB,YAAI,KAAK,OAAO,IAAI;AACnB,eAAK,KAAK;AACV,eAAK,YAAY;AAIjB,UAAAA,QAAO,KAAK,gBAAgB,OAAO,UAAU,GAAG,CAAC,CAAC,SAAS;AAC3D,qBAAW,MAAM,KAAK,gBAAiB,IAAG,MAAM;AAAA,QACjD;AAEA,aAAK,kBAAkB,MAAM;AAAA,MAC9B,CAAC;AAED,SAAG,GAAG,SAAS,MAAM;AAAA,MAErB,CAAC;AAAA,IACF,QAAQ;AACP,WAAK,aAAa;AAClB,WAAK,kBAAkB,MAAM;AAAA,IAC9B;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,QAAsB;AAC/C,UAAM,OAAO,KAAK,MAAM,IAAI,MAAM;AAClC,QAAI,CAAC,QAAQ,KAAK,eAAgB;AAClC,QAAI,KAAK,qBAAqB,0BAA0B;AACvD,MAAAA,QAAO,KAAK,8BAA8B,OAAO,UAAU,GAAG,CAAC,CAAC,4BAAuB;AACvF;AAAA,IACD;AAEA,UAAM,QAAQ,2BAA2B,KAAK,IAAI,KAAK,KAAK,iBAAiB;AAC7E,SAAK;AAEL,SAAK,iBAAiB,WAAW,MAAM;AACtC,WAAK,iBAAiB;AACtB,UAAI,KAAK,MAAM,IAAI,MAAM,KAAK,CAAC,KAAK,IAAI;AACvC,aAAK,iBAAiB,MAAM;AAAA,MAC7B;AAAA,IACD,GAAG,KAAK;AAAA,EACT;AACD;;;AhB53BA,IAAME,UAAS,aAAa,SAAS;AA6B9B,SAAS,iBAA2B;AAC1C,QAAM,WAAW,oBAAI,IAA8C;AACnE,SAAO;AAAA,IACN,MAAM,CAAC,MAAc,UAAoB;AACxC,iBAAW,KAAK,SAAS,IAAI,IAAI,KAAK,CAAC,EAAG,GAAE,KAAK;AACjD,iBAAW,KAAK,SAAS,IAAI,GAAG,KAAK,CAAC,EAAG,GAAE,KAAK;AAAA,IACjD;AAAA,IACA,IAAI,CAAC,MAAc,YAAuC;AACzD,eAAS,IAAI,MAAM,CAAC,GAAI,SAAS,IAAI,IAAI,KAAK,CAAC,GAAI,OAAO,CAAC;AAAA,IAC5D;AAAA,IACA,KAAK,CAAC,MAAc,YAAwC;AAC3D,UAAI,CAAC,QAAS,UAAS,OAAO,IAAI;AAAA;AAEjC,iBAAS;AAAA,UACR;AAAA,WACC,SAAS,IAAI,IAAI,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,MAAM,OAAO;AAAA,QACvD;AAAA,IACF;AAAA,EACD;AACD;AAWA,IAAM,2BAA2B;AAEjC,IAAM,yBAAyB,oBAAI,IAAwB;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAED,IAAM,4BAA4B;AAClC,IAAM,4BAA4B;AAClC,IAAM,yBAAyB,IAAI,KAAK;AAQjC,SAAS,WAAW,UAAkB,QAAgB,eAAgC;AAC5F,SAAO,gBAAgB,GAAG,QAAQ,IAAI,OAAO,UAAU,GAAG,CAAC,CAAC,KAAK;AAClE;AAGO,SAAS,aAAa,MAAc,WAA+B;AACzE,MAAI,CAAC,aAAa,UAAU,WAAW,EAAG,QAAO;AACjD,SAAO,UAAU,KAAK,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AACvD;AAEA,SAAS,iBAAiB,SAAiB,MAAuB;AACjE,MAAI,YAAY,OAAO,YAAY,KAAM,QAAO;AAChD,MAAI,QAAQ,SAAS,GAAG,EAAG,QAAO,KAAK,WAAW,QAAQ,MAAM,GAAG,EAAE,CAAC;AACtE,SAAO;AACR;AAsNO,IAAM,iBAAN,MAAqB;AAAA,EACnB,UAA8B;AAAA,EAC9B,YAAqC;AAAA,EACrC,YAAqC;AAAA,EACrC,gBAA0C;AAAA,EAC1C,OAA2B;AAAA,EAC3B,SAA+B;AAAA,EAC/B,gBAAgB,oBAAI,IAAsB;AAAA;AAAA,EAE1C,mBAAmB,oBAAI,IAAoB;AAAA,EAC3C;AAAA,EACA;AAAA,EACA,eAAoC;AAAA,EACpC,UAAU;AAAA;AAAA,EAEV,iBAAiB,oBAAI,IAAsB;AAAA;AAAA,EAE3C,eAAe,oBAAI,IAAsB;AAAA;AAAA,EAEzC,aAAa,oBAAI,IAAuC;AAAA;AAAA,EAExD,aAAa,oBAAI,IAAoB;AAAA,EACrC;AAAA;AAAA,EAEA,iBAAiB,oBAAI,IAAY;AAAA,EACjC,aAAgC;AAAA;AAAA,EAEhC,aAAgC;AAAA;AAAA,EAEhC,gBAAgB,oBAAI,IAA+D;AAAA,EACnF,iBAAiB;AAAA;AAAA,EAEjB,cAAc,oBAAI,IAA8C;AAAA;AAAA,EAEhE,eAAe,oBAAI,IAAoB;AAAA;AAAA,EAEvC,eAAoC;AAAA;AAAA,EAEpC,eAAoC;AAAA;AAAA,EAEpC,kBAAkB,oBAAI,IAAY;AAAA;AAAA,EAElC,mBAAmB,oBAAI,IAAoC;AAAA;AAAA,EAE3D,gBAAgB,oBAAI,IAG1B;AAAA;AAAA,EAEM,gBAAgB,oBAAI,IAAY;AAAA;AAAA,EAEhC,iBAAiB,oBAAI,IAAY;AAAA;AAAA,EAEjC,UAAU,oBAAI,IAAyB;AAAA;AAAA,EAEvC;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA,QAA6B;AAAA;AAAA,EAE7B,eAAe,oBAAI,IAGzB;AAAA;AAAA,EAEM,iBAAiB,oBAAI,IAAsB;AAAA,EAEnD,YAAY,QAAuB,aAAqB;AACvD,SAAK,SAAS;AACd,SAAK,cAAc;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,cAA6B,KAAgC;AACxE,QAAI,KAAK,QAAS;AAClB,QAAI,CAAC,KAAK,OAAO,QAAS;AAE1B,SAAK,eAAe,gBAAgB;AACpC,UAAM,aAAa;AACnB,SAAK,aAAa,OAAO;AACzB,UAAM,SAAS,KAAK,UAAU;AAG9B,SAAK,UAAU,MAAM,YAAY,MAAM;AACvC,QAAI,CAAC,KAAK,SAAS;AAClB,MAAAA,QAAO,KAAK,wDAAmD;AAC/D,WAAK,UAAU,MAAM,gBAAgB,QAAQ,KAAK,OAAO,gBAAgB,MAAM;AAAA,IAChF;AAEA,IAAAA,QAAO,KAAK,gBAAgB,KAAK,QAAQ,UAAU,EAAE;AACrD,IAAAA,QAAO,KAAK,eAAe,KAAK,QAAQ,eAAe,EAAE;AAIzD,QAAI;AACH,WAAK,iBAAiB,IAAI,KAAK,MAAM,iBAAiB,MAAM,GAAG,KAAK,CAAC;AAAA,IACtE,QAAQ;AACP,WAAK,iBAAiB,oBAAI,IAAI;AAAA,IAC/B;AAGA,UAAM,YAAY,CAAC,GAAuB,aACzC,OAAO,MAAM,YAAY,IAAI,IAAI,IAAI,KAAK,KAAK,MAAO;AACvD,SAAK,aAAa,IAAI;AAAA,MAChB,WAAK,QAAQ,kBAAkB;AAAA,MACpC,UAAU,KAAK,OAAO,OAAO,gBAAgB,qBAAqB;AAAA,IACnE;AACA,UAAM,KAAK,WAAW,KAAK,EAAE,MAAM,MAAM,MAAS;AAElD,SAAK,eAAe,IAAI;AAAA,MAClB,WAAK,QAAQ,oBAAoB;AAAA,MACtC,UAAU,KAAK,OAAO,OAAO,gBAAgB,qBAAqB;AAAA,IACnE;AACA,UAAM,KAAK,aAAa,KAAK,EAAE,MAAM,MAAM,MAAS;AACpD,QAAI,KAAK,OAAO,OAAO,SAAS;AAC/B,WAAK,eAAe,IAAI;AAAA,QAClB,WAAK,QAAQ,oBAAoB;AAAA,QACtC,UAAU,KAAK,OAAO,OAAO,gBAAgB,qBAAqB;AAAA,MACnE;AACA,YAAM,KAAK,aAAa,KAAK,EAAE,MAAM,MAAM,MAAS;AAAA,IACrD;AAGA,UAAM,OAAO,KAAK,OAAO,QAAQ;AACjC,UAAM,kBAAmC;AAAA,MACxC;AAAA,MACA,KAAK,KAAK,OAAO;AAAA,MACjB,gBAAgB,KAAK,OAAO;AAAA,MAC5B,eAAe,KAAK,OAAO;AAAA,MAC3B,sBAAsB,KAAK,OAAO;AAAA,MAClC,cAAc,KAAK,OAAO;AAAA,IAC3B;AACA,SAAK,YAAY,IAAI,iBAAiB,eAAe;AAGrD,SAAK,UAAU;AAAA,MACd,CAAC,OACA,KAAK,WAAW,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,QACzD,KAAK,iBAAiB,EAAE,GAAG;AAAA,IAC7B;AACA,UAAM,KAAK,UAAU,MAAM;AAG3B,UAAM,WAAW,wBAAwB;AAAA,MACxC,WAAW,KAAK,OAAO,aAAa,QAAQ,IAAI;AAAA,MAChD,KAAK,CAAC,CAAC,KAAK,OAAO;AAAA,MACnB,UAAU,KAAK,YAAY;AAAA,MAC3B;AAAA,IACD,CAAC;AAED,UAAM,kBAAmC;AAAA,MACxC;AAAA,MACA,YAAY,KAAK,QAAQ;AAAA,MACzB,cAAc,KAAK,OAAO,gBAAgB;AAAA,MAC1C,MAAM,KAAK,OAAO,QAAQ;AAAA,MAC1B;AAAA,MACA,cAAc,KAAK,gBAAgB;AAAA,MACnC,YAAY,KAAK,QAAQ;AAAA,MACzB,cAAc,KAAK,QAAQ;AAAA,MAC3B,iBAAiB,KAAK,QAAQ;AAAA,MAC9B,qBAAqB,KAAK,OAAO,SAAS,CAAC,GACzC,IAAI,CAAC,MAAM,EAAE,GAAG,EAChB,OAAO,CAAC,MAAmB,OAAO,MAAM,YAAY,EAAE,SAAS,CAAC;AAAA,MAClE,eAAe,KAAK,QAAQ;AAAA,MAC5B,mBAAmB,KAAK,QAAQ;AAAA,IACjC;AACA,SAAK,YAAY,IAAI,iBAAiB,KAAK,WAAW,eAAe;AACrE,UAAM,KAAK,UAAU,MAAM;AAK3B,UAAM,YAAY,KAAK;AACvB,SAAK,UAAU,YAAY,CAAC,MAAM,UAAU,kBAAkB,CAAC,CAAC;AAChE,SAAK,UAAU,aAAa,CAAC,MAAM,UAAU,sBAAsB,CAAC,CAAC;AAGrE,QAAI,KAAK,OAAO,YAAY,SAAS;AACpC,WAAK,UAAU,eAAe,CAAC,MAAM,OAAO,KAAK,iBAAiB,MAAM,EAAE,CAAC;AAC3E,MAAAA,QAAO;AAAA,QACN,0CAAqC,KAAK,OAAO,WAAW,cAAc,MAAM,uBAAuB,KAAK,OAAO,WAAW,cAAc,KAAK;AAAA,MAClJ;AAAA,IACD;AAIA,SAAK,UAAU,oBAAoB,OAAO;AAAA,MACzC,WAAW,KAAK,SAAS,mBAAmB;AAAA,MAC5C,YAAY,KAAK,SAAS;AAAA,MAC1B,GAAI,KAAK,OAAO,eAAe,EAAE,MAAM,KAAK,OAAO,gBAAgB,OAAO,IAAI,CAAC;AAAA,IAChF,EAAE;AACF,SAAK,UAAU,eAAe,CAAC,MAAM,OAAO,KAAK,kBAAkB,MAAM,IAAI,UAAU,CAAC;AACxF,UAAM,KAAK,iBAAiB;AAK5B,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,UAAI,CAAC,KAAK,cAAc,IAAI,QAAQ,IAAI,EAAG;AAC3C,UAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,EAAG;AACjD,WAAK,KAAK,gBAAgB,QAAQ,IAAI;AAAA,IACvC,CAAC;AAGD,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,UAAI,QAAQ,SAAS,eAAe,KAAK,gBAAgB,KAAK,SAAS;AACtE,YAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,EAAG;AACjD,YAAI,CAAC,KAAK,iBAAiB,QAAQ,IAAI,EAAG;AAC1C,cAAM,QAA0B,KAAK,aACnC,KAAK,EACL,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,WAAW,WAAW,CAAC,EAIhD,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,OAAO,CAAC,EAC3C,OAAO,CAAC,MAAM,aAAa,EAAE,MAAM,KAAK,OAAO,OAAO,SAAS,CAAC,EAChE,IAAI,CAAC,OAAO;AAAA,UACZ,MAAM,EAAE;AAAA,UACR,aAAa,EAAE;AAAA,UACf,SAAS,EAAE;AAAA,UACX,YAAY,KAAK,QAAS;AAAA,UAC1B,cAAc,KAAK,OAAO,gBAAgB;AAAA,QAC3C,EAAE;AACH,cAAM,WAAW;AAAA,UAChB;AAAA,UACA,KAAK,QAAQ;AAAA,UACb,QAAQ;AAAA,UACR;AAAA,UACA,KAAK,QAAQ;AAAA,UACb,KAAK,QAAQ;AAAA,QACd;AACA,aAAK,UAAW,WAAW,QAAQ,MAAM,QAAQ;AAAA,MAClD;AAAA,IACD,CAAC;AAGD,SAAK,UAAU,UAAU,OAAO,YAAY;AAC3C,UAAI,QAAQ,SAAS,eAAe,KAAK,gBAAgB,KAAK,SAAS;AACtE,YAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,GAAG;AAChD,UAAAA,QAAO,KAAK,sCAAsC,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,EAAE;AAChF;AAAA,QACD;AACA,cAAM,EAAE,QAAQ,UAAU,OAAO,IAAI,QAAQ;AAK7C,QAAAA,QAAO;AAAA,UACN,yBAAyB,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,KAAK,QAAQ,IAAI,KAAK,UAAU,MAAM,CAAC;AAAA,QAC7F;AAEA,YAAI,CAAC,KAAK,kBAAkB,QAAQ,MAAM,QAAQ,GAAG;AACpD,UAAAA,QAAO,KAAK,0BAA0B,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,KAAK,QAAQ,EAAE;AACjF,eAAK,UAAW;AAAA,YACf,QAAQ;AAAA,YACR;AAAA,cACC;AAAA,cACA,KAAK,QAAQ;AAAA,cACb,QAAQ;AAAA,cACR,EAAE,QAAQ,SAAS,OAAO,OAAO,4BAA4B,QAAQ,GAAG;AAAA,cACxE,KAAK,QAAQ;AAAA,cACb,KAAK,QAAQ;AAAA,YACd;AAAA,UACD;AACA;AAAA,QACD;AAEA,cAAM,OAAO,KAAK,aAAa,IAAI,QAAQ;AAC3C,YAAI;AACJ,YAAI,CAAC,MAAM;AACV,UAAAA,QAAO,KAAK,6BAA6B,QAAQ,aAAa;AAC9D,qBAAW;AAAA,YACV;AAAA,YACA,KAAK,QAAQ;AAAA,YACb,QAAQ;AAAA,YACR;AAAA,cACC;AAAA,cACA,SAAS;AAAA,cACT,OAAO,SAAS,QAAQ;AAAA,YACzB;AAAA,YACA,KAAK,QAAQ;AAAA,YACb,KAAK,QAAQ;AAAA,UACd;AAAA,QACD,OAAO;AACN,cAAI;AACH,kBAAM,YAAY,KAAK,IAAI;AAI3B,kBAAM,aAAa,KAAK,WACrB,aAAa,EACd,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ,IAAI,GAAG;AACtC,kBAAM,SAAS,MAAM,KAAK,QAAQ,mBAAmB,QAAQ,QAAQ,MAAM,UAAU,CAAC;AACtF,kBAAM,aAAa,KAAK,IAAI,IAAI;AAChC,kBAAM,gBACL,OAAO,WAAW,WACf,OAAO,UAAU,GAAG,GAAG,IACvB,KAAK,UAAU,MAAM,EAAE,UAAU,GAAG,GAAG;AAC3C,YAAAA,QAAO;AAAA,cACN,+BAA+B,QAAQ,oBAAe,UAAU,cAAS,aAAa;AAAA,YACvF;AACA,wBAAY,KAAK,yBAAyB;AAAA,cACzC;AAAA,cACA,cAAc,QAAQ,KAAK,UAAU,GAAG,CAAC;AAAA,cACzC,SAAS;AAAA,cACT;AAAA,YACD,CAAC;AACD,uBAAW;AAAA,cACV;AAAA,cACA,KAAK,QAAQ;AAAA,cACb,QAAQ;AAAA,cACR;AAAA,gBACC;AAAA,gBACA,SAAS;AAAA,gBACT;AAAA,cACD;AAAA,cACA,KAAK,QAAQ;AAAA,cACb,KAAK,QAAQ;AAAA,YACd;AAAA,UACD,SAAS,KAAK;AACb,kBAAM,WAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAChE,YAAAA,QAAO,MAAM,4BAA4B,QAAQ,WAAM,QAAQ,EAAE;AACjE,wBAAY,KAAK,yBAAyB;AAAA,cACzC;AAAA,cACA,cAAc,QAAQ,KAAK,UAAU,GAAG,CAAC;AAAA,cACzC,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,OAAO;AAAA,YACR,CAAC;AACD,uBAAW;AAAA,cACV;AAAA,cACA,KAAK,QAAQ;AAAA,cACb,QAAQ;AAAA,cACR;AAAA,gBACC;AAAA,gBACA,SAAS;AAAA,gBACT,OAAO;AAAA,cACR;AAAA,cACA,KAAK,QAAQ;AAAA,cACb,KAAK,QAAQ;AAAA,YACd;AAAA,UACD;AAAA,QACD;AACA,cAAM,KAAK,UAAW,WAAW,QAAQ,MAAM,QAAQ;AACvD,QAAAA,QAAO,KAAK,8BAA8B,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,EAAE;AAAA,MACzE;AAAA,IACD,CAAC;AAGD,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,UACC,QAAQ,SAAS,wBACjB,KAAK,gBACL,KAAK,WACL,KAAK,eACJ;AACD,cAAM,QAAQ,QAAQ;AACtB,YAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,EAAG;AAEjD,cAAM,gBAAgB,KAAK;AAC3B,cAAM,mBAAmB,QAAQ;AAEjC,cAAM,eAAe,KAAK,WAAW,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ,IAAI;AACrF,cAAM,WAAW,cAAc,QAAQ,QAAQ,KAAK,UAAU,GAAG,CAAC;AAElE,cAAM,aACJ,KAAK,WAAW,aAAa,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,SAAS;AACpF,YAAI,WAAW;AACd,UAAAA,QAAO;AAAA,YACN,6BAA6B,QAAQ,mCAA8B,WAAW,UAAU,QAAQ,MAAM,IAAI,CAAC;AAAA,UAC5G;AAAA,QACD;AACA,cAAM,WAAW,aAAa,WAAW,UAAU,QAAQ,MAAM,SAAS,CAAC;AAG3E,mBAAW,KAAK,OAAO;AACtB,cAAI,CAAC,KAAK,cAAe,MAAK,gBAAgB,oBAAI,IAAI;AACtD,gBAAM,YAAY,KAAK,cAAc,IAAI,EAAE,IAAI,KAAK,CAAC;AACrD,cAAI,CAAC,UAAU,SAAS,gBAAgB,GAAG;AAC1C,sBAAU,KAAK,gBAAgB;AAC/B,iBAAK,cAAc,IAAI,EAAE,MAAM,SAAS;AAAA,UACzC;AAAA,QACD;AAEA,cAAMC,aAAY,KAAK;AACvB,cAAM,gBAAgB,KAAK;AAG3B,cAAM,iBAKD,CAAC;AAEN,mBAAW,KAAK,OAAO;AACtB,gBAAM,eAAe,KAAK,aAAc,IAAI,EAAE,IAAI;AAClD,gBAAM,cAAc,gBAAgB,CAAC,aAAa,QAAQ,WAAW,WAAW;AAEhF,cAAI,aAAa;AAEhB;AAAA,UACD;AAGA,gBAAM,kBAAkB,cAAc,IAAI,EAAE,IAAI,KAAK,CAAC,GAAG;AAAA,YACxD,CAAC,OAAO,OAAO;AAAA,UAChB;AACA,cAAI,gBAAgB,eAAe,WAAW,EAAG;AAEjD,cAAI,cAAc;AAOjB,kBAAM,kBAAkB,KAAK,iBAAiB,IAAI,EAAE,IAAI;AAIxD,gBACC,CAAC,aAAa,KAAK,SAAS,GAAG,KAC/B,mBACA,oBAAoB,kBACnB;AACD,oBAAM,eAAeA,WAAU,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,eAAe;AAClF,oBAAM,eAAe,cAAc,QAAQ,gBAAgB,UAAU,GAAG,CAAC;AACzE,oBAAM,cACL,iBAAiB,YACjBA,WAAU,aAAa,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,SAAS;AAC1E,oBAAM,YAAY,GAAG,WAAW,cAAc,iBAAiB,WAAW,CAAC,IAAI,EAAE,IAAI;AACrF,oBAAM,UAAU;AAChB,oBAAM,kBAAkB;AAAA,gBACvB,MAAM;AAAA,gBACN,aAAa,aAAa;AAAA,gBAC1B,YAAY,EAAE,OAAO,MAAM;AAAA,gBAAC,EAAE;AAAA,gBAC9B,MAAM,QAAQ,QAAiB;AAC9B,wBAAM,SAAS,MAAM,cAAc,kBAAkB,SAAS,EAAE,MAAM,MAAM;AAC5E,sBAAI,OAAO,QAAS,QAAO,OAAO;AAClC,wBAAM,IAAI,MAAM,OAAO,SAAS,8BAA8B;AAAA,gBAC/D;AAAA,cACD;AACA,mBAAK,aAAc,SAAS,aAAa,SAAS,CAAC,eAAe,GAAG,KAAK;AAC1E,mBAAK,iBAAiB,IAAI,WAAW,OAAO;AAE5C,mBAAK,iBAAiB,OAAO,EAAE,IAAI;AACnC,cAAAD,QAAO,KAAK,uBAAuB,EAAE,IAAI,WAAM,SAAS,wBAAwB;AAAA,YACjF;AAGA,kBAAM,SACL,aACC,oBAAoB,WACnBC,WAAU,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,eAAe,GAAG,QAAQ,QACxE;AACH,kBAAM,eAAe,GAAG,WAAW,UAAU,kBAAkB,MAAM,CAAC,IAAI,EAAE,IAAI;AAIhF,gBACC,KAAK,aAAc,IAAI,YAAY,KACnC,KAAK,iBAAiB,IAAI,YAAY,MAAM,kBAC3C;AACD;AAAA,YACD;AACA,iBAAK,iBAAiB,IAAI,cAAc,gBAAgB;AACxD,2BAAe,KAAK;AAAA,cACnB,MAAM;AAAA,cACN,aAAa,YAAY,QAAQ,KAAK,EAAE,WAAW;AAAA,cACnD,YAAY,EAAE,OAAO,MAAM;AAAA,cAAC,EAAE;AAAA,cAC9B,MAAM,QAAQ,QAAiB;AAC9B,sBAAM,SAAS,MAAM,cAAc;AAAA,kBAClC;AAAA,kBACA,EAAE;AAAA,kBACF;AAAA,gBACD;AACA,oBAAI,OAAO,QAAS,QAAO,OAAO;AAClC,sBAAM,IAAI,MAAM,OAAO,SAAS,8BAA8B;AAAA,cAC/D;AAAA,YACD,CAAC;AAAA,UACF,OAAO;AAGN,iBAAK,iBAAiB,IAAI,EAAE,MAAM,gBAAgB;AAClD,2BAAe,KAAK;AAAA,cACnB,MAAM,EAAE;AAAA,cACR,aAAa,YAAY,QAAQ,KAAK,EAAE,WAAW;AAAA,cACnD,YAAY,EAAE,OAAO,MAAM;AAAA,cAAC,EAAE;AAAA,cAC9B,MAAM,QAAQ,QAAiB;AAE9B,sBAAM,YAAY,cAAc,IAAI,EAAE,IAAI,KAAK,CAAC,gBAAgB;AAChE,oBAAI,WAAW,UAAU,CAAC;AAE1B,oBAAI,UAAU,SAAS,GAAG;AACzB,wBAAM,YAAYA,WAAU,aAAa;AACzC,sBAAI,WAAW,OAAO;AACtB,6BAAW,OAAO,WAAW;AAC5B,0BAAM,OAAO,UAAU,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG;AAC/C,wBAAI,QAAQ,KAAK,OAAO,UAAU;AACjC,iCAAW,KAAK;AAChB,iCAAW;AAAA,oBACZ;AAAA,kBACD;AAAA,gBACD;AAEA,sBAAM,SAAS,MAAM,cAAc,kBAAkB,UAAU,EAAE,MAAM,MAAM;AAC7E,oBAAI,OAAO,QAAS,QAAO,OAAO;AAClC,sBAAM,IAAI,MAAM,OAAO,SAAS,8BAA8B;AAAA,cAC/D;AAAA,YACD,CAAC;AAAA,UACF;AAAA,QACD;AAEA,YAAI,eAAe,SAAS,GAAG;AAC9B,eAAK,aAAc,SAAS,UAAU,gBAAgB,KAAK;AAC3D,UAAAD,QAAO;AAAA,YACN,cAAc,eAAe,MAAM,sBAAsB,QAAQ,OAAO,QAAQ;AAAA,UACjF;AAAA,QACD;AAAA,MACD;AAAA,IACD,CAAC;AAGD,SAAK,UAAU,UAAU,OAAO,YAAY;AAC3C,UAAI,QAAQ,SAAS,mBAAmB,KAAK,SAAS;AACrD,YAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,GAAG;AAChD,UAAAA,QAAO,KAAK,0CAA0C,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,EAAE;AACpF;AAAA,QACD;AACA,cAAM,UAAU,QAAQ;AAMxB,YAAI,CAAC,SAAS,MAAO;AAGrB,YAAI,CAAC,KAAK,mBAAmB,QAAQ,IAAI,GAAG;AAC3C,UAAAA,QAAO,KAAK,gCAAgC,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,EAAE;AAC1E,gBAAM,KAAK,kBAAkB,QAAQ,MAAM;AAAA,YAC1C,QAAQ,QAAQ;AAAA,YAChB,QAAQ;AAAA,YACR,OAAO;AAAA,UACR,CAAC;AACD;AAAA,QACD;AAEA,QAAAA,QAAO;AAAA,UACN,yBAAyB,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,MAAM,QAAQ,MAAM,UAAU,GAAG,EAAE,CAAC;AAAA,QAC1F;AACA,oBAAY,KAAK,eAAe,EAAE,QAAQ,QAAQ,OAAO,CAAC;AAG1D,cAAM,YAAa,WAAmB;AACtC,YAAI,WAAW;AAGd,gBAAM,SAAS,OAAO,QAAQ,aAAa,YAAY,QAAQ,SAAS,SAAS;AACjF,gBAAM,WAAW,SACd,6BAA6B,QAAQ,QAAQ,MAAM,QAAQ,KAAK,KAChE,QAAQ;AACX,gBAAM,YAAY,SAAS,OAAO,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,KAAK;AACnE,gBAAM,OAAO,UAAU,QAAQ,UAAU,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAOnD,GAAI,SAAS,EAAE,WAAW,UAAU,IAAI,CAAC;AAAA,YACzC,UAAU;AAAA,cACT,eAAe,QAAQ,iBAAiB;AAAA,cACxC,cAAc,QAAQ;AAAA,cACtB,cAAc,QAAQ;AAAA,YACvB;AAAA,UACD,CAAC;AAID,gBAAM,gBAAgB,YAAY,YAAY;AAC7C,kBAAM,IAAI,UAAU,IAAI,KAAK,EAAE;AAC/B,gBACC,MACC,EAAE,WAAW,eAAe,EAAE,WAAW,YAAY,EAAE,WAAW,cAClE;AACD,4BAAc,aAAa;AAC3B,mBAAK,iBAAiB,OAAO,aAAa;AAC1C,oBAAM,KAAK,kBAAkB,QAAQ,MAAM;AAAA,gBAC1C,QAAQ,QAAQ;AAAA,gBAChB,QAAQ,EAAE;AAAA,gBACV,QAAQ,EAAE;AAAA,gBACV,OAAO,EAAE;AAAA,cACV,CAAC;AAAA,YACF;AAAA,UACD,GAAG,GAAI;AACP,eAAK,iBAAiB,IAAI,aAAa;AAAA,QACxC,OAAO;AACN,gBAAM,KAAK,kBAAkB,QAAQ,MAAM;AAAA,YAC1C,QAAQ,QAAQ;AAAA,YAChB,QAAQ;AAAA,YACR,OAAO;AAAA,UACR,CAAC;AAAA,QACF;AAAA,MACD;AAAA,IACD,CAAC;AAKD,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,UAAI,QAAQ,SAAS,eAAgB;AACrC,UAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,GAAG;AAChD,QAAAA,QAAO,KAAK,yCAAyC,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,EAAE;AACnF;AAAA,MACD;AACA,YAAM,UAAU,QAAQ;AACxB,UAAI,CAAC,SAAS,KAAM;AACpB,YAAM,WAAW,QAAQ,YAAY,QAAQ,KAAK,UAAU,GAAG,CAAC;AAChE,WAAK,KAAK,WAAW,QAAQ,MAAM;AAAA,QAClC,KAAK;AAAA,QACL,MAAM,QAAQ;AAAA,QACd;AAAA,QACA,WAAW,QAAQ;AAAA,QACnB,WAAW,QAAQ;AAAA,MACpB,CAAC;AACD,kBAAY,KAAK,gBAAgB;AAAA,QAChC,MAAM,QAAQ;AAAA,QACd;AAAA,QACA,MAAM,QAAQ;AAAA,QACd,WAAW,QAAQ;AAAA,QACnB,WAAW,QAAQ;AAAA,MACpB,CAAC;AAAA,IACF,CAAC;AAGD,UAAM,WAAW,KAAK,OAAO;AAC7B,UAAM,gBAAgB,UAAU,YAAY;AAC5C,UAAM,oBAAoB,UAAU,uBAAuB;AAI3D,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,UAAI,QAAQ,SAAS,YAAY,CAAC,KAAK,WAAW,CAAC,KAAK,UAAW;AACnE,UAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,GAAG;AAChD,QAAAA,QAAO,KAAK,4CAA4C,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,EAAE;AACtF;AAAA,MACD;AACA,YAAM,UAAU,QAAQ;AACxB,UAAI,CAAC,SAAS,MAAM,CAAC,QAAQ,IAAK;AAClC,UAAI,QAAQ,OAAO,KAAK,QAAQ,YAAY;AAE3C,aAAK,cAAc,QAAQ,MAAM,QAAQ,KAAK,UAAU;AACxD;AAAA,MACD;AACA,UAAI,CAAC,UAAU,QAAS;AACxB,YAAM,KAAK,QAAQ;AAInB,YAAM,MAAM,OAAO,QAAQ,QAAQ,WAAW,QAAQ,MAAM;AAC5D,YAAM,SAAS,CAAC,QAAgB,OAAO,UAAU;AAChD,QAAAA,QAAO;AAAA,UACN,iBAAiB,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,SAAI,GAAG,UAAU,GAAG,CAAC,CAAC,KAAK,MAAM;AAAA,QAC/E;AACA,cAAM,MAAM;AAAA,UACX;AAAA,UACA,KAAK,QAAS;AAAA,UACd,QAAQ;AAAA,UACR,EAAE,IAAI,QAAQ,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC,GAAI,GAAI,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,EAAG;AAAA,UACvE,KAAK,QAAS;AAAA,UACd,KAAK,QAAS;AAAA,QACf;AACA,aAAK,KAAK,UAAW,WAAW,QAAQ,MAAM,GAAG;AAAA,MAClD;AAIA,YAAM,eAAe,MAAM;AAC1B,cAAM,WACL,KAAK,WAAW,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ,IAAI,GAAG,QAAQ;AAC5E,aAAK,KAAK,cAAc,OAAO,IAAI,QAAQ,MAAM,QAAQ;AACzD,eAAO,oBAAoB,IAAI;AAAA,MAChC;AACA,UAAI,KAAK,UAAU,OAAO,EAAE,SAAS,cAAe,QAAO,OAAO,WAAW;AAC7E,UAAI,CAAC,eAAe,KAAK,cAAc,QAAQ,MAAM,IAAI,iBAAiB;AACzE,eAAO,OAAO,cAAc;AAC7B,YAAM,QAAQ,KAAK,UAAU,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,MAAM,EAAE,SAAS;AAClF,UAAI,CAAC,MAAO,QAAO,aAAa;AAChC,YAAM,OAAO;AAAA,QACZ;AAAA,QACA,KAAK,QAAQ;AAAA,QACb;AAAA,QACA,EAAE,MAAM,QAAQ,MAAM,KAAK,QAAQ,IAAI;AAAA,QACvC,KAAK,QAAQ;AAAA,QACb,KAAK,QAAQ;AAAA,MACd;AACA,WAAK,KAAK,UAAU,WAAW,IAAI,IAAI,EAAE,KAAK,CAAC,SAAS;AACvD,YAAI,CAAC,KAAM,QAAO,aAAa;AAG/B,YAAI,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,KAAK,UAAW;AAC9C,cAAM,MAAM;AAAA,UACX;AAAA,UACA,KAAK,QAAQ;AAAA,UACb,QAAQ;AAAA,UACR,EAAE,IAAI,IAAI;AAAA,UACV,KAAK,QAAQ;AAAA,UACb,KAAK,QAAQ;AAAA,QACd;AACA,aAAK,KAAK,UAAU,WAAW,QAAQ,MAAM,GAAG;AAAA,MACjD,CAAC;AAAA,IACF,CAAC;AAGD,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,UAAI,QAAQ,SAAS,mBAAmB,CAAC,KAAK,QAAS;AACvD,UAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,EAAG;AACjD,YAAM,UAAU,QAAQ;AACxB,UAAI,CAAC,SAAS,QAAQ,CAAC,QAAQ,IAAK;AAEpC,WAAK,cAAc,QAAQ,MAAM,QAAQ,KAAK,YAAY,QAAQ,IAAI;AAAA,IACvE,CAAC;AAID,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,UAAI,QAAQ,SAAS,SAAU;AAC/B,UAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,EAAG;AACjD,YAAM,UAAU,QAAQ;AACxB,UAAI,CAAC,MAAM,QAAQ,SAAS,OAAO,EAAG;AACtC,YAAM,MAAM,oBAAI,IAA0B;AAC1C,iBAAW,KAAK,QAAQ,SAAS;AAChC,YAAI,GAAG,cAAc,EAAE,eAAe,KAAK,SAAS,WAAY,KAAI,IAAI,EAAE,YAAY,CAAC;AAAA,MACxF;AACA,WAAK,WAAW,IAAI,QAAQ,MAAM,GAAG;AAErC,WAAK,KAAK,gBAAgB;AAAA,IAC3B,CAAC;AAED,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,UAAI,QAAQ,SAAS,cAAe;AACpC,UAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,EAAG;AACjD,YAAM,UAAU,QAAQ;AAMxB,MAAAA,QAAO;AAAA,QACN,8BAA8B,SAAS,IAAI,UAAU,GAAG,CAAC,KAAK,GAAG,KAAK,SAAS,UAAU,SAAS;AAAA,MACnG;AACA,WAAK,YAAY,OAAO;AACxB,kBAAY,KAAK,uBAAuB,EAAE,KAAK,QAAQ,MAAM,GAAG,QAAQ,CAAC;AAAA,IAC1E,CAAC;AAGD,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,UAAI,QAAQ,SAAS,YAAa;AAClC,UAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,EAAG;AACjD,YAAM,UAAU,QAAQ;AACxB,UAAI,OAAO,SAAS,QAAQ,SAAU;AACtC,WAAK,cAAc,IAAI,QAAQ,GAAG,GAAG,OAAO,EAAE,MAAM,MAAM,CAAC;AAAA,IAC5D,CAAC;AAKD,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,UAAI,QAAQ,SAAS,gBAAiB;AACtC,UAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,EAAG;AACjD,YAAM,UAAU,QAAQ;AACxB,UAAI,CAAC,MAAM,QAAQ,SAAS,OAAO,KAAK,QAAQ,QAAQ,WAAW,EAAG;AACtE,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,UAAyB,CAAC;AAChC,iBAAW,KAAK,QAAQ,SAAS;AAChC,YAAI,CAAC,KAAK,OAAO,EAAE,SAAS,YAAY,CAAC,EAAE,KAAM;AAIjD,cAAM,QAAQ,KAAK,aAAa,IAAI,EAAE,IAAI;AAC1C,YAAI,UAAU,UAAa,UAAU,OAAO,EAAE,IAAI,KAAK,GAAI;AAC3D,cAAM,SAAsB;AAAA,UAC3B,MAAM,EAAE;AAAA,UACR,UACC,OAAO,EAAE,aAAa,YAAY,EAAE,WAAW,EAAE,WAAW,KAAK,WAAW,EAAE,IAAI;AAAA,UACnF,OAAO,KAAK,IAAI,GAAG,OAAO,EAAE,KAAK,KAAK,CAAC;AAAA,UACvC,OAAO,OAAO,EAAE,KAAK,KAAK;AAAA,UAC1B,MAAM,OAAO,EAAE,IAAI,KAAK;AAAA,QACzB;AACA,aAAK,YAAY,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ,QAAQ,QAAQ,KAAK,CAAC;AAChE,gBAAQ,KAAK,MAAM;AAAA,MACpB;AACA,UAAI,QAAQ,WAAW,EAAG;AAC1B,MAAAA,QAAO;AAAA,QACN,GAAG,QAAQ,MAAM,sDAAsD,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC;AAAA,MACpG;AACA,kBAAY,KAAK,wBAAwB,EAAE,KAAK,QAAQ,MAAM,MAAM,QAAQ,CAAC;AAAA,IAC9E,CAAC;AAGD,QAAI,UAAU,SAAS;AACtB,YAAM,WAAW,MAAM;AACtB,YAAI,KAAK,YAAa,cAAa,KAAK,WAAW;AACnD,aAAK,cAAc,WAAW,MAAM,KAAK,gBAAgB,GAAG,GAAG;AAAA,MAChE;AACA,WAAK,UAAU,aAAa,QAAQ;AACpC,WAAK,UAAU,gBAAgB,QAAQ;AAIvC,WAAK,UAAU,aAAa,CAAC,OAAO,KAAK,KAAK,iBAAiB,EAAE,CAAC;AAClE,MAAAA,QAAO;AAAA,QACN,6DAAmD,iBAAiB,kBAAkB,aAAa;AAAA,MACpG;AAAA,IACD;AAIA,SAAK,UAAU,UAAU,CAAC,QAAQ,QAAQ,KAAK,gBAAgB,QAAQ,GAAG,CAAC;AAG3E,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,UAAI,QAAQ,SAAS,mBAAmB,CAAC,KAAK,SAAS,eAAe,CAAC,KAAK,UAAW;AACvF,UAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,EAAG;AACjD,YAAM,UAAU,QAAQ;AACxB,UAAI,CAAC,SAAS,IAAK;AACnB,YAAM,MAAM,GAAG,QAAQ,IAAI,IAAI,KAAK,QAAQ,UAAU;AACtD,YAAM,QAAQ,OAAO,KAAK,QAAQ,aAAa,QAAQ,KAAK,KAAK,KAAK,QAAQ,eAAe;AAC7F,UAAI,CAAC,OAAO;AACX,QAAAA,QAAO,KAAK,sBAAsB,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,mBAAmB;AACjF;AAAA,MACD;AACA,UAAI;AACJ,UAAI;AACH,gBAAQ,KAAK,MAAM,MAAM,SAAS,MAAM,CAAC;AAAA,MAC1C,QAAQ;AACP;AAAA,MACD;AAEA,UAAI,MAAM,SAAS,QAAQ,KAAM;AACjC,WAAK,UAAU,cAAc,KAAK;AAAA,IACnC,CAAC;AACD,QAAI,KAAK,OAAO;AACf,MAAAA,QAAO,KAAK,kEAA6D;AAG1E,SAAK,gBAAgB,IAAI;AAAA,MACxB,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,MACb,KAAK,OAAO;AAAA,IACb;AAGA,UAAM,aAAyB;AAAA,MAC9B,QAAQ,KAAK,OAAO,OAAO,UAAU;AAAA,MACrC,SAAS,KAAK,OAAO,OAAO,WAAW;AAAA,IACxC;AACA,SAAK,OAAO,IAAI;AAAA,MACf,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,QAAQ;AAAA,MACb,KAAK,OAAO,gBAAgB;AAAA,MAC5B,KAAK,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,MACb;AAAA,IACD;AAGA,SAAK,KAAK,uBAAuB,OAAO,UAAU;AACjD,YAAME,MAAK,MAAM,OAAO,aAAkB;AAC1C,YAAM,UAAU,MAAM,OAAO,MAAW;AACxC,YAAM,aAAa,QAAQ;AAAA,QAC1B,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM,UAAU,QAAQ,UAAU,GAAG;AAAA,MACtC;AACA,YAAMA,IAAG,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAC9C,YAAM,iBAAiB,QAAQ,KAAK,YAAY,eAAe;AAC/D,YAAM,YAAY,IAAI,KAAK,MAAM,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,CAAC;AACrE,YAAM,OAAO,IAAI,SAAS,KAAK,MAAM,IAAI,KAAK,MAAM,QAAQ,QAAQ,OAAO,GAAG,EAAE,UAAU,GAAG,GAAI,CAAC;AAAA;AAClG,YAAMA,IAAG,WAAW,gBAAgB,MAAM,OAAO;AACjD,MAAAF,QAAO;AAAA,QACN,0BAA0B,MAAM,SAAS,WAAM,MAAM,IAAI,SAAS,MAAM,WAAW,UAAU,GAAG,CAAC,CAAC;AAAA,MACnG;AAAA,IACD,CAAC;AAGD,SAAK,SAAS,IAAI;AAAA,MACjB,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,QAAQ;AAAA,MACb,KAAK,OAAO,gBAAgB;AAAA,MAC5B,KAAK,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,MACb,KAAK,OAAO;AAAA,IACb;AACA,SAAK,OAAO;AAAA,MACX,MAAMA,QAAO,KAAK,oEAAoE;AAAA,MACtF,MAAMA,QAAO,KAAK,uCAAuC;AAAA,IAC1D;AAGA,SAAK,UAAU,iBAAiB,MAAM;AACrC,WAAK,QAAQ,QAAQ;AAAA,IACtB,CAAC;AAID,SAAK,QAAQ,IAAI,aAAa;AAAA,MAC7B,QAAQ,KAAK,OAAO,SAAS,CAAC;AAAA,MAC9B,cAAc,CAAC,CAAC,KAAK,OAAO,OAAO;AAAA,MACnC,aAAa,KAAK;AAAA,MAClB;AAAA,MACA,SAAS,KAAK;AAAA,MACd,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK,OAAO,gBAAgB;AAAA,MAC1C,oBAAoB;AAAA,MACpB,KAAK;AAAA,MACL,cAAc,MAAM,KAAK,aAAa;AAAA,MACtC,kBAAkB,CAAC,QAAQ,KAAK,iBAAiB,GAAG;AAAA,MACpD,iBAAiB,CAAC,WAAW;AAC5B,mBAAW,CAAC,OAAO,MAAM,KAAK,KAAK,YAAY;AAC9C,cAAI,CAAC,OAAO,IAAI,MAAM,EAAG;AACzB,gBAAM,MAAM,KAAK,WAAW,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE,SAAS;AACpF,cAAI,IAAK,QAAO;AAAA,QACjB;AACA,eAAO;AAAA,MACR;AAAA,MACA,sBAAsB,CAAC,KAAK,MAAM,YAAY,KAAK,qBAAqB,KAAK,MAAM,OAAO;AAAA,MAC1F,YAAY,CAAC,QAAQ,UAAU,KAAK,WAAW,QAAQ,KAAK;AAAA,IAC7D,CAAC;AACD,SAAK,UAAU;AAAA,MAAe,CAAC,KAAK,KAAK,aACxC,KAAK,QAAQ,KAAK,MAAM,kBAAkB,KAAK,KAAK,QAAQ,IAAI;AAAA,IACjE;AAEA,SAAK,UAAU,UAAU,CAAC,YAAY;AACrC,UAAI,CAAC,QAAQ,KAAK,WAAW,OAAO,KAAK,CAAC,QAAQ,KAAK,WAAW,aAAa,EAAG;AAClF,UAAI,CAAC,KAAK,WAAW,kBAAkB,OAAO,EAAG;AACjD,WAAK,OAAO,cAAc,SAAS,CAAC,CAAC;AAAA,IACtC,CAAC;AAGA,IAAC,WAAmB,cAAc;AAGnC,QAAI,KAAK,OAAO,OAAO;AAGtB,iBAAW,QAAQ,KAAK,OAAO,OAAO;AACrC,YAAI,CAAC,KAAK,IAAK;AACf,QAAAA,QAAO,KAAK,uBAAuB,KAAK,GAAG,EAAE;AAC7C,cAAM,KAAK,UAAU,cAAc,KAAK,GAAG;AAAA,MAC5C;AAIA,WAAK,mBAAmB,YAAY,MAAM;AACzC,mBAAW,QAAQ,KAAK,OAAO,SAAS,CAAC,GAAG;AAC3C,cAAI,CAAC,KAAK,IAAK;AACf,eAAK,WAAW,cAAc,KAAK,GAAG,EAAE,MAAM,MAAM;AAAA,UAAC,CAAC;AAAA,QACvD;AAAA,MACD,GAAG,IAAM;AAAA,IACV;AAGA,SAAK,KAAK,kBAAkB;AAC5B,SAAK,iBAAiB,YAAY,MAAM,KAAK,KAAK,kBAAkB,GAAG,sBAAsB;AAE7F,SAAK,UAAU;AACf,IAAAA,QAAO,KAAK,0BAAqB,KAAK,OAAO,QAAQ,MAAM,eAAe,IAAI,EAAE;AAAA,EACjF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAc,kBACb,QACA,SACmB;AACnB,QAAI,CAAC,KAAK,WAAW,CAAC,KAAK,UAAW,QAAO;AAC7C,UAAM,UAAU;AAAA,MACf;AAAA,MACA,KAAK,QAAQ;AAAA,MACb;AAAA,MACA;AAAA,MACA,KAAK,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,IACd;AACA,UAAM,OAAO,MAAM,KAAK,UAAU,WAAW,QAAQ,OAAO,EAAE,MAAM,MAAM,KAAK;AAC/E,UAAM,MAAM,OAAO,UAAU,GAAG,CAAC;AACjC,QAAI,MAAM;AACT,YAAM,KAAK,cAAc,OAAO,QAAQ,QAAQ,MAAM;AACtD,MAAAA,QAAO,KAAK,mCAAmC,GAAG,KAAK,QAAQ,MAAM,EAAE;AACvE,aAAO;AAAA,IACR;AACA,UAAM,KAAK,cAAc,IAAI,EAAE,QAAQ,GAAG,SAAS,IAAI,KAAK,IAAI,EAAE,CAAC;AACnE,IAAAA,QAAO,KAAK,+BAA+B,GAAG,8CAAyC;AACvF,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,gBAAgB,QAA+B;AAC5D,UAAM,SAAS,KAAK;AACpB,QAAI,CAAC,UAAU,CAAC,OAAO,IAAI,MAAM,KAAK,KAAK,gBAAgB,IAAI,MAAM,EAAG;AACxE,SAAK,gBAAgB,IAAI,MAAM;AAC/B,QAAI;AACH,iBAAW,SAAS,OAAO,QAAQ,MAAM,GAAG;AAC3C,cAAM,KAAK,MAAM,KAAK,kBAAkB,QAAQ;AAAA,UAC/C,QAAQ,MAAM;AAAA,UACd,QAAQ,MAAM;AAAA,UACd,QAAQ,MAAM;AAAA,UACd,OAAO,MAAM;AAAA,QACd,CAAC;AACD,YAAI,CAAC,IAAI;AACR,gBAAM,OAAO,YAAY,QAAQ,MAAM,MAAM;AAC7C;AAAA,QACD;AAAA,MACD;AAAA,IACD,UAAE;AACD,WAAK,gBAAgB,OAAO,MAAM;AAAA,IACnC;AAAA,EACD;AAAA,EAEA,MAAM,OAAsB;AAC3B,QAAI,CAAC,KAAK,QAAS;AAEnB,QAAI,KAAK,iBAAkB,eAAc,KAAK,gBAAgB;AAC9D,SAAK,mBAAmB;AACxB,QAAI,KAAK,eAAgB,eAAc,KAAK,cAAc;AAC1D,SAAK,iBAAiB;AACtB,eAAW,KAAK,KAAK,iBAAkB,eAAc,CAAC;AACtD,SAAK,iBAAiB,MAAM;AAC5B,QAAI,KAAK,YAAa,cAAa,KAAK,WAAW;AACnD,SAAK,cAAc;AACnB,SAAK,OAAO,KAAK;AACjB,SAAK,QAAQ;AACb,SAAK,QAAQ,KAAK;AAClB,SAAK,MAAM,QAAQ;AACnB,SAAK,eAAe,QAAQ;AAC5B,SAAK,WAAW,KAAK;AACrB,UAAM,KAAK,WAAW,KAAK;AAE3B,SAAK,SAAS;AACd,SAAK,OAAO;AACZ,SAAK,gBAAgB;AACrB,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,SAAK,UAAU;AACd,IAAC,WAAmB,cAAc;AAEnC,IAAAA,QAAO,KAAK,iBAAiB;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,eAAkC;AACjC,WAAO,KAAK,WAAW,aAAa,KAAK,CAAC;AAAA,EAC3C;AAAA;AAAA;AAAA,EAKA,MAAM,SACL,SACA,UACA,MACgE;AAChE,QAAI,CAAC,KAAK,MAAO,QAAO,EAAE,IAAI,OAAO,OAAO,sBAAsB;AAClE,WAAO,KAAK,MAAM,SAAS,SAAS,UAAU,IAAI;AAAA,EACnD;AAAA,EAEA,MAAM,WAAW,YAA8D;AAC9E,QAAI,CAAC,KAAK,MAAO,QAAO,EAAE,IAAI,OAAO,OAAO,sBAAsB;AAClE,WAAO,KAAK,MAAM,WAAW,UAAU;AAAA,EACxC;AAAA,EAEA,MAAM,WAAW,YAAoB,QAA2C;AAC/E,QAAI,CAAC,KAAK,MAAO,QAAO,EAAE,IAAI,MAAM;AACpC,WAAO,KAAK,MAAM,WAAW,YAAY,MAAM;AAAA,EAChD;AAAA,EAEA,MAAM,mBAAmB,YAA8C;AACtE,QAAI,CAAC,KAAK,MAAO,QAAO,EAAE,IAAI,MAAM;AACpC,WAAO,KAAK,MAAM,eAAe,UAAU;AAAA,EAC5C;AAAA,EAEA,oBAAoC;AACnC,WAAO,KAAK,OAAO,cAAc,KAAK,CAAC;AAAA,EACxC;AAAA,EAEA,gBAAgB,YAA8C;AAC7D,WAAO,KAAK,OAAO,YAAY,UAAU;AAAA,EAC1C;AAAA;AAAA,EAGQ,cAAc,QAAwB;AAC7C,WAAO,WAAW,MAAM;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,WAAW,QAAgB,OAAiC;AACzE,UAAM,OAAO,KAAK,cAAc,IAAI,gBAAgB;AACpD,QAAI,MAAM;AACT,UAAI;AACH,cAAM,KAAK,QAAQ;AAAA,UAClB,WAAW,KAAK,cAAc,MAAM;AAAA,UACpC,MAAM,MAAM;AAAA,UACZ,SAAS,MAAM;AAAA,UACf,aAAa,KAAK,OAAO,eAAe,eAAe;AAAA,QACxD,CAAC;AACD;AAAA,MACD,SAAS,KAAK;AACb,QAAAA,QAAO;AAAA,UACN,wCAAwC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QACzF;AAAA,MACD;AAAA,IACD;AACA,UAAM,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,CAAC;AACzC,QAAI,KAAK,KAAK;AACd,QAAI,IAAI,SAAS,IAAK,KAAI,OAAO,GAAG,IAAI,SAAS,GAAG;AACpD,SAAK,QAAQ,IAAI,QAAQ,GAAG;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAe,QAAsC;AAG1D,UAAM,OAAO,KAAK,cAAc,IAAI,gBAAgB,IACjD,KAAK,cAAc,IAAI,iBAAiB,IACxC;AACH,QAAI,MAAM;AACT,UAAI;AACH,cAAM,MAAO,MAAM,KAAK,QAAQ;AAAA,UAC/B,WAAW,KAAK,cAAc,MAAM;AAAA,UACpC,aAAa;AAAA,QACd,CAAC;AACD,YAAI,KAAK,SAAS;AACjB,gBAAM,SAAS,KAAK,gBAAgB;AACpC,gBAAM,WACL,KAAK,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,GAAG,QAAQ,OAAO,UAAU,GAAG,CAAC;AAChF,iBAAO,IAAI,QAAQ,IAAI,CAAC,OAAO;AAAA,YAC9B,KAAK,EAAE,SAAS,QAAS,QAAmB;AAAA,YAC5C,MAAM,EAAE;AAAA,YACR,UAAU,EAAE,SAAS,QAAQ,SAAS;AAAA,YACtC,WAAW,EAAE,KAAK,KAAK,MAAM,EAAE,EAAE,KAAK,IAAI;AAAA,YAC1C,WAAW;AAAA,UACZ,EAAE;AAAA,QACH;AAAA,MACD,SAAS,KAAK;AACb,QAAAA,QAAO;AAAA,UACN,yCAAyC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QAC1F;AAAA,MACD;AAAA,IACD;AACA,WAAO,KAAK,QAAQ,IAAI,MAAM,KAAK,CAAC;AAAA,EACrC;AAAA;AAAA,EAGA,MAAM,UAAU,QAA+B;AAC9C,UAAM,OAAO,KAAK,cAAc,IAAI,gBAAgB,IACjD,KAAK,cAAc,IAAI,eAAe,IACtC;AACH,QAAI,MAAM;AACT,UAAI;AACH,cAAM,KAAK,QAAQ,EAAE,WAAW,KAAK,cAAc,MAAM,EAAE,CAAC;AAC5D;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AACA,SAAK,QAAQ,OAAO,MAAM;AAAA,EAC3B;AAAA;AAAA,EAGA,MAAc,oBAAmC;AAChD,UAAM,aAAa,KAAK,OAAO,eAAe,cAAc;AAC5D,QAAI,CAAC,cAAc,cAAc,EAAG;AACpC,UAAM,WAAW,KAAK,cAAc,IAAI,cAAc;AACtD,UAAM,YAAY,KAAK,cAAc,IAAI,eAAe;AACxD,QAAI,CAAC,YAAY,CAAC,UAAW;AAC7B,QAAI;AACH,YAAM,MAAO,MAAM,SAAS,QAAQ,CAAC,CAAC;AAGtC,YAAM,SAAS,KAAK,IAAI,IAAI,aAAa;AACzC,iBAAW,KAAK,IAAI,YAAY,CAAC,GAAG;AACnC,YAAI,CAAC,EAAE,UAAU,WAAW,UAAU,EAAG;AACzC,cAAM,OAAO,EAAE,aAAa,KAAK,MAAM,EAAE,UAAU,IAAI;AACvD,YAAI,QAAQ,OAAO,QAAQ;AAC1B,gBAAM,UAAU,QAAQ,EAAE,WAAW,EAAE,UAAU,CAAC;AAClD,UAAAA,QAAO,KAAK,qCAAqC,UAAU,OAAO,EAAE,SAAS,EAAE;AAAA,QAChF;AAAA,MACD;AAAA,IACD,SAAS,KAAK;AACb,MAAAA,QAAO;AAAA,QACN,gCAAgC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACjF;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,SACL,QACA,MACmF;AACnF,QAAI,CAAC,KAAK,WAAW,CAAC,KAAK,UAAW,QAAO,EAAE,IAAI,OAAO,OAAO,sBAAsB;AACvF,UAAM,SAAS,KAAK,aAAa,EAAE;AAAA,MAClC,CAAC,MAAM,EAAE,OAAO,UAAU,EAAE,SAAS,UAAU,EAAE,GAAG,WAAW,MAAM;AAAA,IACtE;AAEA,QAAI,CAAC,OAAQ,QAAO,KAAK,iBAAiB,QAAQ,IAAI;AACtD,UAAM,WAAW,KAAK,gBAAgB;AACtC,UAAM,MAAM;AAAA,MACX;AAAA,MACA,KAAK,QAAQ;AAAA,MACb,OAAO;AAAA,MACP,EAAE,MAAM,SAAS;AAAA,MACjB,KAAK,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,IACd;AACA,UAAM,YAAY,MAAM,KAAK,UAAU,WAAW,OAAO,IAAI,GAAG;AAChE,UAAM,KAAK,WAAW,OAAO,IAAI;AAAA,MAChC,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,IAChB,CAAC;AACD,WAAO,EAAE,IAAI,MAAM,UAAU;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,qBACb,SACA,WACA,cACA,KAQE;AACF,QAAI,CAAC,KAAK,WAAW,CAAC,KAAK,UAAW,QAAO,EAAE,IAAI,OAAO,OAAO,sBAAsB;AACvF,eAAW,CAAC,OAAO,MAAM,KAAK,KAAK,YAAY;AAC9C,YAAM,SAAS,CAAC,GAAG,OAAO,OAAO,CAAC,EAAE;AAAA,QACnC,CAAC,MAAM,EAAE,eAAe,WAAW,EAAE,SAAS,WAAW,EAAE,WAAW,WAAW,OAAO;AAAA,MACzF;AACA,UAAI,CAAC,OAAQ;AACb,UAAI,CAAC,OAAO;AACX,eAAO,EAAE,IAAI,OAAO,OAAO,SAAS,OAAO,qCAAqC;AACjF,YAAM,QAAQ;AAAA,QACb;AAAA,QACA,KAAK,QAAQ;AAAA,QACb,OAAO;AAAA,QACP;AAAA,QACA,KAAK,QAAQ;AAAA,QACb,KAAK,QAAQ;AAAA,MACd;AACA,YAAM,MAAM;AAAA,QACX,OAAO;AAAA,QACP,OAAO,KAAK,KAAK,UAAU,KAAK,GAAG,MAAM;AAAA,QACzC,GAAG,KAAK,QAAQ,UAAU,IAAI,OAAO,UAAU;AAAA,QAC/C,OAAO;AAAA,MACR;AACA,UAAI,CAAC,IAAK,QAAO,EAAE,IAAI,OAAO,OAAO,0BAA0B;AAC/D,YAAM,QAAQ;AAAA,QACb;AAAA,QACA,KAAK,QAAQ;AAAA,QACb;AAAA,QACA,EAAE,IAAI,OAAO,YAAY,KAAK,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC,EAAG;AAAA,QACtD,KAAK,QAAQ;AAAA,QACb,KAAK,QAAQ;AAAA,MACd;AACA,YAAM,YAAY,MAAM,KAAK,UAAU,WAAW,OAAO,KAAK;AAC9D,aAAO,EAAE,IAAI,MAAM,WAAW,QAAQ,OAAO,MAAM;AAAA,IACpD;AACA,WAAO,EAAE,IAAI,OAAO,OAAO,uBAAuB,OAAO,IAAI;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,iBACb,SACA,MAOE;AACF,UAAM,WAAW,KAAK,gBAAgB;AACtC,UAAM,SAAS,KAAK,IAAI;AACxB,UAAM,MAAMG,YAAW;AACvB,UAAM,SAAS,KAAK,iBAAiB,OAAO;AAC5C,UAAM,UAAU,KAAK,kBAAkB,KAAK,QAAQ,cAAc,OAAO;AACzE,UAAM,IAAI,MAAM,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA,EAAE,MAAM,UAAU,OAAO;AAAA,MACzB;AAAA,IACD;AACA,QAAI,CAAC,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,OAAO;AACnC,WAAK,cAAc,IAAI,GAAG,GAAG,OAAO,EAAE,MAAM,SAAS,QAAQ,EAAE,SAAS,aAAa,CAAC;AACtF,aAAO,EAAE,IAAI,OAAO,OAAO,EAAE,SAAS,uBAAuB,OAAO,IAAI;AAAA,IACzE;AACA,UAAM,KAAK,WAAW,EAAE,OAAO,YAAY;AAAA,MAC1C,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX,WAAW,EAAE,MAAM;AAAA,MACnB,SAAS;AAAA,IACV,CAAC;AACD,UAAM,IAAI,MAAM;AAChB,YAAQ,EAAE,MAAM;AAAA,MACf,KAAK;AACJ,eAAO,EAAE,IAAI,MAAM,WAAW,MAAM,SAAS,KAAK;AAAA,MACnD,KAAK,QAAQ;AACZ,cAAM,QAAQ,MAAM,KAAK,YAAY,IAAI;AAAA,UACxC,IAAI,EAAE,OAAO;AAAA,UACb,QAAQ,EAAE,OAAO;AAAA,UACjB;AAAA,UACA;AAAA,QACD,CAAC;AACD,YAAI,OAAO;AACV,UAAAH,QAAO,KAAK,GAAG,EAAE,OAAO,IAAI,8DAAyD;AACrF,eAAK,YAAY,KAAK,uBAAuB;AAAA,YAC5C,IAAI,MAAM;AAAA,YACV,QAAQ,MAAM;AAAA,YACd,KAAK,MAAM;AAAA,YACX;AAAA,YACA;AAAA,YACA,KAAK,EAAE,SAAS;AAAA,UACjB,CAAC;AAAA,QACF;AACA,eAAO,EAAE,IAAI,MAAM,WAAW,OAAO,QAAQ,MAAM,SAAS,KAAK;AAAA,MAClE;AAAA,MACA,KAAK;AACJ,eAAO,EAAE,IAAI,MAAM,WAAW,OAAO,SAAS,MAAM,OAAO,EAAE,OAAO;AAAA,MACrE;AACC,eAAO,EAAE,IAAI,MAAM,WAAW,EAAE,WAAW,SAAS,KAAK;AAAA,IAC3D;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,kBAAkB,KAAa,IAAmC;AACzE,WAAO,IAAI,QAAsB,CAACI,aAAY;AAC7C,YAAM,SAAS,CAAC,MAAoB;AACnC,qBAAa,KAAK;AAClB,aAAK,cAAc,OAAO,GAAG;AAC7B,QAAAA,SAAQ,CAAC;AAAA,MACV;AACA,YAAM,QAAQ,WAAW,MAAM,OAAO,EAAE,MAAM,UAAU,CAAC,GAAG,wBAAwB;AACpF,WAAK,cAAc,IAAI,KAAK,EAAE,IAAI,QAAQ,OAAO,CAAC;AAAA,IACnD,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,YAAY,GAAyE;AAC5F,UAAM,UACL,EAAE,QAAQ,EAAE,WAAW,qBACpB,EAAE,MAAM,OAAO,IACf,EAAE,MAAM,SAAS,QAAQ,EAAE,UAAU,UAAU;AACnD,QAAI,OAAO,EAAE,QAAQ,UAAU;AAC9B,WAAK,cAAc,IAAI,EAAE,GAAG,GAAG,OAAO,OAAO;AAC7C;AAAA,IACD;AACA,QAAI,CAAC,EAAE,GAAI;AACX,eAAW,KAAK,CAAC,GAAG,KAAK,cAAc,OAAO,CAAC,EAAG,KAAI,EAAE,OAAO,EAAE,GAAI,GAAE,OAAO,OAAO;AAAA,EACtF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,kBAAiC;AAC9C,QAAI,CAAC,KAAK,cAAc,KAAK,kBAAkB,KAAK,WAAW,SAAS,EAAG;AAC3E,SAAK,iBAAiB;AACtB,QAAI;AACH,iBAAW,KAAK,MAAM,KAAK,WAAW,MAAM,GAAG;AAC9C,QAAAJ,QAAO;AAAA,UACN,2BAA2B,EAAE,UAAU,EAAE,GAAG,UAAU,GAAG,CAAC,CAAC,YAAY,IAAI,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC;AAAA,QACxG;AAAA,MACD;AACA,YAAM,YAAY,oBAAI,IAAY;AAClC,iBAAW,SAAS,KAAK,WAAW,KAAK,GAAG;AAC3C,YAAI,UAAU,IAAI,MAAM,EAAE,EAAG;AAC7B,cAAM,SAAS,KAAK,iBAAiB,MAAM,EAAE;AAC7C,YAAI,CAAC,QAAQ,UAAW;AACxB,cAAM,MAAMG,YAAW;AACvB,cAAM,UAAU,KAAK,kBAAkB,KAAK,MAAM,EAAE;AAGpD,cAAM,OAAO,MAAM,QAAQ;AAC3B,cAAM,WAAW,KAAK,gBAAgB;AACtC,cAAM,IAAI,MAAM,KAAK;AAAA,UACpB,MAAM;AAAA,UACN,SAAS,SAAS,iBAAiB,SAAS,IAAI;AAAA,UAChD,SAAS,SACN,EAAE,MAAM,MAAM,MAAM,UAAU,QAAQ,MAAM,OAAO,IACnD,SAAS,oBACR,EAAE,UAAU,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC,EAAG,IACxD,EAAE,SAAS;AAAA,UACf;AAAA,QACD;AACA,YAAI,CAAC,EAAE,IAAI;AACV,eAAK,cAAc,IAAI,GAAG,GAAG,OAAO,EAAE,MAAM,SAAS,QAAQ,EAAE,SAAS,aAAa,CAAC;AACtF,gBAAM,KAAK,WAAW,YAAY,MAAM,KAAK,EAAE,KAAK;AACpD;AAAA,QACD;AACA,cAAM,IAAI,MAAM;AAChB,YAAI,EAAE,SAAS,SAAS,EAAE,SAAS,WAAW;AAC7C,gBAAM,KAAK,WAAW,OAAO,MAAM,GAAG;AACtC,UAAAH,QAAO;AAAA,YACN,gBAAgB,MAAM,UAAU,MAAM,GAAG,UAAU,GAAG,CAAC,CAAC,gCAAgC,IAAI,KAAK,MAAM,MAAM,EAAE,YAAY,CAAC;AAAA,UAC7H;AACA,eAAK,YAAY,KAAK,wBAAwB;AAAA,YAC7C,IAAI,MAAM;AAAA,YACV,QAAQ,MAAM;AAAA,YACd,KAAK,MAAM;AAAA,YACX,QAAQ,MAAM;AAAA,UACf,CAAC;AAAA,QACF,OAAO;AACN,gBAAM,KAAK,WAAW,YAAY,MAAM,KAAK,EAAE,SAAS,UAAU,EAAE,SAAS,MAAM;AACnF,cAAI,EAAE,SAAS,OAAQ,WAAU,IAAI,MAAM,EAAE;AAAA,QAC9C;AAAA,MACD;AAAA,IACD,UAAE;AACD,WAAK,iBAAiB;AAAA,IACvB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAgB,QAAgB,SAAyC;AAChF,QAAI,CAAC,KAAK,OAAO,WAAW,CAAC,KAAK,SAAS,WAAY,QAAO;AAC9D,QAAI,uBAAuB,IAAI,QAAQ,IAAI,EAAG,QAAO;AACrD,UAAM,OAAO,KAAK,WAAW,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM;AAEvE,QAAI,CAAC,MAAM,cAAc,CAAC,KAAK,eAAgB,QAAO;AACtD,UAAM,MAAM,GAAG,KAAK,QAAQ,UAAU,IAAI,MAAM;AAChD,UAAM,MAAM;AAAA,MACX,KAAK;AAAA,MACL,OAAO,KAAK,KAAK,UAAU,OAAO,GAAG,MAAM;AAAA,MAC3C;AAAA,MACA,KAAK;AAAA,IACN;AACA,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO;AAAA,MACN;AAAA,MACA,KAAK,QAAQ;AAAA,MACb;AAAA,MACA,EAAE,IAAI;AAAA,MACN,KAAK,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,IACd;AAAA,EACD;AAAA;AAAA,EAGQ,iBAAiB,KAAuC;AAC/D,eAAW,UAAU,KAAK,WAAW,OAAO,GAAG;AAC9C,YAAM,IAAI,CAAC,GAAG,OAAO,OAAO,CAAC,EAAE;AAAA,QAC9B,CAAC,MAAM,EAAE,eAAe,OAAO,EAAE,SAAS,OAAO,EAAE,WAAW,WAAW,GAAG;AAAA,MAC7E;AACA,UAAI,EAAG,QAAO;AAAA,IACf;AACA,WAAO;AAAA,EACR;AAAA;AAAA,EAGA,MAAc,mBAAmB,QAAqC;AACrE,QAAI;AACH,YAAM,eAAe,KAAK,UAAU,GAAG,OAAO,SAAS;AAAA,IACxD,SAAS,GAAG;AACX,MAAAA,QAAO;AAAA,QACN,uCAAuC,OAAO,WAAW,UAAU,GAAG,CAAC,CAAC,KAAK,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,MACxH;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,oBACL,SACA,MAC6D;AAC7D,UAAM,WAAW,KAAK,gBAAgB;AACtC,UAAM,IAAI,MAAM,KAAK,oBAAoB,SAAS,mBAAmB,EAAE,UAAU,KAAK,GAAG,IAAI;AAC7F,QAAI,CAAC,EAAE,MAAM,CAAC,EAAE;AACf,aAAO,EAAE,IAAI,OAAO,OAAO,EAAE,SAAS,uBAAuB,OAAO,IAAI;AACzE,SAAK,eAAe,IAAI,EAAE,OAAO,UAAU;AAC3C,SAAK,cAAc,IAAI,EAAE,OAAO,UAAU;AAC1C,UAAM,KAAK,mBAAmB,EAAE,MAAM;AACtC,WAAO,EAAE,IAAI,MAAM,GAAI,EAAE,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC,EAAG;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,oBACb,SACA,MACA,SACA,MACoF;AACpF,UAAM,SAAS,KAAK,iBAAiB,OAAO;AAC5C,UAAM,MAAMG,YAAW;AACvB,UAAM,UAAU,KAAK,kBAAkB,KAAK,QAAQ,cAAc,OAAO;AACzE,UAAM,IAAI,MAAM,KAAK,qBAAqB,SAAS,SAAS,IAAI,IAAI,SAAS,GAAG;AAChF,QAAI,CAAC,EAAE,MAAM,CAAC,EAAE,QAAQ;AACvB,WAAK,cAAc,IAAI,GAAG,GAAG,OAAO,EAAE,MAAM,SAAS,QAAQ,EAAE,SAAS,aAAa,CAAC;AACtF,aAAO,EAAE,IAAI,OAAO,OAAO,EAAE,MAAM;AAAA,IACpC;AACA,UAAM,IAAI,MAAM;AAChB,QAAI,EAAE,SAAS,QAAQ;AACtB,YAAM,KAAK,YAAY,IAAI;AAAA,QAC1B;AAAA,QACA,IAAI,EAAE,OAAO;AAAA,QACb,QAAQ,EAAE,OAAO;AAAA,QACjB,MAAM;AAAA,QACN;AAAA,QACA,QAAQ,KAAK,IAAI;AAAA,MAClB,CAAC;AACD,MAAAH,QAAO,KAAK,GAAG,EAAE,OAAO,IAAI,+BAA0B,IAAI,2BAA2B;AACrF,aAAO,EAAE,IAAI,MAAM,QAAQ,EAAE,QAAQ,QAAQ,KAAK;AAAA,IACnD;AACA,QAAI,EAAE,SAAS,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,EAAE,QAAQ,OAAO,EAAE,OAAO;AAC9E,WAAO,EAAE,IAAI,MAAM,QAAQ,EAAE,OAAO;AAAA,EACrC;AAAA;AAAA,EAGA,MAAM,oBACL,SAC6D;AAC7D,UAAM,SAAS,KAAK,iBAAiB,OAAO;AAC5C,QAAI,CAAC,OAAQ,QAAO,EAAE,IAAI,OAAO,OAAO,qBAAqB,OAAO,IAAI;AACxE,SAAK,eAAe,IAAI,OAAO,UAAU;AACzC,SAAK,eAAe,IAAI,OAAO,UAAU;AACzC,SAAK,cAAc,OAAO,OAAO,UAAU;AAC3C,UAAM,KAAK,mBAAmB,MAAM;AACpC,UAAM,WAAW,KAAK,gBAAgB;AACtC,UAAM,IAAI,MAAM,KAAK,oBAAoB,OAAO,YAAY,kBAAkB,EAAE,SAAS,CAAC;AAC1F,WAAO,EAAE,IAAI,MAAM,GAAI,EAAE,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC,EAAG;AAAA,EAC1D;AAAA;AAAA,EAGA,MAAM,iBAAiB,SAA2D;AACjF,UAAM,SAAS,KAAK,iBAAiB,OAAO;AAC5C,SAAK,cAAc,OAAO,QAAQ,cAAc,OAAO;AACvD,QAAI,QAAQ;AACX,YAAM,WAAW,KAAK,gBAAgB;AACtC,YAAM,KAAK,oBAAoB,OAAO,YAAY,gBAAgB,EAAE,SAAS,CAAC;AAAA,IAC/E;AACA,WAAO,EAAE,IAAI,KAAK;AAAA,EACnB;AAAA;AAAA,EAGA,MAAM,mBAAmB,SAA2C;AACnE,UAAM,KAAK,KAAK,iBAAiB,OAAO,GAAG,cAAc;AACzD,SAAK,eAAe,OAAO,EAAE;AAC7B,SAAK,eAAe,OAAO,EAAE;AAC7B,SAAK,cAAc,OAAO,EAAE;AAC5B,SAAK,cAAc,OAAO,EAAE;AAC5B,UAAM,kBAAkB,KAAK,UAAU,GAAG,EAAE,EAAE,MAAM,MAAM,KAAK;AAC/D,WAAO,EAAE,IAAI,KAAK;AAAA,EACnB;AAAA;AAAA,EAGA,mBAOG;AACF,WAAO,CAAC,GAAG,KAAK,cAAc,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE;AAAA,EACnE;AAAA;AAAA,EAGA,gBAA+B;AAC9B,WAAO,KAAK,YAAY,KAAK,KAAK,CAAC;AAAA,EACpC;AAAA;AAAA,EAGA,iBAA0D;AACzD,WAAO,CAAC,GAAG,KAAK,YAAY,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI;AAAA,EACrE;AAAA;AAAA,EAGQ,cAAc,QAAgB,KAAgB,KAAiB,QAAuB;AAC7F,QAAI,CAAC,KAAK,SAAS,YAAa;AAChC,UAAM,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,MACb;AAAA,MACA,GAAG,MAAM,IAAI,KAAK,QAAQ,UAAU;AAAA,MACpC,KAAK,QAAQ;AAAA,IACd;AACA,QAAI,CAAC,OAAO;AACX,MAAAA,QAAO,KAAK,wBAAwB,OAAO,UAAU,GAAG,CAAC,CAAC,mBAAmB;AAC7E;AAAA,IACD;AACA,QAAI;AACJ,QAAI;AACH,cAAQ,KAAK,MAAM,MAAM,SAAS,MAAM,CAAC;AAAA,IAC1C,QAAQ;AACP;AAAA,IACD;AAGA,UAAM,YACL,MAAM,SAAS,kBACf,MAAM,SAAS,2BACf,MAAM,SAAS,0BACf,MAAM,SAAS,wBACf,MAAM,KAAK,WAAW,OAAO;AAC9B,QAAI,CAAC,WAAW;AACf,MAAAA,QAAO;AAAA,QACN,oBAAoB,MAAM,IAAI,UAAU,OAAO,UAAU,GAAG,CAAC,CAAC;AAAA,MAC/D;AACA;AAAA,IACD;AACA,QAAI,MAAM,SAAS,UAAU,MAAM,OAAO,KAAK,QAAQ,WAAY;AAEnE,UAAM,UAAU,GAAG,MAAM,IAAI,IAAI,MAAM,EAAE;AACzC,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,CAAC,GAAG,EAAE,KAAK,KAAK,WAAY,KAAI,MAAM,KAAK,KAAS,MAAK,WAAW,OAAO,CAAC;AACvF,QAAI,KAAK,WAAW,IAAI,OAAO,EAAG;AAClC,SAAK,WAAW,IAAI,SAAS,GAAG;AAEhC,UAAM,kBAAkB,KAAK,WAAW,kBAAkB,KAAK,KAAK;AACpE,QAAI,QAAQ;AACZ,QAAI,CAAC,OAAO;AACX,YAAM,SAAS,KAAK,iBAAiB,MAAM;AAC3C,YAAM,SAAS,SAAS,eAAe,OAAO,SAAS,IAAI;AAC3D,UAAI,UAAU,OAAO,eAAe;AACnC,gBAAQ,cAAc,OAAO,OAAO,WAAW,OAAO,WAAW,EAAE;AAAA,IACrE;AACA,QAAI,CAAC,OAAO;AACX,MAAAA,QAAO;AAAA,QACN,YAAY,MAAM,IAAI,UAAU,OAAO,UAAU,GAAG,CAAC,CAAC;AAAA,MACvD;AACA;AAAA,IACD;AAIA,QAAI,MAAM,KAAK,WAAW,OAAO,GAAG;AACnC,WAAK,OAAO,cAAc,OAAO,EAAE,SAAS,MAAM,OAAO,CAAC;AAC1D;AAAA,IACD;AAKA,QAAI,MAAM,SAAS,yBAAyB;AAC3C,YAAM,IAAI,MAAM;AAChB,WAAK,mBAAmB,QAAQ,GAAG,UAAU,GAAG,IAAI;AACpD,WAAK,KAAK,yBAAyB;AAAA,QAClC,MAAM;AAAA,QACN,UAAU,GAAG,YAAY,KAAK,WAAW,MAAM;AAAA,QAC/C,MAAM,GAAG;AAAA,MACV,CAAC;AACD;AAAA,IACD;AACA,QAAI,MAAM,SAAS,wBAAwB;AAC1C,WAAK,cAAc,OAAO,MAAM;AAChC,WAAK,eAAe,IAAI,MAAM;AAC9B,WAAK,KAAK,0BAA0B,EAAE,MAAM,QAAQ,UAAU,KAAK,WAAW,MAAM,EAAE,CAAC;AACvF;AAAA,IACD;AACA,QAAI,MAAM,SAAS,sBAAsB;AACxC,WAAK,cAAc,OAAO,MAAM;AAChC,WAAK,KAAK,wBAAwB,EAAE,MAAM,QAAQ,UAAU,KAAK,WAAW,MAAM,EAAE,CAAC;AACrF;AAAA,IACD;AAGA,UAAM,UAAU,MAAM;AACtB,QAAI,CAAC,SAAS,KAAM;AACpB,UAAM,WAAW,QAAQ,YAAY,OAAO,UAAU,GAAG,CAAC;AAI1D,UAAM,SACL,OAAO,QAAQ,WAAW,YAC1B,QAAQ,SAAS,KACjB,QAAQ,UAAU,MAAM,YAAY,MACjC,QAAQ,SACR,MAAM;AACV,QAAI,CAAC,mBAAmB,CAAC,KAAK,gBAAgB,QAAQ,QAAQ,GAAG;AAChE,WAAK,mBAAmB,QAAQ,QAAQ;AACxC,WAAK,KAAK,yBAAyB,EAAE,MAAM,QAAQ,SAAS,CAAC;AAC7D,MAAAA,QAAO;AAAA,QACN,qCAAqC,QAAQ,KAAK,OAAO,UAAU,GAAG,CAAC,CAAC;AAAA,MACzE;AACA;AAAA,IACD;AACA,SAAK,YAAY,OAAO,MAAM;AAC9B,SAAK,aAAa,IAAI,QAAQ,KAAK,IAAI,MAAM,WAAW,KAAK,aAAa,IAAI,MAAM,KAAK,CAAC,CAAC;AAC3F,SAAK,KAAK,WAAW,QAAQ;AAAA,MAC5B,KAAK;AAAA,MACL,MAAM,QAAQ;AAAA,MACd;AAAA,MACA,WAAW;AAAA,MACX,WAAW,MAAM;AAAA,MACjB,SAAS;AAAA,IACV,CAAC;AACD,SAAK,KAAK,gBAAgB;AAAA,MACzB,MAAM;AAAA,MACN;AAAA,MACA,MAAM,QAAQ;AAAA,MACd,WAAW,MAAM;AAAA,MACjB,WAAW;AAAA,MACX,SAAS;AAAA,IACV,CAAC;AAAA,EACF;AAAA;AAAA,EAGQ,gBAAgB,QAAgB,UAA4B;AACnE,UAAM,SAAS,KAAK,OAAO,OAAO;AAClC,QAAI,WAAW,IAAK,QAAO;AAC3B,QAAI,MAAM,QAAQ,MAAM,GAAG;AAC1B,iBAAW,OAAO,QAAQ;AACzB,cAAM,IAAI,OAAO,QAAQ,WAAW,IAAI,KAAK,IAAI;AACjD,YAAI,CAAC,EAAG;AACR,YAAI,MAAM,IAAK,QAAO;AACtB,YAAI,MAAM,UAAU,OAAO,WAAW,CAAC,EAAG,QAAO;AACjD,YAAI,YAAY,MAAM,SAAU,QAAO;AAAA,MACxC;AAAA,IACD;AACA,WAAO,KAAK,eAAe,IAAI,MAAM;AAAA,EACtC;AAAA;AAAA,EAGQ,mBAAmB,QAAgB,UAAmB,MAAqB;AAClF,QAAI,SAAS;AACb,QAAI,aAAa;AACjB,eAAW,CAAC,OAAO,MAAM,KAAK,KAAK,YAAY;AAC9C,UAAI,OAAO,IAAI,MAAM,GAAG;AACvB,iBAAS;AACT,qBAAa,KAAK,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,GAAG,QAAQ,MAAM,UAAU,GAAG,CAAC;AAC1F;AAAA,MACD;AAAA,IACD;AACA,SAAK,cAAc,IAAI,QAAQ;AAAA,MAC9B,IAAI;AAAA,MACJ,MAAM,YAAY,KAAK,WAAW,MAAM;AAAA,MACxC;AAAA,MACA;AAAA,MACA;AAAA,MACA,IAAI,KAAK,IAAI;AAAA,IACd,CAAC;AAAA,EACF;AAAA,EAEQ,WAAW,IAAoB;AACtC,WAAO,KAAK,iBAAiB,EAAE,GAAG,QAAQ,GAAG,UAAU,GAAG,CAAC;AAAA,EAC5D;AAAA,EAEQ,iBAAiB,IAAsC;AAC9D,eAAW,UAAU,KAAK,WAAW,OAAO,GAAG;AAC9C,YAAM,IAAI,OAAO,IAAI,EAAE;AACvB,UAAI,EAAG,QAAO;AAAA,IACf;AACA,WAAO;AAAA,EACR;AAAA;AAAA,EAGQ,kBAAwB;AAC/B,QAAI,CAAC,KAAK,WAAW,CAAC,KAAK,aAAa,CAAC,KAAK,UAAW;AACzD,UAAM,YAAY,IAAI;AAAA,MACrB,KAAK,UACH,SAAS,EACT,OAAO,CAAC,MAAM,EAAE,SAAS,EACzB,IAAI,CAAC,MAAM,EAAE,MAAM;AAAA,IACtB;AACA,UAAM,UAA0B,KAAK,UAAU,aAAa,EAAE,IAAI,CAAC,OAAO;AAAA,MACzE,YAAY,EAAE;AAAA,MACd,MAAM,EAAE;AAAA,MACR,WAAW,EAAE;AAAA,MACb,YAAY,EAAE;AAAA,MACd,gBAAgB,EAAE;AAAA,MAClB,WAAW,UAAU,IAAI,EAAE,EAAE;AAAA,IAC9B,EAAE;AACF,eAAW,MAAM,WAAW;AAC3B,YAAM,MAAM;AAAA,QACX;AAAA,QACA,KAAK,QAAQ;AAAA,QACb;AAAA,QACA,EAAE,QAAQ;AAAA,QACV,KAAK,QAAQ;AAAA,QACb,KAAK,QAAQ;AAAA,MACd;AACA,WAAK,KAAK,UAAU,WAAW,IAAI,GAAG;AAAA,IACvC;AAAA,EACD;AAAA;AAAA,EAGA,MAAc,iBAAiB,UAAiC;AAC/D,QAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,WAAW,CAAC,KAAK,UAAW;AAC5D,UAAM,UAAU,MAAM,KAAK,aAAa,KAAK,QAAQ;AACrD,QAAI,QAAQ,WAAW,EAAG;AAC1B,UAAM,MAAM;AAAA,MACX;AAAA,MACA,KAAK,QAAQ;AAAA,MACb;AAAA,MACA,EAAE,QAAQ;AAAA,MACV,KAAK,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,IACd;AACA,UAAM,OAAO,MAAM,KAAK,UAAU,WAAW,UAAU,GAAG;AAE1D,QAAI,CAAC,KAAM,OAAM,KAAK,aAAa,QAAQ,UAAU,OAAO;AAAA,EAC7D;AAAA;AAAA,EAGA,aAAqD;AACpD,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAYG;AACF,UAAM,SAAS,IAAI,IAAI,KAAK,aAAa,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAC3D,UAAM,MASD,CAAC;AACN,UAAM,OAAO,oBAAI,IAAY;AAC7B,eAAW,CAAC,OAAO,MAAM,KAAK,KAAK,YAAY;AAC9C,YAAM,UAAU,KAAK,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,GAAG,QAAQ,MAAM,UAAU,GAAG,CAAC;AAC7F,iBAAW,KAAK,OAAO,OAAO,GAAG;AAChC,YAAI,EAAE,eAAe,KAAK,SAAS,WAAY;AAC/C,YAAI,OAAO,IAAI,EAAE,UAAU,EAAG;AAC9B,YAAI,KAAK,IAAI,EAAE,UAAU,EAAG;AAC5B,aAAK,IAAI,EAAE,UAAU;AACrB,YAAI,KAAK;AAAA,UACR,IAAI,EAAE;AAAA,UACN,MAAM,EAAE;AAAA,UACR,QAAQ;AAAA,UACR,YAAY;AAAA,UACZ,WAAW,EAAE;AAAA,UACb,UAAU,KAAK,gBAAgB,EAAE,YAAY,EAAE,IAAI;AAAA,UACnD,UAAU,KAAK,cAAc,IAAI,EAAE,UAAU;AAAA,UAC7C,UAAU,KAAK,cAAc,IAAI,EAAE,UAAU,KAAK,CAAC,KAAK,eAAe,IAAI,EAAE,UAAU;AAAA,QACxF,CAAC;AAAA,MACF;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAmC;AAClC,WAAO,KAAK,WAAW,eAAe,KAAK,CAAC;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,UAA4E;AACzF,WAAO,KAAK,WAAW,cAAc,QAAQ,KAAK;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,uBAAiD;AAChD,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,UAA8B;AAC7B,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,YAAkC;AACjC,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,WAAoB;AACnB,WAAO,KAAK,QAAQ,SAAS,KAAK;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBAA8B;AAC7B,QAAI,CAAC,KAAK,QAAS,QAAO;AAC1B,QAAI,KAAK,OAAO,kBAAkB,cAAe,QAAO;AACxD,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAA8C;AAC7C,QAAI,KAAK,OAAO,cAAc,cAAe,QAAO;AACpD,UAAM,YAAY,KAAK,WAAW,aAAa,KAAK,CAAC;AACrD,QAAI,UAAU,WAAW,EAAG,QAAO;AAEnC,UAAM,SAAS,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI;AAG5D,WAAO,OAAO,CAAC,EAAE,OAAO,MAAM,OAAO,CAAC,IAAI;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,kBAAkB,kBAAkD;AACnE,QAAI,KAAK,OAAO,iBAAiB,UAAW,QAAO;AACnD,UAAM,YAAY,KAAK,OAAO,kBAAkB;AAChD,QAAI,mBAAmB,UAAW,QAAO;AAEzC,UAAM,YAAY,KAAK,WAAW,aAAa,KAAK,CAAC;AACrD,QAAI,UAAU,WAAW,EAAG,QAAO;AACnC,UAAM,SAAS,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI;AAC5D,WAAO,OAAO,CAAC;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAqC;AACpC,UAAM,cAAc,KAAK,OAAO;AAChC,QAAI,CAAC,eAAe,gBAAgB,QAAS,QAAO;AAEpD,UAAM,YAAY,KAAK,WAAW,aAAa,KAAK,CAAC;AACrD,QAAI,UAAU,WAAW,EAAG,QAAO;AAEnC,QAAI,gBAAgB,UAAU;AAE7B,YAAM,SAAS,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM;AAC5C,YAAI,EAAE,SAAS,iBAAiB,EAAE,SAAS,cAAe,QAAO;AACjE,YAAI,EAAE,SAAS,iBAAiB,EAAE,SAAS,cAAe,QAAO;AACjE,eAAO;AAAA,MACR,CAAC;AACD,aAAO,OAAO,CAAC,GAAG,MAAM;AAAA,IACzB;AAGA,UAAM,SAAS,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW;AAC3D,WAAO,QAAQ,MAAM;AAAA,EACtB;AAAA;AAAA,EAGA,MAAM,iBAAiB,MAAe,IAAwD;AAC7F,UAAM,KAAK,KAAK,OAAO;AACvB,QAAI,CAAC,IAAI,QAAS,QAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,uBAAuB,EAAE;AAEhF,UAAM,EAAE,WAAW,KAAK,IAAK,QAAQ,CAAC;AACtC,QAAI,CAAC,aAAa,CAAC,eAAe,SAAS,GAAG;AAC7C,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,qBAAqB,EAAE;AAAA,IAC7D;AAGA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,MAAM,GAAG,iBAAiB;AAEhC,QAAI,KAAK,eAAe,OAAO,MAAM;AACpC,iBAAW,CAAC,GAAG,EAAE,KAAK,KAAK,gBAAgB;AAC1C,YAAI,GAAG,WAAW,KAAK,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,IAAQ,MAAK,eAAe,OAAO,CAAC;AAAA,MACtF;AAAA,IACD;AACA,UAAM,UAAU,KAAK,eAAe,IAAI,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,MAAM,IAAI,GAAM;AACjF,QAAI,OAAO,UAAU,IAAK,QAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,eAAe,EAAE;AAChF,WAAO,KAAK,GAAG;AACf,SAAK,eAAe,IAAI,IAAI,MAAM;AAElC,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAS,UAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AAC1C,UAAM,YAAY,QAAQ,SAAS,MAAM,GAAG,EAAE;AAG9C,QAAI,GAAG,iBAAiB;AACvB,YAAS;AAAA,QACH,WAAK,QAAQ,qBAAqB;AAAA,QACvC,GAAG,KAAK,UAAU,EAAE,WAAW,MAAM,UAAU,IAAI,KAAI,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC,CAAC;AAAA;AAAA,QAClF;AAAA,MACD;AACA,aAAO;AAAA,QACN,QAAQ;AAAA,QACR,MAAM,EAAE,IAAI,MAAM,SAAS,MAAM,SAAS,iDAA4C;AAAA,MACvF;AAAA,IACD;AAGA,UAAM,WAAW,MAAM,oBAAoB,MAAM;AACjD,QAAI,SAAS,SAAS,GAAG,YAAY,MAAM;AAC1C,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,eAAe,EAAE;AAAA,IACvD;AAEA,UAAM,UAAU,QAAQ,WAAW,EAAE,cAAc,MAAM,CAAC;AAC1D,UAAM,KAAK,WAAW,iBAAiB;AACvC,IAAAA,QAAO,KAAK,+BAA+B,QAAQ,UAAU,EAAE,EAAE;AACjE,WAAO;AAAA,MACN,QAAQ;AAAA,MACR,MAAM;AAAA,QACL,IAAI;AAAA,QACJ,cAAc,KAAK,SAAS;AAAA,QAC5B,cAAc,KAAK,OAAO,gBAAgB;AAAA,QAC1C,MAAM,KAAK,OAAO,QAAQ;AAAA,MAC3B;AAAA,IACD;AAAA,EACD;AAAA;AAAA,EAIQ,mBAA2B;AAClC,WAAY,WAAK,KAAK,UAAU,GAAG,oBAAoB;AAAA,EACxD;AAAA,EAEA,MAAc,mBAAkC;AAC/C,QAAI;AACH,YAAM,MAAM,KAAK,MAAM,MAAS,aAAS,KAAK,iBAAiB,GAAG,OAAO,CAAC;AAQ1E,iBAAW,KAAK,IAAK,KAAI,GAAG,MAAM,EAAE,UAAW,MAAK,aAAa,IAAI,EAAE,IAAI,CAAC;AAAA,IAC7E,QAAQ;AAAA,IAER;AAAA,EACD;AAAA,EAEA,MAAc,sBAAqC;AAClD,QAAI;AACH,YAAS;AAAA,QACR,KAAK,iBAAiB;AAAA,QACtB,KAAK,UAAU,CAAC,GAAG,KAAK,aAAa,OAAO,CAAC,GAAG,MAAM,CAAC;AAAA,MACxD;AAAA,IACD,SAAS,KAAK;AACb,MAAAA,QAAO,KAAK,oCAAoC,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AAAA,IAC3F;AAAA,EACD;AAAA;AAAA,EAGA,MAAM,kBACL,MACA,IACA,KAC6C;AAC7C,UAAM,EAAE,WAAW,MAAM,MAAM,SAAS,IAAK,QAAQ,CAAC;AAMtD,QAAI,CAAC,aAAa,CAAC,eAAe,SAAS,GAAG;AAC7C,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,qBAAqB,EAAE;AAAA,IAC7D;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,KAAK,eAAe,OAAO,MAAM;AACpC,iBAAW,CAAC,GAAG,EAAE,KAAK,KAAK,gBAAgB;AAC1C,YAAI,GAAG,WAAW,KAAK,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,IAAQ,MAAK,eAAe,OAAO,CAAC;AAAA,MACtF;AAAA,IACD;AACA,UAAM,UAAU,KAAK,eAAe,IAAI,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,MAAM,IAAI,GAAM;AACjF,QAAI,OAAO,UAAU,EAAG,QAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,eAAe,EAAE;AAC9E,WAAO,KAAK,GAAG;AACf,SAAK,eAAe,IAAI,IAAI,MAAM;AAElC,UAAM,SAAS,eAAe,SAAS;AACvC,UAAM,UAAU,MAAM,oBAAoB,KAAK,UAAU,CAAC;AAC1D,QAAI,QAAQ,IAAI,OAAO,UAAU,GAAG;AACnC,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,IAAI,MAAM,gBAAgB,KAAK,EAAE;AAAA,IAChE;AACA,QAAI,KAAK,aAAa,QAAQ,MAAM,CAAC,KAAK,aAAa,IAAI,OAAO,UAAU,GAAG;AAC9E,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,iCAAiC,EAAE;AAAA,IACzE;AACA,UAAM,YACJ,OAAO,SAAS,WAAW,OAAO,IAAI,MAAM,GAAG,EAAE,KAAK,OAAO,WAAW,UAAU,GAAG,CAAC;AACxF,SAAK,aAAa,IAAI,OAAO,YAAY;AAAA,MACxC,IAAI,OAAO;AAAA,MACX,MAAM;AAAA,MACN;AAAA,MACA,UAAU,OAAO,aAAa,WAAW,SAAS,MAAM,GAAG,GAAG,IAAI;AAAA,MAClE,MAAM,OAAO,SAAS,WAAW,KAAK,MAAM,GAAG,GAAG,IAAI;AAAA,MACtD,IAAI;AAAA,IACL,CAAC;AACD,UAAM,KAAK,oBAAoB;AAC/B,SAAK,KAAK,wBAAwB;AAAA,MACjC,MAAM,OAAO;AAAA,MACb,UAAU;AAAA,MACV,MAAM,OAAO,SAAS,WAAW,KAAK,MAAM,GAAG,GAAG,IAAI;AAAA,IACvD,CAAC;AACD,IAAAA,QAAO,KAAK,sBAAsB,QAAQ,MAAM,OAAO,WAAW,UAAU,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE;AAC9F,WAAO;AAAA,MACN,QAAQ;AAAA,MACR,MAAM;AAAA,QACL,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,SAAS;AAAA,MACV;AAAA,IACD;AAAA,EACD;AAAA,EAEA,mBAMG;AACF,WAAO,CAAC,GAAG,KAAK,aAAa,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,GAAG,KAAK,MAAM,IAAI;AAAA,EACjF;AAAA;AAAA,EAGA,MAAM,WAAW,KAAsE;AACtF,UAAM,MAAM,CAAC,GAAG,KAAK,aAAa,OAAO,CAAC,EAAE;AAAA,MAC3C,CAAC,MAAM,EAAE,OAAO,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,WAAW,GAAG;AAAA,IAC7D;AACA,QAAI,CAAC,IAAK,QAAO,EAAE,IAAI,OAAO,OAAO,qCAAqC,GAAG,IAAI;AACjF,UAAM,UAAU,KAAK,UAAU,GAAG,IAAI,SAAS;AAC/C,UAAM,KAAK,WAAW,iBAAiB;AACvC,SAAK,aAAa,OAAO,IAAI,EAAE;AAC/B,UAAM,KAAK,oBAAoB;AAM/B,QAAI,IAAI,UAAU;AACjB,YAAM,KAAK,aAAa,IAAI,QAAQ;AAAA,IACrC;AACA,IAAAA,QAAO;AAAA,MACN,mBAAmB,IAAI,IAAI,MAAM,IAAI,GAAG,UAAU,GAAG,CAAC,CAAC,mBAAmB,IAAI,WAAW,yBAAyB,IAAI,QAAQ,MAAM,wDAAmD;AAAA,IACxL;AACA,WAAO,EAAE,IAAI,MAAM,MAAM,IAAI,KAAK;AAAA,EACnC;AAAA,EAEA,MAAM,SAAS,KAAuC;AACrD,UAAM,MAAM,CAAC,GAAG,KAAK,aAAa,OAAO,CAAC,EAAE;AAAA,MAC3C,CAAC,MAAM,EAAE,OAAO,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,WAAW,GAAG;AAAA,IAC7D;AACA,QAAI,CAAC,IAAK,QAAO,EAAE,IAAI,MAAM;AAC7B,SAAK,aAAa,OAAO,IAAI,EAAE;AAC/B,UAAM,KAAK,oBAAoB;AAC/B,WAAO,EAAE,IAAI,KAAK;AAAA,EACnB;AAAA;AAAA,EAGA,MAAM,UAAU,KAOb;AACF,UAAM,OAAO,IAAI,QAAQ,OAAO,EAAE;AAClC,QAAI;AACJ,QAAI;AACH,YAAM,IAAI,MAAM,MAAM,GAAG,IAAI,iBAAiB,EAAE,QAAQ,YAAY,QAAQ,GAAI,EAAE,CAAC;AACnF,aAAQ,MAAM,EAAE,KAAK;AAAA,IACtB,SAAS,KAAK;AACb,aAAO;AAAA,QACN,IAAI;AAAA,QACJ,OAAO,mBAAmB,IAAI,KAAK,eAAe,QAAQ,IAAI,UAAU,GAAG;AAAA,MAC5E;AAAA,IACD;AACA,UAAM,SAAS,KAAK,YAAY,eAAe,KAAK,SAAS,IAAI;AACjE,QAAI,CAAC,OAAQ,QAAO,EAAE,IAAI,OAAO,OAAO,gDAAgD;AACxF,UAAM,UAAU,MAAM,oBAAoB,KAAK,UAAU,CAAC;AAC1D,WAAO;AAAA,MACN,IAAI;AAAA,MACJ,MAAM,KAAK;AAAA,MACX,aAAa,OAAO;AAAA,MACpB,WAAW,KAAK;AAAA,MAChB,gBAAgB,QAAQ,IAAI,OAAO,UAAU;AAAA,IAC9C;AAAA,EACD;AAAA;AAAA,EAGA,MAAM,QAAQ,KAA4B;AACzC,UAAM,KAAK,aAAa,IAAI,QAAQ,OAAO,EAAE,CAAC;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,WAAW,KAAsB;AAChC,UAAM,OAAO,IAAI,QAAQ,OAAO,EAAE;AAClC,UAAM,SAAS,KAAK,OAAO,OAAO,UAAU;AAC5C,SAAK,OAAO,SAAS,KAAK,OAAO,SAAS,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,QAAQ,OAAO,EAAE,MAAM,IAAI;AAC9F,UAAM,OAAO,KAAK,WAAW,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ,OAAO,EAAE,MAAM,IAAI;AAC9F,QAAI,KAAM,MAAK,WAAW,WAAW,KAAK,EAAE;AAC5C,YAAQ,KAAK,OAAO,MAAM,UAAU,KAAK,UAAU,CAAC,CAAC;AAAA,EACtD;AAAA;AAAA,EAGA,MAAc,aAAa,KAA4B;AACtD,UAAM,OAAO,IAAI,QAAQ,OAAO,EAAE;AAClC,SAAK,OAAO,QAAQ,KAAK,OAAO,SAAS,CAAC;AAC1C,QAAI,CAAC,KAAK,OAAO,MAAM,KAAK,CAAC,MAAM,EAAE,QAAQ,IAAI,GAAG;AACnD,WAAK,OAAO,MAAM,KAAK,EAAE,KAAK,MAAM,OAAO,OAAO,CAAC;AAAA,IACpD;AACA,QAAI;AAIH,YAAM,KAAK,OAAO,cAAc,IAAI;AAAA,IACrC,SAAS,KAAK;AACb,MAAAA,QAAO,KAAK,iCAAiC,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AAAA,IACxF;AACA,SAAK,KAAK,WAAW,cAAc,IAAI,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aACL,KACA,WACA,MACwF;AACxF,QAAI,CAAC,KAAK,QAAS,QAAO,EAAE,IAAI,OAAO,OAAO,sBAAsB;AACpE,UAAM,OAAO,IAAI,QAAQ,OAAO,EAAE;AAClC,QAAI,CAAC,eAAe,SAAS,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO,qBAAqB;AAChF,UAAM,UAAU,KAAK,UAAU,GAAG,SAAS;AAC3C,UAAM,KAAK,WAAW,iBAAiB;AACvC,UAAM,KAAK,aAAa,IAAI;AAC5B,QAAI;AACH,YAAM,IAAI,MAAM,MAAM,GAAG,IAAI,iBAAiB;AAAA,QAC7C,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU;AAAA,UACpB,WAAW,KAAK,QAAQ;AAAA,UACxB,MAAM,KAAK,OAAO,gBAAgB;AAAA,UAClC;AAAA,UACA,UAAU,KAAK,YACZ,wBAAwB;AAAA,YACxB,WAAW,KAAK,OAAO,aAAa,QAAQ,IAAI;AAAA,YAChD,KAAK,CAAC,CAAC,KAAK,OAAO;AAAA,YACnB,UAAU,KAAK,YAAY;AAAA,YAC3B,MAAM,KAAK,OAAO,QAAQ;AAAA,UAC3B,CAAC,IACA;AAAA,QACJ,CAAC;AAAA,QACD,QAAQ,YAAY,QAAQ,GAAI;AAAA,MACjC,CAAC;AACD,YAAM,OAAQ,MAAM,EAAE,KAAK;AAM3B,UAAI,CAAC,KAAK,GAAI,QAAO,EAAE,IAAI,OAAO,OAAO,KAAK,SAAS,wBAAwB,EAAE,MAAM,IAAI;AAC3F,aAAO,EAAE,IAAI,MAAM,SAAS,KAAK,SAAS,gBAAgB,KAAK,eAAe;AAAA,IAC/E,SAAS,KAAK;AACb,aAAO,EAAE,IAAI,OAAO,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,IAC7E;AAAA,EACD;AAAA;AAAA,EAGA,MAAM,aACL,KACgF;AAChF,QAAI,CAAC,KAAK,QAAS,QAAO,EAAE,IAAI,OAAO,OAAO,sBAAsB;AACpE,UAAM,OAAO,IAAI,QAAQ,OAAO,EAAE;AAClC,QAAI;AACH,YAAM,IAAI,MAAM,MAAM,GAAG,IAAI,iBAAiB;AAAA,QAC7C,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU;AAAA,UACpB,WAAW,KAAK,QAAQ;AAAA,UACxB,MAAM,KAAK,OAAO,gBAAgB;AAAA,QACnC,CAAC;AAAA,QACD,QAAQ,YAAY,QAAQ,GAAK;AAAA,MAClC,CAAC;AACD,YAAM,OAAO,MAAM,EAAE,KAAK;AAC1B,UAAI,EAAE,WAAW,OAAO,CAAC,KAAK,KAAK,GAAG;AACrC,eAAO;AAAA,UACN,IAAI;AAAA,UACJ,OAAO;AAAA,QACR;AAAA,MACD;AACA,YAAM,OAAO,KAAK,MAAM,IAAI;AAO5B,UAAI,CAAC,EAAE,MAAM,CAAC,KAAK,GAAI,QAAO,EAAE,IAAI,OAAO,OAAO,KAAK,SAAS,gBAAgB,EAAE,MAAM,IAAI;AAC5F,UAAI,KAAK,QAAS,QAAO,EAAE,IAAI,MAAM,SAAS,MAAM,SAAS,KAAK,aAAa;AAC/E,UAAI,KAAK,gBAAgB,eAAe,KAAK,YAAY,GAAG;AAC3D,cAAM,UAAU,KAAK,UAAU,GAAG,KAAK,YAAY;AACnD,cAAM,KAAK,WAAW,iBAAiB;AAAA,MACxC;AACA,YAAM,KAAK,aAAa,IAAI;AAC5B,aAAO,EAAE,IAAI,MAAM,SAAS,KAAK,aAAa;AAAA,IAC/C,SAAS,KAAK;AACb,aAAO,EAAE,IAAI,OAAO,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,IAC7E;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,QAAyB;AAC3C,WAAO,KAAK,aAAa,MAAM,GAAG,eAAe;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,iBAAiB,QAAyB;AACjD,UAAM,QAAQ,KAAK,aAAa,MAAM;AACtC,QAAI,UAAU,MAAM,UAAU,UAAU,MAAM,UAAU,QAAS,QAAO;AACxE,WAAO,KAAK,OAAO,OAAO,UAAU;AAAA,EACrC;AAAA;AAAA,EAGA,kBAAkB,QAAgB,UAA2B;AAC5D,UAAM,WAAW,KAAK,gBAAgB,MAAM;AAC5C,QAAI,UAAU,WAAW,KAAK,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC,EAAG,QAAO;AAC5E,QAAI,UAAU,cAAc,SAAS,WAAW,SAAS,GAAG;AAC3D,aAAO,SAAS,WAAW,KAAK,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAAA,IACrE;AAEA,QAAI,CAAC,aAAa,UAAU,KAAK,OAAO,OAAO,SAAS,EAAG,QAAO;AAClE,WAAO,KAAK,iBAAiB,MAAM;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,gBAAgB,QAQf;AACR,QAAI,CAAC,KAAK,OAAO,MAAO,QAAO;AAC/B,UAAM,WAAW,KAAK,WAAW,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM;AAC3E,QAAI,CAAC,SAAU,QAAO;AAKtB,eAAW,cAAc,KAAK,OAAO,OAAO;AAC3C,UAAI,WAAW,IAAI;AAClB,cAAM,OAAO,WAAW,GAAG,KAAK;AAChC,YAAI,KAAK,UAAU,MAAM,WAAW,QAAQ,OAAO,WAAW,IAAI,GAAI,QAAO;AAAA,MAC9E,WAAW,WAAW,QAAQ,SAAS,SAAS,WAAW,MAAM;AAChE,eAAO;AAAA,MACR;AAAA,IACD;AAEA,eAAW,cAAc,KAAK,OAAO,OAAO;AAC3C,UAAI,CAAC,WAAW,IAAK;AACrB,UAAI;AACH,cAAM,YAAY,IAAI,IAAI,WAAW,GAAG;AACxC,cAAM,aAAa,UAAU,SAAS,UAAU,aAAa,WAAW,QAAQ;AAGhF,YAAI,SAAS,SAAS,SAAS,IAAI,UAAU,EAAE,GAAG;AACjD,iBAAO;AAAA,QACR;AAGA,YAAI,SAAS,SAAS,SAAS,UAAU,IAAI,GAAG;AAC/C,iBAAO;AAAA,QACR;AAAA,MACD,QAAQ;AACP;AAAA,MACD;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,aACC,QAC8F;AAC9F,UAAM,aAAa,KAAK,gBAAgB,MAAM;AAC9C,QAAI,YAAY;AACf,aAAO;AAAA,QACN,OAAO,WAAW,SAAS;AAAA,QAC3B,YAAY,WAAW;AAAA,QACvB,WAAW,WAAW;AAAA,QACtB,YAAY,WAAW;AAAA,MACxB;AAAA,IACD;AAGA,UAAM,KAAK,KAAK,OAAO;AACvB,QAAI,IAAI,WAAW,KAAK,WAAW,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,GAAG;AAC/E,aAAO,EAAE,OAAO,GAAG,cAAc,QAAQ,YAAY,GAAG,cAAc,MAAM;AAAA,IAC7E;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,kBAA0B;AACzB,WAAO,KAAK,OAAO,gBAAgB;AAAA,EACpC;AAAA,EAEA,aAAiC;AAChC,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,eAAwC;AACvC,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,eAAwC;AACvC,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,WAAoB;AACnB,WAAO,KAAK;AAAA,EACb;AAAA,EAEQ,YAAoB;AAC3B,UAAM,UAAU,KAAK,OAAO,WAAW;AACvC,WAAY,cAAQ,KAAK,aAAkB,cAAQ,OAAO,CAAC;AAAA,EAC5D;AAAA,EAEQ,cAAsB;AAE7B,UAAM,WAAW,KAAK,OAAO,YAAY,QAAQ,IAAI;AACrD,QAAI,UAAU,KAAK,EAAG,QAAO,SAAS,KAAK;AAC3C,UAAM,OAAU,qBAAkB;AAClC,QAAI,CAAC,KAAM,QAAO;AAElB,eAAW,QAAQ,OAAO,KAAK,IAAI,GAAG;AACrC,iBAAW,OAAO,KAAK,IAAI,KAAK,CAAC,GAAG;AACnC,YAAI,IAAI,WAAW,UAAU,CAAC,IAAI,UAAU;AAC3C,iBAAO,IAAI;AAAA,QACZ;AAAA,MACD;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA,EAEQ,kBAA4B;AACnC,UAAM,OAAiB,CAAC;AACxB,QAAI,KAAK,cAAc;AAEtB,YAAM,WAAW,IAAI,IAAI,KAAK,aAAa,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;AACvE,iBAAW,QAAQ,UAAU;AAC5B,YAAI,SAAS,WAAY,MAAK,KAAK,IAAI;AAAA,MACxC;AAAA,IACD;AACA,WAAO;AAAA,EACR;AACD;AAsCO,SAAS,mBACf,QACA,YACA,MAC0B;AAC1B,QAAM,OACL,OAAO,WAAW,YAAY,WAAW,QAAQ,CAAC,MAAM,QAAQ,MAAM,IACnE,EAAE,GAAI,OAAmC,IACzC,CAAC;AACL,OAAK,WAAW,EAAE,YAAY,MAAM,QAAQ,WAAW,UAAU,GAAG,CAAC,EAAE;AACvE,SAAO;AACR;AAOO,SAAS,wBAAwB,MAK7B;AACV,QAAM,WAAW,KAAK,WAAW,KAAK,EAAE,QAAQ,QAAQ,EAAE;AAC1D,MAAI,SAAU,QAAO;AACrB,QAAM,SAAS,KAAK,MAAM,UAAU;AACpC,SAAO,GAAG,MAAM,MAAM,KAAK,QAAQ,IAAI,KAAK,IAAI;AACjD;AAUO,SAAS,cACf,OACA,KAC4C;AAC5C,QAAM,OAAO,CAAC,MAAc,EAAE,KAAK,EAAE,QAAQ,QAAQ,EAAE;AACvD,QAAM,SAAS,CAAC,MAA6B;AAC5C,QAAI;AACH,aAAO,IAAI,IAAI,KAAK,CAAC,CAAC,EAAE,YAAY;AAAA,IACrC,QAAQ;AACP,aAAO;AAAA,IACR;AAAA,EACD;AACA,QAAM,SAAS,KAAK,GAAG;AACvB,MAAI,MAAM,KAAK,CAAC,MAAM,KAAK,EAAE,GAAG,MAAM,MAAM,EAAG,QAAO,EAAE,MAAM;AAC9D,QAAM,aAAa,OAAO,MAAM;AAChC,QAAM,QAAQ,aAAa,MAAM,KAAK,CAAC,MAAM,OAAO,EAAE,GAAG,MAAM,UAAU,IAAI;AAC7E,QAAM,OAAO,QAAQ,MAAM,OAAO,CAAC,MAAM,MAAM,KAAK,IAAI,MAAM,MAAM;AACpE,OAAK,KAAK,QAAQ,EAAE,GAAG,OAAO,KAAK,OAAO,IAAI,EAAE,KAAK,QAAQ,OAAO,OAAO,CAAC;AAC5E,SAAO,EAAE,OAAO,MAAM,UAAU,QAAQ,KAAK,MAAM,GAAG,IAAI,OAAU;AACrE;","names":["randomUUID","fs","path","crypto","fs","path","fs","path","crypto","crypto","mlkem","sign","verify","crypto","sign","verify","logger","crypto","fs","path","Transform","crypto","fs","path","logger","crypto","TAG_BYTES","fs","logger","Transform","stat","resolve","stream","logger","crypto","logger","resolve","fs","path","logger","resolve","fs","logger","resolve","logger","discovery","fs","randomUUID","resolve"]}
|