dsh-deeppilot 0.2.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.
Files changed (53) hide show
  1. package/COMPATIBILITY.md +36 -0
  2. package/LICENSE +21 -0
  3. package/PRIVACY.md +64 -0
  4. package/README.md +97 -0
  5. package/README.zh-CN.md +91 -0
  6. package/SECURITY.md +44 -0
  7. package/THIRD_PARTY_NOTICES.md +20 -0
  8. package/bin/SHA256SUMS +1 -0
  9. package/bin/darwin-arm64/dsh-deeppilot-tunnel +0 -0
  10. package/cordis.patch.yml +20 -0
  11. package/lib/client.js +3398 -0
  12. package/lib/client.js.map +1 -0
  13. package/lib/index.d.ts +586 -0
  14. package/lib/index.js +3654 -0
  15. package/lib/index.js.map +1 -0
  16. package/package.json +102 -0
  17. package/third_party/licenses/filippo.io/edwards25519/LICENSE +27 -0
  18. package/third_party/licenses/github.com/Mars-Sea/dsh-deeppilot/helper/LICENSE +21 -0
  19. package/third_party/licenses/github.com/coder/websocket/LICENSE.txt +13 -0
  20. package/third_party/licenses/github.com/creachadair/msync/trigger/LICENSE +26 -0
  21. package/third_party/licenses/github.com/fxamacker/cbor/v2/LICENSE +21 -0
  22. package/third_party/licenses/github.com/gaissmai/bart/LICENSE +21 -0
  23. package/third_party/licenses/github.com/go-json-experiment/json/LICENSE +27 -0
  24. package/third_party/licenses/github.com/golang/groupcache/lru/LICENSE +191 -0
  25. package/third_party/licenses/github.com/google/btree/LICENSE +202 -0
  26. package/third_party/licenses/github.com/hdevalence/ed25519consensus/LICENSE +28 -0
  27. package/third_party/licenses/github.com/huin/goupnp/LICENSE +23 -0
  28. package/third_party/licenses/github.com/klauspost/compress/LICENSE +304 -0
  29. package/third_party/licenses/github.com/klauspost/compress/internal/snapref/LICENSE +27 -0
  30. package/third_party/licenses/github.com/klauspost/compress/zstd/internal/xxhash/LICENSE.txt +22 -0
  31. package/third_party/licenses/github.com/mitchellh/go-ps/LICENSE.md +21 -0
  32. package/third_party/licenses/github.com/pires/go-proxyproto/LICENSE +201 -0
  33. package/third_party/licenses/github.com/tailscale/certstore/LICENSE.md +21 -0
  34. package/third_party/licenses/github.com/tailscale/hujson/LICENSE +27 -0
  35. package/third_party/licenses/github.com/tailscale/peercred/LICENSE +29 -0
  36. package/third_party/licenses/github.com/tailscale/web-client-prebuilt/LICENSE +28 -0
  37. package/third_party/licenses/github.com/tailscale/wireguard-go/LICENSE +17 -0
  38. package/third_party/licenses/github.com/x448/float16/LICENSE +22 -0
  39. package/third_party/licenses/go4.org/mem/LICENSE +202 -0
  40. package/third_party/licenses/go4.org/netipx/LICENSE +27 -0
  41. package/third_party/licenses/golang.org/x/crypto/LICENSE +27 -0
  42. package/third_party/licenses/golang.org/x/exp/LICENSE +27 -0
  43. package/third_party/licenses/golang.org/x/net/LICENSE +27 -0
  44. package/third_party/licenses/golang.org/x/oauth2/LICENSE +27 -0
  45. package/third_party/licenses/golang.org/x/sync/errgroup/LICENSE +27 -0
  46. package/third_party/licenses/golang.org/x/sys/LICENSE +27 -0
  47. package/third_party/licenses/golang.org/x/term/LICENSE +27 -0
  48. package/third_party/licenses/golang.org/x/text/LICENSE +27 -0
  49. package/third_party/licenses/golang.org/x/time/rate/LICENSE +27 -0
  50. package/third_party/licenses/gvisor.dev/gvisor/pkg/LICENSE +254 -0
  51. package/third_party/licenses/npm/dijkstrajs/LICENSE +17 -0
  52. package/third_party/licenses/npm/qrcode/LICENSE +21 -0
  53. package/third_party/licenses/tailscale.com/LICENSE +28 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["cryptoSign","http2Connect","fsConstants"],"sources":["../src/token.ts","../src/connection.ts","../src/host-bridge.ts","../src/report-service.ts","../src/report-wire.ts","../src/report-remote.ts","../src/relay-test.ts","../src/apns.ts","../src/relay-client.ts","../src/remote-supervisor.ts","../src/local-address.ts","../src/index.ts"],"sourcesContent":["import { randomBytes, timingSafeEqual } from 'node:crypto'\nimport { access, mkdir, readFile, rename, writeFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { dirname, resolve } from 'node:path'\n\n/** Expand a leading ~ using the process home directory. */\nexport function expandHome(p: string): string {\n if (p === '~') return homedir()\n if (p.startsWith('~/')) return resolve(homedir(), p.slice(2))\n return p\n}\n\nfunction dshDataRoot(): string {\n const dshHome = process.env.DSH_HOME\n if (dshHome && dshHome.trim().length > 0) return resolve(dshHome.trim())\n return resolve(homedir(), '.dsh')\n}\n\n/** DeepPilot data directory: under $DSH_HOME when set, else ~/.dsh. */\nexport function bridgeDataDir(): string {\n return resolve(dshDataRoot(), 'deeppilot')\n}\n\n/**\n * Move the pre-DeepPilot data directory as one atomic directory rename.\n * Existing canonical data always wins; secrets are never merged or replaced.\n */\nexport async function migrateLegacyBridgeDataDir(): Promise<string | null> {\n const target = bridgeDataDir()\n try {\n await access(target)\n return null\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n\n const legacy = resolve(dshDataRoot(), 'pocket-bridge')\n try {\n await rename(legacy, target)\n return legacy\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw error\n }\n}\n\n/**\n * Load the pairing token from disk or generate and persist a fresh one.\n * The file is written 0600; the token never appears in logs.\n */\nexport async function loadOrCreateToken(tokenPath: string): Promise<string> {\n const full = expandHome(tokenPath)\n try {\n const existing = (await readFile(full, 'utf8')).trim()\n if (existing.length >= 32) return existing\n } catch {\n // fall through to generation\n }\n const token = randomBytes(32).toString('base64url')\n await mkdir(dirname(full), { recursive: true })\n await writeFile(full, token + '\\n', { mode: 0o600 })\n return token\n}\n\n/**\n * Generate a fresh pairing token and replace the stored one, invalidating\n * every copy of the old secret. The write goes to a same-directory temp file\n * renamed over the target so a crash can never leave a truncated token file.\n */\nexport async function writeNewToken(tokenPath: string): Promise<string> {\n const full = expandHome(tokenPath)\n const token = randomBytes(32).toString('base64url')\n await mkdir(dirname(full), { recursive: true })\n const temp = `${full}.${randomBytes(6).toString('hex')}.tmp`\n await writeFile(temp, token + '\\n', { mode: 0o600 })\n await rename(temp, full)\n return token\n}\n\n/** Constant-time token comparison; both sides are high-entropy secrets. */\nexport function tokenMatches(presented: string | null | undefined, expected: string): boolean {\n if (!presented) return false\n const a = Buffer.from(presented)\n const b = Buffer.from(expected)\n if (a.length !== b.length) {\n // Burn a same-length comparison so wrong-length probes keep flat timing.\n timingSafeEqual(b, b)\n return false\n }\n return timingSafeEqual(a, b)\n}\n\nexport type ApnsEnvironment = 'development' | 'production'\n\n/** Per-device APNs registration (F-9 离线推送). The raw token never leaves the registry file. */\nexport interface DeviceApnsInfo {\n /** Hex device token, lowercased, 32–512 chars. */\n token: string\n environment: ApnsEnvironment\n updatedAt: number\n /** Per-category opt-out mirror; absent keys mean enabled. */\n categories?: Record<string, boolean>\n}\n\nexport interface DeviceRecord {\n deviceId: string\n deviceName: string\n appVersion: string\n firstSeenTs: number\n lastSeenTs: number\n apns?: DeviceApnsInfo\n}\n\n/** Hex shape of an APNs device token as delivered by iOS (usually 64 chars). */\nconst APNS_TOKEN_PATTERN = /^[0-9a-f]{32,512}$/\n\nexport function isValidApnsToken(token: unknown): token is string {\n return typeof token === 'string' && APNS_TOKEN_PATTERN.test(token)\n}\n\n/** Registry row cap: hello spam with fresh ids must not grow the file forever. */\nexport const MAX_DEVICES = 64\n\n/**\n * Paired-device registry persisted as one JSON document. Whole-document\n * writes (serialized, never interleaved); a corrupt file falls back to an\n * empty registry rather than failing the plugin.\n */\nexport class DeviceStore {\n private devices = new Map<string, DeviceRecord>()\n private flushTail: Promise<void> = Promise.resolve()\n\n private constructor(private readonly filePath: string) {}\n\n static async load(filePath: string): Promise<DeviceStore> {\n const store = new DeviceStore(filePath)\n try {\n const raw = JSON.parse(await readFile(expandHome(filePath), 'utf8')) as {\n devices?: DeviceRecord[]\n }\n for (const rec of raw.devices ?? []) {\n if (typeof rec.deviceId === 'string') store.devices.set(rec.deviceId, rec)\n }\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code\n if (code === 'ENOENT') {\n // First run on this home: nothing registered yet — normal.\n } else {\n // A truncated/corrupt registry silently wiping pairings and push\n // registrations would be indistinguishable from \"never registered\".\n // Make data loss loud so the cause (crash mid-write, disk, …) shows.\n console.log('[deeppilot] device registry unreadable, starting empty: ' + String(error))\n }\n }\n return store\n }\n\n touch(record: Omit<DeviceRecord, 'firstSeenTs' | 'lastSeenTs'>, now: number): void {\n const existing = this.devices.get(record.deviceId)\n if (existing) {\n existing.lastSeenTs = now\n existing.deviceName = record.deviceName || existing.deviceName\n existing.appVersion = record.appVersion || existing.appVersion\n } else {\n if (this.devices.size >= MAX_DEVICES) {\n // Evict the least-recently-seen device to make room for the newcomer.\n let oldestId: string | undefined\n let oldestTs = Number.POSITIVE_INFINITY\n for (const [id, value] of this.devices) {\n if (value.lastSeenTs < oldestTs) {\n oldestTs = value.lastSeenTs\n oldestId = id\n }\n }\n if (oldestId !== undefined) this.devices.delete(oldestId)\n }\n this.devices.set(record.deviceId, {\n ...record,\n firstSeenTs: now,\n lastSeenTs: now,\n })\n }\n void this.flush()\n }\n\n list(): DeviceRecord[] {\n return [...this.devices.values()]\n }\n\n /**\n * Store (or refresh) the APNs registration of a paired device. Idempotent:\n * an unchanged registration does not rewrite the registry file, so the\n * app's re-register-on-every-handshake policy stays write-quiet.\n */\n setPushToken(\n deviceId: string,\n token: string,\n environment: ApnsEnvironment,\n categories: Record<string, boolean> | undefined,\n now: number,\n ): void {\n const normalized = token.toLowerCase()\n if (!isValidApnsToken(normalized)) return\n let record = this.devices.get(deviceId)\n if (!record) {\n // A live authenticated socket always has a row (hello touches it), but\n // a rotation could clear the registry mid-session; self-heal instead of\n // dropping the registration.\n record = { deviceId, deviceName: 'unknown', appVersion: 'unknown', firstSeenTs: now, lastSeenTs: now }\n this.devices.set(deviceId, record)\n }\n const next: DeviceApnsInfo = { token: normalized, environment, updatedAt: now }\n if (categories && typeof categories === 'object') {\n const clean: Record<string, boolean> = {}\n for (const [key, value] of Object.entries(categories)) {\n if (/^[a-z.]{1,64}$/.test(key) && typeof value === 'boolean') clean[key] = value\n }\n if (Object.keys(clean).length > 0) next.categories = clean\n }\n const current = record.apns\n if (\n current &&\n current.token === next.token &&\n current.environment === next.environment &&\n JSON.stringify(current.categories ?? {}) === JSON.stringify(next.categories ?? {})\n ) {\n return\n }\n record.apns = next\n void this.flush()\n }\n\n /** Drop a device's APNs registration (APNs reported the token unregistered). */\n clearPushToken(deviceId: string): void {\n const record = this.devices.get(deviceId)\n if (!record?.apns) return\n delete record.apns\n void this.flush()\n }\n\n /**\n * Drop every paired-device record. Used by token rotation: devices paired\n * under the old token can no longer authenticate, so keeping their rows\n * would paint a misleading \"still paired\" picture.\n */\n clear(): void {\n this.devices.clear()\n void this.flush()\n }\n\n /** Serialized so concurrent touches can never interleave half-written JSON. */\n private flush(): Promise<void> {\n const next = this.flushTail.then(() => this.writeFile())\n this.flushTail = next.catch(() => {\n // best-effort persistence; the bridge still works without it\n })\n return next\n }\n\n /** Resolves once every queued registry write has landed (test support). */\n async drain(): Promise<void> {\n await this.flushTail\n }\n\n private async writeFile(): Promise<void> {\n const full = expandHome(this.filePath)\n const body = JSON.stringify(\n { version: 1, devices: this.list() },\n null,\n 2,\n )\n try {\n await mkdir(dirname(full), { recursive: true })\n // Atomic replace: a restart mid-write previously left a truncated JSON\n // behind, which load() treats as corrupt and drops ENTIRELY — losing\n // pairings and push registrations on every unlucky shutdown.\n const temp = `${full}.${randomBytes(6).toString('hex')}.tmp`\n await writeFile(temp, body + '\\n', { mode: 0o600 })\n await rename(temp, full)\n } catch {\n // best-effort persistence; the bridge still works without it\n }\n }\n}\n","import type { WebSocket } from 'ws'\nimport {\n PROTOCOL_VERSION,\n} from './protocol.ts'\nimport type { Envelope, HelloAuthPayload } from './protocol.ts'\nimport type { BridgeSink, HostBridge } from './host-bridge.ts'\nimport type { DeviceStore, ApnsEnvironment } from './token.ts'\nimport { isValidApnsToken, tokenMatches } from './token.ts'\n\nexport const AUTH_TIMEOUT_MS = 5_000\nconst IMAGE_MEDIA_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp', 'image/gif'])\nconst MAX_PROMPT_IMAGES = 4\nconst MAX_BASE64_CHARS_PER_IMAGE = 8 * 1024 * 1024\n/** Bounds a single prompt's text; the frame itself is capped by ws maxPayload. */\nconst MAX_PROMPT_TEXT_CHARS = 256 * 1024\n// Client-supplied identity fields land in logs and devices.json — keep them\n// short and free of control characters so they can neither flood the registry\n// nor forge log lines.\nconst MAX_DEVICE_ID_CHARS = 128\nconst MAX_DEVICE_NAME_CHARS = 64\nconst MAX_APP_VERSION_CHARS = 32\n\nfunction sanitizeDeviceField(value: unknown, maxChars: number): string {\n const raw = typeof value === 'string' ? value : String(value ?? '')\n return raw.replace(/[\\u0000-\\u001f\\u007f]/g, ' ').trim().slice(0, maxChars)\n}\n\nexport interface ConnectionStateDeps {\n bridge: HostBridge\n devices: DeviceStore\n serverVersion: string\n /** Pairing secret used when the HTTP upgrade was not already authenticated. */\n expectedToken: string\n /** True when Authorization or the legacy query parameter passed at upgrade. */\n transportAuthenticated?: boolean\n debug?: boolean\n log: (message: string) => void\n /** Called once when the socket closes (cleanly or not). */\n onClosed?: (connection: BridgeConnection) => void\n /**\n * Zero-touch push enrollment: fired when a distributed app presents the\n * distributor's shared enrollKey during c2s.push.register. May perform the\n * relay round-trip; the register handler awaits it before evaluating push\n * readiness, so the very first registration can switch the feature on.\n */\n onPushEnrollKey?: (enrollKey: string) => Promise<void> | void\n}\n\nexport function helloTokenAccepted(\n transportAuthenticated: boolean | undefined,\n presentedToken: string | undefined,\n expectedToken: string,\n): boolean {\n return transportAuthenticated === true || tokenMatches(presentedToken, expectedToken)\n}\n\n/**\n * One connected phone. Implements BridgeSink so the HostBridge can push\n * projected frames and replays. Bearer/query credentials may authenticate the\n * HTTP upgrade; otherwise the first hello frame is verified here.\n */\nexport class BridgeConnection implements BridgeSink {\n private authenticated = false\n private helloTimer: NodeJS.Timeout | undefined\n private readonly openSessions = new Set<string>()\n /** Sanitized device identity from hello; needed for push registration. */\n private deviceId: string | undefined\n\n constructor(\n private readonly ws: WebSocket,\n private readonly deps: ConnectionStateDeps,\n ) {\n ws.on('message', (data) => {\n void this.onMessage(String(data))\n })\n ws.on('close', () => {\n this.onClose()\n deps.onClosed?.(this)\n })\n ws.on('error', () => {\n /* close follows */\n })\n this.helloTimer = setTimeout(() => {\n if (!this.authenticated) this.close(4402, 'auth timeout')\n }, AUTH_TIMEOUT_MS)\n }\n\n /** Hard-drop the socket (server-side stale sweep). */\n terminate(): void {\n this.ws.terminate()\n }\n\n /** Device identity once hello succeeded; undefined before that. */\n get connectedDeviceId(): string | undefined {\n return this.authenticated ? this.deviceId : undefined\n }\n\n // ---------- BridgeSink ----------\n\n push(type: string, payload: unknown, seq?: number): void {\n if (this.deps.debug === true) this.deps.log('push ' + type + ' seq=' + String(seq))\n this.send(type, payload, undefined, seq)\n }\n\n replay(entries: Array<{ seq: number; type: string; payload: unknown }>): void {\n for (const entry of entries) this.push(entry.type, entry.payload, entry.seq)\n }\n\n replayDone(): void {\n this.push('s2c.resume.done', {})\n }\n\n resync(): void {\n this.push('s2c.resync', { reason: 'gap' })\n }\n\n lastCursor(): number {\n return this.deps.bridge.currentCursor()\n }\n\n // ---------- lifecycle ----------\n\n private onClose(): void {\n if (this.helloTimer !== undefined) clearTimeout(this.helloTimer)\n for (const id of this.openSessions) {\n this.deps.bridge.markSinkClosed(this, id)\n }\n this.openSessions.clear()\n this.deps.bridge.dropSinkSessions(this)\n if (this.authenticated) this.deps.bridge.removeSink(this)\n }\n\n private close(code: number, reason: string): void {\n try {\n this.ws.close(code, reason)\n } catch {\n this.ws.terminate()\n }\n }\n\n private send(type: string, payload: unknown, id?: string, seq?: number): void {\n const envelope: Envelope = {\n v: PROTOCOL_VERSION,\n type,\n ts: Date.now(),\n ...(id !== undefined ? { id } : {}),\n ...(seq !== undefined ? { seq } : {}),\n payload,\n }\n if (this.ws.readyState === this.ws.OPEN) this.ws.send(JSON.stringify(envelope))\n }\n\n private fail(id: string | undefined, code: keyof typeof ERROR_CODES, message: string): void {\n this.send('s2c.error', { code, message }, id)\n }\n\n // ---------- dispatch ----------\n\n private lastActivity = Date.now()\n\n /** True when no inbound frame arrived within maxIdleMs. */\n isStale(now: number, maxIdleMs: number): boolean {\n return now - this.lastActivity > maxIdleMs\n }\n\n private async onMessage(raw: string): Promise<void> {\n this.lastActivity = Date.now()\n let env: Envelope\n try {\n env = JSON.parse(raw) as Envelope\n } catch {\n this.fail(undefined, 'E_PROTOCOL', 'frame is not valid JSON')\n return\n }\n if (env.v !== PROTOCOL_VERSION) {\n this.fail(env.id, 'E_UNSUPPORTED', 'unsupported protocol version')\n this.close(4500, 'protocol version mismatch')\n return\n }\n if (!this.authenticated) {\n if (env.type === 'c2s.ping') {\n this.send('s2c.pong', { serverTime: Date.now() }, env.id)\n return\n }\n if (env.type === 'c2s.hello.auth') {\n await this.hello(env)\n return\n }\n this.fail(env.id, 'E_PROTOCOL', 'authenticate first')\n return\n }\n switch (env.type) {\n case 'c2s.ping': {\n this.send('s2c.pong', { serverTime: Date.now() }, env.id)\n return\n }\n case 'c2s.sessions.list': {\n this.send('s2c.sessions.snapshot', { full: true, sessions: this.deps.bridge.listSessions() }, env.id)\n return\n }\n case 'c2s.workspaces.list': {\n if (!this.deps.bridge.capabilities.projectSelection) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'project selection unavailable on this host version')\n }\n const result = await this.deps.bridge.listWorkspaces()\n if (!result.ok) return this.fail(env.id, managementErrorCode(result.kind), result.message)\n this.send('s2c.workspaces.snapshot', { workspaces: result.value }, env.id)\n return\n }\n case 'c2s.directory.list': {\n const p = env.payload as { path?: unknown } | undefined\n if (p?.path !== undefined && typeof p.path !== 'string') {\n return this.fail(env.id, 'E_PROTOCOL', 'path must be a string')\n }\n const result = await this.deps.bridge.listDirectory(p?.path as string | undefined)\n if (!result.ok) return this.fail(env.id, managementErrorCode(result.kind), result.message)\n this.send('s2c.directory.listing', result.value, env.id)\n return\n }\n case 'c2s.directory.pick': {\n const result = await this.deps.bridge.pickDirectory()\n if (!result.ok) return this.fail(env.id, managementErrorCode(result.kind), result.message)\n this.send('s2c.directory.picked', { path: result.value }, env.id)\n return\n }\n case 'c2s.workspace.create': {\n const p = env.payload as { path?: unknown } | undefined\n const path = typeof p?.path === 'string' ? p.path.trim() : ''\n if (!path) return this.fail(env.id, 'E_PROTOCOL', 'non-empty path required')\n if (!this.deps.bridge.capabilities.projectSelection) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'project selection unavailable on this host version')\n }\n const result = await this.deps.bridge.createWorkspace(path)\n if (!result.ok) return this.fail(env.id, managementErrorCode(result.kind), result.message)\n this.send('s2c.workspace.created', result.value, env.id)\n return\n }\n case 'c2s.session.open': {\n const p = env.payload as { sessionId?: string; tailCount?: number }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n this.openSessions.add(p.sessionId)\n this.deps.bridge.markSinkOpen(this, p.sessionId)\n const ok = await this.deps.bridge.openSession(this, p.sessionId, p.tailCount ?? 100)\n if (!ok) this.fail(env.id, 'E_NOT_FOUND', 'session history unavailable')\n return\n }\n case 'c2s.session.close': {\n const p = env.payload as { sessionId?: string }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n this.openSessions.delete(p.sessionId)\n this.deps.bridge.markSinkClosed(this, p.sessionId)\n this.send('s2c.ack', {}, env.id)\n return\n }\n case 'c2s.session.create': {\n const p = env.payload as { workspaceId?: unknown; cwd?: unknown } | undefined\n const workspaceId = typeof p?.workspaceId === 'string' ? p.workspaceId.trim() : ''\n const cwd = typeof p?.cwd === 'string' ? p.cwd.trim() : ''\n if (workspaceId && cwd) return this.fail(env.id, 'E_PROTOCOL', 'workspaceId and cwd are mutually exclusive')\n const newId = await this.deps.bridge.createSession({\n ...(workspaceId ? { workspaceId } : {}),\n ...(cwd ? { cwd } : {}),\n })\n if (!newId) return this.fail(env.id, 'E_INTERNAL', 'session create failed')\n this.send('s2c.ack', { sessionId: newId }, env.id)\n return\n }\n case 'c2s.session.rename': {\n const p = env.payload as { sessionId?: string; title?: string }\n const title = typeof p?.title === 'string' ? p.title.trim() : ''\n if (!p?.sessionId || title.length === 0) {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId and non-empty title required')\n }\n if (!this.deps.bridge.capabilities.sessionManagement) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'session management unavailable on this host version')\n }\n const result = await this.deps.bridge.renameSession(p.sessionId, title)\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'invalid' ? 'E_PROTOCOL'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.session.renamed', { sessionId: p.sessionId, title: result.value }, env.id)\n return\n }\n case 'c2s.session.archive': {\n const p = env.payload as { sessionId?: string }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n if (!this.deps.bridge.capabilities.sessionManagement) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'session management unavailable on this host version')\n }\n const result = await this.deps.bridge.archiveSession(p.sessionId)\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'invalid' ? 'E_PROTOCOL'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.session.archived', { sessionId: p.sessionId }, env.id)\n return\n }\n case 'c2s.session.cancel': {\n const p = env.payload as { sessionId?: string }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n const result = await this.deps.bridge.cancelSession(p.sessionId)\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'invalid' ? 'E_PROTOCOL'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.ack', { sessionId: p.sessionId }, env.id)\n return\n }\n case 'c2s.session.history': {\n const p = env.payload as { sessionId?: string; beforeSeq?: number; limit?: number }\n if (!p?.sessionId || typeof p.beforeSeq !== 'number') {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId and beforeSeq required')\n }\n const ok = await this.deps.bridge.historyPage(this, p.sessionId, p.beforeSeq, Math.min(p.limit ?? 100, 500))\n if (!ok) this.fail(env.id, 'E_NOT_FOUND', 'history unavailable')\n return\n }\n case 'c2s.session.attachment': {\n const p = env.payload as { sessionId?: string; attachmentId?: string }\n if (!p?.sessionId || typeof p.attachmentId !== 'string' || p.attachmentId.length === 0) {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId and attachmentId required')\n }\n const image = await this.deps.bridge.attachmentData(p.sessionId, p.attachmentId)\n if (!image) return this.fail(env.id, 'E_NOT_FOUND', 'attachment unavailable')\n this.send('s2c.ack', image, env.id)\n return\n }\n case 'c2s.session.models': {\n const p = env.payload as { sessionId?: string }\n if (!p?.sessionId) return this.fail(env.id, 'E_PROTOCOL', 'sessionId required')\n if (!this.deps.bridge.capabilities.models) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'model selection unavailable on this host version')\n }\n const result = await this.deps.bridge.sessionModels(p.sessionId)\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'unavailable' ? 'E_NOT_FOUND'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.session.models', { sessionId: p.sessionId, ...result.value }, env.id)\n return\n }\n case 'c2s.session.selectModel': {\n const p = env.payload as {\n sessionId?: string\n provider?: string\n model?: string\n reasoningEffort?: string\n }\n if (!p?.sessionId || !p.provider?.trim() || !p.model?.trim()) {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId, provider and model required')\n }\n if (!this.deps.bridge.capabilities.models) {\n return this.fail(env.id, 'E_UNSUPPORTED', 'model selection unavailable on this host version')\n }\n const result = await this.deps.bridge.selectSessionModel(p.sessionId, {\n provider: p.provider.trim(),\n model: p.model.trim(),\n ...(p.reasoningEffort?.trim() ? { reasoningEffort: p.reasoningEffort.trim() } : {}),\n })\n if (!result.ok) {\n const code = result.kind === 'not-found' ? 'E_NOT_FOUND'\n : result.kind === 'busy' ? 'E_BUSY'\n : result.kind === 'unsupported' ? 'E_UNSUPPORTED'\n : result.kind === 'unavailable' ? 'E_NOT_FOUND'\n : 'E_INTERNAL'\n return this.fail(env.id, code, result.message)\n }\n this.send('s2c.session.modelSelected', { sessionId: p.sessionId, selected: result.value }, env.id)\n return\n }\n case 'c2s.session.sendPrompt': {\n const p = env.payload as {\n sessionId?: string\n text?: string\n images?: Array<{ mediaType?: string; data?: string; name?: string }>\n }\n const text = typeof p?.text === 'string' ? p.text : ''\n const rawImages = Array.isArray(p?.images) ? p.images : []\n if (!p?.sessionId || (text.trim().length === 0 && rawImages.length === 0)) {\n return this.fail(env.id, 'E_PROTOCOL', 'sessionId and text or images required')\n }\n if (text.length > MAX_PROMPT_TEXT_CHARS) {\n return this.fail(env.id, 'E_PROTOCOL', 'prompt text too long')\n }\n if (rawImages.length > MAX_PROMPT_IMAGES) {\n return this.fail(env.id, 'E_PROTOCOL', 'too many images')\n }\n const images: Array<{\n mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'\n data: string\n name?: string\n }> = []\n for (const image of rawImages) {\n if (!IMAGE_MEDIA_TYPES.has(String(image?.mediaType)) ||\n typeof image?.data !== 'string' || image.data.length === 0 ||\n image.data.length > MAX_BASE64_CHARS_PER_IMAGE) {\n return this.fail(env.id, 'E_PROTOCOL', 'invalid image attachment')\n }\n images.push({\n mediaType: image.mediaType as 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif',\n data: image.data,\n ...(typeof image.name === 'string' && image.name.trim().length > 0\n ? { name: image.name.trim().slice(0, 120) }\n : {}),\n })\n }\n const userSeq = await this.deps.bridge.sendPrompt(p.sessionId, text, images)\n if (userSeq === null) return this.fail(env.id, 'E_BUSY', 'session busy or unavailable')\n this.send('s2c.ack', { userSeq }, env.id)\n return\n }\n case 'c2s.approval.respond': {\n const p = env.payload as { requestId?: string; decision?: string; reason?: string }\n if (!p?.requestId || (p.decision !== 'allow' && p.decision !== 'deny')) {\n return this.fail(env.id, 'E_PROTOCOL', 'requestId and decision required')\n }\n const ok = await this.deps.bridge.respondApproval(p.requestId, p.decision)\n if (!ok) return this.fail(env.id, 'E_NOT_FOUND', 'approval not pending')\n this.send('s2c.ack', {}, env.id)\n return\n }\n case 'c2s.question.respond': {\n const p = env.payload as { requestId?: string; answers?: unknown }\n if (!p?.requestId || !Array.isArray(p.answers)) {\n return this.fail(env.id, 'E_PROTOCOL', 'requestId and answers required')\n }\n const ok = await this.deps.bridge.respondQuestion(p.requestId, p.answers)\n if (!ok) return this.fail(env.id, 'E_NOT_FOUND', 'question not pending')\n this.send('s2c.ack', {}, env.id)\n return\n }\n case 'c2s.push.register': {\n const p = env.payload as { deviceToken?: unknown; environment?: unknown; categories?: unknown; enrollKey?: unknown }\n const token = typeof p?.deviceToken === 'string' ? p.deviceToken.trim() : ''\n if (!isValidApnsToken(token)) {\n return this.fail(env.id, 'E_PROTOCOL', 'hex deviceToken (32-512 chars) required')\n }\n const environment: ApnsEnvironment = p?.environment === 'production' ? 'production' : 'development'\n const categories = typeof p?.categories === 'object' && p.categories !== null\n ? p.categories as Record<string, boolean>\n : undefined\n if (!this.deviceId || !this.authenticated) {\n return this.fail(env.id, 'E_PROTOCOL', 'authenticate first')\n }\n // Zero-touch enrollment MUST run before the capability gate: a fresh,\n // unconfigured bridge receives its first register precisely when push\n // is not yet enabled, and the key itself flips relay mode on. The\n // relay round-trip is awaited so readiness reflects reality.\n if (typeof p?.enrollKey === 'string') {\n const enrollKey = p.enrollKey.trim().replace(/[^\\x20-\\x7e]/g, '').slice(0, 128)\n if (enrollKey.length >= 8 && enrollKey.length <= 128) {\n await this.deps.onPushEnrollKey?.(enrollKey)\n }\n }\n // Store the registration BEFORE the readiness gate: if push is still\n // mid-bootstrap (or the relay is temporarily down), the token is\n // already on file for when it recovers — no extra register needed.\n this.deps.devices.setPushToken(this.deviceId, token, environment, categories, Date.now())\n if (!this.deps.bridge.capabilities.push) {\n if (this.deps.debug === true) this.deps.log('push register held: bridge not ready')\n return this.fail(env.id, 'E_UNSUPPORTED', 'push is not configured on this bridge')\n }\n if (this.deps.debug === true) {\n this.deps.log('push token registered env=' + environment)\n }\n // `enabled` lets the app flip its local capability immediately instead\n // of waiting for the next handshake.\n this.send('s2c.ack', { enabled: true }, env.id)\n return\n }\n default:\n this.fail(env.id, 'E_PROTOCOL', 'unknown type: ' + env.type)\n }\n }\n\n private async hello(env: Envelope): Promise<void> {\n const p = (env.payload ?? {}) as Partial<HelloAuthPayload>\n if (!helloTokenAccepted(this.deps.transportAuthenticated, p.token, this.deps.expectedToken)) {\n this.fail(env.id, 'E_AUTH', 'token missing or invalid')\n this.close(4401, 'invalid token')\n return\n }\n if (!p.deviceId) {\n this.fail(env.id, 'E_PROTOCOL', 'deviceId required')\n this.close(4403, 'deviceId required')\n return\n }\n const deviceId = sanitizeDeviceField(p.deviceId, MAX_DEVICE_ID_CHARS)\n if (!deviceId) {\n this.fail(env.id, 'E_PROTOCOL', 'deviceId required')\n this.close(4403, 'deviceId required')\n return\n }\n const deviceName = sanitizeDeviceField(p.deviceName, MAX_DEVICE_NAME_CHARS) || 'unknown'\n const appVersion = sanitizeDeviceField(p.appVersion, MAX_APP_VERSION_CHARS) || 'unknown'\n this.authenticated = true\n this.deviceId = deviceId\n if (this.helloTimer !== undefined) clearTimeout(this.helloTimer)\n this.deps.devices.touch({ deviceId, deviceName, appVersion }, Date.now())\n this.deps.log('device paired: ' + deviceName + ' (' + deviceId + ')')\n\n const cursor = typeof p.resumeCursor === 'number' && p.resumeCursor >= 0\n ? p.resumeCursor\n : undefined\n const canResume = cursor !== undefined && this.deps.bridge.canResumeFrom(cursor)\n // Welcome strictly precedes any replayed pushes.\n this.send('s2c.welcome', {\n protocolVersion: PROTOCOL_VERSION,\n serverVersion: this.deps.serverVersion,\n capabilities: this.deps.bridge.capabilities,\n cursor: this.deps.bridge.currentCursor(),\n resumed: canResume,\n }, env.id)\n this.deps.bridge.addSink(this)\n if (cursor !== undefined) {\n if (canResume) {\n this.deps.bridge.resumeFrom(cursor, this)\n } else {\n this.resync()\n }\n }\n }\n}\n\nconst ERROR_CODES = {\n E_AUTH: 'token missing or invalid',\n E_PROTOCOL: 'unknown type or malformed payload',\n E_NOT_FOUND: 'session or request not found',\n E_BUSY: 'session is busy',\n E_UNSUPPORTED: 'protocol version or capability unsupported',\n E_INTERNAL: 'internal error',\n} as const\n\nfunction managementErrorCode(\n kind: 'unsupported' | 'not-found' | 'busy' | 'invalid' | 'internal',\n): keyof typeof ERROR_CODES {\n switch (kind) {\n case 'unsupported': return 'E_UNSUPPORTED'\n case 'not-found': return 'E_NOT_FOUND'\n case 'busy': return 'E_BUSY'\n case 'invalid': return 'E_PROTOCOL'\n case 'internal': return 'E_INTERNAL'\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport type { Envelope, MessageProjection, NotifyCategory, PushNotification, SessionEventKind, SessionSummary, SessionTodoItem, SessionTodoStatus } from './protocol.ts'\n\n/* Minimal structural faces over the host apiProxy service. The real types\n * live in @deepseek-ai/dsh-host-apiproxy; keeping them structural lets this\n * plugin compile and degrade independently of host package versions. */\n\ninterface RpcOk<T> { ok: true; value: T }\ninterface RpcErr { ok: false; error: { code: string; message?: string } }\ntype RpcResult<T> = RpcOk<T> | RpcErr\n\ninterface RpcRequestLike<T> { rpcId?: string; payload?: T }\ninterface RpcResponseLike<T> { result?: RpcResult<T> }\n\ninterface SessionsApiLike {\n list(req?: RpcRequestLike<{ cursor?: string }>): Promise<RpcResponseLike<{ items: PhoneSessionRow[] }>>\n history(req: RpcRequestLike<{ sessionId: string; beforeSeq?: number; maxMessages?: number }>):\n Promise<RpcResponseLike<HistoryResult>>\n prompt(req: RpcRequestLike<PromptArgs>): Promise<RpcResponseLike<{ accepted: true }>>\n create(req: RpcRequestLike<{ workspaceId?: string; cwd?: string; agentPreset?: string }>): Promise<RpcResponseLike<{ sessionId: string; agentPreset?: string }>>\n models?(req: RpcRequestLike<{ sessionId: string }>): Promise<RpcResponseLike<HostSessionModels>>\n selectModel?(req: RpcRequestLike<{\n sessionId: string\n provider: string\n model: string\n reasoningEffort?: string\n }>): Promise<RpcResponseLike<{ selected: HostModelSelection }>>\n rename?(req: RpcRequestLike<{ sessionId: string; title: string }>):\n Promise<RpcResponseLike<{ title: string; seq: number }>>\n cancel?(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<Record<string, unknown> | undefined>>\n /** Reads one durable image back after the host verifies the session log references its id. */\n attachment?(req: RpcRequestLike<{ sessionId: string; attachmentId: string }>):\n Promise<RpcResponseLike<{ attachment: { mediaType?: string }; data: string }>>\n}\n\ninterface WorkspaceApiLike {\n list?(req: RpcRequestLike<Record<string, never>>):\n Promise<RpcResponseLike<{ items: WorkspaceViewLike[]; archivedSessionIds: string[] }>>\n create?(req: RpcRequestLike<{ path: string }>):\n Promise<RpcResponseLike<{ workspace: WorkspaceViewLike; created: boolean }>>\n archiveSession?(req: RpcRequestLike<{ sessionId: string }>):\n Promise<RpcResponseLike<{ archivedSessionIds: string[] }>>\n}\n\ninterface HostApiLike {\n listDirectory?(req: RpcRequestLike<{ path?: string }>, signal?: AbortSignal):\n Promise<RpcResponseLike<DirectoryListingLike>>\n pickDirectory?(req: RpcRequestLike<Record<string, never>>, signal?: AbortSignal):\n Promise<RpcResponseLike<{ path: string | null }>>\n}\n\ninterface WorkspaceViewLike {\n workspaceId: string\n path: string\n title: string\n sessionIds: string[]\n createdAt?: string\n updatedAt?: string\n}\n\nexport interface DirectoryEntryLike {\n name: string\n path: string\n hidden: boolean\n}\n\nexport interface DirectoryListingLike {\n path: string\n home: string\n crumbs: DirectoryEntryLike[]\n entries: DirectoryEntryLike[]\n truncated: boolean\n}\n\ninterface HostModelSelection {\n provider: string\n model: string\n reasoningEffort?: string\n}\n\ninterface HostSessionModels {\n current: HostModelSelection\n routable: boolean\n groups: Array<{\n id: string\n name: string\n models: Array<{\n id: string\n name: string\n description?: string\n reasoning?: {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n }\n }>\n }>\n failures: Array<{ id: string; name: string; message: string }>\n}\n\nexport type ModelBridgeResult<T> =\n | { ok: true; value: T }\n | { ok: false; kind: 'unsupported' | 'not-found' | 'busy' | 'unavailable' | 'internal'; message: string }\n\nexport type SessionManagementResult<T> =\n | { ok: true; value: T }\n | { ok: false; kind: 'unsupported' | 'not-found' | 'busy' | 'invalid' | 'internal'; message: string }\n\ninterface PromptArgs {\n sessionId: string\n mode: 'queue' | 'steer'\n content: Array<\n | { type: 'text'; text: string }\n | { type: 'image'; mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'; data: string; name?: string }\n >\n clientTimeZone?: string\n}\n\ninterface PhoneSessionRow {\n sessionId: string\n updatedAt: number\n running: boolean\n blank: boolean\n cwd?: string\n origin?: string\n parentSessionId?: string\n projections?: { asOfSeq?: number; values?: Record<string, unknown> }\n}\n\n/**\n * Subagent sessions are host-internal workers of a parent conversation.\n * They must never surface on the phone: not in the project/session list,\n * and not as turn-completion pushes for a session the device cannot open.\n */\nfunction isSubagentRow(row: PhoneSessionRow): boolean {\n return row.origin === 'subagent' ||\n (typeof row.parentSessionId === 'string' && row.parentSessionId.length > 0);\n}\n\ninterface HistoryResult {\n events: Array<{ event: SessionEventLike; view?: unknown }>\n hasMore: boolean\n projections?: { values?: Record<string, unknown> }\n}\n\nexport interface SessionEventLike {\n type: string\n seq: number\n time?: number\n data?: unknown\n}\n\nexport interface MuxFrameLike {\n type: string\n rpcId?: string\n payload?: any\n sessionId?: string\n event?: SessionEventLike\n key?: string\n value?: unknown\n approvalId?: string\n toolName?: string\n reason?: string\n questions?: unknown\n questionRpcId?: string\n running?: boolean\n}\n\n/**\n * apiProxy stream items are server-request envelopes: the frame lives in\n * `payload`, while the stable request id used to answer approval/question\n * waits lives beside it. Flattening only `payload` loses that id and makes\n * both interactions silently disappear.\n */\ninterface ApiStreamItemLike {\n rpcId?: string\n payload: MuxFrameLike\n}\n\nfunction unwrapStreamItem(item: MuxFrameLike | ApiStreamItemLike): MuxFrameLike {\n const nested = item.payload\n if (nested && typeof nested === 'object' && typeof nested.type === 'string') {\n return nested.rpcId || !item.rpcId ? nested : { ...nested, rpcId: item.rpcId }\n }\n return item as MuxFrameLike\n}\n\nexport interface ApiProxyLike {\n sessions: SessionsApiLike\n workspace?: WorkspaceApiLike\n host?: HostApiLike\n respond(message: { type: 'client-response'; rpcId: string; result: RpcResult<unknown> }):\n Promise<{ accepted: boolean; reason?: string }>\n events: {\n mux(req: RpcRequestLike<Record<string, never>>, signal: AbortSignal): AsyncIterable<MuxFrameLike | ApiStreamItemLike>\n host(req: RpcRequestLike<Record<string, never>>, signal: AbortSignal): AsyncIterable<MuxFrameLike | ApiStreamItemLike>\n }\n}\n\n/** Downward sink every connected phone registers (one per WebSocket). */\nexport interface BridgeSink {\n push(type: string, payload: unknown, seq?: number): void\n lastCursor(): number\n replay(entries: Array<{ seq: number; type: string; payload: unknown }>): void\n replayDone(): void\n resync(): void\n}\n\n/**\n * Offline push fan-out (F-9 离线推送). The bridge forwards every\n * notification-worthy event here; the outlet decides which paired devices\n * (token holders without a live socket) receive an APNs delivery.\n */\nexport interface PushOutlet {\n fanOut(notification: PushNotification): void\n /**\n * Whether offline push is currently configured and usable. Drives the\n * welcome capability bit: advertising push while no APNs credentials are\n * loaded would make clients suppress their own local banners and lose\n * notifications entirely.\n */\n isAvailable(): boolean\n}\n\ninterface PendingApproval {\n rpcId: string\n sessionId: string\n toolName: string\n reason: string\n}\n\ninterface PendingQuestion {\n rpcId: string\n sessionId: string\n questions: unknown\n}\n\nconst MAX_RING_DEFAULT = 2000;\n\n/**\n * Process-wide bridge state: session mirror, pending approvals/questions,\n * and the per-device replay ring. Consumes the in-process mux/host streams\n * and fans projected pushes out to every registered sink.\n */\nlet BRIDGE_SEQ = 0;\n\nexport class HostBridge {\n readonly id = ++BRIDGE_SEQ;\n private summaries = new Map<string, SessionSummary>()\n private approvals = new Map<string, PendingApproval>()\n private questions = new Map<string, PendingQuestion>()\n private archivedSessionIds = new Set<string>()\n private subagentSessionIds = new Set<string>()\n private sinks = new Set<BridgeSink>()\n private ring: Array<{ seq: number; type: string; payload: unknown }> = []\n private cursor = 0;\n private abort = new AbortController();\n\n constructor(\n private readonly apiProxy: ApiProxyLike,\n private readonly historyBufferMax: number = MAX_RING_DEFAULT,\n ) {}\n\n private pushOutlet: PushOutlet | undefined;\n\n /**\n * Wire the offline-push fan-out. Present ⇒ welcome advertises the `push`\n * capability and notify-worthy events are mirrored to APNs.\n */\n setPushOutlet(outlet: PushOutlet | undefined): void {\n this.pushOutlet = outlet;\n }\n\n get capabilities() {\n return {\n historyPaging: true,\n replay: true,\n approvals: true,\n questions: true,\n models: typeof this.apiProxy.sessions.models === 'function' &&\n typeof this.apiProxy.sessions.selectModel === 'function',\n sessionManagement: typeof this.apiProxy.sessions.rename === 'function' &&\n typeof this.apiProxy.workspace?.archiveSession === 'function',\n projectSelection: typeof this.apiProxy.workspace?.list === 'function' &&\n typeof this.apiProxy.workspace?.create === 'function',\n push: this.pushOutlet?.isAvailable() === true,\n };\n }\n\n diagnostic(message: string): void {\n console.log('[deeppilot] ' + message);\n }\n\n currentCursor(): number {\n return this.cursor;\n }\n\n // ---------- sinks ----------\n\n addSink(sink: BridgeSink): void {\n this.sinks.add(sink);\n }\n\n removeSink(sink: BridgeSink): void {\n this.sinks.delete(sink);\n }\n\n /** Whether the ring still holds everything after the cursor. */\n canResumeFrom(cursor: number): boolean {\n const oldest = this.ring.length > 0 ? this.ring[0]!.seq : this.cursor + 1;\n return cursor + 1 >= oldest;\n }\n\n private sinkSessions = new Map<BridgeSink, Set<string>>();\n private lastAssistantText = new Map<string, string>();\n\n /** Mark a sink as actively viewing a session (suppresses its turn notifications). */\n markSinkOpen(sink: BridgeSink, sessionId: string): void {\n let set = this.sinkSessions.get(sink);\n if (!set) { set = new Set(); this.sinkSessions.set(sink, set); }\n set.add(sessionId);\n }\n\n markSinkClosed(sink: BridgeSink, sessionId: string): void {\n this.sinkSessions.get(sink)?.delete(sessionId);\n }\n\n dropSinkSessions(sink: BridgeSink): void {\n this.sinkSessions.delete(sink);\n }\n\n private isViewedBy(sink: BridgeSink, sessionId: string): boolean {\n return this.sinkSessions.get(sink)?.has(sessionId) ?? false;\n }\n\n /** F-9: when a turn completes, notify every device not viewing the session. */\n private emitTurnCompletedNotify(sessionId: string, ok: boolean): void {\n if (this.subagentSessionIds.has(sessionId)) return;\n\n const row = this.summaries.get(sessionId);\n const title = ok ? '任务完成' : '任务异常结束';\n const body = this.lastAssistantText.get(sessionId) ?? row?.title ?? '';\n const truncatedBody = body.length > 120 ? body.slice(0, 119) + '…' : body;\n const category: NotifyCategory = ok ? 'turn.completed' : 'session.error';\n const notificationId = 'n-' + (this.cursor + 1);\n this.record('s2c.notify', {\n notificationId,\n category,\n sessionId,\n title,\n body: truncatedBody,\n ts: Date.now(),\n }, (sink) => this.isViewedBy(sink, sessionId));\n this.fanOutPush({ notificationId, category, sessionId, title, body: truncatedBody });\n }\n\n /**\n * Mirror one notification-worthy event to offline devices. Fire-and-forget:\n * push failures must never block or break the WS data plane.\n */\n private fanOutPush(notification: PushNotification): void {\n try {\n this.pushOutlet?.fanOut(notification);\n } catch {\n // outlet misbehavior must not take down the bridge\n }\n }\n\n\n /** Remember the latest assistant text so notifications can quote it. */\n private captureAssistantText(sessionId: string, event: SessionEventLike): void {\n\n if (event.type !== 'assistant/message') return;\n\n const text = messageText(event.data).trim();\n\n if (text.length > 0) this.lastAssistantText.set(sessionId, text.slice(-160));\n }\n\n /**\n * Replay buffered pushes after the given cursor; false when the gap is\n * unrecoverable. Frames go to `target` only — replaying into every sink\n * duplicated the whole window onto devices that never asked for it.\n */\n resumeFrom(cursor: number, target?: BridgeSink): boolean {\n const oldest = this.ring.length > 0 ? this.ring[0]!.seq : this.cursor + 1;\n if (cursor + 1 < oldest) return false;\n const receivers = target !== undefined ? [target] : [...this.sinks];\n for (const entry of this.ring) {\n if (entry.seq > cursor) {\n for (const sink of receivers) sink.replay([entry]);\n }\n }\n for (const sink of receivers) sink.replayDone();\n return true;\n }\n\n private record(type: string, payload: unknown, except?: (sink: BridgeSink) => boolean): void {\n this.cursor += 1;\n const entry = { seq: this.cursor, type, payload };\n this.ring.push(entry);\n if (this.ring.length > this.historyBufferMax) {\n this.ring.splice(0, this.ring.length - this.historyBufferMax);\n }\n for (const sink of this.sinks) {\n if (except && except(sink)) continue;\n sink.push(type, payload, entry.seq);\n }\n }\n\n\n // ---------- lifecycle ----------\n\n /** Start consuming host + mux streams. Idempotent; aborts on dispose(). */\n start(): void {\n void this.runHostStream();\n void this.runMuxStream();\n void this.refreshSummaries();\n }\n\n dispose(): void {\n this.abort.abort();\n this.sinks.clear();\n }\n\n private async runHostStream(): Promise<void> {\n try {\n for await (const item of this.apiProxy.events.host({ rpcId: randomUUID() }, this.abort.signal)) {\n const frame = unwrapStreamItem(item);\n this.onHostFrame(frame);\n }\n } catch {\n // stream ended (dispose or host shutdown); bridges degrade to RPC-only\n }\n }\n\n private async runMuxStream(): Promise<void> {\n try {\n for await (const item of this.apiProxy.events.mux({ rpcId: randomUUID() }, this.abort.signal)) {\n const frame = unwrapStreamItem(item);\n this.onMuxFrame(frame);\n }\n } catch {\n // ditto\n }\n }\n\n private onHostFrame(frame: MuxFrameLike): void {\n switch (frame.type) {\n case 'host/session-added':\n case 'host/session-removed':\n case 'host/workspace-changed':\n case 'host/workspace-removed':\n case 'host/workspace-order-changed':\n void this.refreshSummaries();\n break;\n case 'host/archived-sessions-changed': {\n const archived = (frame as { archivedSessionIds?: unknown }).archivedSessionIds\n if (Array.isArray(archived)) {\n this.archivedSessionIds = new Set(archived.map(String))\n }\n void this.refreshSummaries();\n break;\n }\n case 'host/session-status': {\n const p = frame as { sessionId?: string; running?: boolean };\n const row = this.summaries.get(String(p.sessionId));\n if (row && typeof p?.running === 'boolean') {\n row.status = p.running ? 'running' : 'idle';\n this.pushSummary(row);\n }\n break;\n }\n default:\n break;\n }\n }\n\n private onMuxFrame(frame: MuxFrameLike): void {\n switch (frame.type) {\n case 'session/event': {\n const event = frame.event as SessionEventLike | undefined;\n const sessionId = String(frame.sessionId ?? '');\n if (!event || !sessionId) break;\n this.noteActivity(sessionId, event);\n this.captureAssistantText(sessionId, event);\n const projection = projectEvent(sessionId, event);\n if (projection) {\n this.record('s2c.session.event', { sessionId, kind: projection.kind, seq: event.seq, data: projection.data });\n }\n if (projection?.kind === 'turn.end') {\n this.emitTurnCompletedNotify(sessionId, projection.data.ok === true);\n }\n break;\n }\n case 'session/projection': {\n const p = frame as { sessionId?: string; key?: string; value?: unknown };\n if (!p.sessionId) break;\n this.applyProjection(p.sessionId, String(p.key ?? ''), p.value);\n break;\n }\n case 'approval/requested': {\n const p = frame as { sessionId?: string; approvalId?: string; toolName?: string; reason?: string };\n if (!p.approvalId || !frame.rpcId) break;\n const toolName = String(p.toolName ?? 'tool');\n const summary = String(p.reason ?? '');\n this.approvals.set(p.approvalId, {\n rpcId: frame.rpcId,\n sessionId: String(p.sessionId ?? ''),\n toolName,\n reason: summary,\n });\n this.record('s2c.pending.approval', {\n requestId: p.approvalId,\n sessionId: String(p.sessionId ?? ''),\n toolName,\n summary,\n riskLevel: riskOf(toolName),\n });\n this.fanOutPush({\n notificationId: 'apr-' + p.approvalId,\n category: 'approval.required',\n sessionId: String(p.sessionId ?? ''),\n title: '需要批准',\n body: toolName + ': ' + summary,\n });\n this.bumpPendingFlags(String(p.sessionId ?? ''));\n break;\n }\n case 'approval/resolved': {\n const p = frame as { approvalId?: string };\n if (!p.approvalId) break;\n const pending = this.approvals.get(p.approvalId);\n this.approvals.delete(p.approvalId);\n this.record('s2c.pending.cleared', { requestId: p.approvalId });\n if (pending) this.bumpPendingFlags(pending.sessionId);\n break;\n }\n case 'question/requested': {\n const p = frame as { sessionId?: string; questions?: unknown };\n if (!frame.rpcId) break;\n const requestId = 'q-' + frame.rpcId;\n const sessionId = String(p?.sessionId ?? '');\n this.questions.set(requestId, { rpcId: frame.rpcId, sessionId, questions: p?.questions });\n this.record('s2c.pending.question', { requestId, sessionId, questions: p?.questions ?? [] });\n this.fanOutPush({\n notificationId: requestId,\n category: 'question.asked',\n sessionId,\n title: '有问题需要回答',\n body: firstQuestionText(p?.questions),\n });\n this.bumpPendingFlags(sessionId);\n break;\n }\n case 'question/resolved': {\n const p = frame as { questionRpcId?: string };\n if (!p.questionRpcId) break;\n const requestId = 'q-' + p.questionRpcId;\n const pending = this.questions.get(requestId);\n this.questions.delete(requestId);\n this.record('s2c.pending.cleared', { requestId });\n if (pending) this.bumpPendingFlags(pending.sessionId);\n break;\n }\n default:\n break;\n }\n }\n\n\n // ---------- session mirror ----------\n\n async refreshSummaries(): Promise<void> {\n try {\n const response = await this.apiProxy.sessions.list({ rpcId: randomUUID(), payload: {} });\n if (!response.result || !response.result.ok) {\n this.diagnostic('sessions.list rejected: ' + JSON.stringify(response.result ?? null).slice(0, 200));\n return;\n }\n let workspaces: WorkspaceViewLike[] = []\n const workspaceList = this.apiProxy.workspace?.list\n if (typeof workspaceList === 'function') {\n const workspaceResponse = await workspaceList.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: {},\n })\n if (workspaceResponse.result?.ok) {\n workspaces = workspaceResponse.result.value.items ?? []\n this.archivedSessionIds = new Set(\n (workspaceResponse.result.value.archivedSessionIds ?? []).map(String),\n )\n }\n }\n const previousIds = new Set(this.summaries.keys())\n const workspaceBySession = new Map<string, WorkspaceViewLike>()\n for (const workspace of workspaces) {\n for (const sessionId of workspace.sessionIds ?? []) {\n workspaceBySession.set(String(sessionId), workspace)\n }\n }\n const next = new Map<string, SessionSummary>();\n const subagentIds = new Set<string>();\n for (const row of response.result.value.items ?? []) {\n if (this.archivedSessionIds.has(row.sessionId)) continue\n if (isSubagentRow(row)) {\n subagentIds.add(row.sessionId);\n continue;\n }\n next.set(row.sessionId, toSummary(row, this.approvals, this.questions, workspaceBySession.get(row.sessionId)));\n }\n this.subagentSessionIds = subagentIds;\n this.summaries = next;\n const removedIds = [...previousIds].filter((id) => !next.has(id))\n for (const id of removedIds) this.lastAssistantText.delete(id);\n this.record('s2c.sessions.delta', { upserted: [...next.values()], removedIds });\n } catch {\n // apiProxy absent or transient failure; keep last mirror\n }\n }\n\n /** Cold sessions may lack a title projection; fall back to first user text. */\n private deriveTitleFallback(sessionId: string, messages: MessageProjection[]): void {\n const row = this.summaries.get(sessionId);\n if (!row || row.title.length > 0) return;\n const firstUser = messages.find(m => m.role === 'user' && (m.text ?? '').trim().length > 0);\n if (!firstUser) return;\n row.title = firstUser!.text!.replace(/\\s+/g, ' ').trim().slice(0, 60);\n this.pushSummary(row);\n }\n\n private noteActivity(sessionId: string, event?: SessionEventLike): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n // Recency must reflect USER-VISIBLE milestones only: a new prompt moves\n // a session to the top and a finished turn settles it into place. Token\n // chunks / tool calls / per-step assistant messages fire constantly while\n // an agent works; counting them made concurrently running sessions fight\n // over the top slot in every recency-sorted list.\n switch (event?.type) {\n case 'user/message':\n case 'turn/start':\n case 'turn/end':\n break;\n default:\n return;\n }\n row.lastActivityTs = Date.now();\n this.pushSummary(row);\n }\n\n private applyProjection(sessionId: string, key: string, value: unknown): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n if (key === 'title') {\n row.title = typeof value === 'string' ? value : '';\n } else if (key === 'todos') {\n const items = Array.isArray(value) ? value as Array<{ content?: unknown; status?: unknown }> : null;\n const sanitized = sanitizeTodoItems(items);\n row.todoItems = sanitized.length > 0 ? sanitized : null;\n row.todos = sanitized.length > 0\n ? { done: sanitized.filter(i => i.status === 'completed').length, total: sanitized.length }\n : null;\n } else if (key === 'sessionListMetadata') {\n const meta = value as { lastPromptAt?: number } | null;\n if (meta?.lastPromptAt) row.lastActivityTs = Math.max(row.lastActivityTs, meta.lastPromptAt);\n } else {\n return;\n }\n this.pushSummary(row);\n }\n\n private bumpPendingFlags(sessionId: string): void {\n const row = this.summaries.get(sessionId);\n if (!row) return;\n let approval = false;\n for (const pending of this.approvals.values()) {\n if (pending.sessionId === sessionId) approval = true;\n }\n let question = false;\n for (const pending of this.questions.values()) {\n if (pending.sessionId === sessionId) question = true;\n }\n row.pendingApproval = approval;\n row.pendingQuestion = question;\n this.pushSummary(row);\n }\n\n private pushSummary(row: SessionSummary): void {\n this.record('s2c.sessions.delta', { upserted: [row], removedIds: [] });\n }\n\n listSessions(): SessionSummary[] {\n return [...this.summaries.values()].sort((a, b) => b.lastActivityTs - a.lastActivityTs);\n }\n\n // ---------- data operations ----------\n\n /** Tail history for an opened session; pushes s2c.session.tail to the sink. */\n async openSession(sink: BridgeSink, sessionId: string, tailCount: number): Promise<boolean> {\n try {\n const response = await this.apiProxy.sessions.history({\n rpcId: randomUUID(),\n payload: { sessionId, maxMessages: clampTail(tailCount) },\n });\n if (!response.result || !response.result.ok) return false;\n const result = response.result.value;\n const messages = projectHistory(result.events ?? []);\n const oldestSeq = messages.length > 0 ? messages[0]!.seq : 0;\n sink.push('s2c.session.tail', {\n sessionId,\n messages,\n oldestSeq,\n hasMore: Boolean(result.hasMore),\n });\n this.deriveTitleFallback(sessionId, messages);\n return true;\n } catch {\n return false;\n }\n }\n\n async historyPage(sink: BridgeSink, sessionId: string, beforeSeq: number, limit: number): Promise<boolean> {\n try {\n const response = await this.apiProxy.sessions.history({\n rpcId: randomUUID(),\n payload: { sessionId, beforeSeq, maxMessages: clampTail(limit) },\n });\n if (!response.result || !response.result.ok) return false;\n const result = response.result.value;\n const messages = projectHistory(result.events ?? []);\n sink.push('s2c.history.page', { sessionId, messages, hasMore: Boolean(result.hasMore) });\n return true;\n } catch {\n return false;\n }\n }\n\n /** Result of one attachment read-back for the phone. */\n async attachmentData(\n sessionId: string,\n attachmentId: string,\n ): Promise<{ mediaType?: string; data: string } | null> {\n const read = this.apiProxy.sessions.attachment\n if (typeof read !== 'function') return null;\n try {\n const response = await read.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId, attachmentId },\n });\n if (!response.result || !response.result.ok) return null;\n const data = response.result.value.data;\n if (typeof data !== 'string' || data.length === 0) return null;\n return {\n ...(typeof response.result.value.attachment?.mediaType === 'string'\n ? { mediaType: response.result.value.attachment.mediaType as string }\n : {}),\n data,\n };\n } catch {\n return null;\n }\n }\n\n async sessionModels(sessionId: string): Promise<ModelBridgeResult<HostSessionModels>> {\n const models = this.apiProxy.sessions.models\n if (typeof models !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'model catalog unavailable on this host version' }\n }\n try {\n const response = await models.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'model catalog returned no result' }\n if (!response.result.ok) return hostModelError(response.result.error)\n return { ok: true, value: projectSessionModels(response.result.value) }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async selectSessionModel(\n sessionId: string,\n selection: HostModelSelection,\n ): Promise<ModelBridgeResult<HostModelSelection>> {\n const selectModel = this.apiProxy.sessions.selectModel\n if (typeof selectModel !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'model selection unavailable on this host version' }\n }\n try {\n const response = await selectModel.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: {\n sessionId,\n provider: selection.provider,\n model: selection.model,\n ...(selection.reasoningEffort ? { reasoningEffort: selection.reasoningEffort } : {}),\n },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'model selection returned no result' }\n if (!response.result.ok) return hostModelError(response.result.error)\n return { ok: true, value: { ...response.result.value.selected } }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async renameSession(sessionId: string, title: string): Promise<SessionManagementResult<string>> {\n const rename = this.apiProxy.sessions.rename\n if (typeof rename !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'session rename unavailable on this host version' }\n }\n try {\n const response = await rename.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId, title },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'session rename returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n const acceptedTitle = String(response.result.value.title)\n const row = this.summaries.get(sessionId)\n if (row) {\n row.title = acceptedTitle\n this.pushSummary(row)\n }\n return { ok: true, value: acceptedTitle }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async archiveSession(sessionId: string): Promise<SessionManagementResult<true>> {\n const archive = this.apiProxy.workspace?.archiveSession\n if (typeof archive !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'session archive unavailable on this host version' }\n }\n try {\n const response = await archive.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'session archive returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n this.archivedSessionIds = new Set(\n (response.result.value.archivedSessionIds ?? []).map(String),\n )\n this.summaries.delete(sessionId)\n this.lastAssistantText.delete(sessionId)\n this.record('s2c.sessions.delta', { upserted: [], removedIds: [sessionId] })\n return { ok: true, value: true }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async cancelSession(sessionId: string): Promise<SessionManagementResult<true>> {\n const cancel = this.apiProxy.sessions.cancel\n if (typeof cancel !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'session cancel unavailable on this host version' }\n }\n try {\n const response = await cancel.call(this.apiProxy.sessions, {\n rpcId: randomUUID(),\n payload: { sessionId },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'session cancel returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n return { ok: true, value: true }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async listWorkspaces(): Promise<SessionManagementResult<Array<{\n id: string; title: string; path: string; sessionIds: string[]\n }>>> {\n const list = this.apiProxy.workspace?.list\n if (typeof list !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'workspace list unavailable on this host version' }\n }\n try {\n const response = await list.call(this.apiProxy.workspace, { rpcId: randomUUID(), payload: {} })\n if (!response.result) return { ok: false, kind: 'internal', message: 'workspace list returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n return { ok: true, value: (response.result.value.items ?? []).map(projectWorkspace) }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async createWorkspace(path: string): Promise<SessionManagementResult<{\n workspace: { id: string; title: string; path: string; sessionIds: string[] }; created: boolean\n }>> {\n const create = this.apiProxy.workspace?.create\n if (typeof create !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'workspace create unavailable on this host version' }\n }\n try {\n const response = await create.call(this.apiProxy.workspace, {\n rpcId: randomUUID(),\n payload: { path },\n })\n if (!response.result) return { ok: false, kind: 'internal', message: 'workspace create returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n await this.refreshSummaries()\n return {\n ok: true,\n value: { workspace: projectWorkspace(response.result.value.workspace), created: response.result.value.created === true },\n }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async listDirectory(path?: string): Promise<SessionManagementResult<DirectoryListingLike>> {\n const list = this.apiProxy.host?.listDirectory\n if (typeof list !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'directory browsing unavailable on this host version' }\n }\n try {\n const response = await list.call(this.apiProxy.host, {\n rpcId: randomUUID(),\n payload: path && path.trim().length > 0 ? { path } : {},\n }, this.abort.signal)\n if (!response.result) return { ok: false, kind: 'internal', message: 'directory list returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n return { ok: true, value: response.result.value }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n async pickDirectory(): Promise<SessionManagementResult<string | null>> {\n const pick = this.apiProxy.host?.pickDirectory\n if (typeof pick !== 'function') {\n return { ok: false, kind: 'unsupported', message: 'native directory picker unavailable on this host version' }\n }\n try {\n const response = await pick.call(this.apiProxy.host, {\n rpcId: randomUUID(),\n payload: {},\n }, this.abort.signal)\n if (!response.result) return { ok: false, kind: 'internal', message: 'directory picker returned no result' }\n if (!response.result.ok) return hostSessionManagementError(response.result.error)\n return { ok: true, value: response.result.value.path }\n } catch (error) {\n return { ok: false, kind: 'internal', message: String(error) }\n }\n }\n\n /** Create a fresh blank session in an existing workspace or legacy cwd. */\n async createSession(destination: { workspaceId?: string; cwd?: string } = {}): Promise<string | null> {\n try {\n const response = await this.apiProxy.sessions.create({\n rpcId: randomUUID(),\n payload: {\n ...(destination.workspaceId?.trim() ? { workspaceId: destination.workspaceId.trim() } : {}),\n ...(destination.cwd?.trim() ? { cwd: destination.cwd.trim() } : {}),\n },\n });\n if (!response.result || !response.result.ok) return null;\n const sessionId = response.result.value.sessionId;\n await this.refreshSummaries();\n return sessionId;\n } catch {\n return null;\n }\n }\n\n async sendPrompt(\n sessionId: string,\n text: string,\n images: Array<{ mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'; data: string; name?: string }> = [],\n ): Promise<number | null> {\n try {\n const content: PromptArgs['content'] = [];\n if (text.trim().length > 0) content.push({ type: 'text', text });\n for (const image of images) content.push({ type: 'image', ...image });\n const response = await this.apiProxy.sessions.prompt({\n rpcId: randomUUID(),\n payload: {\n sessionId,\n mode: 'queue',\n content,\n clientTimeZone: localTimeZone(),\n },\n });\n if (!response.result || !response.result.ok) return null;\n const row = this.summaries.get(sessionId);\n if (row) {\n row.lastActivityTs = Date.now();\n this.pushSummary(row);\n }\n return Date.now();\n } catch {\n return null;\n }\n }\n\n\n async respondApproval(requestId: string, decision: 'allow' | 'deny'): Promise<boolean> {\n const pending = this.approvals.get(requestId);\n if (!pending) return false;\n // Claim the pending entry before responding: a second phone (or a double\n // tap) must not fire another client-response for the same rpcId while the\n // resolved frame is still in flight.\n this.approvals.delete(requestId);\n const outcome = decision === 'allow' ? 'allowed-once' : 'rejected';\n try {\n const receipt = await this.apiProxy.respond({\n type: 'client-response',\n rpcId: pending.rpcId,\n result: { ok: true, value: { sessionId: pending.sessionId, approvalId: requestId, outcome } },\n });\n const accepted = Boolean(receipt?.accepted);\n if (!accepted && !this.approvals.has(requestId)) this.approvals.set(requestId, pending);\n return accepted;\n } catch {\n // Transport-level failure: give the request back so the user can retry.\n if (!this.approvals.has(requestId)) this.approvals.set(requestId, pending);\n return false;\n }\n }\n\n async respondQuestion(requestId: string, answers: unknown): Promise<boolean> {\n const pending = this.questions.get(requestId);\n if (!pending) return false;\n this.questions.delete(requestId);\n try {\n const receipt = await this.apiProxy.respond({\n type: 'client-response',\n rpcId: pending.rpcId,\n result: { ok: true, value: { sessionId: pending.sessionId, answer: { answers } } },\n });\n const accepted = Boolean(receipt?.accepted);\n if (!accepted && !this.questions.has(requestId)) this.questions.set(requestId, pending);\n return accepted;\n } catch {\n if (!this.questions.has(requestId)) this.questions.set(requestId, pending);\n return false;\n }\n }\n}\n\n// ---------- projection helpers ----------\n\nfunction clampTail(n: number): number {\n if (!Number.isFinite(n)) return 100;\n return Math.max(10, Math.min(500, Math.floor(n)));\n}\n\nfunction localTimeZone(): string | undefined {\n try {\n return new Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction riskOf(toolName: string): 'read' | 'write' | 'destructive' {\n if (/bash|pwsh|terminal/.test(toolName)) return 'write';\n if (/edit|write|str_replace|create/.test(toolName)) return 'write';\n if (/delete|remove|kill/.test(toolName)) return 'destructive';\n return 'read';\n}\n\n/** First question's text for the push banner; the questions payload shape is\n * host-version dependent, so extract defensively. */\nexport function firstQuestionText(questions: unknown): string {\n if (!Array.isArray(questions) || questions.length === 0) return 'Agent 等待你的输入';\n const first = questions[0] as { question?: unknown } | undefined;\n const text = String(first?.question ?? '').trim();\n return text || 'Agent 等待你的输入';\n}\n\nconst TODO_STATUSES = new Set(['pending', 'in_progress', 'completed']);\n\n/** Validate a host todo projection once; progress counts and the full\n * checklist both derive from this sanitized list so they never disagree. */\nfunction sanitizeTodoItems(items: Array<{ content?: unknown; status?: unknown }> | null): SessionTodoItem[] {\n if (!items) return [];\n return items\n .map(i => ({ content: String(i.content ?? '').trim(), status: String(i.status ?? '') }))\n .filter(i => i.content.length > 0 && TODO_STATUSES.has(i.status))\n .slice(0, 100)\n .map(i => ({ content: i.content, status: i.status as SessionTodoStatus }));\n}\n\nfunction toSummary(\n row: PhoneSessionRow,\n approvals: Map<string, PendingApproval>,\n questions: Map<string, PendingQuestion>,\n workspace?: WorkspaceViewLike,\n): SessionSummary {\n const values = row.projections?.values ?? {};\n const todos = Array.isArray(values.todos) ? values.todos as Array<{ content?: unknown; status?: unknown }> : null;\n let pendingApproval = false;\n for (const pending of approvals.values()) {\n if (pending.sessionId === row.sessionId) pendingApproval = true;\n }\n let pendingQuestion = false;\n for (const pending of questions.values()) {\n if (pending.sessionId === row.sessionId) pendingQuestion = true;\n }\n const cwd = typeof row.cwd === 'string' ? row.cwd : '';\n const label = workspace?.title ?? (cwd ? cwd.split('/').filter(Boolean).pop() : undefined);\n const todoItems = sanitizeTodoItems(todos);\n return {\n id: row.sessionId,\n title: typeof values.title === 'string' ? values.title : '',\n status: row.running ? 'running' : (row.blank ? 'unknown' : 'idle'),\n lastActivityTs: Number(row.updatedAt ?? Date.now()),\n todos: todoItems.length > 0\n ? {\n done: todoItems.filter(i => i.status === 'completed').length,\n total: todoItems.length,\n }\n : null,\n todoItems: todoItems.length > 0 ? todoItems : null,\n pendingApproval,\n pendingQuestion,\n workspaceLabel: label ?? null,\n workspaceId: workspace?.workspaceId ?? null,\n workspacePath: workspace?.path ?? (cwd || null),\n };\n}\n\nfunction projectWorkspace(workspace: WorkspaceViewLike): {\n id: string; title: string; path: string; sessionIds: string[]\n} {\n return {\n id: String(workspace.workspaceId),\n title: String(workspace.title),\n path: String(workspace.path),\n sessionIds: (workspace.sessionIds ?? []).map(String),\n }\n}\n\n/** Project one raw session event into a protocol push, when it maps to one. */\nfunction projectEvent(sessionId: string, event: SessionEventLike): { kind: SessionEventKind; data: Record<string, unknown> } | null {\n switch (event.type) {\n case 'turn/start':\n return { kind: 'turn.start', data: {} };\n case 'turn/end': {\n const data = event.data as { reason?: { kind?: string } } | undefined;\n return { kind: 'turn.end', data: { ok: data?.reason?.kind === 'completed' } };\n }\n case 'user/message':\n return {\n kind: 'message.final',\n data: {\n seq: event.seq,\n role: 'user',\n text: messageText(event.data),\n ...attachmentProjection(event.data),\n ts: tsOf(event),\n },\n };\n case 'assistant/chunk': {\n if (chunkTypeOf(event.data) === 'reasoning-delta') {\n return { kind: 'thinking.delta', data: { text: chunkText(event.data), ts: tsOf(event) } };\n }\n return { kind: 'message.delta', data: { text: chunkText(event.data), ts: tsOf(event) } };\n }\n case 'assistant/message': {\n const text = messageText(event.data);\n const thinking = messageThinking(event.data);\n // Pure-reasoning or tool-only steps carry no visible answer; emitting\n // them produced empty bubbles on the phone.\n if (!text.trim() && !thinking.trim()) return null;\n return {\n kind: 'message.final',\n data: { seq: event.seq, role: 'assistant', text, ...(thinking ? { thinking } : {}), ts: tsOf(event) },\n };\n }\n case 'tool/call': {\n const data = event.data as { name?: string; arguments?: string } | undefined;\n return {\n kind: 'tool.start',\n data: {\n seq: event.seq,\n role: 'tool',\n tool: { name: String(data?.name ?? 'tool'), state: 'running', summary: summarizeArgs(data?.arguments) },\n ts: tsOf(event)\n }\n };\n }\n case 'tool/result':\n return { kind: 'tool.end', data: { seq: event.seq, role: 'tool', ok: !event.data || (event.data as { error?: unknown }).error === undefined, ts: tsOf(event) } };\n default:\n return null;\n }\n}\n\nfunction tsOf(event: SessionEventLike): number {\n return typeof event.time === 'number' ? event.time : Date.now();\n}\n\n/** Extract plain text from user/assistant message payloads across shapes. */\nfunction messageText(data: unknown): string {\n if (typeof data === 'string') return data;\n if (!data || typeof data !== 'object') return '';\n const obj = data as { content?: unknown; text?: unknown; message?: unknown };\n if (typeof obj.text === 'string') return obj.text;\n // Session events wrap the message: {turn, step, message: {role, content}}\n if (obj.message && typeof obj.message === 'object') return messageText(obj.message);\n return contentText(obj.content);\n}\n\nfunction contentText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n return content.map(part => {\n if (typeof part === 'string') return part;\n if (part && typeof part === 'object') {\n const piece = part as { type?: string; text?: string };\n if (piece.type === 'text' && typeof piece.text === 'string') return piece.text;\n }\n return '';\n }).join('');\n }\n return '';\n}\n\nfunction messageAttachments(data: unknown): Array<{\n kind: 'image'\n name?: string\n mediaType?: string\n attachmentId?: string\n width?: number\n height?: number\n}> {\n if (!data || typeof data !== 'object') return [];\n const obj = data as { content?: unknown; message?: unknown };\n if (obj.message && typeof obj.message === 'object') return messageAttachments(obj.message);\n if (!Array.isArray(obj.content)) return [];\n return obj.content.flatMap(part => {\n if (!part || typeof part !== 'object') return [];\n // The host normalizes uploaded bytes into an ImageAttachmentRef\n // (attachmentId/mediaType/bytes/width/height...); the id is what clients\n // need to read the image back through c2s.session.attachment.\n const block = part as {\n type?: string\n attachment?: {\n name?: unknown\n mediaType?: unknown\n attachmentId?: unknown\n width?: unknown\n height?: unknown\n }\n };\n if (block.type !== 'image' || !block.attachment) return [];\n const attachmentId = typeof block.attachment.attachmentId === 'string' && block.attachment.attachmentId.length > 0\n ? block.attachment.attachmentId\n : undefined;\n const width = typeof block.attachment.width === 'number' && Number.isFinite(block.attachment.width)\n ? block.attachment.width\n : undefined;\n const height = typeof block.attachment.height === 'number' && Number.isFinite(block.attachment.height)\n ? block.attachment.height\n : undefined;\n return [{\n kind: 'image' as const,\n ...(typeof block.attachment.name === 'string' ? { name: block.attachment.name } : {}),\n ...(typeof block.attachment.mediaType === 'string' ? { mediaType: block.attachment.mediaType } : {}),\n ...(attachmentId ? { attachmentId } : {}),\n ...(width !== undefined ? { width } : {}),\n ...(height !== undefined ? { height } : {}),\n }];\n });\n}\n\nfunction attachmentProjection(data: unknown): { attachments?: ReturnType<typeof messageAttachments> } {\n const attachments = messageAttachments(data);\n return attachments.length > 0 ? { attachments } : {};\n}\n\n/** Extract reasoning (\"thinking\") text from assistant message payloads. */\nfunction messageThinking(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n const obj = data as { content?: unknown; message?: unknown };\n // Session events wrap the message: {turn, step, message: {role, content}}\n if (obj.message && typeof obj.message === 'object') return messageThinking(obj.message);\n return reasoningContent(obj.content);\n}\n\nfunction reasoningContent(content: unknown): string {\n if (!Array.isArray(content)) return '';\n return content.map(part => {\n if (part && typeof part === 'object') {\n const piece = part as { type?: string; text?: string };\n if (piece.type === 'reasoning' && typeof piece.text === 'string') return piece.text;\n }\n return '';\n }).join('');\n}\n\n/** Stream chunk type of an assistant/chunk payload ('' when unwrapped). */\nfunction chunkTypeOf(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n const obj = data as { chunk?: unknown };\n if (obj.chunk && typeof obj.chunk === 'object') {\n return String((obj.chunk as { type?: unknown }).type ?? '');\n }\n // Unwrapped payloads carry plain text deltas.\n return 'text-delta';\n}\n\nfunction chunkText(data: unknown): string {\n if (!data || typeof data !== 'object') return '';\n // StreamChunk shape: {turn, step, chunk: {type: 'text-delta'|'reasoning-delta', text}}\n const obj = data as { chunk?: unknown };\n if (obj.chunk && typeof obj.chunk === 'object') {\n const inner = obj.chunk as { type?: string; text?: string };\n if ((inner.type === 'text-delta' || inner.type === 'reasoning-delta') && typeof inner.text === 'string') {\n return inner.text;\n }\n return '';\n }\n const direct = data as { text?: unknown };\n return typeof direct.text === 'string' ? direct.text : '';\n}\n\nfunction summarizeArgs(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '';\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const parts: string[] = [];\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value === 'string') parts.push(key + '=' + truncate(value.replace(/\\s+/g, ' '), 60));\n }\n return truncate(parts.join(' '), 90);\n } catch {\n return truncate(raw, 90);\n }\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length <= max ? text : text.slice(0, max - 1) + '…';\n}\n\nfunction hostModelError(error: { code: string; message?: string }): ModelBridgeResult<never> {\n const message = error.message ?? error.code\n switch (error.code) {\n case 'session-not-found':\n return { ok: false, kind: 'not-found', message }\n case 'agent-busy':\n case 'session-conflict':\n return { ok: false, kind: 'busy', message }\n case 'model-unavailable':\n return { ok: false, kind: 'unavailable', message }\n default:\n return { ok: false, kind: 'internal', message }\n }\n}\n\nfunction hostSessionManagementError(\n error: { code: string; message?: string },\n): SessionManagementResult<never> {\n const message = error.message ?? error.code\n switch (error.code) {\n case 'session-not-found':\n return { ok: false, kind: 'not-found', message }\n case 'agent-busy':\n case 'session-conflict':\n return { ok: false, kind: 'busy', message }\n case 'title-invalid':\n case 'workspace-invalid-path':\n case 'workspace-name-conflict':\n case 'directory-unreadable':\n case 'directory-exists':\n case 'directory-create-failed':\n return { ok: false, kind: 'invalid', message }\n case 'directory-picker-unavailable':\n return { ok: false, kind: 'unsupported', message }\n case 'workspace-not-found':\n return { ok: false, kind: 'not-found', message }\n default:\n return { ok: false, kind: 'internal', message }\n }\n}\n\nfunction projectSessionModels(value: HostSessionModels): HostSessionModels {\n return {\n current: {\n provider: String(value.current.provider),\n model: String(value.current.model),\n ...(value.current.reasoningEffort ? { reasoningEffort: String(value.current.reasoningEffort) } : {}),\n },\n routable: value.routable === true,\n groups: (value.groups ?? []).map((group) => ({\n id: String(group.id),\n name: String(group.name),\n models: (group.models ?? []).map((model) => ({\n id: String(model.id),\n name: String(model.name),\n ...(model.description ? { description: String(model.description) } : {}),\n ...(model.reasoning ? {\n reasoning: {\n efforts: (model.reasoning.efforts ?? []).map((effort) => ({\n id: String(effort.id),\n name: String(effort.name),\n ...(effort.description ? { description: String(effort.description) } : {}),\n })),\n ...(model.reasoning.defaultEffort\n ? { defaultEffort: String(model.reasoning.defaultEffort) }\n : {}),\n },\n } : {}),\n })),\n })),\n failures: (value.failures ?? []).map((failure) => ({\n id: String(failure.id),\n name: String(failure.name),\n message: String(failure.message),\n })),\n }\n}\n\n/** Project a history page (raw events) into MessageProjection rows. */\nexport function projectHistory(events: Array<{ event: SessionEventLike; view?: unknown }>): MessageProjection[] {\n const messages: MessageProjection[] = [];\n const toolByCall = new Map<string, MessageProjection>();\n for (const entry of events) {\n const event = entry.event;\n const base = { seq: event.seq, ts: tsOf(event) };\n switch (event.type) {\n case 'user/message':\n messages.push({\n ...base,\n role: 'user',\n text: messageText(event.data),\n ...attachmentProjection(event.data),\n });\n break;\n case 'assistant/message': {\n const text = messageText(event.data);\n const thinking = messageThinking(event.data);\n if (!text.trim() && !thinking.trim()) break;\n messages.push({ ...base, role: 'assistant', text, ...(thinking ? { thinking } : {}) });\n break;\n }\n case 'tool/call': {\n const data = event.data as { name?: string; arguments?: string; callId?: string } | undefined;\n const row: MessageProjection = {\n ...base,\n role: 'tool',\n tool: { name: String(data?.name ?? 'tool'), state: 'running', summary: summarizeArgs(data?.arguments) },\n };\n messages.push(row);\n if (data?.callId) toolByCall.set(String(data.callId), row);\n break;\n }\n case 'tool/result': {\n const data = event.data as { callId?: string; message?: { content?: unknown }; error?: unknown } | undefined;\n const callId = data?.callId ? String(data.callId) : undefined;\n const target = callId ? toolByCall.get(callId) : undefined;\n const failed = data?.error !== undefined;\n const summary = failed ? '失败' : summarizeResult(data?.message?.content);\n if (target?.tool) {\n target.tool = { ...target.tool, state: failed ? 'error' : 'ok', summary };\n } else {\n messages.push({ ...base, role: 'tool', tool: { name: 'result', state: failed ? 'error' : 'ok', summary } });\n }\n break;\n }\n default:\n break;\n }\n }\n return messages.sort((a, b) => a.seq - b.seq);\n}\n\nfunction summarizeResult(content: unknown): string {\n const text = contentText(content).replace(/\\s+/g, ' ').trim();\n return truncate(text, 90);\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'\nimport type { DeepPilotReport, PushTestResult, RelayTestResult } from './report-wire.ts'\n\n/** Async snapshot source wired by the plugin entry. */\nexport type ReportSnapshot = () => Promise<DeepPilotReport>\n\n/** Reads the pairing secret only for an explicit reveal action. */\nexport type PairingTokenSnapshot = () => Promise<string>\n\n/**\n * Replaces the pairing secret for an explicit rotation action: persists the\n * new token, invalidates the old one, and drops live phone connections.\n */\nexport type PairingTokenRotator = () => Promise<string>\n\n/** Runs the relay connectivity self-test (health + enrollment round-trip). */\nexport type RelayTester = () => Promise<RelayTestResult>\n\n/** Forces one synthetic notification down the active push pathway. */\nexport type PushTester = () => Promise<PushTestResult>\n\n/**\n * The Typert receiver the Gateway resolves for the DeepPilot Bridge report.\n * Snapshot data stays non-secret; the token crosses the boundary only through\n * the explicit, user-triggered revealToken/rotateToken invocations.\n */\nexport class DeepPilotReportService extends TypertRemoteService {\n private readonly snapshot: ReportSnapshot\n private readonly pairingToken: PairingTokenSnapshot\n private readonly rotatePairingToken: PairingTokenRotator\n\n private readonly relayTester: RelayTester\n private readonly pushTester: PushTester\n\n constructor(ctx: Context, snapshot: ReportSnapshot, pairingToken: PairingTokenSnapshot, rotatePairingToken: PairingTokenRotator, relayTester: RelayTester, pushTester: PushTester) {\n super(ctx, 'deeppilotReport', { namespace: 'deeppilot' })\n this.snapshot = snapshot\n this.pairingToken = pairingToken\n this.rotatePairingToken = rotatePairingToken\n this.relayTester = relayTester\n this.pushTester = pushTester\n }\n\n async report(): Promise<DeepPilotReport> {\n return this.snapshot()\n }\n\n async revealToken(): Promise<string> {\n return this.pairingToken()\n }\n\n async rotateToken(): Promise<string> {\n return this.rotatePairingToken()\n }\n\n async testRelay(): Promise<RelayTestResult> {\n return this.relayTester()\n }\n\n async testPush(): Promise<PushTestResult> {\n return this.pushTester()\n }\n}\n","/**\n * Wire contract for the DeepPilot report Remote (`deeppilot/report`).\n *\n * The web settings page needs the bridge's live facts (paired devices,\n * connections, token state) — those live Host-side. This module is the single\n * source both halves share: the strict result codec (hand-rolled, so the\n * client bundle needs no schema library) and the exact descriptor object.\n *\n * @module dsh-deeppilot/report-wire\n */\n\nimport type { InvocationDescriptor, TypertRemoteContribution, TypertSchema } from '@deepseek-ai/dsh-typert-protocol'\n\n/** One paired device row in the report. */\nexport interface ReportDevice {\n deviceId: string\n deviceName: string\n appVersion: string\n firstSeenTs: number\n lastSeenTs: number\n /** Present once the device registered an APNs token (token itself never leaves the host). */\n apns?: { environment: 'development' | 'production'; updatedAt: number }\n}\n\n/** One step of the relay connectivity self-test. */\nexport interface RelayTestStep {\n id: 'health' | 'enroll'\n ok: boolean\n message: string\n latencyMs?: number\n}\n\n/** Result of deeppilot/testRelay. */\nexport interface RelayTestResult {\n url: string\n overall: 'ok' | 'failed'\n tokenIssued: boolean\n steps: RelayTestStep[]\n}\n\n/** Per-device delivery result of deeppilot/testPush. */\nexport interface PushTestDeviceResult {\n name: string\n environment: string\n /** sent | invalid-token | failed — mirrors the dispatch outcome contract. */\n outcome: string\n /** Apple's raw rejection reason when present (BadDeviceToken, …). */\n reason?: string\n /** First 10 hex chars of the stored token, for freshness verification. */\n tokenFingerprint?: string\n}\n\nexport interface PushTestResult {\n transport: 'apns' | 'relay' | 'none'\n /** sent = at least one accepted delivery; no-targets / not-configured / failed. */\n overall: 'sent' | 'failed' | 'no-targets' | 'not-configured'\n message?: string\n results: PushTestDeviceResult[]\n}\n\n/** Everything the settings page renders for the bridge. */\nexport interface DeepPilotReport {\n protocolVersion: number\n serverVersion: string\n /** Whether the master bridge switch is on. */\n enabled: boolean\n /** Absolute token file path (never the token itself). */\n tokenPath: string\n /** Whether the token file exists and is readable. */\n tokenReady: boolean\n /** Currently connected phone sockets. */\n activeConnections: number\n historyBufferMax: number\n debug: boolean\n /** Private IPv4 candidates for local QR pairing. */\n lanAddresses: string[]\n remote: {\n provider: 'tailscale-funnel'\n phase: 'disabled' | 'starting' | 'login_required' | 'online' | 'error' | 'unavailable' | 'stopped'\n publicURL?: string\n authURL?: string\n message?: string\n updatedAt: number\n }\n devices: ReportDevice[]\n}\n\n/** The npm package identity both contribution registrations claim. */\nexport const REPORT_REMOTE_PACKAGE = 'dsh-deeppilot'\n\n/** Canonical `<namespace>/<method>` endpoint of the report Remote. */\nexport const REPORT_ENDPOINT = 'deeppilot/report'\n\n/** Explicit, user-triggered endpoint for revealing the pairing secret. */\nexport const REVEAL_TOKEN_ENDPOINT = 'deeppilot/revealToken'\n\n/**\n * Explicit, user-triggered endpoint that replaces the pairing secret. The old\n * token stops working immediately; the fresh one is returned so the page can\n * show/QR it right away.\n */\nexport const ROTATE_TOKEN_ENDPOINT = 'deeppilot/rotateToken'\n\nfunction reject(field: string): never {\n throw new TypeError(`deeppilot/report result: invalid ${field}`)\n}\n\nfunction str(source: Record<string, unknown>, key: string, field: string): string {\n const value = source[key]\n if (typeof value !== 'string') reject(field)\n return value\n}\nfunction num(source: Record<string, unknown>, key: string, field: string): number {\n const value = source[key]\n if (typeof value !== 'number' || !Number.isFinite(value)) reject(field)\n return value\n}\nfunction bool(source: Record<string, unknown>, key: string, field: string): boolean {\n const value = source[key]\n if (typeof value !== 'boolean') reject(field)\n return value\n}\nfunction rec(value: unknown, field: string): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) reject(field)\n return value as Record<string, unknown>\n}\n\nfunction parseDevice(value: unknown): ReportDevice {\n const s = rec(value, 'device')\n let apns: ReportDevice['apns']\n if (s.apns !== undefined) {\n const a = rec(s.apns, 'device.apns')\n const environment = str(a, 'environment', 'device.apns.environment')\n if (environment !== 'development' && environment !== 'production') reject('device.apns.environment')\n apns = { environment, updatedAt: num(a, 'updatedAt', 'device.apns.updatedAt') }\n }\n return {\n deviceId: str(s, 'deviceId', 'device.deviceId'),\n deviceName: str(s, 'deviceName', 'device.deviceName'),\n appVersion: str(s, 'appVersion', 'device.appVersion'),\n firstSeenTs: num(s, 'firstSeenTs', 'device.firstSeenTs'),\n lastSeenTs: num(s, 'lastSeenTs', 'device.lastSeenTs'),\n ...(apns ? { apns } : {}),\n }\n}\n\nfunction parseRemote(value: unknown): DeepPilotReport['remote'] {\n const s = rec(value, 'remote')\n const provider = str(s, 'provider', 'remote.provider')\n const phase = str(s, 'phase', 'remote.phase')\n if (provider !== 'tailscale-funnel') reject('remote.provider')\n const phases = ['disabled', 'starting', 'login_required', 'online', 'error', 'unavailable', 'stopped']\n if (!phases.includes(phase)) reject('remote.phase')\n const publicURL = s.publicURL\n const authURL = s.authURL\n const message = s.message\n if (publicURL !== undefined && typeof publicURL !== 'string') reject('remote.publicURL')\n if (authURL !== undefined && typeof authURL !== 'string') reject('remote.authURL')\n if (message !== undefined && typeof message !== 'string') reject('remote.message')\n return {\n provider,\n phase: phase as DeepPilotReport['remote']['phase'],\n ...(typeof publicURL === 'string' ? { publicURL } : {}),\n ...(typeof authURL === 'string' ? { authURL } : {}),\n ...(typeof message === 'string' ? { message } : {}),\n updatedAt: num(s, 'updatedAt', 'remote.updatedAt'),\n }\n}\n\nfunction parseRelayTestStep(value: unknown): RelayTestStep {\n const st = rec(value, 'step')\n const id = str(st, 'id', 'step.id')\n if (id !== 'health' && id !== 'enroll') reject('step.id')\n const latencyMs = st.latencyMs\n if (latencyMs !== undefined && typeof latencyMs !== 'number') reject('step.latencyMs')\n return {\n id,\n ok: bool(st, 'ok', 'step.ok'),\n message: str(st, 'message', 'step.message'),\n ...(typeof latencyMs === 'number' ? { latencyMs } : {}),\n }\n}\n\nfunction parseRelayTestResult(value: unknown): RelayTestResult {\n const s = rec(value, 'result')\n const overall = str(s, 'overall', 'overall')\n if (overall !== 'ok' && overall !== 'failed') reject('overall')\n const stepsRaw = s.steps\n if (!Array.isArray(stepsRaw)) reject('steps')\n return {\n url: str(s, 'url', 'url'),\n overall,\n tokenIssued: bool(s, 'tokenIssued', 'tokenIssued'),\n steps: (stepsRaw as unknown[]).map(parseRelayTestStep),\n }\n}\n\nfunction parsePushTestResult(value: unknown): PushTestResult {\n const s = rec(value, 'result')\n const transport = str(s, 'transport', 'transport')\n if (transport !== 'apns' && transport !== 'relay' && transport !== 'none') reject('transport')\n const overall = str(s, 'overall', 'overall')\n const overalls = ['sent', 'failed', 'no-targets', 'not-configured']\n if (!overalls.includes(overall)) reject('overall')\n const resultsRaw = s.results\n if (!Array.isArray(resultsRaw)) reject('results')\n const results = (resultsRaw as unknown[]).map((value) => {\n const r = rec(value, 'device result')\n const reason = r.reason\n const tokenFingerprint = r.tokenFingerprint\n return {\n name: str(r, 'name', 'result.name'),\n environment: str(r, 'environment', 'result.environment'),\n outcome: str(r, 'outcome', 'result.outcome'),\n ...(typeof reason === 'string' && reason.length > 0 ? { reason } : {}),\n ...(typeof tokenFingerprint === 'string' && /^[0-9a-f]{1,32}$/.test(tokenFingerprint)\n ? { tokenFingerprint }\n : {}),\n }\n })\n const message = s.message\n return {\n transport,\n overall: overall as PushTestResult['overall'],\n ...(typeof message === 'string' && message.length > 0 ? { message } : {}),\n results,\n }\n}\n\nfunction parseReport(value: unknown): DeepPilotReport {\n const s = rec(value, 'report')\n const devices = s.devices\n const lanAddresses = s.lanAddresses\n if (!Array.isArray(devices)) reject('devices')\n if (!Array.isArray(lanAddresses) || lanAddresses.some((value) => typeof value !== 'string')) reject('lanAddresses')\n return {\n protocolVersion: num(s, 'protocolVersion', 'protocolVersion'),\n serverVersion: str(s, 'serverVersion', 'serverVersion'),\n enabled: bool(s, 'enabled', 'enabled'),\n tokenPath: str(s, 'tokenPath', 'tokenPath'),\n tokenReady: bool(s, 'tokenReady', 'tokenReady'),\n activeConnections: num(s, 'activeConnections', 'activeConnections'),\n historyBufferMax: num(s, 'historyBufferMax', 'historyBufferMax'),\n debug: bool(s, 'debug', 'debug'),\n lanAddresses: lanAddresses as string[],\n remote: parseRemote(s.remote),\n devices: (devices as unknown[]).map(parseDevice),\n }\n}\n\nexport const reportSchema: TypertSchema<DeepPilotReport> = { parse: parseReport }\n\nexport const relayTestSchema: TypertSchema<RelayTestResult> = { parse: parseRelayTestResult }\n\nexport const pushTestSchema: TypertSchema<PushTestResult> = { parse: parsePushTestResult }\n\nexport const pairingTokenSchema: TypertSchema<string> = {\n parse(value: unknown): string {\n if (typeof value !== 'string' || value.length < 32) {\n throw new TypeError('deeppilot/revealToken result: invalid token')\n }\n return value\n },\n}\n\n/**\n * Connectivity self-test for the push relay: health probe plus (when an\n * enroll key is on file) a real enrollment round-trip. Doubles as repair —\n * a token it issues is cached by the bridge.\n */\nexport const TEST_RELAY_ENDPOINT = 'deeppilot/testRelay'\n\n/**\n * Real-delivery push test: forces one synthetic notification down the active\n * push pathway to every registered device, ignoring connected/muted filters —\n * an explicit user action must always be able to prove the pipeline works.\n */\nexport const TEST_PUSH_ENDPOINT = 'deeppilot/testPush'\n\nexport const REPORT_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${REPORT_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'report',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#DeepPilotReport`,\n schema: reportSchema,\n },\n}\n\nexport const REVEAL_TOKEN_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${REVEAL_TOKEN_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'revealToken',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#PairingToken`,\n schema: pairingTokenSchema,\n },\n}\n\nexport const ROTATE_TOKEN_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${ROTATE_TOKEN_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'rotateToken',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#PairingToken`,\n schema: pairingTokenSchema,\n },\n}\n\nexport const TEST_RELAY_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${TEST_RELAY_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'testRelay',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#RelayTestResult`,\n schema: relayTestSchema,\n },\n}\n\nexport const TEST_PUSH_DESCRIPTOR: InvocationDescriptor = {\n id: `${REPORT_REMOTE_PACKAGE}#${TEST_PUSH_ENDPOINT}`,\n service: 'deeppilotReport',\n namespace: 'deeppilot',\n method: 'testPush',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${REPORT_REMOTE_PACKAGE}#PushTestResult`,\n schema: pushTestSchema,\n },\n}\n\nconst INVOCATION_DESCRIPTORS = [\n REPORT_DESCRIPTOR,\n REVEAL_TOKEN_DESCRIPTOR,\n ROTATE_TOKEN_DESCRIPTOR,\n TEST_RELAY_DESCRIPTOR,\n TEST_PUSH_DESCRIPTOR,\n]\n\nexport const REPORT_HOST_CONTRIBUTION = {\n package: REPORT_REMOTE_PACKAGE,\n face: 'host' as const,\n schemas: [],\n invocations: INVOCATION_DESCRIPTORS,\n}\n\nexport const REPORT_REMOTE_CONTRIBUTION: TypertRemoteContribution = {\n package: REPORT_REMOTE_PACKAGE,\n descriptors: INVOCATION_DESCRIPTORS,\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { DeepPilotReportService } from './report-service.ts'\nimport type { PairingTokenRotator, PairingTokenSnapshot, PushTester, RelayTester, ReportSnapshot } from './report-service.ts'\nimport { REPORT_HOST_CONTRIBUTION } from './report-wire.ts'\n\ninterface TypertContributionRegistry {\n register(contribution: typeof REPORT_HOST_CONTRIBUTION): () => void | Promise<void>\n}\n\n/**\n * Provide the report service and register its Remote descriptor. Rides an\n * optional `typert` inject: profiles without the web stack never activate it.\n */\nexport function applyReportRemote(\n ctx: Context,\n snapshot: ReportSnapshot,\n pairingToken: PairingTokenSnapshot,\n rotatePairingToken: PairingTokenRotator,\n relayTester: RelayTester,\n pushTester: PushTester,\n): void {\n ctx.inject(['typert'], (remoteCtx) => {\n new DeepPilotReportService(remoteCtx, snapshot, pairingToken, rotatePairingToken, relayTester, pushTester)\n const registry = remoteCtx.typert as unknown as TypertContributionRegistry\n const unregister = registry.register(REPORT_HOST_CONTRIBUTION)\n remoteCtx.effect(() => () => void unregister(), 'dsh-deeppilot: report remote')\n })\n}\n","/**\n * Relay connectivity self-test used by the settings page \"测试中继\" button.\n *\n * Two steps mirror what zero-touch enrollment actually does:\n * 1. health — GET {url}/healthz → is the relay reachable?\n * 2. enroll — POST {url}/v1/enroll → does the shared key grant a token?\n *\n * The enroll step doubles as a repair path: a token it issues is handed back\n * via `onEnrolled` so the bridge caches it and flips push readiness without\n * waiting for the next app registration.\n *\n * Transport is injectable (`fetchImpl`) so tests never touch the network.\n */\n\nexport interface RelayTestStep {\n id: 'health' | 'enroll'\n ok: boolean\n /** Human-readable, user-facing (zh) outcome; safe to render verbatim. */\n message: string\n latencyMs?: number\n}\n\nexport interface RelayTestResult {\n url: string\n overall: 'ok' | 'failed'\n /** True when the probe produced/cached a usable relay token. */\n tokenIssued: boolean\n steps: RelayTestStep[]\n}\n\nexport interface RelayProbeOptions {\n url: string\n /** Stable per-bridge identity for enrollment; generated upstream if absent. */\n clientId?: string\n /** Distributor's shared key; absent → enroll step reports why it cannot run. */\n enrollKey?: string\n /** True when the user configured relayToken manually (enroll unnecessary). */\n manualToken?: boolean\n timeoutMs?: number\n fetchImpl?: typeof fetch\n /** Called with an issued token so the caller can persist it. */\n onEnrolled?: (token: string) => void\n}\n\nconst DEFAULT_TIMEOUT_MS = 6_000\n\nasync function requestJson(\n fetchImpl: typeof fetch,\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<{ status: number; body: unknown }> {\n const response = await fetchImpl(url, {\n ...init,\n signal: AbortSignal.timeout(timeoutMs),\n })\n let body: unknown = null\n try {\n body = await response.json()\n } catch {\n // non-JSON body keeps body null; status still drives the verdict\n }\n return { status: response.status, body }\n}\n\nexport async function runRelayProbe(options: RelayProbeOptions): Promise<RelayTestResult> {\n const base = options.url.trim().replace(/\\/+$/, '')\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const fetchImpl = options.fetchImpl ?? fetch\n const steps: RelayTestStep[] = []\n let tokenIssued = false\n\n // ---- step 1: reachability ----\n try {\n const startedAt = Date.now()\n const { status, body } = await requestJson(\n fetchImpl,\n `${base}/healthz`,\n { method: 'GET' },\n timeoutMs,\n )\n const latencyMs = Date.now() - startedAt\n if (status === 200 && (body as { ok?: unknown })?.ok === true) {\n steps.push({ id: 'health', ok: true, message: '中继服务可达', latencyMs })\n } else {\n steps.push({ id: 'health', ok: false, message: `中继响应异常(HTTP ${status})`, latencyMs })\n }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n steps.push({ id: 'health', ok: false, message: '无法连接中继:' + reason })\n }\n\n // ---- step 2: enrollment ----\n if ((options.manualToken ?? false)) {\n steps.push({ id: 'enroll', ok: true, message: '已手动配置 relayToken,跳过注册验证' })\n } else if (!options.enrollKey) {\n steps.push({ id: 'enroll', ok: false, message: '尚无注册密钥:等待分发版 App 首次注册后才能验证注册' })\n } else {\n const clientId = options.clientId ?? 'u_' + Math.random().toString(36).slice(2)\n try {\n const startedAt = Date.now()\n const { status, body } = await requestJson(\n fetchImpl,\n `${base}/v1/enroll`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ clientId, enrollKey: options.enrollKey }),\n },\n timeoutMs,\n )\n const latencyMs = Date.now() - startedAt\n const token = (body as { token?: unknown })?.token\n if (status === 200 && typeof token === 'string' && token.startsWith('rl_')) {\n tokenIssued = true\n options.onEnrolled?.(token)\n steps.push({ id: 'enroll', ok: true, message: '注册成功,已取得推送凭证', latencyMs })\n } else if (status === 403) {\n steps.push({ id: 'enroll', ok: false, message: '注册被拒:注册密钥不匹配(检查 App 内 DSPushEnrollKey 与服务器 RELAY_ENROLL_KEY)', latencyMs })\n } else if (status === 429) {\n steps.push({ id: 'enroll', ok: false, message: '尝试过于频繁,稍后再试', latencyMs })\n } else {\n steps.push({ id: 'enroll', ok: false, message: `注册失败(HTTP ${status})`, latencyMs })\n }\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n steps.push({ id: 'enroll', ok: false, message: '注册请求失败:' + reason })\n }\n }\n\n const executed = steps.filter((step) => step.ok !== undefined)\n const overall: RelayTestResult['overall'] =\n steps.length > 0 && steps.some((step) => step.id === 'health' && step.ok) &&\n executed.every((step) => step.ok)\n ? 'ok'\n : 'failed'\n return { url: base, overall, tokenIssued, steps }\n}\n","/**\n * Minimal APNs provider client (HTTP/2) with zero npm dependencies.\n *\n * Implements exactly what the bridge needs:\n * - ES256 provider token (JWT) signed with an Apple .p8 key, refreshed under\n * the 1-hour freshness window Apple enforces;\n * - one long-lived HTTP/2 session per environment, recreated transparently\n * after GOAWAY/errors;\n * - alert pushes carrying the notify projection (category/thread/collapse),\n * with `interruption-level: time-sensitive` for approval/question events;\n * - outcome classification so callers can prune dead device tokens.\n *\n * Privacy: logs carry outcomes and masked token prefixes only — never message\n * bodies or full tokens.\n */\n\nimport { connect as http2Connect, type ClientHttp2Session } from 'node:http2'\nimport { createPrivateKey, sign as cryptoSign, type KeyObject } from 'node:crypto'\nimport { readFile } from 'node:fs/promises'\nimport type { ApnsEnvironment } from './token.ts'\nimport type { PushNotification } from './protocol.ts'\n\nexport interface ApnsClientOptions {\n teamId: string\n keyId: string\n /** Absolute path to the .p8 private key file. */\n keyPath: string\n bundleId: string\n debug?: boolean\n log: (message: string) => void\n}\n\nexport type ApnsOutcome = 'sent' | 'invalid-token' | 'failed'\n\n/** Outcome plus Apple's rejection reason (when present) for diagnosis. */\nexport interface ApnsSendResult {\n outcome: ApnsOutcome\n reason?: string\n}\n\nconst PROVIDER_TOKEN_TTL_MS = 50 * 60 * 1000 // Apple allows 1h; refresh early.\nconst REQUEST_TIMEOUT_MS = 10_000\n\n/** base64url without padding. */\nfunction b64url(input: Buffer | string): string {\n return Buffer.from(input).toString('base64url')\n}\n\n/** Sign one ES256 JWT for the given signing input with a P-256 private key. */\nexport function es256Jwt(signingInput: string, key: KeyObject): string {\n const signature = cryptoSign('sha256', Buffer.from(signingInput, 'utf8'), {\n key,\n // JOSE/ES256 wants raw r||s; node emits DER by default.\n dsaEncoding: 'ieee-p1363',\n })\n return signingInput + '.' + b64url(signature)\n}\n\n/** Strip PEM armor from a .p8 file and decode to PKCS#8 DER. */\nexport function p8ToDer(pem: string): Buffer {\n const body = pem\n .replace(/-----BEGIN PRIVATE KEY-----/, '')\n .replace(/-----END PRIVATE KEY-----/, '')\n .replace(/\\s+/g, '')\n return Buffer.from(body, 'base64')\n}\n\nexport interface LoadedKey {\n key: KeyObject\n}\n\n/** Pure payload builder so tests can assert the wire format without sockets. */\nexport function apnsPayload(notification: PushNotification): Record<string, unknown> {\n const timeSensitive = notification.category === 'approval.required' || notification.category === 'question.asked'\n return {\n aps: {\n alert: {\n title: notification.title.slice(0, 120),\n body: notification.body.slice(0, 200),\n },\n sound: 'default',\n category: notification.category,\n 'thread-id': notification.sessionId.slice(0, 64),\n ...(timeSensitive ? { 'interruption-level': 'time-sensitive' } : {}),\n },\n sessionId: notification.sessionId,\n notificationId: notification.notificationId,\n kind: notification.category,\n }\n}\n\n/** One delivery request: the notify projection plus its target device token.\n * The environment comes from the device's own registration (its build kind),\n * so one client serves both sandbox and production devices. */\nexport interface ApnsSendRequest extends PushNotification {\n deviceToken: string\n environment: ApnsEnvironment\n}\n\n/** collapse-id accepts ≤64 bytes of ASCII; keep it stable per session+event. */\nexport function collapseIdFor(notification: PushNotification): string {\n const raw = `${notification.category}:${notification.sessionId}`\n return raw.replace(/[^a-zA-Z0-9.:-]/g, '').slice(0, 64)\n}\n\nfunction authorityFor(environment: ApnsEnvironment): string {\n return environment === 'production' ? 'api.push.apple.com' : 'api.sandbox.push.apple.com'\n}\n\nexport class ApnsClient {\n private readonly log: (message: string) => void\n private readonly debug: boolean\n private readonly opts: ApnsClientOptions\n\n private providerToken = ''\n private providerTokenIssuedAt = 0\n private key: KeyObject | undefined\n\n constructor(opts: ApnsClientOptions) {\n this.opts = opts\n this.log = opts.log\n this.debug = opts.debug === true\n }\n\n async dispose(): Promise<void> {\n const sessions = [...this.sessions.values()]\n this.sessions.clear()\n await Promise.all(\n sessions\n .filter((session) => !session.destroyed)\n .map((session) => new Promise<void>((resolve) => session.close(() => resolve()))),\n )\n }\n\n // ---------- credentials ----------\n\n private async ensureProviderToken(): Promise<string> {\n if (this.providerToken && Date.now() - this.providerTokenIssuedAt < PROVIDER_TOKEN_TTL_MS) {\n return this.providerToken\n }\n if (!this.key) {\n const pem = await readFile(this.opts.keyPath, 'utf8')\n this.key = createPrivateKey({ key: p8ToDer(pem), format: 'der', type: 'pkcs8' })\n }\n const issuedAt = Math.floor(Date.now() / 1000)\n const header = b64url(JSON.stringify({ alg: 'ES256', kid: this.opts.keyId }))\n const claims = b64url(JSON.stringify({ iss: this.opts.teamId, iat: issuedAt }))\n this.providerToken = es256Jwt(`${header}.${claims}`, this.key)\n this.providerTokenIssuedAt = Date.now()\n return this.providerToken\n }\n\n // ---------- transport ----------\n\n /** One long-lived HTTP/2 session per Apple host (sandbox + production). */\n private readonly sessions = new Map<string, ClientHttp2Session>()\n\n private ensureSession(authority: string): ClientHttp2Session {\n const existing = this.sessions.get(authority)\n if (existing && !existing.destroyed && !existing.closed) return existing\n const session = http2Connect(`https://${authority}`)\n // Keep a handler attached so background errors never become uncaught\n // exceptions; the next send() recreates the session when dead.\n session.on('error', (error) => {\n if (this.debug) this.log('apns session error (' + authority + '): ' + String(error))\n this.sessions.delete(authority)\n })\n this.sessions.set(authority, session)\n return session\n }\n\n /**\n * Deliver one alert. Never throws — every failure path resolves to an\n * outcome so fan-out loops cannot crash the host on a flaky network.\n */\n async send(request: ApnsSendRequest): Promise<ApnsSendResult> {\n const { deviceToken, environment, ...notification } = request\n let stream: ReturnType<ClientHttp2Session['request']> | undefined\n try {\n const token = await this.ensureProviderToken()\n const body = JSON.stringify(apnsPayload(notification))\n const session = this.ensureSession(authorityFor(environment))\n const done = new Promise<ApnsSendResult>((resolve) => {\n const req = session.request({\n [':method']: 'POST',\n [':path']: '/3/device/' + deviceToken,\n authorization: 'bearer ' + token,\n 'apns-topic': this.opts.bundleId,\n 'apns-push-type': 'alert',\n 'apns-priority': '10',\n // Stale approvals arriving hours later are worse than none.\n 'apns-expiration': String(Math.floor(Date.now() / 1000) + 3600),\n 'apns-collapse-id': collapseIdFor(notification),\n 'content-type': 'application/json',\n 'content-length': String(Buffer.byteLength(body)),\n })\n stream = req\n let status = 0\n let responseBody = ''\n const settle = (outcome: ApnsOutcome, reason?: string): void => {\n if (this.debug) {\n this.log(`apns ${outcome}${reason ? ' (' + reason + ')' : ''} (${this.maskToken(deviceToken)})`)\n }\n resolve(reason !== undefined && reason !== '' ? { outcome, reason } : { outcome })\n }\n const timer = setTimeout(() => {\n req.close()\n settle('failed')\n }, REQUEST_TIMEOUT_MS)\n timer.unref?.()\n req.on('response', (headers) => {\n status = Number(headers[':status'] ?? 0)\n })\n req.on('data', (chunk: Buffer) => {\n responseBody += chunk.toString('utf8')\n })\n req.on('error', () => {\n clearTimeout(timer)\n settle('failed')\n })\n req.on('end', () => {\n clearTimeout(timer)\n if (status === 200) return settle('sent')\n let reason = ''\n try {\n reason = String((JSON.parse(responseBody) as { reason?: string }).reason ?? '')\n } catch {\n // non-JSON body; fall through with empty reason\n }\n if (status !== 200 && !reason) reason = 'HTTP ' + String(status)\n if (reason === 'Unregistered' || reason === 'BadDeviceToken') return settle('invalid-token', reason)\n if (this.debug) this.log(`apns rejected status=${status} reason=${reason}`)\n settle('failed', reason)\n })\n req.end(body)\n })\n return await done\n } catch (error) {\n // Credential load failures and dead sessions land here; force both to be\n // rebuilt on the next attempt.\n this.key = undefined\n this.providerToken = ''\n this.sessions.clear()\n if (this.debug) this.log('apns send failed: ' + String(error))\n return { outcome: 'failed', reason: String(error).slice(0, 120) }\n } finally {\n try { stream?.close() } catch { /* already closed */ }\n }\n }\n\n maskToken(token: string): string {\n return token.length <= 10 ? '…' : token.slice(0, 6) + '…' + token.slice(-4)\n }\n}\n","/**\n * Client for the operator-run push relay (relay/server.js). Used when the App\n * is distributed under the operator's team: bridges cannot hold the\n * distributor's .p8, so they forward notify projections over HTTPS and let\n * the relay do APNs.\n *\n * Outcome contract mirrors src/apns.ts exactly so the dispatcher prunes dead\n * tokens identically on either path:\n * sent | invalid-token (Unregistered/BadDeviceToken) | failed\n */\n\nimport type { ApnsEnvironment } from './token.ts'\nimport type { PushNotification } from './protocol.ts'\n\nexport interface RelayClientOptions {\n /** Base URL of the relay, e.g. https://relay.example.com */\n url: string\n /** Per-user bearer token issued by the relay operator (or via enroll). */\n token?: string\n timeoutMs?: number\n debug?: boolean\n log: (message: string) => void\n}\n\nexport interface RelaySendRequest {\n deviceToken: string\n environment: ApnsEnvironment\n notification: PushNotification\n}\n\nexport class RelayClient {\n private readonly base: string\n private readonly token: string\n private readonly timeoutMs: number\n private readonly debug: boolean\n private readonly log: (message: string) => void\n\n constructor(opts: RelayClientOptions) {\n this.base = opts.url.trim().replace(/\\/+$/, '')\n this.token = (opts.token ?? '').trim()\n this.timeoutMs = opts.timeoutMs ?? 10_000\n this.debug = opts.debug === true\n this.log = opts.log\n }\n\n /**\n * Zero-touch enrollment: exchange the distributor's shared key (baked into\n * the distributed app) for a stable per-bridge bearer token. Idempotent —\n * relays derive the same token for the same clientId. Returns null on any\n * failure; callers treat that as \"not enrolled yet\", not as an error.\n */\n async enroll(clientId: string, enrollKey: string): Promise<string | null> {\n try {\n const response = await fetch(this.base + '/v1/enroll', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ clientId, enrollKey }),\n signal: AbortSignal.timeout(this.timeoutMs),\n })\n if (!response.ok) {\n if (this.debug) this.log(`enroll http ${response.status}`)\n return null\n }\n const body = (await response.json()) as { token?: unknown }\n if (typeof body.token === 'string' && body.token.startsWith('rl_')) return body.token\n if (this.debug) this.log('enroll returned no usable token')\n return null\n } catch (error) {\n if (this.debug) this.log('enroll failed: ' + String(error))\n return null\n }\n }\n\n async send(request: RelaySendRequest): Promise<{ outcome: 'sent' | 'invalid-token' | 'failed'; reason?: string }> {\n try {\n const response = await fetch(this.base + '/v1/push', {\n method: 'POST',\n headers: {\n authorization: 'Bearer ' + this.token,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n deviceToken: request.deviceToken,\n environment: request.environment,\n notification: request.notification,\n }),\n signal: AbortSignal.timeout(this.timeoutMs),\n })\n if (response.status === 401 || response.status === 429) {\n // Credential/quota problems are configuration errors, not token death;\n // surface as failed so the user fixes the config instead of silently\n // losing registrations.\n if (this.debug) this.log(`relay rejected status=${response.status}`)\n return { outcome: 'failed', reason: 'HTTP ' + String(response.status) }\n }\n if (!response.ok) {\n if (this.debug) this.log(`relay http ${response.status}`)\n return { outcome: 'failed', reason: 'HTTP ' + String(response.status) }\n }\n const body = (await response.json()) as { outcome?: string; reason?: string }\n if (body.outcome === 'sent') return { outcome: 'sent' }\n if (body.outcome === 'invalid-token') return { outcome: 'invalid-token', reason: body.reason }\n if (this.debug) this.log('relay outcome=' + String(body.outcome) + ' reason=' + String(body.reason ?? ''))\n return { outcome: 'failed', reason: body.reason }\n } catch (error) {\n if (this.debug) this.log('relay send failed: ' + String(error))\n const message = error instanceof Error ? error.message.slice(0, 120) : String(error).slice(0, 120)\n return { outcome: 'failed', reason: message }\n }\n }\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport { access, mkdir } from 'node:fs/promises'\nimport { constants as fsConstants } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { expandHome } from './token.ts'\n\nexport type RemotePhase =\n | 'disabled'\n | 'starting'\n | 'login_required'\n | 'online'\n | 'error'\n | 'unavailable'\n | 'stopped'\n\nexport interface RemoteStatus {\n provider: 'tailscale-funnel'\n phase: RemotePhase\n publicURL?: string\n authURL?: string\n message?: string\n updatedAt: number\n}\n\nexport interface RemoteSupervisorOptions {\n enabled: boolean\n hostname: string\n statePath: string\n helperPath?: string\n funnelPort?: 443 | 8443 | 10000\n log: (message: string) => void\n}\n\ninterface HelperEvent {\n phase?: RemotePhase\n publicURL?: string\n authURL?: string\n message?: string\n}\n\nconst RESTART_DELAYS_MS = [1_000, 2_000, 4_000, 8_000, 16_000, 30_000]\n\nexport const DEFAULT_REMOTE_HOSTNAME = 'dsh-deeppilot'\n\n/** Preserve custom node names while migrating every pre-DeepPilot default. */\nexport function normalizeRemoteHostname(value: string | undefined): string {\n const hostname = value?.trim() ?? ''\n if (hostname === '' || ['dsh-phone', 'dsh-pocket', 'harnesspocket'].includes(hostname.toLowerCase())) {\n return DEFAULT_REMOTE_HOSTNAME\n }\n return hostname\n}\n\n/** Parse one helper IPC line without ever evaluating or interpolating it. */\nexport function parseHelperEvent(line: string): HelperEvent | null {\n try {\n const value = JSON.parse(line) as Record<string, unknown>\n const phases: RemotePhase[] = ['starting', 'login_required', 'online', 'error', 'stopped']\n if (typeof value.phase !== 'string' || !phases.includes(value.phase as RemotePhase)) return null\n return {\n phase: value.phase as RemotePhase,\n ...(typeof value.publicURL === 'string' ? { publicURL: value.publicURL } : {}),\n ...(typeof value.authURL === 'string' ? { authURL: value.authURL } : {}),\n ...(typeof value.message === 'string' ? { message: value.message.slice(0, 500) } : {}),\n }\n } catch {\n return null\n }\n}\n\nfunction bundledHelperPath(): string {\n const here = dirname(fileURLToPath(import.meta.url))\n return resolve(here, '..', 'bin', `${process.platform}-${process.arch}`, 'dsh-deeppilot-tunnel')\n}\n\n/** Owns exactly one embedded tunnel helper and restarts it after failures. */\nexport class RemoteSupervisor {\n private child: ChildProcess | undefined\n private restartTimer: NodeJS.Timeout | undefined\n private restartAttempt = 0\n private stopping = false\n private statusValue: RemoteStatus\n\n constructor(private readonly options: RemoteSupervisorOptions) {\n this.statusValue = {\n provider: 'tailscale-funnel',\n phase: options.enabled ? 'stopped' : 'disabled',\n updatedAt: Date.now(),\n }\n }\n\n status(): RemoteStatus {\n return { ...this.statusValue }\n }\n\n async start(originURL: string): Promise<void> {\n if (!this.options.enabled || this.child !== undefined || this.stopping) return\n const helper = expandHome(this.options.helperPath ?? bundledHelperPath())\n const statePath = expandHome(this.options.statePath)\n try {\n await access(helper, fsConstants.X_OK)\n await mkdir(statePath, { recursive: true, mode: 0o700 })\n } catch (error) {\n this.setStatus({\n phase: 'unavailable',\n message: `embedded tunnel helper unavailable: ${String(error)}`,\n })\n return\n }\n\n this.setStatus({ phase: 'starting', message: undefined })\n const child = spawn(helper, [\n '--origin', originURL,\n '--hostname', normalizeRemoteHostname(this.options.hostname),\n '--state-dir', statePath,\n '--port', String(this.options.funnelPort ?? 443),\n ], {\n stdio: ['ignore', 'pipe', 'pipe'],\n env: {\n PATH: process.env.PATH ?? '/usr/bin:/bin',\n TMPDIR: process.env.TMPDIR ?? '/tmp',\n },\n })\n this.child = child\n if (child.stdout === null || child.stderr === null) {\n this.setStatus({ phase: 'error', message: 'helper stdio unavailable' })\n child.kill('SIGTERM')\n return\n }\n\n let stdoutBuffer = ''\n child.stdout.setEncoding('utf8')\n child.stdout.on('data', (chunk: string) => {\n stdoutBuffer += chunk\n const lines = stdoutBuffer.split('\\n')\n stdoutBuffer = lines.pop() ?? ''\n for (const line of lines) this.acceptLine(line)\n })\n\n let stderrBuffer = ''\n child.stderr.setEncoding('utf8')\n child.stderr.on('data', (chunk: string) => {\n stderrBuffer = (stderrBuffer + chunk).slice(-2_000)\n })\n\n child.once('error', (error) => {\n this.setStatus({ phase: 'error', message: `helper launch failed: ${String(error)}` })\n })\n child.once('exit', (code, signal) => {\n if (this.child === child) this.child = undefined\n if (this.stopping) {\n this.setStatus({ phase: 'stopped', message: undefined })\n return\n }\n const detail = stderrBuffer.trim().split('\\n').at(-1)\n this.setStatus({\n phase: 'error',\n message: detail || `helper exited (${signal ?? String(code)})`,\n })\n this.scheduleRestart(originURL)\n })\n }\n\n async dispose(): Promise<void> {\n this.stopping = true\n if (this.restartTimer !== undefined) clearTimeout(this.restartTimer)\n this.restartTimer = undefined\n const child = this.child\n this.child = undefined\n if (child === undefined) {\n this.setStatus({ phase: 'stopped', message: undefined })\n return\n }\n await new Promise<void>((resolveDone) => {\n const force = setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL')\n }, 3_000)\n child.once('exit', () => {\n clearTimeout(force)\n resolveDone()\n })\n child.kill('SIGTERM')\n })\n this.setStatus({ phase: 'stopped', message: undefined })\n }\n\n private acceptLine(line: string): void {\n const event = parseHelperEvent(line)\n if (event === null || event.phase === undefined) return\n if (event.phase === 'online') this.restartAttempt = 0\n this.setStatus({ ...event, phase: event.phase })\n }\n\n private scheduleRestart(originURL: string): void {\n if (this.stopping || this.restartTimer !== undefined) return\n const delay = RESTART_DELAYS_MS[Math.min(this.restartAttempt, RESTART_DELAYS_MS.length - 1)]\n this.restartAttempt += 1\n this.restartTimer = setTimeout(() => {\n this.restartTimer = undefined\n void this.start(originURL)\n }, delay)\n }\n\n private setStatus(next: Partial<RemoteStatus> & Pick<RemoteStatus, 'phase'>): void {\n const cleared: Partial<RemoteStatus> = next.phase === 'online'\n ? { authURL: undefined, message: undefined }\n : next.phase === 'login_required'\n ? { publicURL: undefined, message: undefined }\n : next.phase === 'starting' || next.phase === 'stopped' || next.phase === 'disabled'\n ? { publicURL: undefined, authURL: undefined, message: undefined }\n : {}\n this.statusValue = {\n ...this.statusValue,\n ...cleared,\n ...next,\n updatedAt: Date.now(),\n }\n if (next.phase === 'online') {\n this.options.log('remote Funnel online')\n } else if (next.phase === 'login_required') {\n this.options.log('remote Funnel requires browser authorization')\n } else if (next.phase === 'error' || next.phase === 'unavailable') {\n this.options.log(`remote Funnel ${next.phase}: ${next.message ?? 'unknown error'}`)\n }\n }\n}\n","import { networkInterfaces } from 'node:os'\n\nexport function isPrivateIPv4(address: string): boolean {\n const octets = address.split('.').map(Number)\n if (octets.length !== 4 || octets.some((value) => !Number.isInteger(value) || value < 0 || value > 255)) {\n return false\n }\n const [a, b] = octets as [number, number, number, number]\n return a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168)\n}\n\n/** Private IPv4 candidates, preferring physical en* interfaces over tunnels. */\nexport function localLANIPv4Addresses(): string[] {\n const candidates: Array<{ name: string; address: string }> = []\n for (const [name, entries] of Object.entries(networkInterfaces())) {\n for (const entry of entries ?? []) {\n if (entry.family === 'IPv4' && !entry.internal && isPrivateIPv4(entry.address)) {\n candidates.push({ name, address: entry.address })\n }\n }\n }\n const priority = (name: string): number => name === 'en0' ? 0 : name.startsWith('en') ? 1 : name.startsWith('bridge') ? 2 : 3\n candidates.sort((left, right) => priority(left.name) - priority(right.name) || left.name.localeCompare(right.name))\n return [...new Set(candidates.map(({ address }) => address))]\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from 'node:http'\nimport { randomBytes } from 'node:crypto'\nimport { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { Duplex } from 'node:stream'\nimport type { Context } from '@deepseek-ai/cordis'\nimport z from '@deepseek-ai/schemastery'\nimport { WebSocketServer } from 'ws'\nimport { BridgeConnection } from './connection.ts'\nimport { HostBridge } from './host-bridge.ts'\nimport type { ApiProxyLike, PushOutlet } from './host-bridge.ts'\nimport { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'\nimport { applyReportRemote } from './report-remote.ts'\nimport { runRelayProbe } from './relay-test.ts'\nimport type { PushTestResult } from './report-wire.ts'\nimport { DeviceStore, bridgeDataDir, expandHome, loadOrCreateToken, migrateLegacyBridgeDataDir, tokenMatches, writeNewToken } from './token.ts'\nimport type { ApnsEnvironment } from './token.ts'\nimport { ApnsClient } from './apns.ts'\nimport { RelayClient } from './relay-client.ts'\nimport type { PushNotification } from './protocol.ts'\nimport {\n DEFAULT_REMOTE_HOSTNAME,\n normalizeRemoteHostname,\n RemoteSupervisor,\n type RemoteStatus,\n} from './remote-supervisor.ts'\nimport { localLANIPv4Addresses } from './local-address.ts'\n\n/**\n * dsh-deeppilot — data bridge between the DSH host and DeepPilot\n * clients. Registers exactly one WebSocket upgrade route (/phone) plus an\n * optional health probe (/phone/health) on the existing web server. The web\n * UI is never touched.\n *\n * Data plane: an in-process HostBridge consumes apiProxy.events.mux()/host()\n * streams, mirrors session summaries, tracks pending approvals/questions,\n * and fans projected protocol-v1 pushes out to every connected device.\n *\n * Protocol: src/protocol.ts, v1. The private app repository carries the\n * matching normative document and Swift models.\n */\n\nexport const name = 'deeppilot'\n\nexport { HostBridge } from './host-bridge.ts'\n\n/** No eager service requirement: profiles without a web stack simply skip. */\nexport const inject: string[] = []\n\nexport interface Config {\n /** Master switch; when false the plugin activates and does nothing. */\n enabled?: boolean\n /** Pairing token file (0600); generated on first boot when missing. */\n authTokenPath?: string\n /** Paired-device registry JSON path. */\n devicesPath?: string\n /** Replay ring buffer bound (frames) per deployment. */\n historyBufferMax?: number\n /** Verbose per-frame diagnostics (never prints token or message bodies). */\n debug?: boolean\n /** Optional embedded remote transport. Reconciled when settings change. */\n remote?: {\n enabled?: boolean\n provider?: string\n hostname?: string\n statePath?: string\n helperPath?: string\n funnelPort?: number\n }\n /**\n * Offline push (F-9). provider 'apns' sends direct Apple Push Notification\n * deliveries from this Mac — outbound-only, no relay server, requires the\n * user's own Apple developer credentials. provider 'relay' forwards notify\n * projections to an operator-run relay (relay/server.js) holding the\n * distributor's key — used when the App ships via TestFlight/App Store.\n *\n * Deliberately NO environment knob here: each device reports its own\n * environment when registering (derived from its build kind), and\n * deliveries route per device — mixed dev/TestFlight phones coexist.\n */\n push?: {\n /** 'none' (default) | 'apns' | 'relay'. */\n provider?: string\n // --- apns ---\n /** Apple Developer team id (JWT iss claim). */\n teamId?: string\n /** APNs auth key id (JWT kid header). */\n keyId?: string\n /** .p8 private key path; generated keys live under the bridge data dir. */\n keyPath?: string\n /** App bundle id — the apns-topic header. */\n bundleId?: string\n // --- relay ---\n /** Relay base URL (https). See relay/README.md. */\n relayUrl?: string\n /** Per-user bearer token issued by the relay operator. */\n relayToken?: string\n }\n}\n\n/** Operator-run relay used by distributed builds; overridable via config. */\nconst DEFAULT_RELAY_URL = 'https://pilot.hailab.dev'\n\nexport const Config = z.object({\n enabled: z.boolean().default(true),\n authTokenPath: z.string().default(join(bridgeDataDir(), 'auth-token')),\n devicesPath: z.string().default(join(bridgeDataDir(), 'devices.json')),\n historyBufferMax: z.natural().min(100).default(2000),\n debug: z.boolean().default(false),\n remote: z.object({\n enabled: z.boolean().default(false),\n provider: z.string().default('tailscale-funnel'),\n hostname: z.string().default(DEFAULT_REMOTE_HOSTNAME),\n statePath: z.string().default(join(bridgeDataDir(), 'tailscale')),\n helperPath: z.string().default(''),\n funnelPort: z.natural().default(443),\n }).default({\n enabled: false,\n provider: 'tailscale-funnel',\n hostname: DEFAULT_REMOTE_HOSTNAME,\n statePath: join(bridgeDataDir(), 'tailscale'),\n helperPath: '',\n funnelPort: 443,\n }),\n push: z.object({\n provider: z.string().default('none'),\n teamId: z.string().default(''),\n keyId: z.string().default(''),\n keyPath: z.string().default(join(bridgeDataDir(), 'apns', 'AuthKey.p8')),\n bundleId: z.string().default('dev.hailab.deeppilot'),\n relayUrl: z.string().default(DEFAULT_RELAY_URL),\n relayToken: z.string().default(''),\n }).default({\n provider: 'none',\n teamId: '',\n keyId: '',\n keyPath: join(bridgeDataDir(), 'apns', 'AuthKey.p8'),\n bundleId: 'dev.hailab.deeppilot',\n relayUrl: DEFAULT_RELAY_URL,\n relayToken: '',\n }),\n})\n\ninterface WebServerLike {\n register(route: {\n kind: 'exact' | 'prefix'\n path: string\n handler: (req: IncomingMessage, res: ServerResponse) => void | Promise<void>\n }): () => void\n registerUpgrade(route: {\n path: string\n handler: (req: IncomingMessage, socket: Duplex, head: Buffer) => void\n }): () => void\n}\n\ntype SubContext = {\n effect: (setup: () => unknown, name?: string) => unknown\n}\n\nconst SERVER_VERSION = '0.2.0'\nconst MAX_CLIENT_CONNECTIONS = 16\n/**\n * Single-frame bound. Covers the protocol maximum (4 × 8 MB base64 images\n * plus prompt text) with headroom while keeping an unauthenticated client's\n * pre-hello buffering far below ws's 100 MiB default.\n */\nconst MAX_FRAME_BYTES = 64 * 1024 * 1024\n\nfunction rejectUpgrade(socket: Duplex, status: number, reason: string): void {\n const body = JSON.stringify({ error: reason })\n socket.end(\n 'HTTP/1.1 ' + status + ' Forbidden\\r\\n' +\n 'Content-Type: application/json\\r\\n' +\n 'Content-Length: ' + Buffer.byteLength(body) + '\\r\\n' +\n 'Connection: close\\r\\n' +\n '\\r\\n' +\n body,\n )\n}\n\n/** Authorization is preferred; the query form remains for older app builds. */\nexport function requestToken(req: Pick<IncomingMessage, 'url' | 'headers'>): string | null {\n const authorization = req.headers.authorization\n if (typeof authorization === 'string') {\n const match = /^Bearer\\s+(.+)$/i.exec(authorization.trim())\n if (match?.[1]) return match[1]\n }\n try {\n return new URL(req.url ?? '/', 'http://phone.local').searchParams.get('token')\n } catch {\n return null\n }\n}\n\n/**\n * Cordis hands the second argument in different shapes depending on host\n * composition: a reactive options getter, the resolved config value, or\n * nothing when the patch row omits `config`. Normalize all of them.\n */\nfunction normalizeOptions(options: unknown): Config {\n if (typeof options === 'function') {\n return (options as () => Config)()\n }\n if (options && typeof options === 'object') {\n return options as Config\n }\n // Validate undefined through the schema so every default applies.\n const validated = (Config as unknown as (data: unknown) => Config)(undefined)\n return validated ?? {}\n}\n\nexport function apply(ctx: Context, options: unknown): void {\n const cfg = normalizeOptions(options)\n\n const log = (message: string): void => {\n console.log('[deeppilot] ' + message)\n }\n\n /**\n * Settings-section source: while a settings service is attached this holds\n * the user-edited section value; otherwise the composition defaults. Read\n * through currentConfig() everywhere (normalizeOptions prefers it).\n */\n let liveSource: (() => Config) | undefined\n let scheduleRemoteReconcile: (() => void) | undefined\n const currentConfig = (): Config => {\n if (liveSource !== undefined) return normalizeOptions(liveSource())\n return normalizeOptions(options)\n }\n const enabledNow = (): boolean => currentConfig().enabled === true\n\n // Web settings page: a \"deeppilot\" section with the plugin knobs.\n // Values persist through the settings document and re-enter via setSource;\n // `enabled` decides whether the bridge starts at all (next restart). This is\n // registered unconditionally so the master switch stays reachable even while\n // the bridge is off — otherwise a disabled bridge could never be re-enabled.\n installSettingsSection(ctx, settingsNamespace('deeppilot'), Config, normalizeOptions(undefined), {\n setSource: (source) => {\n liveSource = source\n // Settings can attach after webServer. Defer one microtask so the\n // settings service can finish publishing the new source before the\n // remote runtime reads it.\n queueMicrotask(() => scheduleRemoteReconcile?.())\n },\n onChange: () => queueMicrotask(() => scheduleRemoteReconcile?.()),\n })\n\n // Master switch, resolved against the latest available settings document.\n // Individual injected services also read currentConfig() when they activate.\n if (currentConfig().enabled !== true) {\n log('disabled via settings; bridge stays inactive (rumors of /phone below are skipped)')\n }\n\n // Resolved asynchronously; route handlers await readiness. Never rejects:\n // a storage failure degrades the bridge instead of killing the host.\n // The token lives in a mutable cell so rotation can swap the secret (and\n // invalidate the old one) without restarting the host.\n const dataDir = bridgeDataDir()\n\n // ---------- zero-touch push enrollment (distributed builds) ----------\n\n /**\n * Persistent relay-enrollment cell (deeppilot/push-relay.json). A\n * distributed app presents the distributor's shared enrollKey during\n * c2s.push.register; the bridge then auto-enables relay mode, enrolls with\n * the operator's relay and caches the issued token. Users never fill in\n * anything; explicit config always wins over the auto flag.\n */\n interface RelayEnrollmentCell {\n clientId?: string\n autoRelay?: boolean\n enrollKey?: string\n token?: string\n }\n const pushRelayPath = join(dataDir, 'push-relay.json')\n const enrollmentCell: RelayEnrollmentCell = {}\n\n function persistEnrollment(): void {\n void (async () => {\n try {\n await mkdir(dataDir, { recursive: true })\n await writeFile(pushRelayPath, JSON.stringify({ version: 1, ...enrollmentCell }, null, 2) + '\\n', { mode: 0o600 })\n } catch {\n // best-effort persistence; enrollment retries on next trigger\n }\n })()\n }\n\n /** Fired from BridgeConnection when an app presents its built-in key. */\n const handlePushEnrollKey = async (enrollKey: string): Promise<void> => {\n if (enrollmentCell.enrollKey !== enrollKey) {\n enrollmentCell.enrollKey = enrollKey\n }\n const configuredProvider = currentConfig().push?.provider\n // Only flip when the user has not made an explicit choice.\n if (!configuredProvider || configuredProvider === 'none') {\n if (!enrollmentCell.autoRelay) {\n enrollmentCell.autoRelay = true\n log('push relay mode auto-enabled by enrolled app')\n }\n }\n persistEnrollment()\n // Enroll inline so the register handler sees final readiness: the first\n // offline notification must not depend on a reconnect.\n const url = (currentConfig().push?.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n await ensureRelayEnrolled(url)\n }\n const auth: { token: string | null; tokenPath: string; devices: DeviceStore | null } = {\n token: null,\n tokenPath: cfg.authTokenPath ?? join(dataDir, 'auth-token'),\n devices: null,\n }\n const ready = (async () => {\n try {\n try {\n const migratedFrom = await migrateLegacyBridgeDataDir()\n if (migratedFrom !== null) log(`migrated legacy plugin state from ${migratedFrom} to ${dataDir}`)\n } catch (error) {\n log('legacy plugin-state migration skipped: ' + String(error))\n }\n auth.tokenPath = cfg.authTokenPath ?? join(dataDir, 'auth-token')\n auth.token = await loadOrCreateToken(auth.tokenPath)\n auth.devices = await DeviceStore.load(cfg.devicesPath ?? join(dataDir, 'devices.json'))\n {\n // Startup visibility: makes \"registrations vanished after restart\"\n // instantly diagnosable (0 push rows ⇒ the file itself lost data).\n const rows = auth.devices.list()\n const registered = rows.filter((row) => row.apns !== undefined).length\n log(`device registry loaded from ${expandHome(cfg.devicesPath ?? join(dataDir, 'devices.json'))}: ${rows.length} device(s), ${registered} push registration(s)`)\n }\n // Restore zero-touch push enrollment state (best effort).\n try {\n const raw = JSON.parse(await readFile(pushRelayPath, 'utf8')) as RelayEnrollmentCell\n if (typeof raw.clientId === 'string') enrollmentCell.clientId = raw.clientId\n if (typeof raw.enrollKey === 'string') enrollmentCell.enrollKey = raw.enrollKey\n if (typeof raw.token === 'string') enrollmentCell.token = raw.token\n if (raw.autoRelay === true) enrollmentCell.autoRelay = true\n } catch {\n // first boot: no enrollment yet\n }\n } catch (error) {\n log('auth material unavailable, bridge degraded: ' + String(error))\n return { token: null, devices: null }\n }\n return { token: auth.token, devices: auth.devices }\n })()\n\n /**\n * Replace the pairing secret: persist a fresh token, clear the paired-device\n * registry, and drop every live phone socket. Handshake-time auth means an\n * already-open socket would otherwise outlive its token; terminating forces\n * each device to re-pair with the new secret. The old token is invalid the\n * moment the file is rewritten.\n *\n * Serialized through rotateTail so two overlapping invocations can never\n * return a token that a later write already invalidated.\n */\n const doRotate = async (): Promise<string> => {\n const { devices } = await ready\n if (auth.token === null || devices === null) {\n throw new Error('pairing token unavailable')\n }\n auth.token = await writeNewToken(auth.tokenPath)\n devices.clear()\n let dropped = 0\n for (const connection of connections) {\n connection.terminate()\n dropped += 1\n }\n connections.clear()\n log(`pairing token rotated; ${dropped} live phone connection(s) dropped`)\n return auth.token\n }\n let rotateTail: Promise<unknown> = Promise.resolve()\n const rotatePairingToken = (): Promise<string> => {\n const next = rotateTail.then(doRotate)\n rotateTail = next.catch(() => {})\n return next\n }\n\n /**\n * Settings-page push self-test: force one synthetic notification down the\n * active pathway to EVERY registered device, deliberately ignoring the\n * connected-skip and category-mute filters — an explicit user action must\n * always be able to prove delivery end to end.\n */\n const runPushSelfTest = async (): Promise<PushTestResult> => {\n const resolved = resolvePushConfig(currentConfig())\n if (!resolved.ok) {\n return {\n transport: 'none',\n overall: 'not-configured',\n message: '推送未启用(' + resolved.reason + ')。可先用「测试访问与注册」完成中继注册,或在配置中设置 push.provider',\n results: [],\n }\n }\n const tokenized = (auth.devices?.list() ?? []).filter((device) => device.apns !== undefined)\n if (!auth.devices || tokenized.length === 0) {\n return {\n transport: resolved.value.kind,\n overall: 'no-targets',\n message: '还没有设备注册离线推送——在手机上打开 DeepPilot 并允许系统通知,等状态变为「已就绪」后再试',\n results: [],\n }\n }\n const send = await senderFor(resolved.value)\n if (!send) {\n return { transport: resolved.value.kind, overall: 'failed', message: '发送通道不可用(检查 .p8 密钥文件或中继配置)', results: [] }\n }\n const notification: PushNotification = {\n notificationId: 'test-' + Date.now(),\n category: 'turn.completed',\n sessionId: 'push-test',\n title: 'DeepPilot 测试推送',\n body: '收到这条通知说明离线推送链路正常',\n }\n const results = await Promise.all(tokenized.map(async (device) => {\n const registration = device.apns!\n const { outcome, reason } = await send({\n deviceToken: registration.token,\n environment: registration.environment,\n notification,\n })\n return {\n name: device.deviceName,\n environment: registration.environment,\n outcome,\n // First 10 hex chars let the operator verify the stored token matches\n // what the device currently holds (tokens rotate on reinstall).\n tokenFingerprint: registration.token.slice(0, 10),\n ...(reason !== undefined ? { reason } : {}),\n }\n }))\n const overall: PushTestResult['overall'] = results.some((r) => r.outcome === 'sent') ? 'sent' : 'failed'\n log('push self-test: ' + overall + ' (' + results.map((r) => `\"${r.name}\"=${r.outcome}${r.reason ? '/' + r.reason : ''}`).join(', ') + ')')\n return { transport: resolved.value.kind, overall, results }\n }\n\n const connections = new Set<BridgeConnection>()\n\n // ---------- offline push outlet (F-9) ----------\n\n type PushConfigSnapshot =\n | { kind: 'apns'; teamId: string; keyId: string; keyPath: string; bundleId: string }\n | { kind: 'relay'; url: string; token: string }\n\n const resolvePushConfig = (config: Config): { ok: true; value: PushConfigSnapshot } | { ok: false; reason: string } => {\n const push = config.push ?? {}\n // Auto-enabled by an enrolled distributed app when the user made no\n // explicit provider choice.\n const configured = push.provider ?? 'none'\n const effectiveProvider = configured === 'none' && enrollmentCell.autoRelay === true ? 'relay' : configured\n if (effectiveProvider === 'relay') {\n const url = (push.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n const token = (push.relayToken ?? '').trim() || enrollmentCell.token || ''\n if (!/^https:\\/\\//i.test(url)) return { ok: false, reason: 'relayUrl must be an https URL' }\n if (!token) return { ok: false, reason: 'relay token not enrolled yet' }\n return { ok: true, value: { kind: 'relay', url, token } }\n }\n if (effectiveProvider === 'apns') {\n const teamId = (push.teamId ?? '').trim()\n const keyId = (push.keyId ?? '').trim()\n const keyPath = expandHome((push.keyPath ?? '').trim() || join(dataDir, 'apns', 'AuthKey.p8'))\n const bundleId = (push.bundleId ?? '').trim()\n if (!teamId || !keyId || !bundleId) return { ok: false, reason: 'teamId/keyId/bundleId missing' }\n return { ok: true, value: { kind: 'apns', teamId, keyId, keyPath, bundleId } }\n }\n return { ok: false, reason: 'provider disabled' }\n }\n\n /**\n * Zero-touch enrollment against the operator's relay. Idempotent and\n * cached in the persistent cell; a failure disables push for this config\n * fingerprint with one log line until something changes.\n */\n let enrollAttemptFor: string | undefined\n let enrollLastAttemptAt = 0\n const ensureRelayEnrolled = async (url: string): Promise<string | undefined> => {\n if (enrollmentCell.token) return enrollmentCell.token\n const fingerprint = url + ':' + String(enrollmentCell.enrollKey ?? '')\n if (fingerprint !== enrollAttemptFor) {\n enrollAttemptFor = fingerprint\n enrollLastAttemptAt = 0\n }\n // Same fingerprint failing repeatedly: throttle to one attempt/minute so\n // a down relay cannot turn every notification into an outbound storm,\n // while transient failures still recover quickly.\n if (Date.now() - enrollLastAttemptAt < 60_000) return undefined\n enrollLastAttemptAt = Date.now()\n try {\n if (!enrollmentCell.clientId) {\n enrollmentCell.clientId = 'u_' + randomBytes(16).toString('base64url')\n persistEnrollment()\n }\n const client = new RelayClient({ url, debug: currentConfig().debug === true, log })\n const token = await client.enroll(enrollmentCell.clientId, enrollmentCell.enrollKey ?? '')\n if (!token) {\n log('push relay enrollment failed (' + url + '); will retry on next trigger')\n return undefined\n }\n enrollmentCell.token = token\n persistEnrollment()\n log('push relay enrollment succeeded')\n return token\n } catch (error) {\n log('push relay enrollment error: ' + String(error))\n return undefined\n }\n }\n\n interface SendOutcome { outcome: 'sent' | 'invalid-token' | 'failed'; reason?: string }\n type PushSender = (\n request: { deviceToken: string; environment: ApnsEnvironment; notification: Parameters<PushOutlet['fanOut']>[0] },\n ) => Promise<SendOutcome>\n\n interface CachedSender {\n fingerprint: string\n send: PushSender\n dispose?: () => Promise<void>\n }\n let cachedSender: CachedSender | undefined\n let senderFailedFor: string | undefined\n\n /**\n * Lazily build the push sender for the current config. A broken config\n * (unreadable .p8) disables push for that fingerprint with exactly one log\n * line instead of failing on every event.\n */\n const senderFor = async (resolved: PushConfigSnapshot): Promise<PushSender | undefined> => {\n const fingerprint = JSON.stringify(resolved)\n if (cachedSender?.fingerprint === fingerprint) return cachedSender.send\n if (senderFailedFor === fingerprint) return undefined\n if (cachedSender) {\n await cachedSender.dispose?.().catch(() => {})\n cachedSender = undefined\n }\n if (resolved.kind === 'relay') {\n // resolvePushConfig already guarantees a token exists in the cell\n // (config token or a completed enrollment).\n const client = new RelayClient({ url: resolved.url, token: resolved.token, debug: currentConfig().debug === true, log })\n cachedSender = {\n fingerprint,\n send: (request) => client.send(request),\n }\n log('push relay enabled')\n } else {\n try {\n await readFile(expandHome(resolved.keyPath), 'utf8')\n } catch (error) {\n senderFailedFor = fingerprint\n log('apns push unavailable (key unreadable at ' + resolved.keyPath + '): ' + String(error))\n return undefined\n }\n const client = new ApnsClient({\n teamId: resolved.teamId,\n keyId: resolved.keyId,\n keyPath: resolved.keyPath,\n bundleId: resolved.bundleId,\n debug: currentConfig().debug === true,\n log,\n })\n cachedSender = {\n fingerprint,\n send: (request) => client.send({\n ...request.notification,\n deviceToken: request.deviceToken,\n environment: request.environment,\n }),\n dispose: () => client.dispose(),\n }\n log('apns push enabled')\n }\n senderFailedFor = undefined\n return cachedSender.send\n }\n\n /**\n * Fan one notification-worthy event out to paired devices holding an APNs\n * token. Rules:\n * - devices with a live WebSocket are skipped (they already got the WS\n * frame and will raise the local notification themselves);\n * - each device is delivered on ITS registered environment (the build\n * kind it self-reported), so sandbox and production devices coexist;\n * - the device's per-category switches suppress muted categories;\n * - Unregistered/BadDeviceToken outcomes prune the stored token.\n */\n const makePushOutlet = (): PushOutlet => ({\n // The capability bit must tell the truth: only advertise push when the\n // provider is fully configured, otherwise clients would suppress their\n // local banners expecting a delivery that never happens.\n isAvailable: () => {\n const resolved = resolvePushConfig(currentConfig())\n if (!resolved.ok) return false\n // Relay mode is only truly ready once enrollment produced a token;\n // advertising earlier would make clients suppress their local banners\n // for deliveries that cannot happen yet.\n if (resolved.value.kind === 'relay' && !resolved.value.token) return false\n return true\n },\n fanOut: (notification) => {\n void (async () => {\n let resolved = resolvePushConfig(currentConfig())\n if (!resolved.ok && resolved.reason === 'relay token not enrolled yet') {\n // A registration carried the enrollKey but enrollment has not run —\n // try once now, then re-resolve.\n const relayUrl = (currentConfig().push?.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n await ensureRelayEnrolled(relayUrl)\n resolved = resolvePushConfig(currentConfig())\n }\n if (!resolved.ok) return\n const devices = auth.devices\n if (!devices) return\n const send = await senderFor(resolved.value)\n if (!send) return\n const transport = resolved.value.kind\n const connectedIds = new Set<string>()\n for (const connection of connections) {\n const id = connection.connectedDeviceId\n if (id) connectedIds.add(id)\n }\n // Observability first: push failures used to be completely silent\n // (outcomes were debug-gated), which made field diagnosis impossible.\n // Every dispatch and every skip now leaves one flat log line —\n // categories and outcomes only, never message bodies.\n const candidates = devices.list().filter((device) => {\n const registration = device.apns\n if (!registration) return false\n if (connectedIds.has(device.deviceId)) return false\n if (registration.categories?.[notification.category] === false) {\n if (currentConfig().debug === true) {\n log(`push skip \"${device.deviceName}\": category ${notification.category} muted`)\n }\n return false\n }\n return true\n })\n if (candidates.length === 0) {\n const tokenized = devices.list().filter((device) => device.apns !== undefined).length\n log(`push(${transport}) ${notification.category}: no offline targets (connected=${connectedIds.size}, tokenized=${tokenized})`)\n return\n }\n for (const device of candidates) {\n const registration = device.apns!\n void send({ deviceToken: registration.token, environment: registration.environment, notification })\n .then(({ outcome, reason }) => {\n log(`push(${transport}) ${notification.category} → \"${device.deviceName}\" [${registration.environment}] = ${outcome}${reason ? ' (' + reason + ')' : ''}`)\n if (outcome === 'invalid-token') {\n devices.clearPushToken(device.deviceId)\n log(`push: pruned stale token of \"${device.deviceName}\" (${reason ?? 'unknown'}) — app re-registers on next launch`)\n }\n })\n .catch(() => {})\n }\n })()\n },\n })\n\n\n const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_FRAME_BYTES })\n let remoteSupervisor: RemoteSupervisor | undefined\n const remoteStatus = (): RemoteStatus => remoteSupervisor?.status() ?? {\n provider: 'tailscale-funnel',\n phase: currentConfig().remote?.enabled === true ? 'stopped' : 'disabled',\n updatedAt: Date.now(),\n }\n\n // Typert Remote for the web settings page (deeppilot/report).\n applyReportRemote(ctx, async () => {\n let tokenReady = false\n let devices: Array<{ deviceId: string; deviceName: string; appVersion: string; firstSeenTs: number; lastSeenTs: number; apns?: { environment: 'development' | 'production'; updatedAt: number } }> = []\n try {\n await ready\n tokenReady = auth.token !== null\n // Strip the raw APNs token at the source; the report carries only the\n // registration fact (environment + freshness).\n devices = (auth.devices?.list() ?? []).map(({ deviceId, deviceName, appVersion, firstSeenTs, lastSeenTs, apns }) => ({\n deviceId,\n deviceName,\n appVersion,\n firstSeenTs,\n lastSeenTs,\n ...(apns ? { apns: { environment: apns.environment, updatedAt: apns.updatedAt } } : {}),\n }))\n } catch {\n // degraded: report the minimum without token facts\n }\n return {\n protocolVersion: 1,\n serverVersion: SERVER_VERSION,\n enabled: currentConfig().enabled === true,\n tokenPath: expandHome(currentConfig().authTokenPath ?? join(bridgeDataDir(), 'auth-token')),\n tokenReady,\n activeConnections: connections.size,\n historyBufferMax: currentConfig().historyBufferMax ?? 2000,\n debug: currentConfig().debug === true,\n lanAddresses: localLANIPv4Addresses(),\n remote: remoteStatus(),\n devices,\n }\n }, async () => {\n await ready\n if (auth.token === null) throw new Error('pairing token unavailable')\n return auth.token\n }, rotatePairingToken, async () => {\n const push = currentConfig().push ?? {}\n const configured = push.provider ?? 'none'\n const effective = configured === 'none' && enrollmentCell.autoRelay === true ? 'relay' : configured\n if (effective !== 'relay') {\n return {\n url: '',\n overall: 'failed' as const,\n tokenIssued: false,\n steps: [{ id: 'health' as const, ok: false, message: `当前推送模式不是中继(provider=${configured})。启用方式二选一:① 零配置——在 ios/project.yml 填写 DSPushEnrollKey(与服务器 RELAY_ENROLL_KEY 一致)并重新安装 App,打开 App 即自动启用;② 手动——将 push.provider 设为 relay 并填入 relayToken` }],\n }\n }\n const url = (push.relayUrl ?? '').trim() || DEFAULT_RELAY_URL\n // The enroll step needs an identity; mint one now so a successful test\n // doubles as a completed enrollment.\n if (!enrollmentCell.clientId && enrollmentCell.enrollKey) {\n enrollmentCell.clientId = 'u_' + randomBytes(16).toString('base64url')\n persistEnrollment()\n }\n return await runRelayProbe({\n url,\n clientId: enrollmentCell.clientId,\n enrollKey: enrollmentCell.enrollKey,\n manualToken: Boolean((push.relayToken ?? '').trim()),\n onEnrolled: (token) => {\n enrollmentCell.token = token\n persistEnrollment()\n log('push relay enrollment succeeded (via settings self-test)')\n },\n })\n }, async () => {\n return await runPushSelfTest()\n })\n const state: { bridge?: HostBridge } = {}\n\n const handleUpgrade = (req: IncomingMessage, socket: Duplex, head: Buffer): void => {\n void (async () => {\n try {\n if (!enabledNow()) {\n rejectUpgrade(socket, 503, 'bridge disabled')\n return\n }\n if (connections.size >= MAX_CLIENT_CONNECTIONS) {\n rejectUpgrade(socket, 429, 'too many connections')\n return\n }\n const { devices } = await ready\n const token = auth.token\n if (!token || !devices) {\n rejectUpgrade(socket, 503, 'bridge degraded')\n return\n }\n const presentedToken = requestToken(req)\n if (presentedToken !== null && !tokenMatches(presentedToken, token)) {\n rejectUpgrade(socket, 401, 'invalid token')\n return\n }\n const bridge = state.bridge\n if (!bridge) {\n rejectUpgrade(socket, 503, 'bridge not ready')\n return\n }\n // Rotation may have swapped the secret while this handshake was in\n // flight; a connection validated against the previous token must not\n // survive it.\n if (auth.token !== token) {\n rejectUpgrade(socket, 401, 'invalid token')\n return\n }\n wss.handleUpgrade(req, socket, head, (ws) => {\n const connection = new BridgeConnection(ws, {\n bridge,\n devices,\n serverVersion: SERVER_VERSION,\n expectedToken: token,\n transportAuthenticated: presentedToken !== null,\n log,\n debug: currentConfig().debug === true,\n onClosed: (closed) => connections.delete(closed),\n onPushEnrollKey: handlePushEnrollKey,\n })\n connections.add(connection)\n })\n } catch (error) {\n log('upgrade failed: ' + String(error))\n rejectUpgrade(socket, 500, 'internal error')\n }\n })()\n }\n\n const handleHealth = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n try {\n await ready\n const token = auth.token\n res.setHeader('Content-Type', 'application/json')\n if (!token) {\n res.statusCode = 503\n res.end(JSON.stringify({ ok: false, degraded: true }))\n return\n }\n if (!tokenMatches(requestToken(req), token)) {\n res.statusCode = 401\n res.end(JSON.stringify({ ok: false }))\n return\n }\n res.statusCode = 200\n res.end(JSON.stringify({\n ok: true,\n enabled: enabledNow(),\n protocolVersion: 1,\n serverVersion: SERVER_VERSION,\n dataPlane: Boolean(state.bridge),\n }))\n } catch {\n res.statusCode = 500\n res.end(JSON.stringify({ ok: false }))\n }\n }\n\n // Data plane: consume mux/host streams once the API gateway exists.\n ;(ctx as unknown as { inject: (deps: string[], fn: (sub: unknown) => void) => void }).inject(\n ['apiProxy'],\n (sub) => {\n if (currentConfig().enabled !== true) {\n log('bridge disabled; data plane stays inactive')\n return\n }\n const apiCtx = sub as unknown as { apiProxy?: ApiProxyLike } & SubContext\n const proxy = apiCtx.apiProxy\n if (!proxy) {\n log('apiProxy service absent; data plane stays inactive')\n return\n }\n const bridge = new HostBridge(proxy, cfg.historyBufferMax)\n bridge.setPushOutlet(makePushOutlet())\n state.bridge = bridge\n bridge.start()\n log('data plane active (mux + host streams)')\n apiCtx.effect(() => () => bridge.dispose(), 'deeppilot: host streams')\n },\n )\n\n // Transport plane: /phone WebSocket + /phone/health probe.\n ;(ctx as unknown as { inject: (deps: string[], fn: (sub: unknown) => void) => void }).inject(\n ['webServer'],\n (sub) => {\n const webCtx = sub as unknown as { webServer?: WebServerLike } & SubContext\n const web = webCtx.webServer\n if (!web) {\n log('webServer service absent in this profile; bridge stays inactive')\n return\n }\n // Routes are registered regardless of the master switch so a mid-session\n // toggle always stays serviceable; every handler refuses work when the\n // bridge is currently disabled (evaluated per request so it is robust to\n // the settings scope attaching before or after webServer).\n webCtx.effect(\n () =>\n web.registerUpgrade({\n path: '/phone',\n handler: handleUpgrade,\n }),\n 'deeppilot: /phone WebSocket',\n )\n\n webCtx.effect(\n () =>\n web.register({\n kind: 'exact',\n path: '/phone/health',\n handler: handleHealth,\n }),\n 'deeppilot: /phone/health',\n )\n\n const sweep = setInterval(() => {\n const now = Date.now()\n for (const connection of connections) {\n if (connection.isStale(now, 60_000)) {\n log('dropping stale connection')\n connection.terminate()\n connections.delete(connection)\n }\n }\n }, 30_000)\n webCtx.effect(() => () => clearInterval(sweep), 'deeppilot: stale sweep')\n\n // The settings source is not guaranteed to attach before webServer.\n // Keep a loopback origin ready and reconcile the helper whenever that\n // source appears or changes, instead of freezing the schema defaults at\n // injection time.\n const originServer = createServer((req, res) => {\n let path = '/'\n try { path = new URL(req.url ?? '/', 'http://phone.local').pathname } catch { /* reject below */ }\n if (path === '/phone/health') {\n void handleHealth(req, res)\n } else {\n res.statusCode = 404\n res.end('not found')\n }\n })\n originServer.on('upgrade', (req, socket, head) => {\n let path = '/'\n try { path = new URL(req.url ?? '/', 'http://phone.local').pathname } catch { /* reject below */ }\n if (path !== '/phone') {\n socket.end('HTTP/1.1 404 Not Found\\r\\nConnection: close\\r\\n\\r\\n')\n return\n }\n handleUpgrade(req, socket, head)\n })\n\n let originURL: string | undefined\n let appliedRemoteKey: string | undefined\n let remoteDisposed = false\n let reconcileTail = Promise.resolve()\n const reconcileRemote = async (): Promise<void> => {\n if (remoteDisposed || originURL === undefined) return\n const config = currentConfig()\n const remoteConfig = config.remote ?? {}\n const remotePort: 443 | 8443 | 10000 = remoteConfig.funnelPort === 8443 || remoteConfig.funnelPort === 10000\n ? remoteConfig.funnelPort\n : 443\n const helperPath = remoteConfig.helperPath?.trim() || undefined\n const next = {\n enabled: config.enabled === true && remoteConfig.enabled === true && remoteConfig.provider === 'tailscale-funnel',\n hostname: normalizeRemoteHostname(remoteConfig.hostname),\n statePath: remoteConfig.statePath?.trim() || join(dataDir, 'tailscale'),\n helperPath,\n funnelPort: remotePort,\n }\n const nextKey = JSON.stringify(next)\n if (nextKey === appliedRemoteKey) return\n\n const previous = remoteSupervisor\n remoteSupervisor = undefined\n if (previous !== undefined) await previous.dispose()\n if (remoteDisposed) return\n\n const supervisor = new RemoteSupervisor({\n enabled: next.enabled,\n hostname: next.hostname,\n statePath: next.statePath,\n ...(next.helperPath ? { helperPath: next.helperPath } : {}),\n funnelPort: next.funnelPort,\n log,\n })\n remoteSupervisor = supervisor\n appliedRemoteKey = nextKey\n await supervisor.start(originURL)\n }\n scheduleRemoteReconcile = () => {\n reconcileTail = reconcileTail\n .then(reconcileRemote)\n .catch((error) => log('remote reconcile failed: ' + String(error)))\n }\n\n originServer.listen(0, '127.0.0.1', () => {\n const address = originServer.address()\n if (address && typeof address === 'object') {\n originURL = `http://127.0.0.1:${address.port}`\n scheduleRemoteReconcile?.()\n }\n })\n originServer.on('error', (error) => log('remote origin failed: ' + String(error)))\n webCtx.effect(() => () => {\n remoteDisposed = true\n scheduleRemoteReconcile = undefined\n originServer.close()\n reconcileTail = reconcileTail.then(async () => {\n const supervisor = remoteSupervisor\n remoteSupervisor = undefined\n if (supervisor !== undefined) await supervisor.dispose()\n })\n }, 'deeppilot: embedded Funnel')\n if (enabledNow()) {\n log('/phone WebSocket registered')\n } else {\n log('bridge disabled; /phone refuses connections until re-enabled and restarted')\n }\n },\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;AAMA,SAAgB,WAAW,GAAmB;CAC5C,IAAI,MAAM,KAAK,OAAO,QAAQ;CAC9B,IAAI,EAAE,WAAW,IAAI,GAAG,OAAO,QAAQ,QAAQ,GAAG,EAAE,MAAM,CAAC,CAAC;CAC5D,OAAO;AACT;AAEA,SAAS,cAAsB;CAC7B,MAAM,UAAU,QAAQ,IAAI;CAC5B,IAAI,WAAW,QAAQ,KAAK,CAAC,CAAC,SAAS,GAAG,OAAO,QAAQ,QAAQ,KAAK,CAAC;CACvE,OAAO,QAAQ,QAAQ,GAAG,MAAM;AAClC;;AAGA,SAAgB,gBAAwB;CACtC,OAAO,QAAQ,YAAY,GAAG,WAAW;AAC3C;;;;;AAMA,eAAsB,6BAAqD;CACzE,MAAM,SAAS,cAAc;CAC7B,IAAI;EACF,MAAM,OAAO,MAAM;EACnB,OAAO;CACT,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,MAAM;CAChE;CAEA,MAAM,SAAS,QAAQ,YAAY,GAAG,eAAe;CACrD,IAAI;EACF,MAAM,OAAO,QAAQ,MAAM;EAC3B,OAAO;CACT,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,OAAO;EAC/D,MAAM;CACR;AACF;;;;;AAMA,eAAsB,kBAAkB,WAAoC;CAC1E,MAAM,OAAO,WAAW,SAAS;CACjC,IAAI;EACF,MAAM,YAAY,MAAM,SAAS,MAAM,MAAM,EAAA,CAAG,KAAK;EACrD,IAAI,SAAS,UAAU,IAAI,OAAO;CACpC,QAAQ,CAER;CACA,MAAM,QAAQ,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;CAClD,MAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;CAC9C,MAAM,UAAU,MAAM,QAAQ,MAAM,EAAE,MAAM,IAAM,CAAC;CACnD,OAAO;AACT;;;;;;AAOA,eAAsB,cAAc,WAAoC;CACtE,MAAM,OAAO,WAAW,SAAS;CACjC,MAAM,QAAQ,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;CAClD,MAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;CAC9C,MAAM,OAAO,GAAG,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,SAAS,KAAK,EAAE;CACvD,MAAM,UAAU,MAAM,QAAQ,MAAM,EAAE,MAAM,IAAM,CAAC;CACnD,MAAM,OAAO,MAAM,IAAI;CACvB,OAAO;AACT;;AAGA,SAAgB,aAAa,WAAsC,UAA2B;CAC5F,IAAI,CAAC,WAAW,OAAO;CACvB,MAAM,IAAI,OAAO,KAAK,SAAS;CAC/B,MAAM,IAAI,OAAO,KAAK,QAAQ;CAC9B,IAAI,EAAE,WAAW,EAAE,QAAQ;EAEzB,gBAAgB,GAAG,CAAC;EACpB,OAAO;CACT;CACA,OAAO,gBAAgB,GAAG,CAAC;AAC7B;;AAwBA,MAAM,qBAAqB;AAE3B,SAAgB,iBAAiB,OAAiC;CAChE,OAAO,OAAO,UAAU,YAAY,mBAAmB,KAAK,KAAK;AACnE;;;;;;AAUA,IAAa,cAAb,MAAa,YAAY;CAIc;CAHrC,0BAAkB,IAAI,IAA0B;CAChD,YAAmC,QAAQ,QAAQ;CAEnD,YAAoB,UAAmC;EAAlB,KAAA,WAAA;CAAmB;CAExD,aAAa,KAAK,UAAwC;EACxD,MAAM,QAAQ,IAAI,YAAY,QAAQ;EACtC,IAAI;GACF,MAAM,MAAM,KAAK,MAAM,MAAM,SAAS,WAAW,QAAQ,GAAG,MAAM,CAAC;GAGnE,KAAK,MAAM,OAAO,IAAI,WAAW,CAAC,GAChC,IAAI,OAAO,IAAI,aAAa,UAAU,MAAM,QAAQ,IAAI,IAAI,UAAU,GAAG;EAE7E,SAAS,OAAO;GAEd,IADc,OAAiC,SAClC,UAAU,CAEvB,OAIE,QAAQ,IAAI,6DAA6D,OAAO,KAAK,CAAC;EAE1F;EACA,OAAO;CACT;CAEA,MAAM,QAA0D,KAAmB;EACjF,MAAM,WAAW,KAAK,QAAQ,IAAI,OAAO,QAAQ;EACjD,IAAI,UAAU;GACZ,SAAS,aAAa;GACtB,SAAS,aAAa,OAAO,cAAc,SAAS;GACpD,SAAS,aAAa,OAAO,cAAc,SAAS;EACtD,OAAO;GACL,IAAI,KAAK,QAAQ,QAAA,IAAqB;IAEpC,IAAI;IACJ,IAAI,WAAW,OAAO;IACtB,KAAK,MAAM,CAAC,IAAI,UAAU,KAAK,SAC7B,IAAI,MAAM,aAAa,UAAU;KAC/B,WAAW,MAAM;KACjB,WAAW;IACb;IAEF,IAAI,aAAa,KAAA,GAAW,KAAK,QAAQ,OAAO,QAAQ;GAC1D;GACA,KAAK,QAAQ,IAAI,OAAO,UAAU;IAChC,GAAG;IACH,aAAa;IACb,YAAY;GACd,CAAC;EACH;EACA,KAAU,MAAM;CAClB;CAEA,OAAuB;EACrB,OAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC;CAClC;;;;;;CAOA,aACE,UACA,OACA,aACA,YACA,KACM;EACN,MAAM,aAAa,MAAM,YAAY;EACrC,IAAI,CAAC,iBAAiB,UAAU,GAAG;EACnC,IAAI,SAAS,KAAK,QAAQ,IAAI,QAAQ;EACtC,IAAI,CAAC,QAAQ;GAIX,SAAS;IAAE;IAAU,YAAY;IAAW,YAAY;IAAW,aAAa;IAAK,YAAY;GAAI;GACrG,KAAK,QAAQ,IAAI,UAAU,MAAM;EACnC;EACA,MAAM,OAAuB;GAAE,OAAO;GAAY;GAAa,WAAW;EAAI;EAC9E,IAAI,cAAc,OAAO,eAAe,UAAU;GAChD,MAAM,QAAiC,CAAC;GACxC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,UAAU,GAClD,IAAI,iBAAiB,KAAK,GAAG,KAAK,OAAO,UAAU,WAAW,MAAM,OAAO;GAE7E,IAAI,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,GAAG,KAAK,aAAa;EACvD;EACA,MAAM,UAAU,OAAO;EACvB,IACE,WACA,QAAQ,UAAU,KAAK,SACvB,QAAQ,gBAAgB,KAAK,eAC7B,KAAK,UAAU,QAAQ,cAAc,CAAC,CAAC,MAAM,KAAK,UAAU,KAAK,cAAc,CAAC,CAAC,GAEjF;EAEF,OAAO,OAAO;EACd,KAAU,MAAM;CAClB;;CAGA,eAAe,UAAwB;EACrC,MAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ;EACxC,IAAI,CAAC,QAAQ,MAAM;EACnB,OAAO,OAAO;EACd,KAAU,MAAM;CAClB;;;;;;CAOA,QAAc;EACZ,KAAK,QAAQ,MAAM;EACnB,KAAU,MAAM;CAClB;;CAGA,QAA+B;EAC7B,MAAM,OAAO,KAAK,UAAU,WAAW,KAAK,UAAU,CAAC;EACvD,KAAK,YAAY,KAAK,YAAY,CAElC,CAAC;EACD,OAAO;CACT;;CAGA,MAAM,QAAuB;EAC3B,MAAM,KAAK;CACb;CAEA,MAAc,YAA2B;EACvC,MAAM,OAAO,WAAW,KAAK,QAAQ;EACrC,MAAM,OAAO,KAAK,UAChB;GAAE,SAAS;GAAG,SAAS,KAAK,KAAK;EAAE,GACnC,MACA,CACF;EACA,IAAI;GACF,MAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;GAI9C,MAAM,OAAO,GAAG,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,SAAS,KAAK,EAAE;GACvD,MAAM,UAAU,MAAM,OAAO,MAAM,EAAE,MAAM,IAAM,CAAC;GAClD,MAAM,OAAO,MAAM,IAAI;EACzB,QAAQ,CAER;CACF;AACF;;;AClRA,MAAa,kBAAkB;AAC/B,MAAM,oCAAoB,IAAI,IAAI;CAAC;CAAa;CAAc;CAAc;AAAW,CAAC;AACxF,MAAM,oBAAoB;AAC1B,MAAM,6BAA6B;;AAEnC,MAAM,wBAAwB;AAI9B,MAAM,sBAAsB;AAC5B,MAAM,wBAAwB;AAC9B,MAAM,wBAAwB;AAE9B,SAAS,oBAAoB,OAAgB,UAA0B;CAErE,QADY,OAAO,UAAU,WAAW,QAAQ,OAAO,SAAS,EAAE,EAAA,CACvD,QAAQ,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,QAAQ;AAC5E;AAuBA,SAAgB,mBACd,wBACA,gBACA,eACS;CACT,OAAO,2BAA2B,QAAQ,aAAa,gBAAgB,aAAa;AACtF;;;;;;AAOA,IAAa,mBAAb,MAAoD;CAQ/B;CACA;CARnB,gBAAwB;CACxB;CACA,+BAAgC,IAAI,IAAY;;CAEhD;CAEA,YACE,IACA,MACA;EAFiB,KAAA,KAAA;EACA,KAAA,OAAA;EAEjB,GAAG,GAAG,YAAY,SAAS;GACzB,KAAU,UAAU,OAAO,IAAI,CAAC;EAClC,CAAC;EACD,GAAG,GAAG,eAAe;GACnB,KAAK,QAAQ;GACb,KAAK,WAAW,IAAI;EACtB,CAAC;EACD,GAAG,GAAG,eAAe,CAErB,CAAC;EACD,KAAK,aAAa,iBAAiB;GACjC,IAAI,CAAC,KAAK,eAAe,KAAK,MAAM,MAAM,cAAc;EAC1D,GAAG,eAAe;CACpB;;CAGA,YAAkB;EAChB,KAAK,GAAG,UAAU;CACpB;;CAGA,IAAI,oBAAwC;EAC1C,OAAO,KAAK,gBAAgB,KAAK,WAAW,KAAA;CAC9C;CAIA,KAAK,MAAc,SAAkB,KAAoB;EACvD,IAAI,KAAK,KAAK,UAAU,MAAM,KAAK,KAAK,IAAI,UAAU,OAAO,UAAU,OAAO,GAAG,CAAC;EAClF,KAAK,KAAK,MAAM,SAAS,KAAA,GAAW,GAAG;CACzC;CAEA,OAAO,SAAuE;EAC5E,KAAK,MAAM,SAAS,SAAS,KAAK,KAAK,MAAM,MAAM,MAAM,SAAS,MAAM,GAAG;CAC7E;CAEA,aAAmB;EACjB,KAAK,KAAK,mBAAmB,CAAC,CAAC;CACjC;CAEA,SAAe;EACb,KAAK,KAAK,cAAc,EAAE,QAAQ,MAAM,CAAC;CAC3C;CAEA,aAAqB;EACnB,OAAO,KAAK,KAAK,OAAO,cAAc;CACxC;CAIA,UAAwB;EACtB,IAAI,KAAK,eAAe,KAAA,GAAW,aAAa,KAAK,UAAU;EAC/D,KAAK,MAAM,MAAM,KAAK,cACpB,KAAK,KAAK,OAAO,eAAe,MAAM,EAAE;EAE1C,KAAK,aAAa,MAAM;EACxB,KAAK,KAAK,OAAO,iBAAiB,IAAI;EACtC,IAAI,KAAK,eAAe,KAAK,KAAK,OAAO,WAAW,IAAI;CAC1D;CAEA,MAAc,MAAc,QAAsB;EAChD,IAAI;GACF,KAAK,GAAG,MAAM,MAAM,MAAM;EAC5B,QAAQ;GACN,KAAK,GAAG,UAAU;EACpB;CACF;CAEA,KAAa,MAAc,SAAkB,IAAa,KAAoB;EAC5E,MAAM,WAAqB;GACzB,GAAA;GACA;GACA,IAAI,KAAK,IAAI;GACb,GAAI,OAAO,KAAA,IAAY,EAAE,GAAG,IAAI,CAAC;GACjC,GAAI,QAAQ,KAAA,IAAY,EAAE,IAAI,IAAI,CAAC;GACnC;EACF;EACA,IAAI,KAAK,GAAG,eAAe,KAAK,GAAG,MAAM,KAAK,GAAG,KAAK,KAAK,UAAU,QAAQ,CAAC;CAChF;CAEA,KAAa,IAAwB,MAAgC,SAAuB;EAC1F,KAAK,KAAK,aAAa;GAAE;GAAM;EAAQ,GAAG,EAAE;CAC9C;CAIE,eAAuB,KAAK,IAAI;;CAGhC,QAAQ,KAAa,WAA4B;EAC/C,OAAO,MAAM,KAAK,eAAe;CACnC;CAEA,MAAc,UAAU,KAA4B;EAClD,KAAK,eAAe,KAAK,IAAI;EAC7B,IAAI;EACN,IAAI;GACF,MAAM,KAAK,MAAM,GAAG;EACtB,QAAQ;GACN,KAAK,KAAK,KAAA,GAAW,cAAc,yBAAyB;GAC5D;EACF;EACA,IAAI,IAAI,MAAA,GAAwB;GAC9B,KAAK,KAAK,IAAI,IAAI,iBAAiB,8BAA8B;GACjE,KAAK,MAAM,MAAM,2BAA2B;GAC5C;EACF;EACA,IAAI,CAAC,KAAK,eAAe;GACvB,IAAI,IAAI,SAAS,YAAY;IAC3B,KAAK,KAAK,YAAY,EAAE,YAAY,KAAK,IAAI,EAAE,GAAG,IAAI,EAAE;IACxD;GACF;GACA,IAAI,IAAI,SAAS,kBAAkB;IACjC,MAAM,KAAK,MAAM,GAAG;IACpB;GACF;GACA,KAAK,KAAK,IAAI,IAAI,cAAc,oBAAoB;GACpD;EACF;EACA,QAAQ,IAAI,MAAZ;GACE,KAAK;IACH,KAAK,KAAK,YAAY,EAAE,YAAY,KAAK,IAAI,EAAE,GAAG,IAAI,EAAE;IACxD;GAEF,KAAK;IACH,KAAK,KAAK,yBAAyB;KAAE,MAAM;KAAM,UAAU,KAAK,KAAK,OAAO,aAAa;IAAE,GAAG,IAAI,EAAE;IACpG;GAEF,KAAK,uBAAuB;IAC1B,IAAI,CAAC,KAAK,KAAK,OAAO,aAAa,kBACjC,OAAO,KAAK,KAAK,IAAI,IAAI,iBAAiB,oDAAoD;IAEhG,MAAM,SAAS,MAAM,KAAK,KAAK,OAAO,eAAe;IACrD,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,KAAK,IAAI,IAAI,oBAAoB,OAAO,IAAI,GAAG,OAAO,OAAO;IACzF,KAAK,KAAK,2BAA2B,EAAE,YAAY,OAAO,MAAM,GAAG,IAAI,EAAE;IACzE;GACF;GACA,KAAK,sBAAsB;IACzB,MAAM,IAAI,IAAI;IACd,IAAI,GAAG,SAAS,KAAA,KAAa,OAAO,EAAE,SAAS,UAC7C,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,uBAAuB;IAEhE,MAAM,SAAS,MAAM,KAAK,KAAK,OAAO,cAAc,GAAG,IAA0B;IACjF,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,KAAK,IAAI,IAAI,oBAAoB,OAAO,IAAI,GAAG,OAAO,OAAO;IACzF,KAAK,KAAK,yBAAyB,OAAO,OAAO,IAAI,EAAE;IACvD;GACF;GACA,KAAK,sBAAsB;IACzB,MAAM,SAAS,MAAM,KAAK,KAAK,OAAO,cAAc;IACpD,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,KAAK,IAAI,IAAI,oBAAoB,OAAO,IAAI,GAAG,OAAO,OAAO;IACzF,KAAK,KAAK,wBAAwB,EAAE,MAAM,OAAO,MAAM,GAAG,IAAI,EAAE;IAChE;GACF;GACA,KAAK,wBAAwB;IAC3B,MAAM,IAAI,IAAI;IACd,MAAM,OAAO,OAAO,GAAG,SAAS,WAAW,EAAE,KAAK,KAAK,IAAI;IAC3D,IAAI,CAAC,MAAM,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,yBAAyB;IAC3E,IAAI,CAAC,KAAK,KAAK,OAAO,aAAa,kBACjC,OAAO,KAAK,KAAK,IAAI,IAAI,iBAAiB,oDAAoD;IAEhG,MAAM,SAAS,MAAM,KAAK,KAAK,OAAO,gBAAgB,IAAI;IAC1D,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,KAAK,IAAI,IAAI,oBAAoB,OAAO,IAAI,GAAG,OAAO,OAAO;IACzF,KAAK,KAAK,yBAAyB,OAAO,OAAO,IAAI,EAAE;IACvD;GACF;GACA,KAAK,oBAAoB;IACvB,MAAM,IAAI,IAAI;IACd,IAAI,CAAC,GAAG,WAAW,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,oBAAoB;IAC9E,KAAK,aAAa,IAAI,EAAE,SAAS;IACjC,KAAK,KAAK,OAAO,aAAa,MAAM,EAAE,SAAS;IAE/C,IAAI,CAAC,MADY,KAAK,KAAK,OAAO,YAAY,MAAM,EAAE,WAAW,EAAE,aAAa,GAAG,GAC1E,KAAK,KAAK,IAAI,IAAI,eAAe,6BAA6B;IACvE;GACF;GACA,KAAK,qBAAqB;IACxB,MAAM,IAAI,IAAI;IACd,IAAI,CAAC,GAAG,WAAW,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,oBAAoB;IAC9E,KAAK,aAAa,OAAO,EAAE,SAAS;IACpC,KAAK,KAAK,OAAO,eAAe,MAAM,EAAE,SAAS;IACjD,KAAK,KAAK,WAAW,CAAC,GAAG,IAAI,EAAE;IAC/B;GACF;GACA,KAAK,sBAAsB;IACzB,MAAM,IAAI,IAAI;IACd,MAAM,cAAc,OAAO,GAAG,gBAAgB,WAAW,EAAE,YAAY,KAAK,IAAI;IAChF,MAAM,MAAM,OAAO,GAAG,QAAQ,WAAW,EAAE,IAAI,KAAK,IAAI;IACxD,IAAI,eAAe,KAAK,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,4CAA4C;IAC3G,MAAM,QAAQ,MAAM,KAAK,KAAK,OAAO,cAAc;KACjD,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;KACrC,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;IACvB,CAAC;IACD,IAAI,CAAC,OAAO,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,uBAAuB;IAC1E,KAAK,KAAK,WAAW,EAAE,WAAW,MAAM,GAAG,IAAI,EAAE;IACjD;GACF;GACA,KAAK,sBAAsB;IACzB,MAAM,IAAI,IAAI;IACd,MAAM,QAAQ,OAAO,GAAG,UAAU,WAAW,EAAE,MAAM,KAAK,IAAI;IAC9D,IAAI,CAAC,GAAG,aAAa,MAAM,WAAW,GACpC,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,wCAAwC;IAEjF,IAAI,CAAC,KAAK,KAAK,OAAO,aAAa,mBACjC,OAAO,KAAK,KAAK,IAAI,IAAI,iBAAiB,qDAAqD;IAEjG,MAAM,SAAS,MAAM,KAAK,KAAK,OAAO,cAAc,EAAE,WAAW,KAAK;IACtE,IAAI,CAAC,OAAO,IAAI;KACd,MAAM,OAAO,OAAO,SAAS,cAAc,gBACvC,OAAO,SAAS,SAAS,WACvB,OAAO,SAAS,gBAAgB,kBAC9B,OAAO,SAAS,YAAY,eAC1B;KACV,OAAO,KAAK,KAAK,IAAI,IAAI,MAAM,OAAO,OAAO;IAC/C;IACA,KAAK,KAAK,uBAAuB;KAAE,WAAW,EAAE;KAAW,OAAO,OAAO;IAAM,GAAG,IAAI,EAAE;IACxF;GACF;GACA,KAAK,uBAAuB;IAC1B,MAAM,IAAI,IAAI;IACd,IAAI,CAAC,GAAG,WAAW,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,oBAAoB;IAC9E,IAAI,CAAC,KAAK,KAAK,OAAO,aAAa,mBACjC,OAAO,KAAK,KAAK,IAAI,IAAI,iBAAiB,qDAAqD;IAEjG,MAAM,SAAS,MAAM,KAAK,KAAK,OAAO,eAAe,EAAE,SAAS;IAChE,IAAI,CAAC,OAAO,IAAI;KACd,MAAM,OAAO,OAAO,SAAS,cAAc,gBACvC,OAAO,SAAS,SAAS,WACvB,OAAO,SAAS,gBAAgB,kBAC9B,OAAO,SAAS,YAAY,eAC1B;KACV,OAAO,KAAK,KAAK,IAAI,IAAI,MAAM,OAAO,OAAO;IAC/C;IACA,KAAK,KAAK,wBAAwB,EAAE,WAAW,EAAE,UAAU,GAAG,IAAI,EAAE;IACpE;GACF;GACA,KAAK,sBAAsB;IACzB,MAAM,IAAI,IAAI;IACd,IAAI,CAAC,GAAG,WAAW,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,oBAAoB;IAC9E,MAAM,SAAS,MAAM,KAAK,KAAK,OAAO,cAAc,EAAE,SAAS;IAC/D,IAAI,CAAC,OAAO,IAAI;KACd,MAAM,OAAO,OAAO,SAAS,cAAc,gBACvC,OAAO,SAAS,SAAS,WACvB,OAAO,SAAS,gBAAgB,kBAC9B,OAAO,SAAS,YAAY,eAC1B;KACV,OAAO,KAAK,KAAK,IAAI,IAAI,MAAM,OAAO,OAAO;IAC/C;IACA,KAAK,KAAK,WAAW,EAAE,WAAW,EAAE,UAAU,GAAG,IAAI,EAAE;IACvD;GACF;GACA,KAAK,uBAAuB;IAC1B,MAAM,IAAI,IAAI;IACd,IAAI,CAAC,GAAG,aAAa,OAAO,EAAE,cAAc,UAC1C,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,kCAAkC;IAG3E,IAAI,CAAC,MADY,KAAK,KAAK,OAAO,YAAY,MAAM,EAAE,WAAW,EAAE,WAAW,KAAK,IAAI,EAAE,SAAS,KAAK,GAAG,CAAC,GAClG,KAAK,KAAK,IAAI,IAAI,eAAe,qBAAqB;IAC/D;GACF;GACA,KAAK,0BAA0B;IAC7B,MAAM,IAAI,IAAI;IACd,IAAI,CAAC,GAAG,aAAa,OAAO,EAAE,iBAAiB,YAAY,EAAE,aAAa,WAAW,GACnF,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,qCAAqC;IAE9E,MAAM,QAAQ,MAAM,KAAK,KAAK,OAAO,eAAe,EAAE,WAAW,EAAE,YAAY;IAC/E,IAAI,CAAC,OAAO,OAAO,KAAK,KAAK,IAAI,IAAI,eAAe,wBAAwB;IAC5E,KAAK,KAAK,WAAW,OAAO,IAAI,EAAE;IAClC;GACF;GACA,KAAK,sBAAsB;IACzB,MAAM,IAAI,IAAI;IACd,IAAI,CAAC,GAAG,WAAW,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,oBAAoB;IAC9E,IAAI,CAAC,KAAK,KAAK,OAAO,aAAa,QACjC,OAAO,KAAK,KAAK,IAAI,IAAI,iBAAiB,kDAAkD;IAE9F,MAAM,SAAS,MAAM,KAAK,KAAK,OAAO,cAAc,EAAE,SAAS;IAC/D,IAAI,CAAC,OAAO,IAAI;KACd,MAAM,OAAO,OAAO,SAAS,cAAc,gBACvC,OAAO,SAAS,SAAS,WACvB,OAAO,SAAS,gBAAgB,kBAC9B,OAAO,SAAS,gBAAgB,gBAC9B;KACV,OAAO,KAAK,KAAK,IAAI,IAAI,MAAM,OAAO,OAAO;IAC/C;IACA,KAAK,KAAK,sBAAsB;KAAE,WAAW,EAAE;KAAW,GAAG,OAAO;IAAM,GAAG,IAAI,EAAE;IACnF;GACF;GACA,KAAK,2BAA2B;IAC9B,MAAM,IAAI,IAAI;IAMd,IAAI,CAAC,GAAG,aAAa,CAAC,EAAE,UAAU,KAAK,KAAK,CAAC,EAAE,OAAO,KAAK,GACzD,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,wCAAwC;IAEjF,IAAI,CAAC,KAAK,KAAK,OAAO,aAAa,QACjC,OAAO,KAAK,KAAK,IAAI,IAAI,iBAAiB,kDAAkD;IAE9F,MAAM,SAAS,MAAM,KAAK,KAAK,OAAO,mBAAmB,EAAE,WAAW;KACpE,UAAU,EAAE,SAAS,KAAK;KAC1B,OAAO,EAAE,MAAM,KAAK;KACpB,GAAI,EAAE,iBAAiB,KAAK,IAAI,EAAE,iBAAiB,EAAE,gBAAgB,KAAK,EAAE,IAAI,CAAC;IACnF,CAAC;IACD,IAAI,CAAC,OAAO,IAAI;KACd,MAAM,OAAO,OAAO,SAAS,cAAc,gBACvC,OAAO,SAAS,SAAS,WACvB,OAAO,SAAS,gBAAgB,kBAC9B,OAAO,SAAS,gBAAgB,gBAC9B;KACV,OAAO,KAAK,KAAK,IAAI,IAAI,MAAM,OAAO,OAAO;IAC/C;IACA,KAAK,KAAK,6BAA6B;KAAE,WAAW,EAAE;KAAW,UAAU,OAAO;IAAM,GAAG,IAAI,EAAE;IACjG;GACF;GACA,KAAK,0BAA0B;IAC7B,MAAM,IAAI,IAAI;IAKd,MAAM,OAAO,OAAO,GAAG,SAAS,WAAW,EAAE,OAAO;IACpD,MAAM,YAAY,MAAM,QAAQ,GAAG,MAAM,IAAI,EAAE,SAAS,CAAC;IACzD,IAAI,CAAC,GAAG,aAAc,KAAK,KAAK,CAAC,CAAC,WAAW,KAAK,UAAU,WAAW,GACrE,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,uCAAuC;IAEhF,IAAI,KAAK,SAAS,uBAChB,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,sBAAsB;IAE/D,IAAI,UAAU,SAAS,mBACrB,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,iBAAiB;IAE1D,MAAM,SAID,CAAC;IACN,KAAK,MAAM,SAAS,WAAW;KAC7B,IAAI,CAAC,kBAAkB,IAAI,OAAO,OAAO,SAAS,CAAC,KAC/C,OAAO,OAAO,SAAS,YAAY,MAAM,KAAK,WAAW,KACzD,MAAM,KAAK,SAAS,4BACtB,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,0BAA0B;KAEnE,OAAO,KAAK;MACV,WAAW,MAAM;MACjB,MAAM,MAAM;MACZ,GAAI,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,KAAK,CAAC,CAAC,SAAS,IAC7D,EAAE,MAAM,MAAM,KAAK,KAAK,CAAC,CAAC,MAAM,GAAG,GAAG,EAAE,IACxC,CAAC;KACP,CAAC;IACH;IACA,MAAM,UAAU,MAAM,KAAK,KAAK,OAAO,WAAW,EAAE,WAAW,MAAM,MAAM;IAC3E,IAAI,YAAY,MAAM,OAAO,KAAK,KAAK,IAAI,IAAI,UAAU,6BAA6B;IACtF,KAAK,KAAK,WAAW,EAAE,QAAQ,GAAG,IAAI,EAAE;IACxC;GACF;GACA,KAAK,wBAAwB;IAC3B,MAAM,IAAI,IAAI;IACd,IAAI,CAAC,GAAG,aAAc,EAAE,aAAa,WAAW,EAAE,aAAa,QAC7D,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,iCAAiC;IAG1E,IAAI,CAAC,MADY,KAAK,KAAK,OAAO,gBAAgB,EAAE,WAAW,EAAE,QAAQ,GAChE,OAAO,KAAK,KAAK,IAAI,IAAI,eAAe,sBAAsB;IACvE,KAAK,KAAK,WAAW,CAAC,GAAG,IAAI,EAAE;IAC/B;GACF;GACA,KAAK,wBAAwB;IAC3B,MAAM,IAAI,IAAI;IACd,IAAI,CAAC,GAAG,aAAa,CAAC,MAAM,QAAQ,EAAE,OAAO,GAC3C,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,gCAAgC;IAGzE,IAAI,CAAC,MADY,KAAK,KAAK,OAAO,gBAAgB,EAAE,WAAW,EAAE,OAAO,GAC/D,OAAO,KAAK,KAAK,IAAI,IAAI,eAAe,sBAAsB;IACvE,KAAK,KAAK,WAAW,CAAC,GAAG,IAAI,EAAE;IAC/B;GACF;GACA,KAAK,qBAAqB;IACxB,MAAM,IAAI,IAAI;IACd,MAAM,QAAQ,OAAO,GAAG,gBAAgB,WAAW,EAAE,YAAY,KAAK,IAAI;IAC1E,IAAI,CAAC,iBAAiB,KAAK,GACzB,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,yCAAyC;IAElF,MAAM,cAA+B,GAAG,gBAAgB,eAAe,eAAe;IACtF,MAAM,aAAa,OAAO,GAAG,eAAe,YAAY,EAAE,eAAe,OACrE,EAAE,aACF,KAAA;IACJ,IAAI,CAAC,KAAK,YAAY,CAAC,KAAK,eAC1B,OAAO,KAAK,KAAK,IAAI,IAAI,cAAc,oBAAoB;IAM7D,IAAI,OAAO,GAAG,cAAc,UAAU;KACpC,MAAM,YAAY,EAAE,UAAU,KAAK,CAAC,CAAC,QAAQ,iBAAiB,EAAE,CAAC,CAAC,MAAM,GAAG,GAAG;KAC9E,IAAI,UAAU,UAAU,KAAK,UAAU,UAAU,KAC/C,MAAM,KAAK,KAAK,kBAAkB,SAAS;IAE/C;IAIA,KAAK,KAAK,QAAQ,aAAa,KAAK,UAAU,OAAO,aAAa,YAAY,KAAK,IAAI,CAAC;IACxF,IAAI,CAAC,KAAK,KAAK,OAAO,aAAa,MAAM;KACvC,IAAI,KAAK,KAAK,UAAU,MAAM,KAAK,KAAK,IAAI,sCAAsC;KAClF,OAAO,KAAK,KAAK,IAAI,IAAI,iBAAiB,uCAAuC;IACnF;IACA,IAAI,KAAK,KAAK,UAAU,MACtB,KAAK,KAAK,IAAI,+BAA+B,WAAW;IAI1D,KAAK,KAAK,WAAW,EAAE,SAAS,KAAK,GAAG,IAAI,EAAE;IAC9C;GACF;GACA,SACE,KAAK,KAAK,IAAI,IAAI,cAAc,mBAAmB,IAAI,IAAI;EAC/D;CACF;CAEA,MAAc,MAAM,KAA8B;EAChD,MAAM,IAAK,IAAI,WAAW,CAAC;EAC3B,IAAI,CAAC,mBAAmB,KAAK,KAAK,wBAAwB,EAAE,OAAO,KAAK,KAAK,aAAa,GAAG;GAC3F,KAAK,KAAK,IAAI,IAAI,UAAU,0BAA0B;GACtD,KAAK,MAAM,MAAM,eAAe;GAChC;EACF;EACA,IAAI,CAAC,EAAE,UAAU;GACf,KAAK,KAAK,IAAI,IAAI,cAAc,mBAAmB;GACnD,KAAK,MAAM,MAAM,mBAAmB;GACpC;EACF;EACA,MAAM,WAAW,oBAAoB,EAAE,UAAU,mBAAmB;EACpE,IAAI,CAAC,UAAU;GACb,KAAK,KAAK,IAAI,IAAI,cAAc,mBAAmB;GACnD,KAAK,MAAM,MAAM,mBAAmB;GACpC;EACF;EACA,MAAM,aAAa,oBAAoB,EAAE,YAAY,qBAAqB,KAAK;EAC/E,MAAM,aAAa,oBAAoB,EAAE,YAAY,qBAAqB,KAAK;EAC/E,KAAK,gBAAgB;EACrB,KAAK,WAAW;EAChB,IAAI,KAAK,eAAe,KAAA,GAAW,aAAa,KAAK,UAAU;EAC/D,KAAK,KAAK,QAAQ,MAAM;GAAE;GAAU;GAAY;EAAW,GAAG,KAAK,IAAI,CAAC;EACxE,KAAK,KAAK,IAAI,oBAAoB,aAAa,OAAO,WAAW,GAAG;EAEpE,MAAM,SAAS,OAAO,EAAE,iBAAiB,YAAY,EAAE,gBAAgB,IACnE,EAAE,eACF,KAAA;EACJ,MAAM,YAAY,WAAW,KAAA,KAAa,KAAK,KAAK,OAAO,cAAc,MAAM;EAE/E,KAAK,KAAK,eAAe;GACvB,iBAAA;GACA,eAAe,KAAK,KAAK;GACzB,cAAc,KAAK,KAAK,OAAO;GAC/B,QAAQ,KAAK,KAAK,OAAO,cAAc;GACvC,SAAS;EACX,GAAG,IAAI,EAAE;EACT,KAAK,KAAK,OAAO,QAAQ,IAAI;EAC7B,IAAI,WAAW,KAAA,GAAW;GACxB,IAAI,WACF,KAAK,KAAK,OAAO,WAAW,QAAQ,IAAI;QAExC,KAAK,OAAO;EAEhB;CACF;AACF;AAWA,SAAS,oBACP,MAC0B;CAC1B,QAAQ,MAAR;EACE,KAAK,eAAe,OAAO;EAC3B,KAAK,aAAa,OAAO;EACzB,KAAK,QAAQ,OAAO;EACpB,KAAK,WAAW,OAAO;EACvB,KAAK,YAAY,OAAO;CAC1B;AACF;;;;;;;;AC1aA,SAAS,cAAc,KAA+B;CACpD,OAAO,IAAI,WAAW,cACnB,OAAO,IAAI,oBAAoB,YAAY,IAAI,gBAAgB,SAAS;AAC7E;AA0CA,SAAS,iBAAiB,MAAsD;CAC9E,MAAM,SAAS,KAAK;CACpB,IAAI,UAAU,OAAO,WAAW,YAAY,OAAO,OAAO,SAAS,UACjE,OAAO,OAAO,SAAS,CAAC,KAAK,QAAQ,SAAS;EAAE,GAAG;EAAQ,OAAO,KAAK;CAAM;CAE/E,OAAO;AACT;AAoDA,MAAM,mBAAmB;;;;;;AAOzB,IAAI,aAAa;AAEjB,IAAa,aAAb,MAAwB;CAaH;CACA;CAbnB,KAAc,EAAE;CAChB,4BAAoB,IAAI,IAA4B;CACpD,4BAAoB,IAAI,IAA6B;CACrD,4BAAoB,IAAI,IAA6B;CACrD,qCAA6B,IAAI,IAAY;CAC7C,qCAA6B,IAAI,IAAY;CAC7C,wBAAgB,IAAI,IAAgB;CACpC,OAAuE,CAAC;CACxE,SAAiB;CACjB,QAAgB,IAAI,gBAAgB;CAEpC,YACE,UACA,mBAA4C,kBAC5C;EAFiB,KAAA,WAAA;EACA,KAAA,mBAAA;CAChB;CAEH;;;;;CAMA,cAAc,QAAsC;EAClD,KAAK,aAAa;CACpB;CAEA,IAAI,eAAe;EACjB,OAAO;GACL,eAAe;GACf,QAAQ;GACR,WAAW;GACX,WAAW;GACX,QAAQ,OAAO,KAAK,SAAS,SAAS,WAAW,cAC/C,OAAO,KAAK,SAAS,SAAS,gBAAgB;GAChD,mBAAmB,OAAO,KAAK,SAAS,SAAS,WAAW,cAC1D,OAAO,KAAK,SAAS,WAAW,mBAAmB;GACrD,kBAAkB,OAAO,KAAK,SAAS,WAAW,SAAS,cACzD,OAAO,KAAK,SAAS,WAAW,WAAW;GAC7C,MAAM,KAAK,YAAY,YAAY,MAAM;EAC3C;CACF;CAEA,WAAW,SAAuB;EAChC,QAAQ,IAAI,iBAAiB,OAAO;CACtC;CAEA,gBAAwB;EACtB,OAAO,KAAK;CACd;CAIA,QAAQ,MAAwB;EAC9B,KAAK,MAAM,IAAI,IAAI;CACrB;CAEA,WAAW,MAAwB;EACjC,KAAK,MAAM,OAAO,IAAI;CACxB;;CAGA,cAAc,QAAyB;EACrC,MAAM,SAAS,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,CAAE,MAAM,KAAK,SAAS;EACxE,OAAO,SAAS,KAAK;CACvB;CAEA,+BAAuB,IAAI,IAA6B;CACxD,oCAA4B,IAAI,IAAoB;;CAGpD,aAAa,MAAkB,WAAyB;EACtD,IAAI,MAAM,KAAK,aAAa,IAAI,IAAI;EACpC,IAAI,CAAC,KAAK;GAAE,sBAAM,IAAI,IAAI;GAAG,KAAK,aAAa,IAAI,MAAM,GAAG;EAAG;EAC/D,IAAI,IAAI,SAAS;CACnB;CAEA,eAAe,MAAkB,WAAyB;EACxD,KAAK,aAAa,IAAI,IAAI,CAAC,EAAE,OAAO,SAAS;CAC/C;CAEA,iBAAiB,MAAwB;EACvC,KAAK,aAAa,OAAO,IAAI;CAC/B;CAEA,WAAmB,MAAkB,WAA4B;EAC/D,OAAO,KAAK,aAAa,IAAI,IAAI,CAAC,EAAE,IAAI,SAAS,KAAK;CACxD;;CAGA,wBAAgC,WAAmB,IAAmB;EACpE,IAAI,KAAK,mBAAmB,IAAI,SAAS,GAAG;EAE5C,MAAM,MAAM,KAAK,UAAU,IAAI,SAAS;EACxC,MAAM,QAAQ,KAAK,SAAS;EAC5B,MAAM,OAAO,KAAK,kBAAkB,IAAI,SAAS,KAAK,KAAK,SAAS;EACpE,MAAM,gBAAgB,KAAK,SAAS,MAAM,KAAK,MAAM,GAAG,GAAG,IAAI,MAAM;EACrE,MAAM,WAA2B,KAAK,mBAAmB;EACzD,MAAM,iBAAiB,QAAQ,KAAK,SAAS;EAC7C,KAAK,OAAO,cAAc;GACxB;GACA;GACA;GACA;GACA,MAAM;GACN,IAAI,KAAK,IAAI;EACf,IAAI,SAAS,KAAK,WAAW,MAAM,SAAS,CAAC;EAC7C,KAAK,WAAW;GAAE;GAAgB;GAAU;GAAW;GAAO,MAAM;EAAc,CAAC;CACrF;;;;;CAMA,WAAmB,cAAsC;EACvD,IAAI;GACF,KAAK,YAAY,OAAO,YAAY;EACtC,QAAQ,CAER;CACF;;CAIA,qBAA6B,WAAmB,OAA+B;EAE7E,IAAI,MAAM,SAAS,qBAAqB;EAExC,MAAM,OAAO,YAAY,MAAM,IAAI,CAAC,CAAC,KAAK;EAE1C,IAAI,KAAK,SAAS,GAAG,KAAK,kBAAkB,IAAI,WAAW,KAAK,MAAM,IAAI,CAAC;CAC7E;;;;;;CAOA,WAAW,QAAgB,QAA8B;EACvD,MAAM,SAAS,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,CAAE,MAAM,KAAK,SAAS;EACxE,IAAI,SAAS,IAAI,QAAQ,OAAO;EAChC,MAAM,YAAY,WAAW,KAAA,IAAY,CAAC,MAAM,IAAI,CAAC,GAAG,KAAK,KAAK;EAClE,KAAK,MAAM,SAAS,KAAK,MACvB,IAAI,MAAM,MAAM,QACd,KAAK,MAAM,QAAQ,WAAW,KAAK,OAAO,CAAC,KAAK,CAAC;EAGrD,KAAK,MAAM,QAAQ,WAAW,KAAK,WAAW;EAC9C,OAAO;CACT;CAEA,OAAe,MAAc,SAAkB,QAA8C;EAC3F,KAAK,UAAU;EACf,MAAM,QAAQ;GAAE,KAAK,KAAK;GAAQ;GAAM;EAAQ;EAChD,KAAK,KAAK,KAAK,KAAK;EACpB,IAAI,KAAK,KAAK,SAAS,KAAK,kBAC1B,KAAK,KAAK,OAAO,GAAG,KAAK,KAAK,SAAS,KAAK,gBAAgB;EAE9D,KAAK,MAAM,QAAQ,KAAK,OAAO;GAC7B,IAAI,UAAU,OAAO,IAAI,GAAG;GAC5B,KAAK,KAAK,MAAM,SAAS,MAAM,GAAG;EACpC;CACF;;CAMA,QAAc;EACZ,KAAU,cAAc;EACxB,KAAU,aAAa;EACvB,KAAU,iBAAiB;CAC7B;CAEA,UAAgB;EACd,KAAK,MAAM,MAAM;EACjB,KAAK,MAAM,MAAM;CACnB;CAEA,MAAc,gBAA+B;EAC3C,IAAI;GACF,WAAW,MAAM,QAAQ,KAAK,SAAS,OAAO,KAAK,EAAE,OAAO,WAAW,EAAE,GAAG,KAAK,MAAM,MAAM,GAAG;IAC9F,MAAM,QAAQ,iBAAiB,IAAI;IACnC,KAAK,YAAY,KAAK;GACxB;EACF,QAAQ,CAER;CACF;CAEA,MAAc,eAA8B;EAC1C,IAAI;GACF,WAAW,MAAM,QAAQ,KAAK,SAAS,OAAO,IAAI,EAAE,OAAO,WAAW,EAAE,GAAG,KAAK,MAAM,MAAM,GAAG;IAC7F,MAAM,QAAQ,iBAAiB,IAAI;IACnC,KAAK,WAAW,KAAK;GACvB;EACF,QAAQ,CAER;CACF;CAEA,YAAoB,OAA2B;EAC7C,QAAQ,MAAM,MAAd;GACE,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;IACH,KAAU,iBAAiB;IAC3B;GACF,KAAK,kCAAkC;IACrC,MAAM,WAAY,MAA2C;IAC7D,IAAI,MAAM,QAAQ,QAAQ,GACxB,KAAK,qBAAqB,IAAI,IAAI,SAAS,IAAI,MAAM,CAAC;IAExD,KAAU,iBAAiB;IAC3B;GACF;GACA,KAAK,uBAAuB;IAC1B,MAAM,IAAI;IACV,MAAM,MAAM,KAAK,UAAU,IAAI,OAAO,EAAE,SAAS,CAAC;IAClD,IAAI,OAAO,OAAO,GAAG,YAAY,WAAW;KAC1C,IAAI,SAAS,EAAE,UAAU,YAAY;KACrC,KAAK,YAAY,GAAG;IACtB;IACA;GACF;EAGF;CACF;CAEA,WAAmB,OAA2B;EAC5C,QAAQ,MAAM,MAAd;GACE,KAAK,iBAAiB;IACpB,MAAM,QAAQ,MAAM;IACpB,MAAM,YAAY,OAAO,MAAM,aAAa,EAAE;IAC9C,IAAI,CAAC,SAAS,CAAC,WAAW;IAC1B,KAAK,aAAa,WAAW,KAAK;IAClC,KAAK,qBAAqB,WAAW,KAAK;IAC1C,MAAM,aAAa,aAAa,WAAW,KAAK;IAChD,IAAI,YACF,KAAK,OAAO,qBAAqB;KAAE;KAAW,MAAM,WAAW;KAAM,KAAK,MAAM;KAAK,MAAM,WAAW;IAAK,CAAC;IAE9G,IAAI,YAAY,SAAS,YACvB,KAAK,wBAAwB,WAAW,WAAW,KAAK,OAAO,IAAI;IAErE;GACF;GACA,KAAK,sBAAsB;IACzB,MAAM,IAAI;IACV,IAAI,CAAC,EAAE,WAAW;IAClB,KAAK,gBAAgB,EAAE,WAAW,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK;IAC9D;GACF;GACA,KAAK,sBAAsB;IACzB,MAAM,IAAI;IACV,IAAI,CAAC,EAAE,cAAc,CAAC,MAAM,OAAO;IACnC,MAAM,WAAW,OAAO,EAAE,YAAY,MAAM;IAC5C,MAAM,UAAU,OAAO,EAAE,UAAU,EAAE;IACrC,KAAK,UAAU,IAAI,EAAE,YAAY;KAC/B,OAAO,MAAM;KACb,WAAW,OAAO,EAAE,aAAa,EAAE;KACnC;KACA,QAAQ;IACV,CAAC;IACD,KAAK,OAAO,wBAAwB;KAClC,WAAW,EAAE;KACb,WAAW,OAAO,EAAE,aAAa,EAAE;KACnC;KACA;KACA,WAAW,OAAO,QAAQ;IAC5B,CAAC;IACD,KAAK,WAAW;KACd,gBAAgB,SAAS,EAAE;KAC3B,UAAU;KACV,WAAW,OAAO,EAAE,aAAa,EAAE;KACnC,OAAO;KACP,MAAM,WAAW,OAAO;IAC1B,CAAC;IACD,KAAK,iBAAiB,OAAO,EAAE,aAAa,EAAE,CAAC;IAC/C;GACF;GACA,KAAK,qBAAqB;IACxB,MAAM,IAAI;IACV,IAAI,CAAC,EAAE,YAAY;IACnB,MAAM,UAAU,KAAK,UAAU,IAAI,EAAE,UAAU;IAC/C,KAAK,UAAU,OAAO,EAAE,UAAU;IAClC,KAAK,OAAO,uBAAuB,EAAE,WAAW,EAAE,WAAW,CAAC;IAC9D,IAAI,SAAS,KAAK,iBAAiB,QAAQ,SAAS;IACpD;GACF;GACA,KAAK,sBAAsB;IACzB,MAAM,IAAI;IACV,IAAI,CAAC,MAAM,OAAO;IAClB,MAAM,YAAY,OAAO,MAAM;IAC/B,MAAM,YAAY,OAAO,GAAG,aAAa,EAAE;IAC3C,KAAK,UAAU,IAAI,WAAW;KAAE,OAAO,MAAM;KAAO;KAAW,WAAW,GAAG;IAAU,CAAC;IACxF,KAAK,OAAO,wBAAwB;KAAE;KAAW;KAAW,WAAW,GAAG,aAAa,CAAC;IAAE,CAAC;IAC3F,KAAK,WAAW;KACd,gBAAgB;KAChB,UAAU;KACV;KACA,OAAO;KACP,MAAM,kBAAkB,GAAG,SAAS;IACtC,CAAC;IACD,KAAK,iBAAiB,SAAS;IAC/B;GACF;GACA,KAAK,qBAAqB;IACxB,MAAM,IAAI;IACV,IAAI,CAAC,EAAE,eAAe;IACtB,MAAM,YAAY,OAAO,EAAE;IAC3B,MAAM,UAAU,KAAK,UAAU,IAAI,SAAS;IAC5C,KAAK,UAAU,OAAO,SAAS;IAC/B,KAAK,OAAO,uBAAuB,EAAE,UAAU,CAAC;IAChD,IAAI,SAAS,KAAK,iBAAiB,QAAQ,SAAS;IACpD;GACF;EAGF;CACF;CAKA,MAAM,mBAAkC;EACtC,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,SAAS,SAAS,KAAK;IAAE,OAAO,WAAW;IAAG,SAAS,CAAC;GAAE,CAAC;GACvF,IAAI,CAAC,SAAS,UAAU,CAAC,SAAS,OAAO,IAAI;IAC3C,KAAK,WAAW,6BAA6B,KAAK,UAAU,SAAS,UAAU,IAAI,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC;IAClG;GACF;GACA,IAAI,aAAkC,CAAC;GACvC,MAAM,gBAAgB,KAAK,SAAS,WAAW;GAC/C,IAAI,OAAO,kBAAkB,YAAY;IACvC,MAAM,oBAAoB,MAAM,cAAc,KAAK,KAAK,SAAS,WAAW;KAC1E,OAAO,WAAW;KAClB,SAAS,CAAC;IACZ,CAAC;IACD,IAAI,kBAAkB,QAAQ,IAAI;KAChC,aAAa,kBAAkB,OAAO,MAAM,SAAS,CAAC;KACtD,KAAK,qBAAqB,IAAI,KAC3B,kBAAkB,OAAO,MAAM,sBAAsB,CAAC,EAAA,CAAG,IAAI,MAAM,CACtE;IACF;GACF;GACA,MAAM,cAAc,IAAI,IAAI,KAAK,UAAU,KAAK,CAAC;GACjD,MAAM,qCAAqB,IAAI,IAA+B;GAC9D,KAAK,MAAM,aAAa,YACtB,KAAK,MAAM,aAAa,UAAU,cAAc,CAAC,GAC/C,mBAAmB,IAAI,OAAO,SAAS,GAAG,SAAS;GAGvD,MAAM,uBAAO,IAAI,IAA4B;GAC7C,MAAM,8BAAc,IAAI,IAAY;GACpC,KAAK,MAAM,OAAO,SAAS,OAAO,MAAM,SAAS,CAAC,GAAG;IACnD,IAAI,KAAK,mBAAmB,IAAI,IAAI,SAAS,GAAG;IAChD,IAAI,cAAc,GAAG,GAAG;KACtB,YAAY,IAAI,IAAI,SAAS;KAC7B;IACF;IACA,KAAK,IAAI,IAAI,WAAW,UAAU,KAAK,KAAK,WAAW,KAAK,WAAW,mBAAmB,IAAI,IAAI,SAAS,CAAC,CAAC;GAC/G;GACA,KAAK,qBAAqB;GAC1B,KAAK,YAAY;GACjB,MAAM,aAAa,CAAC,GAAG,WAAW,CAAC,CAAC,QAAQ,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;GAChE,KAAK,MAAM,MAAM,YAAY,KAAK,kBAAkB,OAAO,EAAE;GAC7D,KAAK,OAAO,sBAAsB;IAAE,UAAU,CAAC,GAAG,KAAK,OAAO,CAAC;IAAG;GAAW,CAAC;EAChF,QAAQ,CAER;CACF;;CAGA,oBAA4B,WAAmB,UAAqC;EAClF,MAAM,MAAM,KAAK,UAAU,IAAI,SAAS;EACxC,IAAI,CAAC,OAAO,IAAI,MAAM,SAAS,GAAG;EAClC,MAAM,YAAY,SAAS,MAAK,MAAK,EAAE,SAAS,WAAW,EAAE,QAAQ,GAAA,CAAI,KAAK,CAAC,CAAC,SAAS,CAAC;EAC1F,IAAI,CAAC,WAAW;EAChB,IAAI,QAAQ,UAAW,KAAM,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;EACpE,KAAK,YAAY,GAAG;CACtB;CAEA,aAAqB,WAAmB,OAAgC;EACtE,MAAM,MAAM,KAAK,UAAU,IAAI,SAAS;EACxC,IAAI,CAAC,KAAK;EAMV,QAAQ,OAAO,MAAf;GACE,KAAK;GACL,KAAK;GACL,KAAK,YACH;GACF,SACE;EACJ;EACA,IAAI,iBAAiB,KAAK,IAAI;EAC9B,KAAK,YAAY,GAAG;CACtB;CAEA,gBAAwB,WAAmB,KAAa,OAAsB;EAC5E,MAAM,MAAM,KAAK,UAAU,IAAI,SAAS;EACxC,IAAI,CAAC,KAAK;EACV,IAAI,QAAQ,SACV,IAAI,QAAQ,OAAO,UAAU,WAAW,QAAQ;OAC3C,IAAI,QAAQ,SAAS;GAE1B,MAAM,YAAY,kBADJ,MAAM,QAAQ,KAAK,IAAI,QAA0D,IACtD;GACzC,IAAI,YAAY,UAAU,SAAS,IAAI,YAAY;GACnD,IAAI,QAAQ,UAAU,SAAS,IAC3B;IAAE,MAAM,UAAU,QAAO,MAAK,EAAE,WAAW,WAAW,CAAC,CAAC;IAAQ,OAAO,UAAU;GAAO,IACxF;EACN,OAAO,IAAI,QAAQ,uBAAuB;GACxC,MAAM,OAAO;GACb,IAAI,MAAM,cAAc,IAAI,iBAAiB,KAAK,IAAI,IAAI,gBAAgB,KAAK,YAAY;EAC7F,OACE;EAEF,KAAK,YAAY,GAAG;CACtB;CAEA,iBAAyB,WAAyB;EAChD,MAAM,MAAM,KAAK,UAAU,IAAI,SAAS;EACxC,IAAI,CAAC,KAAK;EACV,IAAI,WAAW;EACf,KAAK,MAAM,WAAW,KAAK,UAAU,OAAO,GAC1C,IAAI,QAAQ,cAAc,WAAW,WAAW;EAElD,IAAI,WAAW;EACf,KAAK,MAAM,WAAW,KAAK,UAAU,OAAO,GAC1C,IAAI,QAAQ,cAAc,WAAW,WAAW;EAElD,IAAI,kBAAkB;EACtB,IAAI,kBAAkB;EACtB,KAAK,YAAY,GAAG;CACtB;CAEA,YAAoB,KAA2B;EAC7C,KAAK,OAAO,sBAAsB;GAAE,UAAU,CAAC,GAAG;GAAG,YAAY,CAAC;EAAE,CAAC;CACvE;CAEA,eAAiC;EAC/B,OAAO,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;CACxF;;CAKA,MAAM,YAAY,MAAkB,WAAmB,WAAqC;EAC1F,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,SAAS,SAAS,QAAQ;IACpD,OAAO,WAAW;IAClB,SAAS;KAAE;KAAW,aAAa,UAAU,SAAS;IAAE;GAC1D,CAAC;GACD,IAAI,CAAC,SAAS,UAAU,CAAC,SAAS,OAAO,IAAI,OAAO;GACpD,MAAM,SAAS,SAAS,OAAO;GAC/B,MAAM,WAAW,eAAe,OAAO,UAAU,CAAC,CAAC;GACnD,MAAM,YAAY,SAAS,SAAS,IAAI,SAAS,EAAE,CAAE,MAAM;GAC3D,KAAK,KAAK,oBAAoB;IAC5B;IACA;IACA;IACA,SAAS,QAAQ,OAAO,OAAO;GACjC,CAAC;GACD,KAAK,oBAAoB,WAAW,QAAQ;GAC5C,OAAO;EACT,QAAQ;GACN,OAAO;EACT;CACF;CAEA,MAAM,YAAY,MAAkB,WAAmB,WAAmB,OAAiC;EACzG,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,SAAS,SAAS,QAAQ;IACpD,OAAO,WAAW;IAClB,SAAS;KAAE;KAAW;KAAW,aAAa,UAAU,KAAK;IAAE;GACjE,CAAC;GACD,IAAI,CAAC,SAAS,UAAU,CAAC,SAAS,OAAO,IAAI,OAAO;GACpD,MAAM,SAAS,SAAS,OAAO;GAC/B,MAAM,WAAW,eAAe,OAAO,UAAU,CAAC,CAAC;GACnD,KAAK,KAAK,oBAAoB;IAAE;IAAW;IAAU,SAAS,QAAQ,OAAO,OAAO;GAAE,CAAC;GACvF,OAAO;EACT,QAAQ;GACN,OAAO;EACT;CACF;;CAGA,MAAM,eACJ,WACA,cACsD;EACtD,MAAM,OAAO,KAAK,SAAS,SAAS;EACpC,IAAI,OAAO,SAAS,YAAY,OAAO;EACvC,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,KAAK,KAAK,SAAS,UAAU;IACvD,OAAO,WAAW;IAClB,SAAS;KAAE;KAAW;IAAa;GACrC,CAAC;GACD,IAAI,CAAC,SAAS,UAAU,CAAC,SAAS,OAAO,IAAI,OAAO;GACpD,MAAM,OAAO,SAAS,OAAO,MAAM;GACnC,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAAG,OAAO;GAC1D,OAAO;IACL,GAAI,OAAO,SAAS,OAAO,MAAM,YAAY,cAAc,WACvD,EAAE,WAAW,SAAS,OAAO,MAAM,WAAW,UAAoB,IAClE,CAAC;IACL;GACF;EACF,QAAQ;GACN,OAAO;EACT;CACF;CAEA,MAAM,cAAc,WAAkE;EACpF,MAAM,SAAS,KAAK,SAAS,SAAS;EACtC,IAAI,OAAO,WAAW,YACpB,OAAO;GAAE,IAAI;GAAO,MAAM;GAAe,SAAS;EAAiD;EAErG,IAAI;GACF,MAAM,WAAW,MAAM,OAAO,KAAK,KAAK,SAAS,UAAU;IACzD,OAAO,WAAW;IAClB,SAAS,EAAE,UAAU;GACvB,CAAC;GACD,IAAI,CAAC,SAAS,QAAQ,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS;GAAmC;GACxG,IAAI,CAAC,SAAS,OAAO,IAAI,OAAO,eAAe,SAAS,OAAO,KAAK;GACpE,OAAO;IAAE,IAAI;IAAM,OAAO,qBAAqB,SAAS,OAAO,KAAK;GAAE;EACxE,SAAS,OAAO;GACd,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS,OAAO,KAAK;GAAE;EAC/D;CACF;CAEA,MAAM,mBACJ,WACA,WACgD;EAChD,MAAM,cAAc,KAAK,SAAS,SAAS;EAC3C,IAAI,OAAO,gBAAgB,YACzB,OAAO;GAAE,IAAI;GAAO,MAAM;GAAe,SAAS;EAAmD;EAEvG,IAAI;GACF,MAAM,WAAW,MAAM,YAAY,KAAK,KAAK,SAAS,UAAU;IAC9D,OAAO,WAAW;IAClB,SAAS;KACP;KACA,UAAU,UAAU;KACpB,OAAO,UAAU;KACjB,GAAI,UAAU,kBAAkB,EAAE,iBAAiB,UAAU,gBAAgB,IAAI,CAAC;IACpF;GACF,CAAC;GACD,IAAI,CAAC,SAAS,QAAQ,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS;GAAqC;GAC1G,IAAI,CAAC,SAAS,OAAO,IAAI,OAAO,eAAe,SAAS,OAAO,KAAK;GACpE,OAAO;IAAE,IAAI;IAAM,OAAO,EAAE,GAAG,SAAS,OAAO,MAAM,SAAS;GAAE;EAClE,SAAS,OAAO;GACd,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS,OAAO,KAAK;GAAE;EAC/D;CACF;CAEA,MAAM,cAAc,WAAmB,OAAyD;EAC9F,MAAM,SAAS,KAAK,SAAS,SAAS;EACtC,IAAI,OAAO,WAAW,YACpB,OAAO;GAAE,IAAI;GAAO,MAAM;GAAe,SAAS;EAAkD;EAEtG,IAAI;GACF,MAAM,WAAW,MAAM,OAAO,KAAK,KAAK,SAAS,UAAU;IACzD,OAAO,WAAW;IAClB,SAAS;KAAE;KAAW;IAAM;GAC9B,CAAC;GACD,IAAI,CAAC,SAAS,QAAQ,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS;GAAoC;GACzG,IAAI,CAAC,SAAS,OAAO,IAAI,OAAO,2BAA2B,SAAS,OAAO,KAAK;GAChF,MAAM,gBAAgB,OAAO,SAAS,OAAO,MAAM,KAAK;GACxD,MAAM,MAAM,KAAK,UAAU,IAAI,SAAS;GACxC,IAAI,KAAK;IACP,IAAI,QAAQ;IACZ,KAAK,YAAY,GAAG;GACtB;GACA,OAAO;IAAE,IAAI;IAAM,OAAO;GAAc;EAC1C,SAAS,OAAO;GACd,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS,OAAO,KAAK;GAAE;EAC/D;CACF;CAEA,MAAM,eAAe,WAA2D;EAC9E,MAAM,UAAU,KAAK,SAAS,WAAW;EACzC,IAAI,OAAO,YAAY,YACrB,OAAO;GAAE,IAAI;GAAO,MAAM;GAAe,SAAS;EAAmD;EAEvG,IAAI;GACF,MAAM,WAAW,MAAM,QAAQ,KAAK,KAAK,SAAS,WAAW;IAC3D,OAAO,WAAW;IAClB,SAAS,EAAE,UAAU;GACvB,CAAC;GACD,IAAI,CAAC,SAAS,QAAQ,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS;GAAqC;GAC1G,IAAI,CAAC,SAAS,OAAO,IAAI,OAAO,2BAA2B,SAAS,OAAO,KAAK;GAChF,KAAK,qBAAqB,IAAI,KAC3B,SAAS,OAAO,MAAM,sBAAsB,CAAC,EAAA,CAAG,IAAI,MAAM,CAC7D;GACA,KAAK,UAAU,OAAO,SAAS;GAC/B,KAAK,kBAAkB,OAAO,SAAS;GACvC,KAAK,OAAO,sBAAsB;IAAE,UAAU,CAAC;IAAG,YAAY,CAAC,SAAS;GAAE,CAAC;GAC3E,OAAO;IAAE,IAAI;IAAM,OAAO;GAAK;EACjC,SAAS,OAAO;GACd,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS,OAAO,KAAK;GAAE;EAC/D;CACF;CAEA,MAAM,cAAc,WAA2D;EAC7E,MAAM,SAAS,KAAK,SAAS,SAAS;EACtC,IAAI,OAAO,WAAW,YACpB,OAAO;GAAE,IAAI;GAAO,MAAM;GAAe,SAAS;EAAkD;EAEtG,IAAI;GACF,MAAM,WAAW,MAAM,OAAO,KAAK,KAAK,SAAS,UAAU;IACzD,OAAO,WAAW;IAClB,SAAS,EAAE,UAAU;GACvB,CAAC;GACD,IAAI,CAAC,SAAS,QAAQ,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS;GAAoC;GACzG,IAAI,CAAC,SAAS,OAAO,IAAI,OAAO,2BAA2B,SAAS,OAAO,KAAK;GAChF,OAAO;IAAE,IAAI;IAAM,OAAO;GAAK;EACjC,SAAS,OAAO;GACd,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS,OAAO,KAAK;GAAE;EAC/D;CACF;CAEA,MAAM,iBAED;EACH,MAAM,OAAO,KAAK,SAAS,WAAW;EACtC,IAAI,OAAO,SAAS,YAClB,OAAO;GAAE,IAAI;GAAO,MAAM;GAAe,SAAS;EAAkD;EAEtG,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,KAAK,KAAK,SAAS,WAAW;IAAE,OAAO,WAAW;IAAG,SAAS,CAAC;GAAE,CAAC;GAC9F,IAAI,CAAC,SAAS,QAAQ,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS;GAAoC;GACzG,IAAI,CAAC,SAAS,OAAO,IAAI,OAAO,2BAA2B,SAAS,OAAO,KAAK;GAChF,OAAO;IAAE,IAAI;IAAM,QAAQ,SAAS,OAAO,MAAM,SAAS,CAAC,EAAA,CAAG,IAAI,gBAAgB;GAAE;EACtF,SAAS,OAAO;GACd,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS,OAAO,KAAK;GAAE;EAC/D;CACF;CAEA,MAAM,gBAAgB,MAElB;EACF,MAAM,SAAS,KAAK,SAAS,WAAW;EACxC,IAAI,OAAO,WAAW,YACpB,OAAO;GAAE,IAAI;GAAO,MAAM;GAAe,SAAS;EAAoD;EAExG,IAAI;GACF,MAAM,WAAW,MAAM,OAAO,KAAK,KAAK,SAAS,WAAW;IAC1D,OAAO,WAAW;IAClB,SAAS,EAAE,KAAK;GAClB,CAAC;GACD,IAAI,CAAC,SAAS,QAAQ,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS;GAAsC;GAC3G,IAAI,CAAC,SAAS,OAAO,IAAI,OAAO,2BAA2B,SAAS,OAAO,KAAK;GAChF,MAAM,KAAK,iBAAiB;GAC5B,OAAO;IACL,IAAI;IACJ,OAAO;KAAE,WAAW,iBAAiB,SAAS,OAAO,MAAM,SAAS;KAAG,SAAS,SAAS,OAAO,MAAM,YAAY;IAAK;GACzH;EACF,SAAS,OAAO;GACd,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS,OAAO,KAAK;GAAE;EAC/D;CACF;CAEA,MAAM,cAAc,MAAuE;EACzF,MAAM,OAAO,KAAK,SAAS,MAAM;EACjC,IAAI,OAAO,SAAS,YAClB,OAAO;GAAE,IAAI;GAAO,MAAM;GAAe,SAAS;EAAsD;EAE1G,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,KAAK,KAAK,SAAS,MAAM;IACnD,OAAO,WAAW;IAClB,SAAS,QAAQ,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,EAAE,KAAK,IAAI,CAAC;GACxD,GAAG,KAAK,MAAM,MAAM;GACpB,IAAI,CAAC,SAAS,QAAQ,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS;GAAoC;GACzG,IAAI,CAAC,SAAS,OAAO,IAAI,OAAO,2BAA2B,SAAS,OAAO,KAAK;GAChF,OAAO;IAAE,IAAI;IAAM,OAAO,SAAS,OAAO;GAAM;EAClD,SAAS,OAAO;GACd,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS,OAAO,KAAK;GAAE;EAC/D;CACF;CAEA,MAAM,gBAAiE;EACrE,MAAM,OAAO,KAAK,SAAS,MAAM;EACjC,IAAI,OAAO,SAAS,YAClB,OAAO;GAAE,IAAI;GAAO,MAAM;GAAe,SAAS;EAA2D;EAE/G,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,KAAK,KAAK,SAAS,MAAM;IACnD,OAAO,WAAW;IAClB,SAAS,CAAC;GACZ,GAAG,KAAK,MAAM,MAAM;GACpB,IAAI,CAAC,SAAS,QAAQ,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS;GAAsC;GAC3G,IAAI,CAAC,SAAS,OAAO,IAAI,OAAO,2BAA2B,SAAS,OAAO,KAAK;GAChF,OAAO;IAAE,IAAI;IAAM,OAAO,SAAS,OAAO,MAAM;GAAK;EACvD,SAAS,OAAO;GACd,OAAO;IAAE,IAAI;IAAO,MAAM;IAAY,SAAS,OAAO,KAAK;GAAE;EAC/D;CACF;;CAGA,MAAM,cAAc,cAAsD,CAAC,GAA2B;EACpG,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,SAAS,SAAS,OAAO;IACnD,OAAO,WAAW;IAClB,SAAS;KACP,GAAI,YAAY,aAAa,KAAK,IAAI,EAAE,aAAa,YAAY,YAAY,KAAK,EAAE,IAAI,CAAC;KACzF,GAAI,YAAY,KAAK,KAAK,IAAI,EAAE,KAAK,YAAY,IAAI,KAAK,EAAE,IAAI,CAAC;IACnE;GACF,CAAC;GACD,IAAI,CAAC,SAAS,UAAU,CAAC,SAAS,OAAO,IAAI,OAAO;GACpD,MAAM,YAAY,SAAS,OAAO,MAAM;GACxC,MAAM,KAAK,iBAAiB;GAC5B,OAAO;EACT,QAAQ;GACN,OAAO;EACT;CACF;CAEA,MAAM,WACJ,WACA,MACA,SAAqH,CAAC,GAC9F;EACxB,IAAI;GACF,MAAM,UAAiC,CAAC;GACxC,IAAI,KAAK,KAAK,CAAC,CAAC,SAAS,GAAG,QAAQ,KAAK;IAAE,MAAM;IAAQ;GAAK,CAAC;GAC/D,KAAK,MAAM,SAAS,QAAQ,QAAQ,KAAK;IAAE,MAAM;IAAS,GAAG;GAAM,CAAC;GACpE,MAAM,WAAW,MAAM,KAAK,SAAS,SAAS,OAAO;IACnD,OAAO,WAAW;IAClB,SAAS;KACP;KACA,MAAM;KACN;KACA,gBAAgB,cAAc;IAChC;GACF,CAAC;GACD,IAAI,CAAC,SAAS,UAAU,CAAC,SAAS,OAAO,IAAI,OAAO;GACpD,MAAM,MAAM,KAAK,UAAU,IAAI,SAAS;GACxC,IAAI,KAAK;IACP,IAAI,iBAAiB,KAAK,IAAI;IAC9B,KAAK,YAAY,GAAG;GACtB;GACA,OAAO,KAAK,IAAI;EAClB,QAAQ;GACN,OAAO;EACT;CACF;CAGA,MAAM,gBAAgB,WAAmB,UAA8C;EACrF,MAAM,UAAU,KAAK,UAAU,IAAI,SAAS;EAC5C,IAAI,CAAC,SAAS,OAAO;EAIrB,KAAK,UAAU,OAAO,SAAS;EAC/B,MAAM,UAAU,aAAa,UAAU,iBAAiB;EACxD,IAAI;GACF,MAAM,UAAU,MAAM,KAAK,SAAS,QAAQ;IAC1C,MAAM;IACN,OAAO,QAAQ;IACf,QAAQ;KAAE,IAAI;KAAM,OAAO;MAAE,WAAW,QAAQ;MAAW,YAAY;MAAW;KAAQ;IAAE;GAC9F,CAAC;GACD,MAAM,WAAW,QAAQ,SAAS,QAAQ;GAC1C,IAAI,CAAC,YAAY,CAAC,KAAK,UAAU,IAAI,SAAS,GAAG,KAAK,UAAU,IAAI,WAAW,OAAO;GACtF,OAAO;EACT,QAAQ;GAEN,IAAI,CAAC,KAAK,UAAU,IAAI,SAAS,GAAG,KAAK,UAAU,IAAI,WAAW,OAAO;GACzE,OAAO;EACT;CACF;CAEA,MAAM,gBAAgB,WAAmB,SAAoC;EAC3E,MAAM,UAAU,KAAK,UAAU,IAAI,SAAS;EAC5C,IAAI,CAAC,SAAS,OAAO;EACrB,KAAK,UAAU,OAAO,SAAS;EAC/B,IAAI;GACF,MAAM,UAAU,MAAM,KAAK,SAAS,QAAQ;IAC1C,MAAM;IACN,OAAO,QAAQ;IACf,QAAQ;KAAE,IAAI;KAAM,OAAO;MAAE,WAAW,QAAQ;MAAW,QAAQ,EAAE,QAAQ;KAAE;IAAE;GACnF,CAAC;GACD,MAAM,WAAW,QAAQ,SAAS,QAAQ;GAC1C,IAAI,CAAC,YAAY,CAAC,KAAK,UAAU,IAAI,SAAS,GAAG,KAAK,UAAU,IAAI,WAAW,OAAO;GACtF,OAAO;EACT,QAAQ;GACN,IAAI,CAAC,KAAK,UAAU,IAAI,SAAS,GAAG,KAAK,UAAU,IAAI,WAAW,OAAO;GACzE,OAAO;EACT;CACF;AACF;AAIA,SAAS,UAAU,GAAmB;CACpC,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO;CAChC,OAAO,KAAK,IAAI,IAAI,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC;AAClD;AAEA,SAAS,gBAAoC;CAC3C,IAAI;EACF,OAAO,IAAI,KAAK,eAAe,CAAC,CAAC,gBAAgB,CAAC,CAAC,YAAY,KAAA;CACjE,QAAQ;EACN;CACF;AACF;AAEA,SAAS,OAAO,UAAoD;CAClE,IAAI,qBAAqB,KAAK,QAAQ,GAAG,OAAO;CAChD,IAAI,gCAAgC,KAAK,QAAQ,GAAG,OAAO;CAC3D,IAAI,qBAAqB,KAAK,QAAQ,GAAG,OAAO;CAChD,OAAO;AACT;;;AAIA,SAAgB,kBAAkB,WAA4B;CAC5D,IAAI,CAAC,MAAM,QAAQ,SAAS,KAAK,UAAU,WAAW,GAAG,OAAO;CAChE,MAAM,QAAQ,UAAU;CAExB,OADa,OAAO,OAAO,YAAY,EAAE,CAAC,CAAC,KACjC,KAAK;AACjB;AAEA,MAAM,gCAAgB,IAAI,IAAI;CAAC;CAAW;CAAe;AAAW,CAAC;;;AAIrE,SAAS,kBAAkB,OAAiF;CAC1G,IAAI,CAAC,OAAO,OAAO,CAAC;CACpB,OAAO,MACJ,KAAI,OAAM;EAAE,SAAS,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC,KAAK;EAAG,QAAQ,OAAO,EAAE,UAAU,EAAE;CAAE,EAAE,CAAC,CACvF,QAAO,MAAK,EAAE,QAAQ,SAAS,KAAK,cAAc,IAAI,EAAE,MAAM,CAAC,CAAC,CAChE,MAAM,GAAG,GAAG,CAAC,CACb,KAAI,OAAM;EAAE,SAAS,EAAE;EAAS,QAAQ,EAAE;CAA4B,EAAE;AAC7E;AAEA,SAAS,UACP,KACA,WACA,WACA,WACgB;CAChB,MAAM,SAAS,IAAI,aAAa,UAAU,CAAC;CAC3C,MAAM,QAAQ,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,QAA0D;CAC7G,IAAI,kBAAkB;CACtB,KAAK,MAAM,WAAW,UAAU,OAAO,GACrC,IAAI,QAAQ,cAAc,IAAI,WAAW,kBAAkB;CAE7D,IAAI,kBAAkB;CACtB,KAAK,MAAM,WAAW,UAAU,OAAO,GACrC,IAAI,QAAQ,cAAc,IAAI,WAAW,kBAAkB;CAE7D,MAAM,MAAM,OAAO,IAAI,QAAQ,WAAW,IAAI,MAAM;CACpD,MAAM,QAAQ,WAAW,UAAU,MAAM,IAAI,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,IAAI,IAAI,KAAA;CAChF,MAAM,YAAY,kBAAkB,KAAK;CACzC,OAAO;EACL,IAAI,IAAI;EACR,OAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;EACzD,QAAQ,IAAI,UAAU,YAAa,IAAI,QAAQ,YAAY;EAC3D,gBAAgB,OAAO,IAAI,aAAa,KAAK,IAAI,CAAC;EAClD,OAAO,UAAU,SAAS,IACtB;GACE,MAAM,UAAU,QAAO,MAAK,EAAE,WAAW,WAAW,CAAC,CAAC;GACtD,OAAO,UAAU;EACnB,IACA;EACJ,WAAW,UAAU,SAAS,IAAI,YAAY;EAC9C;EACA;EACA,gBAAgB,SAAS;EACzB,aAAa,WAAW,eAAe;EACvC,eAAe,WAAW,SAAS,OAAO;CAC5C;AACF;AAEA,SAAS,iBAAiB,WAExB;CACA,OAAO;EACL,IAAI,OAAO,UAAU,WAAW;EAChC,OAAO,OAAO,UAAU,KAAK;EAC7B,MAAM,OAAO,UAAU,IAAI;EAC3B,aAAa,UAAU,cAAc,CAAC,EAAA,CAAG,IAAI,MAAM;CACrD;AACF;;AAGA,SAAS,aAAa,WAAmB,OAA2F;CAClI,QAAQ,MAAM,MAAd;EACE,KAAK,cACH,OAAO;GAAE,MAAM;GAAc,MAAM,CAAC;EAAE;EACxC,KAAK,YAEH,OAAO;GAAE,MAAM;GAAY,MAAM,EAAE,IADtB,MAAM,MAC0B,QAAQ,SAAS,YAAY;EAAE;EAE9E,KAAK,gBACH,OAAO;GACL,MAAM;GACN,MAAM;IACJ,KAAK,MAAM;IACX,MAAM;IACN,MAAM,YAAY,MAAM,IAAI;IAC5B,GAAG,qBAAqB,MAAM,IAAI;IAClC,IAAI,KAAK,KAAK;GAChB;EACF;EACF,KAAK;GACH,IAAI,YAAY,MAAM,IAAI,MAAM,mBAC9B,OAAO;IAAE,MAAM;IAAkB,MAAM;KAAE,MAAM,UAAU,MAAM,IAAI;KAAG,IAAI,KAAK,KAAK;IAAE;GAAE;GAE1F,OAAO;IAAE,MAAM;IAAiB,MAAM;KAAE,MAAM,UAAU,MAAM,IAAI;KAAG,IAAI,KAAK,KAAK;IAAE;GAAE;EAEzF,KAAK,qBAAqB;GACxB,MAAM,OAAO,YAAY,MAAM,IAAI;GACnC,MAAM,WAAW,gBAAgB,MAAM,IAAI;GAG3C,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,SAAS,KAAK,GAAG,OAAO;GAC7C,OAAO;IACL,MAAM;IACN,MAAM;KAAE,KAAK,MAAM;KAAK,MAAM;KAAa;KAAM,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;KAAI,IAAI,KAAK,KAAK;IAAE;GACtG;EACF;EACA,KAAK,aAAa;GAChB,MAAM,OAAO,MAAM;GACnB,OAAO;IACL,MAAM;IACN,MAAM;KACJ,KAAK,MAAM;KACX,MAAM;KACN,MAAM;MAAE,MAAM,OAAO,MAAM,QAAQ,MAAM;MAAG,OAAO;MAAW,SAAS,cAAc,MAAM,SAAS;KAAE;KACtG,IAAI,KAAK,KAAK;IAChB;GACF;EACF;EACA,KAAK,eACH,OAAO;GAAE,MAAM;GAAY,MAAM;IAAE,KAAK,MAAM;IAAK,MAAM;IAAQ,IAAI,CAAC,MAAM,QAAS,MAAM,KAA6B,UAAU,KAAA;IAAW,IAAI,KAAK,KAAK;GAAE;EAAE;EACjK,SACE,OAAO;CACX;AACF;AAEA,SAAS,KAAK,OAAiC;CAC7C,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAK,IAAI;AAChE;;AAGA,SAAS,YAAY,MAAuB;CAC1C,IAAI,OAAO,SAAS,UAAU,OAAO;CACrC,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,MAAM,MAAM;CACZ,IAAI,OAAO,IAAI,SAAS,UAAU,OAAO,IAAI;CAE7C,IAAI,IAAI,WAAW,OAAO,IAAI,YAAY,UAAU,OAAO,YAAY,IAAI,OAAO;CAClF,OAAO,YAAY,IAAI,OAAO;AAChC;AAEA,SAAS,YAAY,SAA0B;CAC7C,IAAI,OAAO,YAAY,UAAU,OAAO;CACxC,IAAI,MAAM,QAAQ,OAAO,GACvB,OAAO,QAAQ,KAAI,SAAQ;EACzB,IAAI,OAAO,SAAS,UAAU,OAAO;EACrC,IAAI,QAAQ,OAAO,SAAS,UAAU;GACpC,MAAM,QAAQ;GACd,IAAI,MAAM,SAAS,UAAU,OAAO,MAAM,SAAS,UAAU,OAAO,MAAM;EAC5E;EACA,OAAO;CACT,CAAC,CAAC,CAAC,KAAK,EAAE;CAEZ,OAAO;AACT;AAEA,SAAS,mBAAmB,MAOzB;CACD,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO,CAAC;CAC/C,MAAM,MAAM;CACZ,IAAI,IAAI,WAAW,OAAO,IAAI,YAAY,UAAU,OAAO,mBAAmB,IAAI,OAAO;CACzF,IAAI,CAAC,MAAM,QAAQ,IAAI,OAAO,GAAG,OAAO,CAAC;CACzC,OAAO,IAAI,QAAQ,SAAQ,SAAQ;EACjC,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO,CAAC;EAI/C,MAAM,QAAQ;EAUd,IAAI,MAAM,SAAS,WAAW,CAAC,MAAM,YAAY,OAAO,CAAC;EACzD,MAAM,eAAe,OAAO,MAAM,WAAW,iBAAiB,YAAY,MAAM,WAAW,aAAa,SAAS,IAC7G,MAAM,WAAW,eACjB,KAAA;EACJ,MAAM,QAAQ,OAAO,MAAM,WAAW,UAAU,YAAY,OAAO,SAAS,MAAM,WAAW,KAAK,IAC9F,MAAM,WAAW,QACjB,KAAA;EACJ,MAAM,SAAS,OAAO,MAAM,WAAW,WAAW,YAAY,OAAO,SAAS,MAAM,WAAW,MAAM,IACjG,MAAM,WAAW,SACjB,KAAA;EACJ,OAAO,CAAC;GACN,MAAM;GACN,GAAI,OAAO,MAAM,WAAW,SAAS,WAAW,EAAE,MAAM,MAAM,WAAW,KAAK,IAAI,CAAC;GACnF,GAAI,OAAO,MAAM,WAAW,cAAc,WAAW,EAAE,WAAW,MAAM,WAAW,UAAU,IAAI,CAAC;GAClG,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;GACvC,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;GACvC,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;EAC3C,CAAC;CACH,CAAC;AACH;AAEA,SAAS,qBAAqB,MAAwE;CACpG,MAAM,cAAc,mBAAmB,IAAI;CAC3C,OAAO,YAAY,SAAS,IAAI,EAAE,YAAY,IAAI,CAAC;AACrD;;AAGA,SAAS,gBAAgB,MAAuB;CAC9C,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,MAAM,MAAM;CAEZ,IAAI,IAAI,WAAW,OAAO,IAAI,YAAY,UAAU,OAAO,gBAAgB,IAAI,OAAO;CACtF,OAAO,iBAAiB,IAAI,OAAO;AACrC;AAEA,SAAS,iBAAiB,SAA0B;CAClD,IAAI,CAAC,MAAM,QAAQ,OAAO,GAAG,OAAO;CACpC,OAAO,QAAQ,KAAI,SAAQ;EACzB,IAAI,QAAQ,OAAO,SAAS,UAAU;GACpC,MAAM,QAAQ;GACd,IAAI,MAAM,SAAS,eAAe,OAAO,MAAM,SAAS,UAAU,OAAO,MAAM;EACjF;EACA,OAAO;CACT,CAAC,CAAC,CAAC,KAAK,EAAE;AACZ;;AAGA,SAAS,YAAY,MAAuB;CAC1C,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,MAAM,MAAM;CACZ,IAAI,IAAI,SAAS,OAAO,IAAI,UAAU,UACpC,OAAO,OAAQ,IAAI,MAA6B,QAAQ,EAAE;CAG5D,OAAO;AACT;AAEA,SAAS,UAAU,MAAuB;CACxC,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAE9C,MAAM,MAAM;CACZ,IAAI,IAAI,SAAS,OAAO,IAAI,UAAU,UAAU;EAC9C,MAAM,QAAQ,IAAI;EAClB,KAAK,MAAM,SAAS,gBAAgB,MAAM,SAAS,sBAAsB,OAAO,MAAM,SAAS,UAC7F,OAAO,MAAM;EAEf,OAAO;CACT;CACA,MAAM,SAAS;CACf,OAAO,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AACzD;AAEA,SAAS,cAAc,KAAsB;CAC3C,IAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,GAAG,OAAO;CACxD,IAAI;EACF,MAAM,SAAS,KAAK,MAAM,GAAG;EAC7B,MAAM,QAAkB,CAAC;EACzB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAC9C,IAAI,OAAO,UAAU,UAAU,MAAM,KAAK,MAAM,MAAM,SAAS,MAAM,QAAQ,QAAQ,GAAG,GAAG,EAAE,CAAC;EAEhG,OAAO,SAAS,MAAM,KAAK,GAAG,GAAG,EAAE;CACrC,QAAQ;EACN,OAAO,SAAS,KAAK,EAAE;CACzB;AACF;AAEA,SAAS,SAAS,MAAc,KAAqB;CACnD,OAAO,KAAK,UAAU,MAAM,OAAO,KAAK,MAAM,GAAG,MAAM,CAAC,IAAI;AAC9D;AAEA,SAAS,eAAe,OAAqE;CAC3F,MAAM,UAAU,MAAM,WAAW,MAAM;CACvC,QAAQ,MAAM,MAAd;EACE,KAAK,qBACH,OAAO;GAAE,IAAI;GAAO,MAAM;GAAa;EAAQ;EACjD,KAAK;EACL,KAAK,oBACH,OAAO;GAAE,IAAI;GAAO,MAAM;GAAQ;EAAQ;EAC5C,KAAK,qBACH,OAAO;GAAE,IAAI;GAAO,MAAM;GAAe;EAAQ;EACnD,SACE,OAAO;GAAE,IAAI;GAAO,MAAM;GAAY;EAAQ;CAClD;AACF;AAEA,SAAS,2BACP,OACgC;CAChC,MAAM,UAAU,MAAM,WAAW,MAAM;CACvC,QAAQ,MAAM,MAAd;EACE,KAAK,qBACH,OAAO;GAAE,IAAI;GAAO,MAAM;GAAa;EAAQ;EACjD,KAAK;EACL,KAAK,oBACH,OAAO;GAAE,IAAI;GAAO,MAAM;GAAQ;EAAQ;EAC5C,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,2BACH,OAAO;GAAE,IAAI;GAAO,MAAM;GAAW;EAAQ;EAC/C,KAAK,gCACH,OAAO;GAAE,IAAI;GAAO,MAAM;GAAe;EAAQ;EACnD,KAAK,uBACH,OAAO;GAAE,IAAI;GAAO,MAAM;GAAa;EAAQ;EACjD,SACE,OAAO;GAAE,IAAI;GAAO,MAAM;GAAY;EAAQ;CAClD;AACF;AAEA,SAAS,qBAAqB,OAA6C;CACzE,OAAO;EACL,SAAS;GACP,UAAU,OAAO,MAAM,QAAQ,QAAQ;GACvC,OAAO,OAAO,MAAM,QAAQ,KAAK;GACjC,GAAI,MAAM,QAAQ,kBAAkB,EAAE,iBAAiB,OAAO,MAAM,QAAQ,eAAe,EAAE,IAAI,CAAC;EACpG;EACA,UAAU,MAAM,aAAa;EAC7B,SAAS,MAAM,UAAU,CAAC,EAAA,CAAG,KAAK,WAAW;GAC3C,IAAI,OAAO,MAAM,EAAE;GACnB,MAAM,OAAO,MAAM,IAAI;GACvB,SAAS,MAAM,UAAU,CAAC,EAAA,CAAG,KAAK,WAAW;IAC3C,IAAI,OAAO,MAAM,EAAE;IACnB,MAAM,OAAO,MAAM,IAAI;IACvB,GAAI,MAAM,cAAc,EAAE,aAAa,OAAO,MAAM,WAAW,EAAE,IAAI,CAAC;IACtE,GAAI,MAAM,YAAY,EACpB,WAAW;KACT,UAAU,MAAM,UAAU,WAAW,CAAC,EAAA,CAAG,KAAK,YAAY;MACxD,IAAI,OAAO,OAAO,EAAE;MACpB,MAAM,OAAO,OAAO,IAAI;MACxB,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,OAAO,WAAW,EAAE,IAAI,CAAC;KAC1E,EAAE;KACF,GAAI,MAAM,UAAU,gBAChB,EAAE,eAAe,OAAO,MAAM,UAAU,aAAa,EAAE,IACvD,CAAC;IACP,EACF,IAAI,CAAC;GACP,EAAE;EACJ,EAAE;EACF,WAAW,MAAM,YAAY,CAAC,EAAA,CAAG,KAAK,aAAa;GACjD,IAAI,OAAO,QAAQ,EAAE;GACrB,MAAM,OAAO,QAAQ,IAAI;GACzB,SAAS,OAAO,QAAQ,OAAO;EACjC,EAAE;CACJ;AACF;;AAGA,SAAgB,eAAe,QAAiF;CAC9G,MAAM,WAAgC,CAAC;CACvC,MAAM,6BAAa,IAAI,IAA+B;CACtD,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,QAAQ,MAAM;EACpB,MAAM,OAAO;GAAE,KAAK,MAAM;GAAK,IAAI,KAAK,KAAK;EAAE;EAC/C,QAAQ,MAAM,MAAd;GACE,KAAK;IACH,SAAS,KAAK;KACZ,GAAG;KACH,MAAM;KACN,MAAM,YAAY,MAAM,IAAI;KAC5B,GAAG,qBAAqB,MAAM,IAAI;IACpC,CAAC;IACD;GACF,KAAK,qBAAqB;IACxB,MAAM,OAAO,YAAY,MAAM,IAAI;IACnC,MAAM,WAAW,gBAAgB,MAAM,IAAI;IAC3C,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,SAAS,KAAK,GAAG;IACtC,SAAS,KAAK;KAAE,GAAG;KAAM,MAAM;KAAa;KAAM,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;IAAG,CAAC;IACrF;GACF;GACA,KAAK,aAAa;IAChB,MAAM,OAAO,MAAM;IACnB,MAAM,MAAyB;KAC7B,GAAG;KACH,MAAM;KACN,MAAM;MAAE,MAAM,OAAO,MAAM,QAAQ,MAAM;MAAG,OAAO;MAAW,SAAS,cAAc,MAAM,SAAS;KAAE;IACxG;IACA,SAAS,KAAK,GAAG;IACjB,IAAI,MAAM,QAAQ,WAAW,IAAI,OAAO,KAAK,MAAM,GAAG,GAAG;IACzD;GACF;GACA,KAAK,eAAe;IAClB,MAAM,OAAO,MAAM;IACnB,MAAM,SAAS,MAAM,SAAS,OAAO,KAAK,MAAM,IAAI,KAAA;IACpD,MAAM,SAAS,SAAS,WAAW,IAAI,MAAM,IAAI,KAAA;IACjD,MAAM,SAAS,MAAM,UAAU,KAAA;IAC/B,MAAM,UAAU,SAAS,OAAO,gBAAgB,MAAM,SAAS,OAAO;IACtE,IAAI,QAAQ,MACV,OAAO,OAAO;KAAE,GAAG,OAAO;KAAM,OAAO,SAAS,UAAU;KAAM;IAAQ;SAExE,SAAS,KAAK;KAAE,GAAG;KAAM,MAAM;KAAQ,MAAM;MAAE,MAAM;MAAU,OAAO,SAAS,UAAU;MAAM;KAAQ;IAAE,CAAC;IAE5G;GACF;EAGF;CACF;CACA,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG;AAC9C;AAEA,SAAS,gBAAgB,SAA0B;CAEjD,OAAO,SADM,YAAY,OAAO,CAAC,CAAC,QAAQ,QAAQ,GAAG,CAAC,CAAC,KACpC,GAAG,EAAE;AAC1B;;;;;;;;AC16CA,IAAa,yBAAb,cAA4C,oBAAoB;CAC9D;CACA;CACA;CAEA;CACA;CAEA,YAAY,KAAc,UAA0B,cAAoC,oBAAyC,aAA0B,YAAwB;EACjL,MAAM,KAAK,mBAAmB,EAAE,WAAW,YAAY,CAAC;EACxD,KAAK,WAAW;EAChB,KAAK,eAAe;EACpB,KAAK,qBAAqB;EAC1B,KAAK,cAAc;EACnB,KAAK,aAAa;CACpB;CAEA,MAAM,SAAmC;EACvC,OAAO,KAAK,SAAS;CACvB;CAEA,MAAM,cAA+B;EACnC,OAAO,KAAK,aAAa;CAC3B;CAEA,MAAM,cAA+B;EACnC,OAAO,KAAK,mBAAmB;CACjC;CAEA,MAAM,YAAsC;EAC1C,OAAO,KAAK,YAAY;CAC1B;CAEA,MAAM,WAAoC;EACxC,OAAO,KAAK,WAAW;CACzB;AACF;;;;ACyBA,MAAa,wBAAwB;;AAGrC,MAAa,kBAAkB;;AAG/B,MAAa,wBAAwB;;;;;;AAOrC,MAAa,wBAAwB;AAErC,SAAS,OAAO,OAAsB;CACpC,MAAM,IAAI,UAAU,oCAAoC,OAAO;AACjE;AAEA,SAAS,IAAI,QAAiC,KAAa,OAAuB;CAChF,MAAM,QAAQ,OAAO;CACrB,IAAI,OAAO,UAAU,UAAU,OAAO,KAAK;CAC3C,OAAO;AACT;AACA,SAAS,IAAI,QAAiC,KAAa,OAAuB;CAChF,MAAM,QAAQ,OAAO;CACrB,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GAAG,OAAO,KAAK;CACtE,OAAO;AACT;AACA,SAAS,KAAK,QAAiC,KAAa,OAAwB;CAClF,MAAM,QAAQ,OAAO;CACrB,IAAI,OAAO,UAAU,WAAW,OAAO,KAAK;CAC5C,OAAO;AACT;AACA,SAAS,IAAI,OAAgB,OAAwC;CACnE,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG,OAAO,KAAK;CACrF,OAAO;AACT;AAEA,SAAS,YAAY,OAA8B;CACjD,MAAM,IAAI,IAAI,OAAO,QAAQ;CAC7B,IAAI;CACJ,IAAI,EAAE,SAAS,KAAA,GAAW;EACxB,MAAM,IAAI,IAAI,EAAE,MAAM,aAAa;EACnC,MAAM,cAAc,IAAI,GAAG,eAAe,yBAAyB;EACnE,IAAI,gBAAgB,iBAAiB,gBAAgB,cAAc,OAAO,yBAAyB;EACnG,OAAO;GAAE;GAAa,WAAW,IAAI,GAAG,aAAa,uBAAuB;EAAE;CAChF;CACA,OAAO;EACL,UAAU,IAAI,GAAG,YAAY,iBAAiB;EAC9C,YAAY,IAAI,GAAG,cAAc,mBAAmB;EACpD,YAAY,IAAI,GAAG,cAAc,mBAAmB;EACpD,aAAa,IAAI,GAAG,eAAe,oBAAoB;EACvD,YAAY,IAAI,GAAG,cAAc,mBAAmB;EACpD,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;CACzB;AACF;AAEA,SAAS,YAAY,OAA2C;CAC9D,MAAM,IAAI,IAAI,OAAO,QAAQ;CAC7B,MAAM,WAAW,IAAI,GAAG,YAAY,iBAAiB;CACrD,MAAM,QAAQ,IAAI,GAAG,SAAS,cAAc;CAC5C,IAAI,aAAa,oBAAoB,OAAO,iBAAiB;CAE7D,IAAI,CAAC;EADW;EAAY;EAAY;EAAkB;EAAU;EAAS;EAAe;CAClF,CAAC,CAAC,SAAS,KAAK,GAAG,OAAO,cAAc;CAClD,MAAM,YAAY,EAAE;CACpB,MAAM,UAAU,EAAE;CAClB,MAAM,UAAU,EAAE;CAClB,IAAI,cAAc,KAAA,KAAa,OAAO,cAAc,UAAU,OAAO,kBAAkB;CACvF,IAAI,YAAY,KAAA,KAAa,OAAO,YAAY,UAAU,OAAO,gBAAgB;CACjF,IAAI,YAAY,KAAA,KAAa,OAAO,YAAY,UAAU,OAAO,gBAAgB;CACjF,OAAO;EACL;EACO;EACP,GAAI,OAAO,cAAc,WAAW,EAAE,UAAU,IAAI,CAAC;EACrD,GAAI,OAAO,YAAY,WAAW,EAAE,QAAQ,IAAI,CAAC;EACjD,GAAI,OAAO,YAAY,WAAW,EAAE,QAAQ,IAAI,CAAC;EACjD,WAAW,IAAI,GAAG,aAAa,kBAAkB;CACnD;AACF;AAEA,SAAS,mBAAmB,OAA+B;CACzD,MAAM,KAAK,IAAI,OAAO,MAAM;CAC5B,MAAM,KAAK,IAAI,IAAI,MAAM,SAAS;CAClC,IAAI,OAAO,YAAY,OAAO,UAAU,OAAO,SAAS;CACxD,MAAM,YAAY,GAAG;CACrB,IAAI,cAAc,KAAA,KAAa,OAAO,cAAc,UAAU,OAAO,gBAAgB;CACrF,OAAO;EACL;EACA,IAAI,KAAK,IAAI,MAAM,SAAS;EAC5B,SAAS,IAAI,IAAI,WAAW,cAAc;EAC1C,GAAI,OAAO,cAAc,WAAW,EAAE,UAAU,IAAI,CAAC;CACvD;AACF;AAEA,SAAS,qBAAqB,OAAiC;CAC7D,MAAM,IAAI,IAAI,OAAO,QAAQ;CAC7B,MAAM,UAAU,IAAI,GAAG,WAAW,SAAS;CAC3C,IAAI,YAAY,QAAQ,YAAY,UAAU,OAAO,SAAS;CAC9D,MAAM,WAAW,EAAE;CACnB,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG,OAAO,OAAO;CAC5C,OAAO;EACL,KAAK,IAAI,GAAG,OAAO,KAAK;EACxB;EACA,aAAa,KAAK,GAAG,eAAe,aAAa;EACjD,OAAQ,SAAuB,IAAI,kBAAkB;CACvD;AACF;AAEA,SAAS,oBAAoB,OAAgC;CAC3D,MAAM,IAAI,IAAI,OAAO,QAAQ;CAC7B,MAAM,YAAY,IAAI,GAAG,aAAa,WAAW;CACjD,IAAI,cAAc,UAAU,cAAc,WAAW,cAAc,QAAQ,OAAO,WAAW;CAC7F,MAAM,UAAU,IAAI,GAAG,WAAW,SAAS;CAE3C,IAAI,CAAC;EADa;EAAQ;EAAU;EAAc;CACtC,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,SAAS;CACjD,MAAM,aAAa,EAAE;CACrB,IAAI,CAAC,MAAM,QAAQ,UAAU,GAAG,OAAO,SAAS;CAChD,MAAM,UAAW,WAAyB,KAAK,UAAU;EACvD,MAAM,IAAI,IAAI,OAAO,eAAe;EACpC,MAAM,SAAS,EAAE;EACjB,MAAM,mBAAmB,EAAE;EAC3B,OAAO;GACL,MAAM,IAAI,GAAG,QAAQ,aAAa;GAClC,aAAa,IAAI,GAAG,eAAe,oBAAoB;GACvD,SAAS,IAAI,GAAG,WAAW,gBAAgB;GAC3C,GAAI,OAAO,WAAW,YAAY,OAAO,SAAS,IAAI,EAAE,OAAO,IAAI,CAAC;GACpE,GAAI,OAAO,qBAAqB,YAAY,mBAAmB,KAAK,gBAAgB,IAChF,EAAE,iBAAiB,IACnB,CAAC;EACP;CACF,CAAC;CACD,MAAM,UAAU,EAAE;CAClB,OAAO;EACL;EACS;EACT,GAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,IAAI,EAAE,QAAQ,IAAI,CAAC;EACvE;CACF;AACF;AAEA,SAAS,YAAY,OAAiC;CACpD,MAAM,IAAI,IAAI,OAAO,QAAQ;CAC7B,MAAM,UAAU,EAAE;CAClB,MAAM,eAAe,EAAE;CACvB,IAAI,CAAC,MAAM,QAAQ,OAAO,GAAG,OAAO,SAAS;CAC7C,IAAI,CAAC,MAAM,QAAQ,YAAY,KAAK,aAAa,MAAM,UAAU,OAAO,UAAU,QAAQ,GAAG,OAAO,cAAc;CAClH,OAAO;EACL,iBAAiB,IAAI,GAAG,mBAAmB,iBAAiB;EAC5D,eAAe,IAAI,GAAG,iBAAiB,eAAe;EACtD,SAAS,KAAK,GAAG,WAAW,SAAS;EACrC,WAAW,IAAI,GAAG,aAAa,WAAW;EAC1C,YAAY,KAAK,GAAG,cAAc,YAAY;EAC9C,mBAAmB,IAAI,GAAG,qBAAqB,mBAAmB;EAClE,kBAAkB,IAAI,GAAG,oBAAoB,kBAAkB;EAC/D,OAAO,KAAK,GAAG,SAAS,OAAO;EACjB;EACd,QAAQ,YAAY,EAAE,MAAM;EAC5B,SAAU,QAAsB,IAAI,WAAW;CACjD;AACF;AAEA,MAAa,eAA8C,EAAE,OAAO,YAAY;AAEhF,MAAa,kBAAiD,EAAE,OAAO,qBAAqB;AAE5F,MAAa,iBAA+C,EAAE,OAAO,oBAAoB;AAEzF,MAAa,qBAA2C,EACtD,MAAM,OAAwB;CAC5B,IAAI,OAAO,UAAU,YAAY,MAAM,SAAS,IAC9C,MAAM,IAAI,UAAU,6CAA6C;CAEnE,OAAO;AACT,EACF;AA8FA,MAAa,2BAA2B;CACtC,SAAS;CACT,MAAM;CACN,SAAS,CAAC;CACV,aAAa;EAXb;GAtEA,IAAI,GAAG,sBAAsB,GAAG;GAChC,SAAS;GACT,WAAW;GACX,QAAQ;GACR,YAAY,EAAE,MAAM,SAAS;GAC7B,YAAY,CAAC;GACb,QAAQ;IACN,MAAM;IACN,YAAY,GAAG,sBAAsB;IACrC,QAAQ;GACV;EA4DA;EACA;GAzDA,IAAI,GAAG,sBAAsB,GAAG;GAChC,SAAS;GACT,WAAW;GACX,QAAQ;GACR,YAAY,EAAE,MAAM,SAAS;GAC7B,YAAY,CAAC;GACb,QAAQ;IACN,MAAM;IACN,YAAY,GAAG,sBAAsB;IACrC,QAAQ;GACV;EA+CA;EACA;GA5CA,IAAI,GAAG,sBAAsB,GAAG;GAChC,SAAS;GACT,WAAW;GACX,QAAQ;GACR,YAAY,EAAE,MAAM,SAAS;GAC7B,YAAY,CAAC;GACb,QAAQ;IACN,MAAM;IACN,YAAY,GAAG,sBAAsB;IACrC,QAAQ;GACV;EAkCA;EACA;GA/BA,IAAI,GAAG,sBAAsB;GAC7B,SAAS;GACT,WAAW;GACX,QAAQ;GACR,YAAY,EAAE,MAAM,SAAS;GAC7B,YAAY,CAAC;GACb,QAAQ;IACN,MAAM;IACN,YAAY,GAAG,sBAAsB;IACrC,QAAQ;GACV;EAqBA;EACA;GAlBA,IAAI,GAAG,sBAAsB;GAC7B,SAAS;GACT,WAAW;GACX,QAAQ;GACR,YAAY,EAAE,MAAM,SAAS;GAC7B,YAAY,CAAC;GACb,QAAQ;IACN,MAAM;IACN,YAAY,GAAG,sBAAsB;IACrC,QAAQ;GACV;EAQA;CAOa;AACf;;;;;;;AC7VA,SAAgB,kBACd,KACA,UACA,cACA,oBACA,aACA,YACM;CACN,IAAI,OAAO,CAAC,QAAQ,IAAI,cAAc;EACpC,IAAI,uBAAuB,WAAW,UAAU,cAAc,oBAAoB,aAAa,UAAU;EAEzG,MAAM,aADW,UAAU,OACC,SAAS,wBAAwB;EAC7D,UAAU,mBAAmB,KAAK,WAAW,GAAG,8BAA8B;CAChF,CAAC;AACH;;;ACiBA,MAAM,qBAAqB;AAE3B,eAAe,YACb,WACA,KACA,MACA,WAC4C;CAC5C,MAAM,WAAW,MAAM,UAAU,KAAK;EACpC,GAAG;EACH,QAAQ,YAAY,QAAQ,SAAS;CACvC,CAAC;CACD,IAAI,OAAgB;CACpB,IAAI;EACF,OAAO,MAAM,SAAS,KAAK;CAC7B,QAAQ,CAER;CACA,OAAO;EAAE,QAAQ,SAAS;EAAQ;CAAK;AACzC;AAEA,eAAsB,cAAc,SAAsD;CACxF,MAAM,OAAO,QAAQ,IAAI,KAAK,CAAC,CAAC,QAAQ,QAAQ,EAAE;CAClD,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,QAAyB,CAAC;CAChC,IAAI,cAAc;CAGlB,IAAI;EACF,MAAM,YAAY,KAAK,IAAI;EAC3B,MAAM,EAAE,QAAQ,SAAS,MAAM,YAC7B,WACA,GAAG,KAAK,WACR,EAAE,QAAQ,MAAM,GAChB,SACF;EACA,MAAM,YAAY,KAAK,IAAI,IAAI;EAC/B,IAAI,WAAW,OAAQ,MAA2B,OAAO,MACvD,MAAM,KAAK;GAAE,IAAI;GAAU,IAAI;GAAM,SAAS;GAAU;EAAU,CAAC;OAEnE,MAAM,KAAK;GAAE,IAAI;GAAU,IAAI;GAAO,SAAS,eAAe,OAAO;GAAI;EAAU,CAAC;CAExF,SAAS,OAAO;EACd,MAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACpE,MAAM,KAAK;GAAE,IAAI;GAAU,IAAI;GAAO,SAAS,YAAY;EAAO,CAAC;CACrE;CAGA,IAAK,QAAQ,eAAe,OAC1B,MAAM,KAAK;EAAE,IAAI;EAAU,IAAI;EAAM,SAAS;CAA0B,CAAC;MACpE,IAAI,CAAC,QAAQ,WAClB,MAAM,KAAK;EAAE,IAAI;EAAU,IAAI;EAAO,SAAS;CAA+B,CAAC;MAC1E;EACL,MAAM,WAAW,QAAQ,YAAY,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC;EAC9E,IAAI;GACF,MAAM,YAAY,KAAK,IAAI;GAC3B,MAAM,EAAE,QAAQ,SAAS,MAAM,YAC7B,WACA,GAAG,KAAK,aACR;IACE,QAAQ;IACR,SAAS,EAAE,gBAAgB,mBAAmB;IAC9C,MAAM,KAAK,UAAU;KAAE;KAAU,WAAW,QAAQ;IAAU,CAAC;GACjE,GACA,SACF;GACA,MAAM,YAAY,KAAK,IAAI,IAAI;GAC/B,MAAM,QAAS,MAA8B;GAC7C,IAAI,WAAW,OAAO,OAAO,UAAU,YAAY,MAAM,WAAW,KAAK,GAAG;IAC1E,cAAc;IACd,QAAQ,aAAa,KAAK;IAC1B,MAAM,KAAK;KAAE,IAAI;KAAU,IAAI;KAAM,SAAS;KAAgB;IAAU,CAAC;GAC3E,OAAO,IAAI,WAAW,KACpB,MAAM,KAAK;IAAE,IAAI;IAAU,IAAI;IAAO,SAAS;IAAgE;GAAU,CAAC;QACrH,IAAI,WAAW,KACpB,MAAM,KAAK;IAAE,IAAI;IAAU,IAAI;IAAO,SAAS;IAAe;GAAU,CAAC;QAEzE,MAAM,KAAK;IAAE,IAAI;IAAU,IAAI;IAAO,SAAS,aAAa,OAAO;IAAI;GAAU,CAAC;EAEtF,SAAS,OAAO;GACd,MAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACpE,MAAM,KAAK;IAAE,IAAI;IAAU,IAAI;IAAO,SAAS,YAAY;GAAO,CAAC;EACrE;CACF;CAEA,MAAM,WAAW,MAAM,QAAQ,SAAS,KAAK,OAAO,KAAA,CAAS;CAM7D,OAAO;EAAE,KAAK;EAAM,SAJlB,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,KAAK,OAAO,YAAY,KAAK,EAAE,KACxE,SAAS,OAAO,SAAS,KAAK,EAAE,IAC5B,OACA;EACuB;EAAa;CAAM;AAClD;;;;;;;;;;;;;;;;;;ACjGA,MAAM,wBAAwB;AAC9B,MAAM,qBAAqB;;AAG3B,SAAS,OAAO,OAAgC;CAC9C,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,WAAW;AAChD;;AAGA,SAAgB,SAAS,cAAsB,KAAwB;CACrE,MAAM,YAAYA,KAAW,UAAU,OAAO,KAAK,cAAc,MAAM,GAAG;EACxE;EAEA,aAAa;CACf,CAAC;CACD,OAAO,eAAe,MAAM,OAAO,SAAS;AAC9C;;AAGA,SAAgB,QAAQ,KAAqB;CAC3C,MAAM,OAAO,IACV,QAAQ,+BAA+B,EAAE,CAAC,CAC1C,QAAQ,6BAA6B,EAAE,CAAC,CACxC,QAAQ,QAAQ,EAAE;CACrB,OAAO,OAAO,KAAK,MAAM,QAAQ;AACnC;;AAOA,SAAgB,YAAY,cAAyD;CACnF,MAAM,gBAAgB,aAAa,aAAa,uBAAuB,aAAa,aAAa;CACjG,OAAO;EACL,KAAK;GACH,OAAO;IACL,OAAO,aAAa,MAAM,MAAM,GAAG,GAAG;IACtC,MAAM,aAAa,KAAK,MAAM,GAAG,GAAG;GACtC;GACA,OAAO;GACP,UAAU,aAAa;GACvB,aAAa,aAAa,UAAU,MAAM,GAAG,EAAE;GAC/C,GAAI,gBAAgB,EAAE,sBAAsB,iBAAiB,IAAI,CAAC;EACpE;EACA,WAAW,aAAa;EACxB,gBAAgB,aAAa;EAC7B,MAAM,aAAa;CACrB;AACF;;AAWA,SAAgB,cAAc,cAAwC;CAEpE,OAAO,GADQ,aAAa,SAAS,GAAG,aAAa,YAC1C,QAAQ,oBAAoB,EAAE,CAAC,CAAC,MAAM,GAAG,EAAE;AACxD;AAEA,SAAS,aAAa,aAAsC;CAC1D,OAAO,gBAAgB,eAAe,uBAAuB;AAC/D;AAEA,IAAa,aAAb,MAAwB;CACtB;CACA;CACA;CAEA,gBAAwB;CACxB,wBAAgC;CAChC;CAEA,YAAY,MAAyB;EACnC,KAAK,OAAO;EACZ,KAAK,MAAM,KAAK;EAChB,KAAK,QAAQ,KAAK,UAAU;CAC9B;CAEA,MAAM,UAAyB;EAC7B,MAAM,WAAW,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC;EAC3C,KAAK,SAAS,MAAM;EACpB,MAAM,QAAQ,IACZ,SACG,QAAQ,YAAY,CAAC,QAAQ,SAAS,CAAC,CACvC,KAAK,YAAY,IAAI,SAAe,YAAY,QAAQ,YAAY,QAAQ,CAAC,CAAC,CAAC,CACpF;CACF;CAIA,MAAc,sBAAuC;EACnD,IAAI,KAAK,iBAAiB,KAAK,IAAI,IAAI,KAAK,wBAAwB,uBAClE,OAAO,KAAK;EAEd,IAAI,CAAC,KAAK,KAAK;GACb,MAAM,MAAM,MAAM,SAAS,KAAK,KAAK,SAAS,MAAM;GACpD,KAAK,MAAM,iBAAiB;IAAE,KAAK,QAAQ,GAAG;IAAG,QAAQ;IAAO,MAAM;GAAQ,CAAC;EACjF;EACA,MAAM,WAAW,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;EAC7C,MAAM,SAAS,OAAO,KAAK,UAAU;GAAE,KAAK;GAAS,KAAK,KAAK,KAAK;EAAM,CAAC,CAAC;EAC5E,MAAM,SAAS,OAAO,KAAK,UAAU;GAAE,KAAK,KAAK,KAAK;GAAQ,KAAK;EAAS,CAAC,CAAC;EAC9E,KAAK,gBAAgB,SAAS,GAAG,OAAO,GAAG,UAAU,KAAK,GAAG;EAC7D,KAAK,wBAAwB,KAAK,IAAI;EACtC,OAAO,KAAK;CACd;;CAKA,2BAA4B,IAAI,IAAgC;CAEhE,cAAsB,WAAuC;EAC3D,MAAM,WAAW,KAAK,SAAS,IAAI,SAAS;EAC5C,IAAI,YAAY,CAAC,SAAS,aAAa,CAAC,SAAS,QAAQ,OAAO;EAChE,MAAM,UAAUC,QAAa,WAAW,WAAW;EAGnD,QAAQ,GAAG,UAAU,UAAU;GAC7B,IAAI,KAAK,OAAO,KAAK,IAAI,yBAAyB,YAAY,QAAQ,OAAO,KAAK,CAAC;GACnF,KAAK,SAAS,OAAO,SAAS;EAChC,CAAC;EACD,KAAK,SAAS,IAAI,WAAW,OAAO;EACpC,OAAO;CACT;;;;;CAMA,MAAM,KAAK,SAAmD;EAC5D,MAAM,EAAE,aAAa,aAAa,GAAG,iBAAiB;EACtD,IAAI;EACJ,IAAI;GACF,MAAM,QAAQ,MAAM,KAAK,oBAAoB;GAC7C,MAAM,OAAO,KAAK,UAAU,YAAY,YAAY,CAAC;GACrD,MAAM,UAAU,KAAK,cAAc,aAAa,WAAW,CAAC;GAuD5D,OAAO,MAAM,IAtDI,SAAyB,YAAY;IACpD,MAAM,MAAM,QAAQ,QAAQ;MACzB,YAAY;MACZ,UAAU,eAAe;KAC1B,eAAe,YAAY;KAC3B,cAAc,KAAK,KAAK;KACxB,kBAAkB;KAClB,iBAAiB;KAEjB,mBAAmB,OAAO,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI,IAAI;KAC9D,oBAAoB,cAAc,YAAY;KAC9C,gBAAgB;KAChB,kBAAkB,OAAO,OAAO,WAAW,IAAI,CAAC;IAClD,CAAC;IACD,SAAS;IACT,IAAI,SAAS;IACb,IAAI,eAAe;IACnB,MAAM,UAAU,SAAsB,WAA0B;KAC9D,IAAI,KAAK,OACP,KAAK,IAAI,QAAQ,UAAU,SAAS,OAAO,SAAS,MAAM,GAAG,IAAI,KAAK,UAAU,WAAW,EAAE,EAAE;KAEjG,QAAQ,WAAW,KAAA,KAAa,WAAW,KAAK;MAAE;MAAS;KAAO,IAAI,EAAE,QAAQ,CAAC;IACnF;IACA,MAAM,QAAQ,iBAAiB;KAC7B,IAAI,MAAM;KACV,OAAO,QAAQ;IACjB,GAAG,kBAAkB;IACrB,MAAM,QAAQ;IACd,IAAI,GAAG,aAAa,YAAY;KAC9B,SAAS,OAAO,QAAQ,cAAc,CAAC;IACzC,CAAC;IACD,IAAI,GAAG,SAAS,UAAkB;KAChC,gBAAgB,MAAM,SAAS,MAAM;IACvC,CAAC;IACD,IAAI,GAAG,eAAe;KACpB,aAAa,KAAK;KAClB,OAAO,QAAQ;IACjB,CAAC;IACD,IAAI,GAAG,aAAa;KAClB,aAAa,KAAK;KAClB,IAAI,WAAW,KAAK,OAAO,OAAO,MAAM;KACxC,IAAI,SAAS;KACb,IAAI;MACF,SAAS,OAAQ,KAAK,MAAM,YAAY,CAAC,CAAyB,UAAU,EAAE;KAChF,QAAQ,CAER;KACA,IAAI,WAAW,OAAO,CAAC,QAAQ,SAAS,UAAU,OAAO,MAAM;KAC/D,IAAI,WAAW,kBAAkB,WAAW,kBAAkB,OAAO,OAAO,iBAAiB,MAAM;KACnG,IAAI,KAAK,OAAO,KAAK,IAAI,wBAAwB,OAAO,UAAU,QAAQ;KAC1E,OAAO,UAAU,MAAM;IACzB,CAAC;IACD,IAAI,IAAI,IAAI;GACd,CACgB;EAClB,SAAS,OAAO;GAGd,KAAK,MAAM,KAAA;GACX,KAAK,gBAAgB;GACrB,KAAK,SAAS,MAAM;GACpB,IAAI,KAAK,OAAO,KAAK,IAAI,uBAAuB,OAAO,KAAK,CAAC;GAC7D,OAAO;IAAE,SAAS;IAAU,QAAQ,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,GAAG;GAAE;EAClE,UAAU;GACR,IAAI;IAAE,QAAQ,MAAM;GAAE,QAAQ,CAAuB;EACvD;CACF;CAEA,UAAU,OAAuB;EAC/B,OAAO,MAAM,UAAU,KAAK,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,MAAM,MAAM,MAAM,EAAE;CAC5E;AACF;;;AC/NA,IAAa,cAAb,MAAyB;CACvB;CACA;CACA;CACA;CACA;CAEA,YAAY,MAA0B;EACpC,KAAK,OAAO,KAAK,IAAI,KAAK,CAAC,CAAC,QAAQ,QAAQ,EAAE;EAC9C,KAAK,SAAS,KAAK,SAAS,GAAA,CAAI,KAAK;EACrC,KAAK,YAAY,KAAK,aAAa;EACnC,KAAK,QAAQ,KAAK,UAAU;EAC5B,KAAK,MAAM,KAAK;CAClB;;;;;;;CAQA,MAAM,OAAO,UAAkB,WAA2C;EACxE,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,KAAK,OAAO,cAAc;IACrD,QAAQ;IACR,SAAS,EAAE,gBAAgB,mBAAmB;IAC9C,MAAM,KAAK,UAAU;KAAE;KAAU;IAAU,CAAC;IAC5C,QAAQ,YAAY,QAAQ,KAAK,SAAS;GAC5C,CAAC;GACD,IAAI,CAAC,SAAS,IAAI;IAChB,IAAI,KAAK,OAAO,KAAK,IAAI,eAAe,SAAS,QAAQ;IACzD,OAAO;GACT;GACA,MAAM,OAAQ,MAAM,SAAS,KAAK;GAClC,IAAI,OAAO,KAAK,UAAU,YAAY,KAAK,MAAM,WAAW,KAAK,GAAG,OAAO,KAAK;GAChF,IAAI,KAAK,OAAO,KAAK,IAAI,iCAAiC;GAC1D,OAAO;EACT,SAAS,OAAO;GACd,IAAI,KAAK,OAAO,KAAK,IAAI,oBAAoB,OAAO,KAAK,CAAC;GAC1D,OAAO;EACT;CACF;CAEA,MAAM,KAAK,SAAuG;EAChH,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,KAAK,OAAO,YAAY;IACnD,QAAQ;IACR,SAAS;KACP,eAAe,YAAY,KAAK;KAChC,gBAAgB;IAClB;IACA,MAAM,KAAK,UAAU;KACnB,aAAa,QAAQ;KACrB,aAAa,QAAQ;KACrB,cAAc,QAAQ;IACxB,CAAC;IACD,QAAQ,YAAY,QAAQ,KAAK,SAAS;GAC5C,CAAC;GACD,IAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;IAItD,IAAI,KAAK,OAAO,KAAK,IAAI,yBAAyB,SAAS,QAAQ;IACnE,OAAO;KAAE,SAAS;KAAU,QAAQ,UAAU,OAAO,SAAS,MAAM;IAAE;GACxE;GACA,IAAI,CAAC,SAAS,IAAI;IAChB,IAAI,KAAK,OAAO,KAAK,IAAI,cAAc,SAAS,QAAQ;IACxD,OAAO;KAAE,SAAS;KAAU,QAAQ,UAAU,OAAO,SAAS,MAAM;IAAE;GACxE;GACA,MAAM,OAAQ,MAAM,SAAS,KAAK;GAClC,IAAI,KAAK,YAAY,QAAQ,OAAO,EAAE,SAAS,OAAO;GACtD,IAAI,KAAK,YAAY,iBAAiB,OAAO;IAAE,SAAS;IAAiB,QAAQ,KAAK;GAAO;GAC7F,IAAI,KAAK,OAAO,KAAK,IAAI,mBAAmB,OAAO,KAAK,OAAO,IAAI,aAAa,OAAO,KAAK,UAAU,EAAE,CAAC;GACzG,OAAO;IAAE,SAAS;IAAU,QAAQ,KAAK;GAAO;EAClD,SAAS,OAAO;GACd,IAAI,KAAK,OAAO,KAAK,IAAI,wBAAwB,OAAO,KAAK,CAAC;GAE9D,OAAO;IAAE,SAAS;IAAU,QADZ,iBAAiB,QAAQ,MAAM,QAAQ,MAAM,GAAG,GAAG,IAAI,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,GAAG;GACrD;EAC9C;CACF;AACF;;;ACrEA,MAAM,oBAAoB;CAAC;CAAO;CAAO;CAAO;CAAO;CAAQ;AAAM;AAErE,MAAa,0BAA0B;;AAGvC,SAAgB,wBAAwB,OAAmC;CACzE,MAAM,WAAW,OAAO,KAAK,KAAK;CAClC,IAAI,aAAa,MAAM;EAAC;EAAa;EAAc;CAAe,CAAC,CAAC,SAAS,SAAS,YAAY,CAAC,GACjG,OAAO;CAET,OAAO;AACT;;AAGA,SAAgB,iBAAiB,MAAkC;CACjE,IAAI;EACF,MAAM,QAAQ,KAAK,MAAM,IAAI;EAE7B,IAAI,OAAO,MAAM,UAAU,YAAY,CAAC;GADT;GAAY;GAAkB;GAAU;GAAS;EACnC,CAAC,CAAC,SAAS,MAAM,KAAoB,GAAG,OAAO;EAC5F,OAAO;GACL,OAAO,MAAM;GACb,GAAI,OAAO,MAAM,cAAc,WAAW,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;GAC5E,GAAI,OAAO,MAAM,YAAY,WAAW,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;GACtE,GAAI,OAAO,MAAM,YAAY,WAAW,EAAE,SAAS,MAAM,QAAQ,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC;EACtF;CACF,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,oBAA4B;CACnC,MAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;CACnD,OAAO,QAAQ,MAAM,MAAM,OAAO,GAAG,QAAQ,SAAS,GAAG,QAAQ,QAAQ,sBAAsB;AACjG;;AAGA,IAAa,mBAAb,MAA8B;CAOC;CAN7B;CACA;CACA,iBAAyB;CACzB,WAAmB;CACnB;CAEA,YAAY,SAAmD;EAAlC,KAAA,UAAA;EAC3B,KAAK,cAAc;GACjB,UAAU;GACV,OAAO,QAAQ,UAAU,YAAY;GACrC,WAAW,KAAK,IAAI;EACtB;CACF;CAEA,SAAuB;EACrB,OAAO,EAAE,GAAG,KAAK,YAAY;CAC/B;CAEA,MAAM,MAAM,WAAkC;EAC5C,IAAI,CAAC,KAAK,QAAQ,WAAW,KAAK,UAAU,KAAA,KAAa,KAAK,UAAU;EACxE,MAAM,SAAS,WAAW,KAAK,QAAQ,cAAc,kBAAkB,CAAC;EACxE,MAAM,YAAY,WAAW,KAAK,QAAQ,SAAS;EACnD,IAAI;GACF,MAAM,OAAO,QAAQC,UAAY,IAAI;GACrC,MAAM,MAAM,WAAW;IAAE,WAAW;IAAM,MAAM;GAAM,CAAC;EACzD,SAAS,OAAO;GACd,KAAK,UAAU;IACb,OAAO;IACP,SAAS,uCAAuC,OAAO,KAAK;GAC9D,CAAC;GACD;EACF;EAEA,KAAK,UAAU;GAAE,OAAO;GAAY,SAAS,KAAA;EAAU,CAAC;EACxD,MAAM,QAAQ,MAAM,QAAQ;GAC1B;GAAY;GACZ;GAAc,wBAAwB,KAAK,QAAQ,QAAQ;GAC3D;GAAe;GACf;GAAU,OAAO,KAAK,QAAQ,cAAc,GAAG;EACjD,GAAG;GACD,OAAO;IAAC;IAAU;IAAQ;GAAM;GAChC,KAAK;IACH,MAAM,QAAQ,IAAI,QAAQ;IAC1B,QAAQ,QAAQ,IAAI,UAAU;GAChC;EACF,CAAC;EACD,KAAK,QAAQ;EACb,IAAI,MAAM,WAAW,QAAQ,MAAM,WAAW,MAAM;GAClD,KAAK,UAAU;IAAE,OAAO;IAAS,SAAS;GAA2B,CAAC;GACtE,MAAM,KAAK,SAAS;GACpB;EACF;EAEA,IAAI,eAAe;EACnB,MAAM,OAAO,YAAY,MAAM;EAC/B,MAAM,OAAO,GAAG,SAAS,UAAkB;GACzC,gBAAgB;GAChB,MAAM,QAAQ,aAAa,MAAM,IAAI;GACrC,eAAe,MAAM,IAAI,KAAK;GAC9B,KAAK,MAAM,QAAQ,OAAO,KAAK,WAAW,IAAI;EAChD,CAAC;EAED,IAAI,eAAe;EACnB,MAAM,OAAO,YAAY,MAAM;EAC/B,MAAM,OAAO,GAAG,SAAS,UAAkB;GACzC,gBAAgB,eAAe,MAAA,CAAO,MAAM,IAAM;EACpD,CAAC;EAED,MAAM,KAAK,UAAU,UAAU;GAC7B,KAAK,UAAU;IAAE,OAAO;IAAS,SAAS,yBAAyB,OAAO,KAAK;GAAI,CAAC;EACtF,CAAC;EACD,MAAM,KAAK,SAAS,MAAM,WAAW;GACnC,IAAI,KAAK,UAAU,OAAO,KAAK,QAAQ,KAAA;GACvC,IAAI,KAAK,UAAU;IACjB,KAAK,UAAU;KAAE,OAAO;KAAW,SAAS,KAAA;IAAU,CAAC;IACvD;GACF;GACA,MAAM,SAAS,aAAa,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,EAAE;GACpD,KAAK,UAAU;IACb,OAAO;IACP,SAAS,UAAU,kBAAkB,UAAU,OAAO,IAAI,EAAE;GAC9D,CAAC;GACD,KAAK,gBAAgB,SAAS;EAChC,CAAC;CACH;CAEA,MAAM,UAAyB;EAC7B,KAAK,WAAW;EAChB,IAAI,KAAK,iBAAiB,KAAA,GAAW,aAAa,KAAK,YAAY;EACnE,KAAK,eAAe,KAAA;EACpB,MAAM,QAAQ,KAAK;EACnB,KAAK,QAAQ,KAAA;EACb,IAAI,UAAU,KAAA,GAAW;GACvB,KAAK,UAAU;IAAE,OAAO;IAAW,SAAS,KAAA;GAAU,CAAC;GACvD;EACF;EACA,MAAM,IAAI,SAAe,gBAAgB;GACvC,MAAM,QAAQ,iBAAiB;IAC7B,IAAI,MAAM,aAAa,QAAQ,MAAM,eAAe,MAAM,MAAM,KAAK,SAAS;GAChF,GAAG,GAAK;GACR,MAAM,KAAK,cAAc;IACvB,aAAa,KAAK;IAClB,YAAY;GACd,CAAC;GACD,MAAM,KAAK,SAAS;EACtB,CAAC;EACD,KAAK,UAAU;GAAE,OAAO;GAAW,SAAS,KAAA;EAAU,CAAC;CACzD;CAEA,WAAmB,MAAoB;EACrC,MAAM,QAAQ,iBAAiB,IAAI;EACnC,IAAI,UAAU,QAAQ,MAAM,UAAU,KAAA,GAAW;EACjD,IAAI,MAAM,UAAU,UAAU,KAAK,iBAAiB;EACpD,KAAK,UAAU;GAAE,GAAG;GAAO,OAAO,MAAM;EAAM,CAAC;CACjD;CAEA,gBAAwB,WAAyB;EAC/C,IAAI,KAAK,YAAY,KAAK,iBAAiB,KAAA,GAAW;EACtD,MAAM,QAAQ,kBAAkB,KAAK,IAAI,KAAK,gBAAgB,kBAAkB,SAAS,CAAC;EAC1F,KAAK,kBAAkB;EACvB,KAAK,eAAe,iBAAiB;GACnC,KAAK,eAAe,KAAA;GACpB,KAAU,MAAM,SAAS;EAC3B,GAAG,KAAK;CACV;CAEA,UAAkB,MAAiE;EACjF,MAAM,UAAiC,KAAK,UAAU,WAClD;GAAE,SAAS,KAAA;GAAW,SAAS,KAAA;EAAU,IACzC,KAAK,UAAU,mBACb;GAAE,WAAW,KAAA;GAAW,SAAS,KAAA;EAAU,IAC3C,KAAK,UAAU,cAAc,KAAK,UAAU,aAAa,KAAK,UAAU,aACtE;GAAE,WAAW,KAAA;GAAW,SAAS,KAAA;GAAW,SAAS,KAAA;EAAU,IAC/D,CAAC;EACT,KAAK,cAAc;GACjB,GAAG,KAAK;GACR,GAAG;GACH,GAAG;GACH,WAAW,KAAK,IAAI;EACtB;EACA,IAAI,KAAK,UAAU,UACjB,KAAK,QAAQ,IAAI,sBAAsB;OAClC,IAAI,KAAK,UAAU,kBACxB,KAAK,QAAQ,IAAI,8CAA8C;OAC1D,IAAI,KAAK,UAAU,WAAW,KAAK,UAAU,eAClD,KAAK,QAAQ,IAAI,iBAAiB,KAAK,MAAM,IAAI,KAAK,WAAW,iBAAiB;CAEtF;AACF;;;AChOA,SAAgB,cAAc,SAA0B;CACtD,MAAM,SAAS,QAAQ,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;CAC5C,IAAI,OAAO,WAAW,KAAK,OAAO,MAAM,UAAU,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,KAAK,QAAQ,GAAG,GACpG,OAAO;CAET,MAAM,CAAC,GAAG,KAAK;CACf,OAAO,MAAM,MAAO,MAAM,OAAO,KAAK,MAAM,KAAK,MAAQ,MAAM,OAAO,MAAM;AAC9E;;AAGA,SAAgB,wBAAkC;CAChD,MAAM,aAAuD,CAAC;CAC9D,KAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,kBAAkB,CAAC,GAC9D,KAAK,MAAM,SAAS,WAAW,CAAC,GAC9B,IAAI,MAAM,WAAW,UAAU,CAAC,MAAM,YAAY,cAAc,MAAM,OAAO,GAC3E,WAAW,KAAK;EAAE;EAAM,SAAS,MAAM;CAAQ,CAAC;CAItD,MAAM,YAAY,SAAyB,SAAS,QAAQ,IAAI,KAAK,WAAW,IAAI,IAAI,IAAI,KAAK,WAAW,QAAQ,IAAI,IAAI;CAC5H,WAAW,MAAM,MAAM,UAAU,SAAS,KAAK,IAAI,IAAI,SAAS,MAAM,IAAI,KAAK,KAAK,KAAK,cAAc,MAAM,IAAI,CAAC;CAClH,OAAO,CAAC,GAAG,IAAI,IAAI,WAAW,KAAK,EAAE,cAAc,OAAO,CAAC,CAAC;AAC9D;;;;;;;;;;;;;;;;ACkBA,MAAa,OAAO;;AAKpB,MAAa,SAAmB,CAAC;;AAsDjC,MAAM,oBAAoB;AAE1B,MAAa,SAAS,EAAE,OAAO;CAC7B,SAAS,EAAE,QAAQ,CAAC,CAAC,QAAQ,IAAI;CACjC,eAAe,EAAE,OAAO,CAAC,CAAC,QAAQ,KAAK,cAAc,GAAG,YAAY,CAAC;CACrE,aAAa,EAAE,OAAO,CAAC,CAAC,QAAQ,KAAK,cAAc,GAAG,cAAc,CAAC;CACrE,kBAAkB,EAAE,QAAQ,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,GAAI;CACnD,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,KAAK;CAChC,QAAQ,EAAE,OAAO;EACf,SAAS,EAAE,QAAQ,CAAC,CAAC,QAAQ,KAAK;EAClC,UAAU,EAAE,OAAO,CAAC,CAAC,QAAQ,kBAAkB;EAC/C,UAAU,EAAE,OAAO,CAAC,CAAC,QAAQ,uBAAuB;EACpD,WAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,KAAK,cAAc,GAAG,WAAW,CAAC;EAChE,YAAY,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE;EACjC,YAAY,EAAE,QAAQ,CAAC,CAAC,QAAQ,GAAG;CACrC,CAAC,CAAC,CAAC,QAAQ;EACT,SAAS;EACT,UAAU;EACV,UAAU;EACV,WAAW,KAAK,cAAc,GAAG,WAAW;EAC5C,YAAY;EACZ,YAAY;CACd,CAAC;CACD,MAAM,EAAE,OAAO;EACb,UAAU,EAAE,OAAO,CAAC,CAAC,QAAQ,MAAM;EACnC,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE;EAC7B,OAAO,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE;EAC5B,SAAS,EAAE,OAAO,CAAC,CAAC,QAAQ,KAAK,cAAc,GAAG,QAAQ,YAAY,CAAC;EACvE,UAAU,EAAE,OAAO,CAAC,CAAC,QAAQ,sBAAsB;EACnD,UAAU,EAAE,OAAO,CAAC,CAAC,QAAQ,iBAAiB;EAC9C,YAAY,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE;CACnC,CAAC,CAAC,CAAC,QAAQ;EACT,UAAU;EACV,QAAQ;EACR,OAAO;EACP,SAAS,KAAK,cAAc,GAAG,QAAQ,YAAY;EACnD,UAAU;EACV,UAAU;EACV,YAAY;CACd,CAAC;AACH,CAAC;AAkBD,MAAM,iBAAiB;AACvB,MAAM,yBAAyB;;;;;;AAM/B,MAAM,kBAAkB;AAExB,SAAS,cAAc,QAAgB,QAAgB,QAAsB;CAC3E,MAAM,OAAO,KAAK,UAAU,EAAE,OAAO,OAAO,CAAC;CAC7C,OAAO,IACL,cAAc,SAAS,qEAEF,OAAO,WAAW,IAAI,IAAI,kCAG/C,IACF;AACF;;AAGA,SAAgB,aAAa,KAA8D;CACzF,MAAM,gBAAgB,IAAI,QAAQ;CAClC,IAAI,OAAO,kBAAkB,UAAU;EACrC,MAAM,QAAQ,mBAAmB,KAAK,cAAc,KAAK,CAAC;EAC1D,IAAI,QAAQ,IAAI,OAAO,MAAM;CAC/B;CACA,IAAI;EACF,OAAO,IAAI,IAAI,IAAI,OAAO,KAAK,oBAAoB,CAAC,CAAC,aAAa,IAAI,OAAO;CAC/E,QAAQ;EACN,OAAO;CACT;AACF;;;;;;AAOA,SAAS,iBAAiB,SAA0B;CAClD,IAAI,OAAO,YAAY,YACrB,OAAQ,QAAyB;CAEnC,IAAI,WAAW,OAAO,YAAY,UAChC,OAAO;CAIT,OADmB,OAAgD,KAAA,CACpD,KAAK,CAAC;AACvB;AAEA,SAAgB,MAAM,KAAc,SAAwB;CAC1D,MAAM,MAAM,iBAAiB,OAAO;CAEpC,MAAM,OAAO,YAA0B;EACrC,QAAQ,IAAI,iBAAiB,OAAO;CACtC;;;;;;CAOA,IAAI;CACJ,IAAI;CACJ,MAAM,sBAA8B;EAClC,IAAI,eAAe,KAAA,GAAW,OAAO,iBAAiB,WAAW,CAAC;EAClE,OAAO,iBAAiB,OAAO;CACjC;CACA,MAAM,mBAA4B,cAAc,CAAC,CAAC,YAAY;CAO9D,uBAAuB,KAAK,kBAAkB,WAAW,GAAG,QAAQ,iBAAiB,KAAA,CAAS,GAAG;EAC/F,YAAY,WAAW;GACrB,aAAa;GAIb,qBAAqB,0BAA0B,CAAC;EAClD;EACA,gBAAgB,qBAAqB,0BAA0B,CAAC;CAClE,CAAC;CAID,IAAI,cAAc,CAAC,CAAC,YAAY,MAC9B,IAAI,mFAAmF;CAOzF,MAAM,UAAU,cAAc;CAiB9B,MAAM,gBAAgB,KAAK,SAAS,iBAAiB;CACrD,MAAM,iBAAsC,CAAC;CAE7C,SAAS,oBAA0B;EACjC,CAAM,YAAY;GAChB,IAAI;IACF,MAAM,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;IACxC,MAAM,UAAU,eAAe,KAAK,UAAU;KAAE,SAAS;KAAG,GAAG;IAAe,GAAG,MAAM,CAAC,IAAI,MAAM,EAAE,MAAM,IAAM,CAAC;GACnH,QAAQ,CAER;EACF,EAAA,CAAG;CACL;;CAGA,MAAM,sBAAsB,OAAO,cAAqC;EACtE,IAAI,eAAe,cAAc,WAC/B,eAAe,YAAY;EAE7B,MAAM,qBAAqB,cAAc,CAAC,CAAC,MAAM;EAEjD,IAAI,CAAC,sBAAsB,uBAAuB,QAC5C;OAAA,CAAC,eAAe,WAAW;IAC7B,eAAe,YAAY;IAC3B,IAAI,8CAA8C;GACpD;;EAEF,kBAAkB;EAGlB,MAAM,OAAO,cAAc,CAAC,CAAC,MAAM,YAAY,GAAA,CAAI,KAAK,KAAK;EAC7D,MAAM,oBAAoB,GAAG;CAC/B;CACA,MAAM,OAAiF;EACrF,OAAO;EACP,WAAW,IAAI,iBAAiB,KAAK,SAAS,YAAY;EAC1D,SAAS;CACX;CACA,MAAM,SAAS,YAAY;EACzB,IAAI;GACF,IAAI;IACF,MAAM,eAAe,MAAM,2BAA2B;IACtD,IAAI,iBAAiB,MAAM,IAAI,qCAAqC,aAAa,MAAM,SAAS;GAClG,SAAS,OAAO;IACd,IAAI,4CAA4C,OAAO,KAAK,CAAC;GAC/D;GACA,KAAK,YAAY,IAAI,iBAAiB,KAAK,SAAS,YAAY;GAChE,KAAK,QAAQ,MAAM,kBAAkB,KAAK,SAAS;GACnD,KAAK,UAAU,MAAM,YAAY,KAAK,IAAI,eAAe,KAAK,SAAS,cAAc,CAAC;GACtF;IAGE,MAAM,OAAO,KAAK,QAAQ,KAAK;IAC/B,MAAM,aAAa,KAAK,QAAQ,QAAQ,IAAI,SAAS,KAAA,CAAS,CAAC,CAAC;IAChE,IAAI,+BAA+B,WAAW,IAAI,eAAe,KAAK,SAAS,cAAc,CAAC,EAAE,IAAI,KAAK,OAAO,cAAc,WAAW,sBAAsB;GACjK;GAEA,IAAI;IACF,MAAM,MAAM,KAAK,MAAM,MAAM,SAAS,eAAe,MAAM,CAAC;IAC5D,IAAI,OAAO,IAAI,aAAa,UAAU,eAAe,WAAW,IAAI;IACpE,IAAI,OAAO,IAAI,cAAc,UAAU,eAAe,YAAY,IAAI;IACtE,IAAI,OAAO,IAAI,UAAU,UAAU,eAAe,QAAQ,IAAI;IAC9D,IAAI,IAAI,cAAc,MAAM,eAAe,YAAY;GACzD,QAAQ,CAER;EACF,SAAS,OAAO;GACd,IAAI,iDAAiD,OAAO,KAAK,CAAC;GAClE,OAAO;IAAE,OAAO;IAAM,SAAS;GAAK;EACtC;EACA,OAAO;GAAE,OAAO,KAAK;GAAO,SAAS,KAAK;EAAQ;CACpD,EAAA,CAAG;;;;;;;;;;;CAYH,MAAM,WAAW,YAA6B;EAC5C,MAAM,EAAE,YAAY,MAAM;EAC1B,IAAI,KAAK,UAAU,QAAQ,YAAY,MACrC,MAAM,IAAI,MAAM,2BAA2B;EAE7C,KAAK,QAAQ,MAAM,cAAc,KAAK,SAAS;EAC/C,QAAQ,MAAM;EACd,IAAI,UAAU;EACd,KAAK,MAAM,cAAc,aAAa;GACpC,WAAW,UAAU;GACrB,WAAW;EACb;EACA,YAAY,MAAM;EAClB,IAAI,0BAA0B,QAAQ,kCAAkC;EACxE,OAAO,KAAK;CACd;CACA,IAAI,aAA+B,QAAQ,QAAQ;CACnD,MAAM,2BAA4C;EAChD,MAAM,OAAO,WAAW,KAAK,QAAQ;EACrC,aAAa,KAAK,YAAY,CAAC,CAAC;EAChC,OAAO;CACT;;;;;;;CAQA,MAAM,kBAAkB,YAAqC;EAC3D,MAAM,WAAW,kBAAkB,cAAc,CAAC;EAClD,IAAI,CAAC,SAAS,IACZ,OAAO;GACL,WAAW;GACX,SAAS;GACT,SAAS,WAAW,SAAS,SAAS;GACtC,SAAS,CAAC;EACZ;EAEF,MAAM,aAAa,KAAK,SAAS,KAAK,KAAK,CAAC,EAAA,CAAG,QAAQ,WAAW,OAAO,SAAS,KAAA,CAAS;EAC3F,IAAI,CAAC,KAAK,WAAW,UAAU,WAAW,GACxC,OAAO;GACL,WAAW,SAAS,MAAM;GAC1B,SAAS;GACT,SAAS;GACT,SAAS,CAAC;EACZ;EAEF,MAAM,OAAO,MAAM,UAAU,SAAS,KAAK;EAC3C,IAAI,CAAC,MACH,OAAO;GAAE,WAAW,SAAS,MAAM;GAAM,SAAS;GAAU,SAAS;GAA6B,SAAS,CAAC;EAAE;EAEhH,MAAM,eAAiC;GACrC,gBAAgB,UAAU,KAAK,IAAI;GACnC,UAAU;GACV,WAAW;GACX,OAAO;GACP,MAAM;EACR;EACA,MAAM,UAAU,MAAM,QAAQ,IAAI,UAAU,IAAI,OAAO,WAAW;GAChE,MAAM,eAAe,OAAO;GAC5B,MAAM,EAAE,SAAS,WAAW,MAAM,KAAK;IACrC,aAAa,aAAa;IAC1B,aAAa,aAAa;IAC1B;GACF,CAAC;GACD,OAAO;IACL,MAAM,OAAO;IACb,aAAa,aAAa;IAC1B;IAGA,kBAAkB,aAAa,MAAM,MAAM,GAAG,EAAE;IAChD,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;GAC3C;EACF,CAAC,CAAC;EACF,MAAM,UAAqC,QAAQ,MAAM,MAAM,EAAE,YAAY,MAAM,IAAI,SAAS;EAChG,IAAI,qBAAqB,UAAU,OAAO,QAAQ,KAAK,MAAM,IAAI,EAAE,KAAK,IAAI,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,SAAS,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,GAAG;EAC1I,OAAO;GAAE,WAAW,SAAS,MAAM;GAAM;GAAS;EAAQ;CAC5D;CAEA,MAAM,8BAAc,IAAI,IAAsB;CAQ9C,MAAM,qBAAqB,WAA4F;EACrH,MAAM,OAAO,OAAO,QAAQ,CAAC;EAG7B,MAAM,aAAa,KAAK,YAAY;EACpC,MAAM,oBAAoB,eAAe,UAAU,eAAe,cAAc,OAAO,UAAU;EACjG,IAAI,sBAAsB,SAAS;GACjC,MAAM,OAAO,KAAK,YAAY,GAAA,CAAI,KAAK,KAAK;GAC5C,MAAM,SAAS,KAAK,cAAc,GAAA,CAAI,KAAK,KAAK,eAAe,SAAS;GACxE,IAAI,CAAC,eAAe,KAAK,GAAG,GAAG,OAAO;IAAE,IAAI;IAAO,QAAQ;GAAgC;GAC3F,IAAI,CAAC,OAAO,OAAO;IAAE,IAAI;IAAO,QAAQ;GAA+B;GACvE,OAAO;IAAE,IAAI;IAAM,OAAO;KAAE,MAAM;KAAS;KAAK;IAAM;GAAE;EAC1D;EACA,IAAI,sBAAsB,QAAQ;GAChC,MAAM,UAAU,KAAK,UAAU,GAAA,CAAI,KAAK;GACxC,MAAM,SAAS,KAAK,SAAS,GAAA,CAAI,KAAK;GACtC,MAAM,UAAU,YAAY,KAAK,WAAW,GAAA,CAAI,KAAK,KAAK,KAAK,SAAS,QAAQ,YAAY,CAAC;GAC7F,MAAM,YAAY,KAAK,YAAY,GAAA,CAAI,KAAK;GAC5C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,UAAU,OAAO;IAAE,IAAI;IAAO,QAAQ;GAAgC;GAChG,OAAO;IAAE,IAAI;IAAM,OAAO;KAAE,MAAM;KAAQ;KAAQ;KAAO;KAAS;IAAS;GAAE;EAC/E;EACA,OAAO;GAAE,IAAI;GAAO,QAAQ;EAAoB;CAClD;;;;;;CAOA,IAAI;CACJ,IAAI,sBAAsB;CAC1B,MAAM,sBAAsB,OAAO,QAA6C;EAC9E,IAAI,eAAe,OAAO,OAAO,eAAe;EAChD,MAAM,cAAc,MAAM,MAAM,OAAO,eAAe,aAAa,EAAE;EACrE,IAAI,gBAAgB,kBAAkB;GACpC,mBAAmB;GACnB,sBAAsB;EACxB;EAIA,IAAI,KAAK,IAAI,IAAI,sBAAsB,KAAQ,OAAO,KAAA;EACtD,sBAAsB,KAAK,IAAI;EAC/B,IAAI;GACF,IAAI,CAAC,eAAe,UAAU;IAC5B,eAAe,WAAW,OAAO,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;IACrE,kBAAkB;GACpB;GAEA,MAAM,QAAQ,MAAM,IADD,YAAY;IAAE;IAAK,OAAO,cAAc,CAAC,CAAC,UAAU;IAAM;GAAI,CACxD,CAAC,CAAC,OAAO,eAAe,UAAU,eAAe,aAAa,EAAE;GACzF,IAAI,CAAC,OAAO;IACV,IAAI,mCAAmC,MAAM,+BAA+B;IAC5E;GACF;GACA,eAAe,QAAQ;GACvB,kBAAkB;GAClB,IAAI,iCAAiC;GACrC,OAAO;EACT,SAAS,OAAO;GACd,IAAI,kCAAkC,OAAO,KAAK,CAAC;GACnD;EACF;CACF;CAYA,IAAI;CACJ,IAAI;;;;;;CAOJ,MAAM,YAAY,OAAO,aAAkE;EACzF,MAAM,cAAc,KAAK,UAAU,QAAQ;EAC3C,IAAI,cAAc,gBAAgB,aAAa,OAAO,aAAa;EACnE,IAAI,oBAAoB,aAAa,OAAO,KAAA;EAC5C,IAAI,cAAc;GAChB,MAAM,aAAa,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC;GAC7C,eAAe,KAAA;EACjB;EACA,IAAI,SAAS,SAAS,SAAS;GAG7B,MAAM,SAAS,IAAI,YAAY;IAAE,KAAK,SAAS;IAAK,OAAO,SAAS;IAAO,OAAO,cAAc,CAAC,CAAC,UAAU;IAAM;GAAI,CAAC;GACvH,eAAe;IACb;IACA,OAAO,YAAY,OAAO,KAAK,OAAO;GACxC;GACA,IAAI,oBAAoB;EAC1B,OAAO;GACL,IAAI;IACF,MAAM,SAAS,WAAW,SAAS,OAAO,GAAG,MAAM;GACrD,SAAS,OAAO;IACd,kBAAkB;IAClB,IAAI,8CAA8C,SAAS,UAAU,QAAQ,OAAO,KAAK,CAAC;IAC1F;GACF;GACA,MAAM,SAAS,IAAI,WAAW;IAC5B,QAAQ,SAAS;IACjB,OAAO,SAAS;IAChB,SAAS,SAAS;IAClB,UAAU,SAAS;IACnB,OAAO,cAAc,CAAC,CAAC,UAAU;IACjC;GACF,CAAC;GACD,eAAe;IACb;IACA,OAAO,YAAY,OAAO,KAAK;KAC7B,GAAG,QAAQ;KACX,aAAa,QAAQ;KACrB,aAAa,QAAQ;IACvB,CAAC;IACD,eAAe,OAAO,QAAQ;GAChC;GACA,IAAI,mBAAmB;EACzB;EACA,kBAAkB,KAAA;EAClB,OAAO,aAAa;CACtB;;;;;;;;;;;CAYA,MAAM,wBAAoC;EAIxC,mBAAmB;GACjB,MAAM,WAAW,kBAAkB,cAAc,CAAC;GAClD,IAAI,CAAC,SAAS,IAAI,OAAO;GAIzB,IAAI,SAAS,MAAM,SAAS,WAAW,CAAC,SAAS,MAAM,OAAO,OAAO;GACrE,OAAO;EACT;EACA,SAAS,iBAAiB;GACxB,CAAM,YAAY;IAChB,IAAI,WAAW,kBAAkB,cAAc,CAAC;IAChD,IAAI,CAAC,SAAS,MAAM,SAAS,WAAW,gCAAgC;KAGtE,MAAM,YAAY,cAAc,CAAC,CAAC,MAAM,YAAY,GAAA,CAAI,KAAK,KAAK;KAClE,MAAM,oBAAoB,QAAQ;KAClC,WAAW,kBAAkB,cAAc,CAAC;IAC9C;IACA,IAAI,CAAC,SAAS,IAAI;IAClB,MAAM,UAAU,KAAK;IACrB,IAAI,CAAC,SAAS;IACd,MAAM,OAAO,MAAM,UAAU,SAAS,KAAK;IAC3C,IAAI,CAAC,MAAM;IACX,MAAM,YAAY,SAAS,MAAM;IACjC,MAAM,+BAAe,IAAI,IAAY;IACrC,KAAK,MAAM,cAAc,aAAa;KACpC,MAAM,KAAK,WAAW;KACtB,IAAI,IAAI,aAAa,IAAI,EAAE;IAC7B;IAKA,MAAM,aAAa,QAAQ,KAAK,CAAC,CAAC,QAAQ,WAAW;KACnD,MAAM,eAAe,OAAO;KAC5B,IAAI,CAAC,cAAc,OAAO;KAC1B,IAAI,aAAa,IAAI,OAAO,QAAQ,GAAG,OAAO;KAC9C,IAAI,aAAa,aAAa,aAAa,cAAc,OAAO;MAC9D,IAAI,cAAc,CAAC,CAAC,UAAU,MAC5B,IAAI,cAAc,OAAO,WAAW,cAAc,aAAa,SAAS,OAAO;MAEjF,OAAO;KACT;KACA,OAAO;IACT,CAAC;IACD,IAAI,WAAW,WAAW,GAAG;KAC3B,MAAM,YAAY,QAAQ,KAAK,CAAC,CAAC,QAAQ,WAAW,OAAO,SAAS,KAAA,CAAS,CAAC,CAAC;KAC/E,IAAI,QAAQ,UAAU,IAAI,aAAa,SAAS,kCAAkC,aAAa,KAAK,cAAc,UAAU,EAAE;KAC9H;IACF;IACA,KAAK,MAAM,UAAU,YAAY;KAC/B,MAAM,eAAe,OAAO;KAC5B,KAAU;MAAE,aAAa,aAAa;MAAO,aAAa,aAAa;MAAa;KAAa,CAAC,CAAC,CAChG,MAAM,EAAE,SAAS,aAAa;MAC7B,IAAI,QAAQ,UAAU,IAAI,aAAa,SAAS,MAAM,OAAO,WAAW,KAAK,aAAa,YAAY,MAAM,UAAU,SAAS,OAAO,SAAS,MAAM,IAAI;MACzJ,IAAI,YAAY,iBAAiB;OAC/B,QAAQ,eAAe,OAAO,QAAQ;OACtC,IAAI,gCAAgC,OAAO,WAAW,KAAK,UAAU,UAAU,oCAAoC;MACrH;KACF,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;IACnB;GACF,EAAA,CAAG;EACL;CACF;CAGA,MAAM,MAAM,IAAI,gBAAgB;EAAE,UAAU;EAAM,YAAY;CAAgB,CAAC;CAC/E,IAAI;CACJ,MAAM,qBAAmC,kBAAkB,OAAO,KAAK;EACrE,UAAU;EACV,OAAO,cAAc,CAAC,CAAC,QAAQ,YAAY,OAAO,YAAY;EAC9D,WAAW,KAAK,IAAI;CACtB;CAGA,kBAAkB,KAAK,YAAY;EACjC,IAAI,aAAa;EACjB,IAAI,UAAiM,CAAC;EACtM,IAAI;GACF,MAAM;GACN,aAAa,KAAK,UAAU;GAG5B,WAAW,KAAK,SAAS,KAAK,KAAK,CAAC,EAAA,CAAG,KAAK,EAAE,UAAU,YAAY,YAAY,aAAa,YAAY,YAAY;IACnH;IACA;IACA;IACA;IACA;IACA,GAAI,OAAO,EAAE,MAAM;KAAE,aAAa,KAAK;KAAa,WAAW,KAAK;IAAU,EAAE,IAAI,CAAC;GACvF,EAAE;EACJ,QAAQ,CAER;EACA,OAAO;GACL,iBAAiB;GACjB,eAAe;GACf,SAAS,cAAc,CAAC,CAAC,YAAY;GACrC,WAAW,WAAW,cAAc,CAAC,CAAC,iBAAiB,KAAK,cAAc,GAAG,YAAY,CAAC;GAC1F;GACA,mBAAmB,YAAY;GAC/B,kBAAkB,cAAc,CAAC,CAAC,oBAAoB;GACtD,OAAO,cAAc,CAAC,CAAC,UAAU;GACjC,cAAc,sBAAsB;GACpC,QAAQ,aAAa;GACrB;EACF;CACF,GAAG,YAAY;EACb,MAAM;EACN,IAAI,KAAK,UAAU,MAAM,MAAM,IAAI,MAAM,2BAA2B;EACpE,OAAO,KAAK;CACd,GAAG,oBAAoB,YAAY;EAC/B,MAAM,OAAO,cAAc,CAAC,CAAC,QAAQ,CAAC;EACtC,MAAM,aAAa,KAAK,YAAY;EAEpC,KADkB,eAAe,UAAU,eAAe,cAAc,OAAO,UAAU,gBACvE,SAChB,OAAO;GACL,KAAK;GACL,SAAS;GACT,aAAa;GACb,OAAO,CAAC;IAAE,IAAI;IAAmB,IAAI;IAAO,SAAS,uBAAuB,WAAW;GAAqJ,CAAC;EAC/O;EAEF,MAAM,OAAO,KAAK,YAAY,GAAA,CAAI,KAAK,KAAK;EAG5C,IAAI,CAAC,eAAe,YAAY,eAAe,WAAW;GACxD,eAAe,WAAW,OAAO,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;GACrE,kBAAkB;EACpB;EACA,OAAO,MAAM,cAAc;GACzB;GACA,UAAU,eAAe;GACzB,WAAW,eAAe;GAC1B,aAAa,SAAS,KAAK,cAAc,GAAA,CAAI,KAAK,CAAC;GACnD,aAAa,UAAU;IACrB,eAAe,QAAQ;IACvB,kBAAkB;IAClB,IAAI,0DAA0D;GAChE;EACF,CAAC;CACH,GAAG,YAAY;EACb,OAAO,MAAM,gBAAgB;CAC/B,CAAC;CACH,MAAM,QAAiC,CAAC;CAExC,MAAM,iBAAiB,KAAsB,QAAgB,SAAuB;EAClF,CAAM,YAAY;GAChB,IAAI;IACF,IAAI,CAAC,WAAW,GAAG;KACjB,cAAc,QAAQ,KAAK,iBAAiB;KAC5C;IACF;IACA,IAAI,YAAY,QAAQ,wBAAwB;KAC9C,cAAc,QAAQ,KAAK,sBAAsB;KACjD;IACF;IACA,MAAM,EAAE,YAAY,MAAM;IAC1B,MAAM,QAAQ,KAAK;IACnB,IAAI,CAAC,SAAS,CAAC,SAAS;KACtB,cAAc,QAAQ,KAAK,iBAAiB;KAC5C;IACF;IACA,MAAM,iBAAiB,aAAa,GAAG;IACvC,IAAI,mBAAmB,QAAQ,CAAC,aAAa,gBAAgB,KAAK,GAAG;KACnE,cAAc,QAAQ,KAAK,eAAe;KAC1C;IACF;IACA,MAAM,SAAS,MAAM;IACrB,IAAI,CAAC,QAAQ;KACX,cAAc,QAAQ,KAAK,kBAAkB;KAC7C;IACF;IAIA,IAAI,KAAK,UAAU,OAAO;KACxB,cAAc,QAAQ,KAAK,eAAe;KAC1C;IACF;IACA,IAAI,cAAc,KAAK,QAAQ,OAAO,OAAO;KAC3C,MAAM,aAAa,IAAI,iBAAiB,IAAI;MAC1C;MACA;MACA,eAAe;MACf,eAAe;MACf,wBAAwB,mBAAmB;MAC3C;MACA,OAAO,cAAc,CAAC,CAAC,UAAU;MACjC,WAAW,WAAW,YAAY,OAAO,MAAM;MAC/C,iBAAiB;KACnB,CAAC;KACD,YAAY,IAAI,UAAU;IAC5B,CAAC;GACH,SAAS,OAAO;IACd,IAAI,qBAAqB,OAAO,KAAK,CAAC;IACtC,cAAc,QAAQ,KAAK,gBAAgB;GAC7C;EACF,EAAA,CAAG;CACL;CAEA,MAAM,eAAe,OAAO,KAAsB,QAAuC;EACvF,IAAI;GACF,MAAM;GACN,MAAM,QAAQ,KAAK;GACnB,IAAI,UAAU,gBAAgB,kBAAkB;GAChD,IAAI,CAAC,OAAO;IACV,IAAI,aAAa;IACjB,IAAI,IAAI,KAAK,UAAU;KAAE,IAAI;KAAO,UAAU;IAAK,CAAC,CAAC;IACrD;GACF;GACA,IAAI,CAAC,aAAa,aAAa,GAAG,GAAG,KAAK,GAAG;IAC3C,IAAI,aAAa;IACjB,IAAI,IAAI,KAAK,UAAU,EAAE,IAAI,MAAM,CAAC,CAAC;IACrC;GACF;GACA,IAAI,aAAa;GACjB,IAAI,IAAI,KAAK,UAAU;IACrB,IAAI;IACJ,SAAS,WAAW;IACpB,iBAAiB;IACjB,eAAe;IACf,WAAW,QAAQ,MAAM,MAAM;GACjC,CAAC,CAAC;EACJ,QAAQ;GACN,IAAI,aAAa;GACjB,IAAI,IAAI,KAAK,UAAU,EAAE,IAAI,MAAM,CAAC,CAAC;EACvC;CACF;CAGC,IAAqF,OACpF,CAAC,UAAU,IACV,QAAQ;EACP,IAAI,cAAc,CAAC,CAAC,YAAY,MAAM;GACpC,IAAI,4CAA4C;GAChD;EACF;EACA,MAAM,SAAS;EACf,MAAM,QAAQ,OAAO;EACrB,IAAI,CAAC,OAAO;GACV,IAAI,oDAAoD;GACxD;EACF;EACA,MAAM,SAAS,IAAI,WAAW,OAAO,IAAI,gBAAgB;EACzD,OAAO,cAAc,eAAe,CAAC;EACrC,MAAM,SAAS;EACf,OAAO,MAAM;EACb,IAAI,wCAAwC;EAC5C,OAAO,mBAAmB,OAAO,QAAQ,GAAG,yBAAyB;CACvE,CACF;CAGC,IAAqF,OACpF,CAAC,WAAW,IACX,QAAQ;EACP,MAAM,SAAS;EACf,MAAM,MAAM,OAAO;EACnB,IAAI,CAAC,KAAK;GACR,IAAI,iEAAiE;GACrE;EACF;EAKA,OAAO,aAEH,IAAI,gBAAgB;GAClB,MAAM;GACN,SAAS;EACX,CAAC,GACH,6BACF;EAEA,OAAO,aAEH,IAAI,SAAS;GACX,MAAM;GACN,MAAM;GACN,SAAS;EACX,CAAC,GACH,0BACF;EAEA,MAAM,QAAQ,kBAAkB;GAC9B,MAAM,MAAM,KAAK,IAAI;GACrB,KAAK,MAAM,cAAc,aACvB,IAAI,WAAW,QAAQ,KAAK,GAAM,GAAG;IACnC,IAAI,2BAA2B;IAC/B,WAAW,UAAU;IACrB,YAAY,OAAO,UAAU;GAC/B;EAEJ,GAAG,GAAM;EACT,OAAO,mBAAmB,cAAc,KAAK,GAAG,wBAAwB;EAMxE,MAAM,eAAe,cAAc,KAAK,QAAQ;GAC9C,IAAI,OAAO;GACX,IAAI;IAAE,OAAO,IAAI,IAAI,IAAI,OAAO,KAAK,oBAAoB,CAAC,CAAC;GAAS,QAAQ,CAAqB;GACjG,IAAI,SAAS,iBACX,aAAkB,KAAK,GAAG;QACrB;IACL,IAAI,aAAa;IACjB,IAAI,IAAI,WAAW;GACrB;EACF,CAAC;EACD,aAAa,GAAG,YAAY,KAAK,QAAQ,SAAS;GAChD,IAAI,OAAO;GACX,IAAI;IAAE,OAAO,IAAI,IAAI,IAAI,OAAO,KAAK,oBAAoB,CAAC,CAAC;GAAS,QAAQ,CAAqB;GACjG,IAAI,SAAS,UAAU;IACrB,OAAO,IAAI,qDAAqD;IAChE;GACF;GACA,cAAc,KAAK,QAAQ,IAAI;EACjC,CAAC;EAED,IAAI;EACJ,IAAI;EACJ,IAAI,iBAAiB;EACrB,IAAI,gBAAgB,QAAQ,QAAQ;EACpC,MAAM,kBAAkB,YAA2B;GACjD,IAAI,kBAAkB,cAAc,KAAA,GAAW;GAC/C,MAAM,SAAS,cAAc;GAC7B,MAAM,eAAe,OAAO,UAAU,CAAC;GACvC,MAAM,aAAiC,aAAa,eAAe,QAAQ,aAAa,eAAe,MACnG,aAAa,aACb;GACJ,MAAM,aAAa,aAAa,YAAY,KAAK,KAAK,KAAA;GACtD,MAAM,OAAO;IACX,SAAS,OAAO,YAAY,QAAQ,aAAa,YAAY,QAAQ,aAAa,aAAa;IAC/F,UAAU,wBAAwB,aAAa,QAAQ;IACvD,WAAW,aAAa,WAAW,KAAK,KAAK,KAAK,SAAS,WAAW;IACtE;IACA,YAAY;GACd;GACA,MAAM,UAAU,KAAK,UAAU,IAAI;GACnC,IAAI,YAAY,kBAAkB;GAElC,MAAM,WAAW;GACjB,mBAAmB,KAAA;GACnB,IAAI,aAAa,KAAA,GAAW,MAAM,SAAS,QAAQ;GACnD,IAAI,gBAAgB;GAEpB,MAAM,aAAa,IAAI,iBAAiB;IACtC,SAAS,KAAK;IACd,UAAU,KAAK;IACf,WAAW,KAAK;IAChB,GAAI,KAAK,aAAa,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;IACzD,YAAY,KAAK;IACjB;GACF,CAAC;GACD,mBAAmB;GACnB,mBAAmB;GACnB,MAAM,WAAW,MAAM,SAAS;EAClC;EACA,gCAAgC;GAC9B,gBAAgB,cACb,KAAK,eAAe,CAAC,CACrB,OAAO,UAAU,IAAI,8BAA8B,OAAO,KAAK,CAAC,CAAC;EACtE;EAEA,aAAa,OAAO,GAAG,mBAAmB;GACxC,MAAM,UAAU,aAAa,QAAQ;GACrC,IAAI,WAAW,OAAO,YAAY,UAAU;IAC1C,YAAY,oBAAoB,QAAQ;IACxC,0BAA0B;GAC5B;EACF,CAAC;EACD,aAAa,GAAG,UAAU,UAAU,IAAI,2BAA2B,OAAO,KAAK,CAAC,CAAC;EACjF,OAAO,mBAAmB;GACxB,iBAAiB;GACjB,0BAA0B,KAAA;GAC1B,aAAa,MAAM;GACnB,gBAAgB,cAAc,KAAK,YAAY;IAC7C,MAAM,aAAa;IACnB,mBAAmB,KAAA;IACnB,IAAI,eAAe,KAAA,GAAW,MAAM,WAAW,QAAQ;GACzD,CAAC;EACH,GAAG,4BAA4B;EAC/B,IAAI,WAAW,GACb,IAAI,6BAA6B;OAEjC,IAAI,4EAA4E;CAEpF,CACF;AACF"}