botmux 2.12.0 → 2.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/cli/claude-code.d.ts +9 -3
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +20 -12
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +11 -1
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +13 -9
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.js +10 -9
- package/dist/daemon.js.map +1 -1
- package/dist/services/bridge-rotation-policy.d.ts +139 -0
- package/dist/services/bridge-rotation-policy.d.ts.map +1 -0
- package/dist/services/bridge-rotation-policy.js +125 -0
- package/dist/services/bridge-rotation-policy.js.map +1 -0
- package/dist/services/bridge-turn-queue.d.ts +9 -1
- package/dist/services/bridge-turn-queue.d.ts.map +1 -1
- package/dist/services/bridge-turn-queue.js +9 -2
- package/dist/services/bridge-turn-queue.js.map +1 -1
- package/dist/services/claude-transcript.d.ts +67 -0
- package/dist/services/claude-transcript.d.ts.map +1 -1
- package/dist/services/claude-transcript.js +228 -1
- package/dist/services/claude-transcript.js.map +1 -1
- package/dist/services/codex-bridge-queue.d.ts +56 -0
- package/dist/services/codex-bridge-queue.d.ts.map +1 -0
- package/dist/services/codex-bridge-queue.js +150 -0
- package/dist/services/codex-bridge-queue.js.map +1 -0
- package/dist/services/codex-transcript.d.ts +68 -0
- package/dist/services/codex-transcript.d.ts.map +1 -0
- package/dist/services/codex-transcript.js +233 -0
- package/dist/services/codex-transcript.js.map +1 -0
- package/dist/utils/idle-detector.d.ts.map +1 -1
- package/dist/utils/idle-detector.js +15 -4
- package/dist/utils/idle-detector.js.map +1 -1
- package/dist/worker.js +743 -114
- package/dist/worker.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reader for Codex's per-session rollout JSONL.
|
|
3
|
+
*
|
|
4
|
+
* Codex stores each session's full transcript at
|
|
5
|
+
* ~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-<ts>-<cliSessionId>.jsonl
|
|
6
|
+
* and creates the file lazily on the first user submit. Inside, the bridge
|
|
7
|
+
* fallback only cares about two `response_item.payload.type === 'message'`
|
|
8
|
+
* shapes:
|
|
9
|
+
*
|
|
10
|
+
* - role=user → the user's prompt text (input_text content)
|
|
11
|
+
* - role=assistant +
|
|
12
|
+
* phase=final_answer → the model's final reply (output_text content)
|
|
13
|
+
*
|
|
14
|
+
* Why these and not `event_msg`:
|
|
15
|
+
* - `response_item` is the canonical transcript record; `event_msg` is a
|
|
16
|
+
* UI-event stream that can carry the same final text via two channels
|
|
17
|
+
* (`agent_message phase=final_answer` AND `task_complete.last_agent_message`).
|
|
18
|
+
* Picking `response_item` keeps the reader to a single source of truth
|
|
19
|
+
* and avoids any chance of double-emit if both paths are present.
|
|
20
|
+
* - Skipping role=developer (system instructions), phase=commentary
|
|
21
|
+
* (mid-turn status), reasoning, and function_call* keeps the bridge
|
|
22
|
+
* focused on what the user actually said and what the model finally
|
|
23
|
+
* answered — same scope as the Claude bridge.
|
|
24
|
+
*
|
|
25
|
+
* Pure I/O. Attribution belongs in CodexBridgeQueue.
|
|
26
|
+
*/
|
|
27
|
+
import { existsSync, statSync, openSync, readSync, closeSync, readdirSync, readlinkSync } from 'node:fs';
|
|
28
|
+
import { homedir } from 'node:os';
|
|
29
|
+
import { join } from 'node:path';
|
|
30
|
+
const CODEX_SESSIONS_ROOT = join(homedir(), '.codex', 'sessions');
|
|
31
|
+
/** Extract the cliSessionId encoded in a rollout filename. Codex's session
|
|
32
|
+
* id is UUID-shaped (8-4-4-4-12 hex), which lets us anchor the regex on
|
|
33
|
+
* the UUID alone — the `<ts>` segment between "rollout-" and the sid
|
|
34
|
+
* contains its own dashes that would otherwise let a greedy match swallow
|
|
35
|
+
* parts of the sid. Returns undefined for paths that don't match. */
|
|
36
|
+
export function codexSessionIdFromRolloutPath(path) {
|
|
37
|
+
const m = /rollout-.*-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i.exec(path);
|
|
38
|
+
return m ? m[1] : undefined;
|
|
39
|
+
}
|
|
40
|
+
/** Find the rollout file an externally-running Codex process has open by
|
|
41
|
+
* walking `/proc/<pid>/fd/*`. The Codex process keeps fd open on its
|
|
42
|
+
* current rollout for the entire lifetime of the session, so this is the
|
|
43
|
+
* authoritative way to bind a Codex pid to its sessionId — far more
|
|
44
|
+
* reliable than scanning `~/.codex/sessions` by mtime (which would race
|
|
45
|
+
* with sibling Codex panes in the same project).
|
|
46
|
+
*
|
|
47
|
+
* Linux-only: relies on `/proc`. macOS/BSD callers should fall back to
|
|
48
|
+
* the cliSessionId path (read from `~/.codex/history.jsonl`'s last
|
|
49
|
+
* matching cwd entry) — but the use case for /proc-based discovery is
|
|
50
|
+
* /adopt, which already runs on Linux servers in practice. */
|
|
51
|
+
export function findCodexRolloutByPid(pid) {
|
|
52
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
53
|
+
return undefined;
|
|
54
|
+
const fdDir = `/proc/${pid}/fd`;
|
|
55
|
+
if (!existsSync(fdDir))
|
|
56
|
+
return undefined;
|
|
57
|
+
let entries;
|
|
58
|
+
try {
|
|
59
|
+
entries = readdirSync(fdDir);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
for (const fd of entries) {
|
|
65
|
+
let target;
|
|
66
|
+
try {
|
|
67
|
+
target = readlinkSync(join(fdDir, fd));
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (!target.endsWith('.jsonl'))
|
|
73
|
+
continue;
|
|
74
|
+
if (!target.includes('/.codex/sessions/'))
|
|
75
|
+
continue;
|
|
76
|
+
const sid = codexSessionIdFromRolloutPath(target);
|
|
77
|
+
if (sid)
|
|
78
|
+
return { path: target, cliSessionId: sid };
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
/** Split a drained event list into "history" (older than the live cutoff)
|
|
83
|
+
* and "live" (cutoff or newer). The Codex adopt bridge uses this when
|
|
84
|
+
* it discovers the rollout file LATE (after the user already typed in
|
|
85
|
+
* iTerm or sent a Lark message): drain-from-0 produces a mix of pre-
|
|
86
|
+
* adopt history and post-adopt live events. The worker then `absorb()`s
|
|
87
|
+
* the history (so it isn't replayed) and `ingest()`s the live partition
|
|
88
|
+
* (so the local-turn synthesis / fingerprint match still works). Pure
|
|
89
|
+
* function — no I/O, easy to test against fixed timestamps. */
|
|
90
|
+
export function splitCodexEventsByCutoff(events, liveSinceMs) {
|
|
91
|
+
const history = [];
|
|
92
|
+
const live = [];
|
|
93
|
+
for (const ev of events) {
|
|
94
|
+
if (ev.timestampMs < liveSinceMs)
|
|
95
|
+
history.push(ev);
|
|
96
|
+
else
|
|
97
|
+
live.push(ev);
|
|
98
|
+
}
|
|
99
|
+
return { history, live };
|
|
100
|
+
}
|
|
101
|
+
/** Locate the rollout file for a given Codex sessionId. Codex names files
|
|
102
|
+
* `rollout-<ts>-<sid>.jsonl`, so a suffix match is unambiguous. The
|
|
103
|
+
* directory tree is small (year/month/day) — a one-shot recursive scan
|
|
104
|
+
* is cheap enough that we don't bother caching. */
|
|
105
|
+
export function findCodexRolloutBySessionId(cliSessionId) {
|
|
106
|
+
if (!cliSessionId || !existsSync(CODEX_SESSIONS_ROOT))
|
|
107
|
+
return undefined;
|
|
108
|
+
const suffix = `-${cliSessionId}.jsonl`;
|
|
109
|
+
const stack = [CODEX_SESSIONS_ROOT];
|
|
110
|
+
while (stack.length > 0) {
|
|
111
|
+
const dir = stack.pop();
|
|
112
|
+
let entries;
|
|
113
|
+
try {
|
|
114
|
+
entries = readdirSync(dir);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
for (const name of entries) {
|
|
120
|
+
const full = join(dir, name);
|
|
121
|
+
let st;
|
|
122
|
+
try {
|
|
123
|
+
st = statSync(full);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (st.isDirectory()) {
|
|
129
|
+
stack.push(full);
|
|
130
|
+
}
|
|
131
|
+
else if (st.isFile() && name.endsWith(suffix)) {
|
|
132
|
+
return full;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
/** Concatenate all text blocks of a content array. Codex rollout content
|
|
139
|
+
* is always an array of `{type, text}`; the kinds we care about are
|
|
140
|
+
* `input_text` (user) and `output_text` (assistant). Other block types
|
|
141
|
+
* (image_url, audio, etc.) are ignored — the bridge only forwards text. */
|
|
142
|
+
function joinTextBlocks(content, kind) {
|
|
143
|
+
if (!Array.isArray(content))
|
|
144
|
+
return '';
|
|
145
|
+
const parts = [];
|
|
146
|
+
for (const block of content) {
|
|
147
|
+
if (block && typeof block === 'object' && block.type === kind) {
|
|
148
|
+
const text = block.text;
|
|
149
|
+
if (typeof text === 'string')
|
|
150
|
+
parts.push(text);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return parts.join('');
|
|
154
|
+
}
|
|
155
|
+
/** Increment-read the rollout from `fromOffset`. Mirrors the byte-offset
|
|
156
|
+
* contract of claude-transcript.drainTranscript so callers can swap them
|
|
157
|
+
* out and reuse the existing fs.watch / poll wakeup machinery. */
|
|
158
|
+
export function drainCodexRollout(path, fromOffset) {
|
|
159
|
+
if (!existsSync(path))
|
|
160
|
+
return { events: [], newOffset: 0, pendingTail: '' };
|
|
161
|
+
let size;
|
|
162
|
+
try {
|
|
163
|
+
size = statSync(path).size;
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return { events: [], newOffset: fromOffset, pendingTail: '' };
|
|
167
|
+
}
|
|
168
|
+
let start = fromOffset;
|
|
169
|
+
// Truncated/rotated jsonl — re-read from the top. Codex doesn't normally
|
|
170
|
+
// rewrite rollouts, but mirror Claude's defensive handling.
|
|
171
|
+
if (size < start)
|
|
172
|
+
start = 0;
|
|
173
|
+
if (size === start)
|
|
174
|
+
return { events: [], newOffset: start, pendingTail: '' };
|
|
175
|
+
const len = size - start;
|
|
176
|
+
const buf = Buffer.alloc(len);
|
|
177
|
+
const fd = openSync(path, 'r');
|
|
178
|
+
try {
|
|
179
|
+
readSync(fd, buf, 0, len, start);
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
closeSync(fd);
|
|
183
|
+
}
|
|
184
|
+
const text = buf.toString('utf8');
|
|
185
|
+
const lastNl = text.lastIndexOf('\n');
|
|
186
|
+
const completeText = lastNl >= 0 ? text.slice(0, lastNl + 1) : '';
|
|
187
|
+
const pendingTail = lastNl >= 0 ? text.slice(lastNl + 1) : text;
|
|
188
|
+
const newOffset = start + Buffer.byteLength(completeText, 'utf8');
|
|
189
|
+
const events = [];
|
|
190
|
+
// Track byte offset within the file as we walk lines so synthetic uuids
|
|
191
|
+
// are stable across re-drains.
|
|
192
|
+
let cursor = start;
|
|
193
|
+
for (const line of completeText.split('\n')) {
|
|
194
|
+
if (line.length === 0) {
|
|
195
|
+
cursor += 1; // the \n after an empty line
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const lineByteLen = Buffer.byteLength(line, 'utf8') + 1; // include \n
|
|
199
|
+
const lineStart = cursor;
|
|
200
|
+
cursor += lineByteLen;
|
|
201
|
+
let obj;
|
|
202
|
+
try {
|
|
203
|
+
obj = JSON.parse(line);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (obj?.type !== 'response_item')
|
|
209
|
+
continue;
|
|
210
|
+
const p = obj.payload;
|
|
211
|
+
if (!p || typeof p !== 'object' || p.type !== 'message')
|
|
212
|
+
continue;
|
|
213
|
+
const ts = typeof obj.timestamp === 'string' ? Date.parse(obj.timestamp) : NaN;
|
|
214
|
+
const timestampMs = Number.isFinite(ts) ? ts : Date.now();
|
|
215
|
+
if (p.role === 'user') {
|
|
216
|
+
const text = joinTextBlocks(p.content, 'input_text');
|
|
217
|
+
if (!text)
|
|
218
|
+
continue;
|
|
219
|
+
events.push({ uuid: `${path}:${lineStart}`, timestampMs, kind: 'user', text });
|
|
220
|
+
}
|
|
221
|
+
else if (p.role === 'assistant' && p.phase === 'final_answer') {
|
|
222
|
+
const text = joinTextBlocks(p.content, 'output_text');
|
|
223
|
+
if (!text)
|
|
224
|
+
continue;
|
|
225
|
+
events.push({ uuid: `${path}:${lineStart}`, timestampMs, kind: 'assistant_final', text });
|
|
226
|
+
}
|
|
227
|
+
// Skip role=developer (instructions), phase=commentary (mid-turn
|
|
228
|
+
// status), and any reasoning / function_call* events — see file
|
|
229
|
+
// header for rationale.
|
|
230
|
+
}
|
|
231
|
+
return { events, newOffset, pendingTail };
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=codex-transcript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-transcript.js","sourceRoot":"","sources":["../../src/services/codex-transcript.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idle-detector.d.ts","sourceRoot":"","sources":["../../src/utils/idle-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAY3D,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,eAAe,CAA8C;IACrE,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,SAAS,CAAS;gBAEd,GAAG,EAAE,UAAU;IAK3B,MAAM,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI;IAI5B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"idle-detector.d.ts","sourceRoot":"","sources":["../../src/utils/idle-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAY3D,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,eAAe,CAA8C;IACrE,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,SAAS,CAAS;gBAEd,GAAG,EAAE,UAAU;IAK3B,MAAM,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI;IAI5B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAoDxB,KAAK,IAAI,IAAI;IAQb,OAAO,IAAI,IAAI;IAKf,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,SAAS;CAKlB"}
|
|
@@ -23,8 +23,16 @@ export class IdleDetector {
|
|
|
23
23
|
this.idleCallback = cb;
|
|
24
24
|
}
|
|
25
25
|
feed(data) {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// A botmux-owned submit calls reset() before writing input, but adopted
|
|
27
|
+
// panes can also receive local terminal input while we are already idle.
|
|
28
|
+
// Treat any later PTY data as a fresh cycle so that local work can become
|
|
29
|
+
// idle and flush transcript-driven fallback output.
|
|
30
|
+
if (this.isIdle) {
|
|
31
|
+
this.isIdle = false;
|
|
32
|
+
this.outputTail = '';
|
|
33
|
+
this.readySeen = false;
|
|
34
|
+
this.lastSpinnerAt = Date.now();
|
|
35
|
+
}
|
|
28
36
|
const stripped = this.stripAnsi(data);
|
|
29
37
|
this.outputTail = (this.outputTail + stripped).slice(-500);
|
|
30
38
|
// Track when the CLI's input prompt appears.
|
|
@@ -38,11 +46,14 @@ export class IdleDetector {
|
|
|
38
46
|
}
|
|
39
47
|
// Track spinner — but not if it's part of completion marker,
|
|
40
48
|
// and not after ready pattern is seen (status bar chars like · are not real spinners)
|
|
41
|
-
if (SPINNER_RE.test(stripped) && !(this.completionPattern?.test(this.outputTail)) && !this.readySeen) {
|
|
49
|
+
if (SPINNER_RE.test(stripped) && !(this.completionPattern?.test(stripped) || this.completionPattern?.test(this.outputTail)) && !this.readySeen) {
|
|
42
50
|
this.lastSpinnerAt = Date.now();
|
|
43
51
|
}
|
|
44
52
|
// Strategy 1: CLI-specific completion marker
|
|
45
|
-
|
|
53
|
+
// Check the current chunk too: a single full-screen redraw can contain
|
|
54
|
+
// the completion line and enough trailing status text to push it out of
|
|
55
|
+
// the 500-char tail before this check runs.
|
|
56
|
+
if (this.completionPattern?.test(stripped) || this.completionPattern?.test(this.outputTail)) {
|
|
46
57
|
this.clearTimer();
|
|
47
58
|
this.quiescenceTimer = setTimeout(() => {
|
|
48
59
|
this.quiescenceTimer = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idle-detector.js","sourceRoot":"","sources":["../../src/utils/idle-detector.ts"],"names":[],"mappings":"AAEA;;4CAE4C;AAC5C,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAE1C,+EAA+E;AAC/E,MAAM,aAAa,GAAG,KAAK,CAAC;AAC5B,4EAA4E;AAC5E,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B,MAAM,OAAO,YAAY;IACf,UAAU,GAAG,EAAE,CAAC;IAChB,aAAa,GAAG,CAAC,CAAC;IAClB,eAAe,GAAyC,IAAI,CAAC;IAC7D,MAAM,GAAG,KAAK,CAAC;IACf,YAAY,GAAwB,IAAI,CAAC;IACzC,iBAAiB,CAAqB;IACtC,YAAY,CAAqB;IACjC,SAAS,GAAG,KAAK,CAAC;IAE1B,YAAY,GAAe;QACzB,IAAI,CAAC,iBAAiB,GAAG,GAAG,CAAC,iBAAiB,CAAC;QAC/C,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;IACvC,CAAC;IAED,MAAM,CAAC,EAAc;QACnB,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,IAAI,CAAC,IAAY;QACf,IAAI,IAAI,CAAC,MAAM;
|
|
1
|
+
{"version":3,"file":"idle-detector.js","sourceRoot":"","sources":["../../src/utils/idle-detector.ts"],"names":[],"mappings":"AAEA;;4CAE4C;AAC5C,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAE1C,+EAA+E;AAC/E,MAAM,aAAa,GAAG,KAAK,CAAC;AAC5B,4EAA4E;AAC5E,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B,MAAM,OAAO,YAAY;IACf,UAAU,GAAG,EAAE,CAAC;IAChB,aAAa,GAAG,CAAC,CAAC;IAClB,eAAe,GAAyC,IAAI,CAAC;IAC7D,MAAM,GAAG,KAAK,CAAC;IACf,YAAY,GAAwB,IAAI,CAAC;IACzC,iBAAiB,CAAqB;IACtC,YAAY,CAAqB;IACjC,SAAS,GAAG,KAAK,CAAC;IAE1B,YAAY,GAAe;QACzB,IAAI,CAAC,iBAAiB,GAAG,GAAG,CAAC,iBAAiB,CAAC;QAC/C,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;IACvC,CAAC;IAED,MAAM,CAAC,EAAc;QACnB,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,IAAI,CAAC,IAAY;QACf,wEAAwE;QACxE,yEAAyE;QACzE,0EAA0E;QAC1E,oDAAoD;QACpD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;YACpB,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;YACrB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACvB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAClC,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,UAAU,GAAG,CAAC,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;QAE3D,6CAA6C;QAC7C,sEAAsE;QACtE,uEAAuE;QACvE,wDAAwD;QACxD,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACzC,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBAChF,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACxB,CAAC;QACH,CAAC;QAED,6DAA6D;QAC7D,sFAAsF;QACtF,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAC/I,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAClC,CAAC;QAED,6CAA6C;QAC7C,uEAAuE;QACvE,wEAAwE;QACxE,4CAA4C;QAC5C,IAAI,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5F,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,eAAe,GAAG,UAAU,CAAC,GAAG,EAAE;gBACrC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;gBAC5B,IAAI,CAAC,IAAI,CAAC,MAAM;oBAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,CAAC,EAAE,GAAG,CAAC,CAAC;YACR,OAAO;QACT,CAAC;QAED,2DAA2D;QAC3D,gFAAgF;QAChF,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO;QAEjD,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,eAAe,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,aAAa,CAAC,CAAC;IACjF,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC,UAAU,EAAE,CAAC;IACpB,CAAC;IAED,OAAO;QACL,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;IAC3B,CAAC;IAEO,eAAe;QACrB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;QACrD,IAAI,YAAY,GAAG,gBAAgB,EAAE,CAAC;YACpC,IAAI,CAAC,eAAe,GAAG,UAAU,CAC/B,GAAG,EAAE,CAAC,IAAI,CAAC,eAAe,EAAE,EAC5B,gBAAgB,GAAG,YAAY,GAAG,GAAG,CACtC,CAAC;YACF,OAAO;QACT,CAAC;QACD,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC;IAEO,QAAQ;QACd,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;QACrB,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;IACxB,CAAC;IAEO,UAAU;QAChB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACnC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,GAAW;QAC3B,OAAO,GAAG;aACP,OAAO,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;aAC/D,OAAO,CAAC,uFAAuF,EAAE,EAAE,CAAC,CAAC;IAC1G,CAAC;CACF"}
|