@nexrall/code-core 1.4.59 → 1.4.61

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.
@@ -0,0 +1,64 @@
1
+ export type PeerInboundPolicy = 'accept' | 'hold' | 'refuse';
2
+ export interface PeerRecord {
3
+ id: string;
4
+ name: string;
5
+ pid: number;
6
+ clientType: 'cli' | 'vscode' | 'desktop';
7
+ workDir: string;
8
+ /** Absolute path to this peer's Unix domain socket (see peerTransport.ts). Absent on platforms without socket support yet (see that module's header). */
9
+ socketPath?: string;
10
+ status: 'idle' | 'running';
11
+ inbound: PeerInboundPolicy;
12
+ startedAt: number;
13
+ updatedAt: number;
14
+ }
15
+ declare function peersRoot(): string;
16
+ declare function recordPath(id: string): string;
17
+ declare function isRecordAlive(r: PeerRecord): boolean;
18
+ /** Generate a readable random peer name, same shape Claude Code uses ("graceful-unicorn"). */
19
+ export declare function randomPeerName(): string;
20
+ export interface RegisterPeerOptions {
21
+ clientType: PeerRecord['clientType'];
22
+ workDir: string;
23
+ name?: string;
24
+ inbound?: PeerInboundPolicy;
25
+ socketPath?: string;
26
+ }
27
+ export interface PeerHandle {
28
+ record: PeerRecord;
29
+ /** Call periodically (~every 10s — see HEARTBEAT_INTERVAL_MS) so other sessions' liveness checks keep seeing this one as alive. Idempotent. */
30
+ heartbeat(): void;
31
+ /** Update this peer's status ('idle' | 'running') visibly to other sessions — e.g. so a peer picking a message target can see who's actually free. */
32
+ setStatus(status: PeerRecord['status']): void;
33
+ /** Remove this peer's record — call on clean exit so it stops appearing in other sessions' listPeers() immediately, rather than waiting out HEARTBEAT_STALE_MS. */
34
+ unregister(): void;
35
+ }
36
+ /**
37
+ * Register this session as a discoverable peer. Picks a name automatically
38
+ * (retrying on collision with a live peer) unless `opts.name` is given and
39
+ * free. Returns a handle for heartbeating/updating/unregistering — the
40
+ * caller (CLI/VS Code/Desktop host) owns calling heartbeat() on an interval;
41
+ * this module does not start its own timer, so it stays trivially testable
42
+ * and has no surprise background work in environments (like a short-lived
43
+ * test process) that never want one.
44
+ */
45
+ export declare function registerPeer(opts: RegisterPeerOptions): PeerHandle;
46
+ /**
47
+ * List every currently-alive peer on the machine, own-name-first when
48
+ * `ownId` is given (mirrors Claude Code's `/list-agents` printing the
49
+ * caller's own name on the first line). Opportunistically removes any
50
+ * record found to be dead (see this module's header for why no separate
51
+ * GC process is needed).
52
+ */
53
+ export declare function listPeers(ownId?: string): PeerRecord[];
54
+ /** Find a live peer by name (case-sensitive — names are short readable tokens, not meant to require fuzzy matching). */
55
+ export declare function findPeerByName(name: string): PeerRecord | null;
56
+ export declare const _peerRegistryInternal: {
57
+ peersRoot: typeof peersRoot;
58
+ recordPath: typeof recordPath;
59
+ isRecordAlive: typeof isRecordAlive;
60
+ HEARTBEAT_STALE_MS: number;
61
+ HEARTBEAT_INTERVAL_MS: number;
62
+ };
63
+ export {};
64
+ //# sourceMappingURL=peerRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peerRegistry.d.ts","sourceRoot":"","sources":["../../src/agent/peerRegistry.ts"],"names":[],"mappings":"AA2CA,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE7D,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,yJAAyJ;IACzJ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AASD,iBAAS,SAAS,IAAI,MAAM,CAE3B;AAED,iBAAS,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEtC;AAMD,iBAAS,aAAa,CAAC,CAAC,EAAE,UAAU,GAAG,OAAO,CAG7C;AA0BD,8FAA8F;AAC9F,wBAAgB,cAAc,IAAI,MAAM,CAIvC;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,+IAA+I;IAC/I,SAAS,IAAI,IAAI,CAAC;IAClB,sJAAsJ;IACtJ,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC9C,mKAAmK;IACnK,UAAU,IAAI,IAAI,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,UAAU,CAyClE;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE,CAwBtD;AAED,wHAAwH;AACxH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAE9D;AAED,eAAO,MAAM,qBAAqB;;;;;;CAAsF,CAAC"}
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports._peerRegistryInternal = void 0;
37
+ exports.randomPeerName = randomPeerName;
38
+ exports.registerPeer = registerPeer;
39
+ exports.listPeers = listPeers;
40
+ exports.findPeerByName = findPeerByName;
41
+ /**
42
+ * Peer registry — lets independent sessions (two CLI windows, CLI + VS Code,
43
+ * VS Code + Desktop, all on the same machine) DISCOVER each other, mirroring
44
+ * Claude Code's cross-session messaging discovery mechanism: "Each session
45
+ * registers itself in files on disk... two sessions can reach each other
46
+ * only when they can see the same files."
47
+ *
48
+ * This module is discovery ONLY — one JSON record per live session, readable
49
+ * by every other session on the machine. The actual message transport
50
+ * (Unix socket / named pipe) is a SEPARATE module (peerTransport.ts) that
51
+ * reads `socketPath` from a record found here. Splitting them mirrors
52
+ * worktree.ts vs worktreeEnforcement.ts's own split: one module owns "what
53
+ * exists", another owns "what you're allowed to do with it".
54
+ *
55
+ * STORAGE
56
+ * -------
57
+ * ~/.nexrall/peers/<sessionId>.json
58
+ *
59
+ * ONE FLAT DIRECTORY, not scoped per-project — unlike sharedTasks.ts (which
60
+ * scopes by workDir because a task list only makes sense per-project),
61
+ * discovery is deliberately GLOBAL: Claude Code's own cross-session
62
+ * messaging works across different projects on the same machine (you can
63
+ * message a session in an unrelated folder), and there's no reason for
64
+ * Nexrall Code's peer discovery to be narrower than that. A session that
65
+ * only wants same-project peers can filter listPeers() by `workDir` itself.
66
+ *
67
+ * LIVENESS
68
+ * --------
69
+ * A registered peer is considered alive when BOTH:
70
+ * - its pid is still running (process.kill(pid, 0))
71
+ * - its record was heartbeat-updated within HEARTBEAT_STALE_MS
72
+ * A session must call `heartbeat()` periodically (see registerPeer's return
73
+ * value) or its record is treated as dead by every OTHER session's
74
+ * `listPeers()`, and cleaned up opportunistically by whichever session next
75
+ * calls listPeers() and notices the staleness (no separate GC daemon needed
76
+ * — same "GC on next read" pattern crossProcessLock.ts and worktree.ts use
77
+ * for their own stale state).
78
+ */
79
+ const fs = __importStar(require("fs"));
80
+ const os = __importStar(require("os"));
81
+ const path = __importStar(require("path"));
82
+ const crypto = __importStar(require("crypto"));
83
+ // A record not heartbeat-updated within this long is treated as dead even if
84
+ // its pid happens to still resolve (e.g. pid reuse by an unrelated process
85
+ // after a crash — the same class of gap crossProcessLock's stale-lock check
86
+ // already documents for the identical reason).
87
+ const HEARTBEAT_STALE_MS = 20000; // 20s — sessions heartbeat every ~10s (see registerPeer)
88
+ const HEARTBEAT_INTERVAL_MS = 10000;
89
+ function peersRoot() {
90
+ return process.env.NEXRALL_PEERS_DIR || path.join(os.homedir(), '.nexrall', 'peers');
91
+ }
92
+ function recordPath(id) {
93
+ return path.join(peersRoot(), `${id}.json`);
94
+ }
95
+ function isPidAlive(pid) {
96
+ try {
97
+ process.kill(pid, 0);
98
+ return true;
99
+ }
100
+ catch {
101
+ return false;
102
+ }
103
+ }
104
+ function isRecordAlive(r) {
105
+ if (Date.now() - r.updatedAt > HEARTBEAT_STALE_MS)
106
+ return false;
107
+ return isPidAlive(r.pid);
108
+ }
109
+ function readRecord(id) {
110
+ try {
111
+ const raw = JSON.parse(fs.readFileSync(recordPath(id), 'utf-8'));
112
+ if (raw && typeof raw.id === 'string' && typeof raw.pid === 'number')
113
+ return raw;
114
+ return null;
115
+ }
116
+ catch {
117
+ return null;
118
+ }
119
+ }
120
+ function writeRecord(r) {
121
+ fs.mkdirSync(peersRoot(), { recursive: true });
122
+ const p = recordPath(r.id);
123
+ // Atomic write-via-temp-then-rename — same reasoning as sharedTasks.ts's
124
+ // writeTask: a crash mid-write must never leave a half-written record that
125
+ // another session's listPeers() then fails to parse.
126
+ const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;
127
+ fs.writeFileSync(tmp, JSON.stringify(r));
128
+ fs.renameSync(tmp, p);
129
+ }
130
+ const ADJECTIVES = ['graceful', 'quiet', 'brave', 'calm', 'swift', 'keen', 'bold', 'clear', 'gentle', 'sharp', 'steady', 'curious'];
131
+ const NOUNS = ['unicorn', 'falcon', 'otter', 'harbor', 'meadow', 'ember', 'summit', 'cedar', 'quartz', 'heron', 'lynx', 'delta'];
132
+ /** Generate a readable random peer name, same shape Claude Code uses ("graceful-unicorn"). */
133
+ function randomPeerName() {
134
+ const a = ADJECTIVES[crypto.randomInt(ADJECTIVES.length)];
135
+ const n = NOUNS[crypto.randomInt(NOUNS.length)];
136
+ return `${a}-${n}`;
137
+ }
138
+ /**
139
+ * Register this session as a discoverable peer. Picks a name automatically
140
+ * (retrying on collision with a live peer) unless `opts.name` is given and
141
+ * free. Returns a handle for heartbeating/updating/unregistering — the
142
+ * caller (CLI/VS Code/Desktop host) owns calling heartbeat() on an interval;
143
+ * this module does not start its own timer, so it stays trivially testable
144
+ * and has no surprise background work in environments (like a short-lived
145
+ * test process) that never want one.
146
+ */
147
+ function registerPeer(opts) {
148
+ const id = crypto.randomBytes(8).toString('hex');
149
+ const takenNames = new Set(listPeers().map((p) => p.name));
150
+ let name = opts.name?.trim();
151
+ if (name && takenNames.has(name)) {
152
+ // Requested name collides with a LIVE peer — disambiguate rather than
153
+ // silently overwrite/confuse two sessions with the same displayed name.
154
+ name = `${name}-${id.slice(0, 4)}`;
155
+ }
156
+ if (!name) {
157
+ do {
158
+ name = randomPeerName();
159
+ } while (takenNames.has(name));
160
+ }
161
+ const now = Date.now();
162
+ const record = {
163
+ id, name, pid: process.pid,
164
+ clientType: opts.clientType,
165
+ workDir: opts.workDir,
166
+ socketPath: opts.socketPath,
167
+ status: 'idle',
168
+ inbound: opts.inbound ?? 'accept',
169
+ startedAt: now,
170
+ updatedAt: now,
171
+ };
172
+ writeRecord(record);
173
+ return {
174
+ record,
175
+ heartbeat() {
176
+ record.updatedAt = Date.now();
177
+ writeRecord(record);
178
+ },
179
+ setStatus(status) {
180
+ record.status = status;
181
+ record.updatedAt = Date.now();
182
+ writeRecord(record);
183
+ },
184
+ unregister() {
185
+ try {
186
+ fs.unlinkSync(recordPath(id));
187
+ }
188
+ catch { /* already gone */ }
189
+ },
190
+ };
191
+ }
192
+ /**
193
+ * List every currently-alive peer on the machine, own-name-first when
194
+ * `ownId` is given (mirrors Claude Code's `/list-agents` printing the
195
+ * caller's own name on the first line). Opportunistically removes any
196
+ * record found to be dead (see this module's header for why no separate
197
+ * GC process is needed).
198
+ */
199
+ function listPeers(ownId) {
200
+ let entries;
201
+ try {
202
+ entries = fs.readdirSync(peersRoot());
203
+ }
204
+ catch {
205
+ return [];
206
+ }
207
+ const alive = [];
208
+ for (const name of entries) {
209
+ if (!name.endsWith('.json'))
210
+ continue;
211
+ const id = name.slice(0, -'.json'.length);
212
+ const record = readRecord(id);
213
+ if (!record)
214
+ continue;
215
+ if (isRecordAlive(record)) {
216
+ alive.push(record);
217
+ }
218
+ else {
219
+ // Opportunistic GC — a dead record left behind by a crash (no chance
220
+ // to call unregister()) shouldn't linger forever cluttering every
221
+ // future listPeers() call.
222
+ try {
223
+ fs.unlinkSync(recordPath(id));
224
+ }
225
+ catch { /* raced with another GC — fine */ }
226
+ }
227
+ }
228
+ if (ownId) {
229
+ alive.sort((a, b) => (a.id === ownId ? -1 : b.id === ownId ? 1 : 0));
230
+ }
231
+ return alive;
232
+ }
233
+ /** Find a live peer by name (case-sensitive — names are short readable tokens, not meant to require fuzzy matching). */
234
+ function findPeerByName(name) {
235
+ return listPeers().find((p) => p.name === name) ?? null;
236
+ }
237
+ exports._peerRegistryInternal = { peersRoot, recordPath, isRecordAlive, HEARTBEAT_STALE_MS, HEARTBEAT_INTERVAL_MS };
238
+ //# sourceMappingURL=peerRegistry.js.map
@@ -0,0 +1,122 @@
1
+ export interface PeerMessage {
2
+ /** The sending peer's name (from peerRegistry.ts), for display + as a reply target. */
3
+ from: string;
4
+ /** The sending peer's id — used internally to look up a reply address; not meant for display. */
5
+ fromId: string;
6
+ text: string;
7
+ sentAt: number;
8
+ }
9
+ /**
10
+ * Where this peer's transport endpoint lives.
11
+ *
12
+ * macOS/Linux: a real file at `~/.nexrall/peers/<id>.sock` — a Unix domain
13
+ * socket, backed by an actual filesystem entry `verifySocketSafety` can
14
+ * `lstat` and validate — UNLESS that path would exceed
15
+ * MAX_SOCKET_PATH_LENGTH, in which case fallbackSocketDir() is used instead
16
+ * (see its own doc comment).
17
+ *
18
+ * Windows: `\\.\pipe\nexrall-peer-<id>` — a NAMED PIPE, which lives in the
19
+ * NT kernel object namespace, NOT the regular NTFS filesystem tree. There is
20
+ * no directory to create and no on-disk file for `fs.lstatSync` to inspect
21
+ * (see verifySocketSafety's Windows branch for what this means for the
22
+ * safety checks), and no `sockaddr_un`-style length limit either — Windows
23
+ * named pipe paths have a much larger practical limit, so no fallback logic
24
+ * applies there. `net.createServer().listen(path)` / `net.createConnection
25
+ * (path)` both accept this exact path form natively — no separate Windows
26
+ * API surface needed, Node's own `net` module dispatches to named pipes
27
+ * automatically when the path matches this pattern.
28
+ */
29
+ export declare function socketPathFor(peerId: string): string;
30
+ /**
31
+ * Validate that `socketPath` is safe to connect to BEFORE connecting —
32
+ * see this module's header for exactly which bypass this closes. Returns an
33
+ * error string on failure, null when safe to proceed.
34
+ *
35
+ * WINDOWS CAVEAT: named pipes are kernel objects, not filesystem entries, so
36
+ * `fs.lstatSync` (what the symlink/uid checks below are built on) does not
37
+ * apply to them — there is no on-disk path component for a symlink to spoof
38
+ * in the first place, so that specific attack class does not carry over.
39
+ * The uid-ownership check has no direct equivalent from plain Node `net`
40
+ * either (that would require reading the pipe's Windows security descriptor
41
+ * via a native call, which this module deliberately does not add). The
42
+ * actual safety boundary on Windows is therefore the OS's own named-pipe
43
+ * ACL (set by whoever created it, defaulting to the creating user + admins)
44
+ * plus the connection attempt itself failing cleanly (ENOENT/EACCES) for a
45
+ * pipe that does not exist or is not accessible — handled by sendPeerMessage's
46
+ * own error path, not by a pre-check here. This is a genuinely weaker
47
+ * guarantee than the Unix path's explicit checks, called out here rather
48
+ * than silently assumed equivalent.
49
+ */
50
+ declare function verifySocketSafety(socketPath: string): string | null;
51
+ export interface PeerInboxOptions {
52
+ /** Called for each message as it arrives — see loop.ts's drainInbox usage, once wired. */
53
+ onMessage: (msg: PeerMessage) => void;
54
+ /**
55
+ * Called if the underlying listen() call itself fails (e.g. EINVAL from a
56
+ * socket path exceeding the OS's sockaddr_un length limit — see
57
+ * MAX_SOCKET_PATH_LENGTH's own comment for why this used to fail
58
+ * completely silently). Optional: a caller that omits this still gets a
59
+ * peer that LOOKS registered but can never actually receive a message,
60
+ * same as before this callback existed — but now at least has the option
61
+ * to find out and surface it (e.g. log it, or skip registering the peer
62
+ * record's socketPath at all so senders get a clear "not reachable" error
63
+ * up front instead of a silent black hole).
64
+ */
65
+ onError?: (err: Error) => void;
66
+ }
67
+ export interface PeerInbox {
68
+ socketPath: string;
69
+ close(): void;
70
+ }
71
+ /**
72
+ * Start listening for incoming peer messages on this session's own
73
+ * socket/pipe. Unix domain socket on macOS/Linux, named pipe on Windows —
74
+ * see socketPathFor's own doc comment for the platform split. Not tested
75
+ * against a real Windows machine (dev/CI for this repo runs macOS/Linux);
76
+ * the implementation follows Node's own documented cross-platform `net`
77
+ * behavior for named-pipe paths, but treat the Windows path as less
78
+ * battle-tested than the Unix one until it has run on real Windows.
79
+ */
80
+ export declare function startPeerInbox(peerId: string, opts: PeerInboxOptions): PeerInbox;
81
+ export interface SendPeerMessageResult {
82
+ ok: boolean;
83
+ error?: string;
84
+ }
85
+ /**
86
+ * Send a message to a peer at `targetSocketPath`. Fire-and-forget from the
87
+ * caller's perspective (resolves once the write completes or fails) — there
88
+ * is no delivery receipt, matching Claude Code's own "best effort" framing
89
+ * for cross-session messaging.
90
+ */
91
+ export declare function sendPeerMessage(targetSocketPath: string, message: Omit<PeerMessage, 'sentAt'>): Promise<SendPeerMessageResult>;
92
+ export interface BroadcastTarget {
93
+ name: string;
94
+ socketPath: string;
95
+ }
96
+ export interface BroadcastResult {
97
+ name: string;
98
+ ok: boolean;
99
+ error?: string;
100
+ }
101
+ /**
102
+ * Send the SAME message to every target in `targets`, in parallel. Each
103
+ * target gets its own independent sendPeerMessage call — one unreachable or
104
+ * slow peer cannot block or fail delivery to the others (no partial-failure
105
+ * cascade, no head-of-line blocking on a single slow socket).
106
+ *
107
+ * Deliberately a thin fan-out over the existing single-target
108
+ * sendPeerMessage rather than a new wire-level primitive: broadcast is a
109
+ * CALLER-SIDE concept (send to N addresses), not a transport-level one —
110
+ * there is no multicast socket underneath this, just N ordinary point-to-
111
+ * point sends, which keeps every security check (verifySocketSafety, size
112
+ * caps) shared with the single-message path instead of duplicated.
113
+ */
114
+ export declare function broadcastPeerMessage(targets: BroadcastTarget[], message: Omit<PeerMessage, 'sentAt'>): Promise<BroadcastResult[]>;
115
+ export declare const _peerTransportInternal: {
116
+ verifySocketSafety: typeof verifySocketSafety;
117
+ MAX_MESSAGE_BYTES: number;
118
+ CONNECT_TIMEOUT_MS: number;
119
+ isWindows: boolean;
120
+ };
121
+ export {};
122
+ //# sourceMappingURL=peerTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peerTransport.d.ts","sourceRoot":"","sources":["../../src/agent/peerTransport.ts"],"names":[],"mappings":"AAiDA,MAAM,WAAW,WAAW;IAC1B,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAC;IACb,iGAAiG;IACjG,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAwCD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKpD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,iBAAS,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmB7D;AAED,MAAM,WAAW,gBAAgB;IAC/B,0FAA0F;IAC1F,SAAS,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,SAAS,CA8EhF;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,GACnC,OAAO,CAAC,qBAAqB,CAAC,CAkChC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,eAAe,EAAE,EAC1B,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,GACnC,OAAO,CAAC,eAAe,EAAE,CAAC,CAQ5B;AAED,eAAO,MAAM,sBAAsB;;;;;CAA2E,CAAC"}