@agent-relay/daemon 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-manager.d.ts +134 -0
- package/dist/agent-manager.d.ts.map +1 -0
- package/dist/agent-manager.js +578 -0
- package/dist/agent-manager.js.map +1 -0
- package/dist/agent-registry.d.ts +99 -0
- package/dist/agent-registry.d.ts.map +1 -0
- package/dist/agent-registry.js +213 -0
- package/dist/agent-registry.js.map +1 -0
- package/dist/agent-signing.d.ts +158 -0
- package/dist/agent-signing.d.ts.map +1 -0
- package/dist/agent-signing.js +523 -0
- package/dist/agent-signing.js.map +1 -0
- package/dist/api.d.ts +106 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +876 -0
- package/dist/api.js.map +1 -0
- package/dist/auth.d.ts +94 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +197 -0
- package/dist/auth.js.map +1 -0
- package/dist/channel-membership-store.d.ts +55 -0
- package/dist/channel-membership-store.d.ts.map +1 -0
- package/dist/channel-membership-store.js +176 -0
- package/dist/channel-membership-store.js.map +1 -0
- package/dist/cli-auth.d.ts +89 -0
- package/dist/cli-auth.d.ts.map +1 -0
- package/dist/cli-auth.js +792 -0
- package/dist/cli-auth.js.map +1 -0
- package/dist/cloud-sync.d.ts +150 -0
- package/dist/cloud-sync.d.ts.map +1 -0
- package/dist/cloud-sync.js +446 -0
- package/dist/cloud-sync.js.map +1 -0
- package/dist/connection.d.ts +130 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +438 -0
- package/dist/connection.js.map +1 -0
- package/dist/consensus-integration.d.ts +167 -0
- package/dist/consensus-integration.d.ts.map +1 -0
- package/dist/consensus-integration.js +371 -0
- package/dist/consensus-integration.js.map +1 -0
- package/dist/consensus.d.ts +271 -0
- package/dist/consensus.d.ts.map +1 -0
- package/dist/consensus.js +632 -0
- package/dist/consensus.js.map +1 -0
- package/dist/delivery-tracker.d.ts +34 -0
- package/dist/delivery-tracker.d.ts.map +1 -0
- package/dist/delivery-tracker.js +104 -0
- package/dist/delivery-tracker.js.map +1 -0
- package/dist/enhanced-features.d.ts +118 -0
- package/dist/enhanced-features.d.ts.map +1 -0
- package/dist/enhanced-features.js +176 -0
- package/dist/enhanced-features.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/index.d.ts +73 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +241 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/orchestrator.d.ts +217 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +1143 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/rate-limiter.d.ts +68 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +130 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/registry.d.ts +9 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +9 -0
- package/dist/registry.js.map +1 -0
- package/dist/relay-ledger.d.ts +261 -0
- package/dist/relay-ledger.d.ts.map +1 -0
- package/dist/relay-ledger.js +532 -0
- package/dist/relay-ledger.js.map +1 -0
- package/dist/relay-watchdog.d.ts +125 -0
- package/dist/relay-watchdog.d.ts.map +1 -0
- package/dist/relay-watchdog.js +611 -0
- package/dist/relay-watchdog.js.map +1 -0
- package/dist/repo-manager.d.ts +116 -0
- package/dist/repo-manager.d.ts.map +1 -0
- package/dist/repo-manager.js +384 -0
- package/dist/repo-manager.js.map +1 -0
- package/dist/router.d.ts +370 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +1437 -0
- package/dist/router.js.map +1 -0
- package/dist/server.d.ts +174 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1001 -0
- package/dist/server.js.map +1 -0
- package/dist/spawn-manager.d.ts +78 -0
- package/dist/spawn-manager.d.ts.map +1 -0
- package/dist/spawn-manager.js +165 -0
- package/dist/spawn-manager.js.map +1 -0
- package/dist/sync-queue.d.ts +116 -0
- package/dist/sync-queue.d.ts.map +1 -0
- package/dist/sync-queue.js +361 -0
- package/dist/sync-queue.js.map +1 -0
- package/dist/types.d.ts +133 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/workspace-manager.d.ts +80 -0
- package/dist/workspace-manager.d.ts.map +1 -0
- package/dist/workspace-manager.js +314 -0
- package/dist/workspace-manager.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relay Watchdog - File-based relay message detection and processing
|
|
3
|
+
*
|
|
4
|
+
* Monitors agent outbox directories for new relay files and processes them:
|
|
5
|
+
* 1. Detects new files via fs.watch + periodic reconciliation
|
|
6
|
+
* 2. Validates files (size > 0, not symlink, settled)
|
|
7
|
+
* 3. Claims files atomically via ledger
|
|
8
|
+
* 4. Processes and archives files
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - fsevents/inotify watchers with overflow fallback
|
|
12
|
+
* - Configurable settle time for file stability
|
|
13
|
+
* - Symlink rejection (security)
|
|
14
|
+
* - Orphaned .pending file cleanup
|
|
15
|
+
* - Crash recovery on startup
|
|
16
|
+
*/
|
|
17
|
+
import { EventEmitter } from 'node:events';
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import crypto from 'node:crypto';
|
|
21
|
+
import { RelayLedger } from './relay-ledger.js';
|
|
22
|
+
import { getBaseRelayPaths } from '@agent-relay/config/relay-file-writer';
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Constants
|
|
25
|
+
// ============================================================================
|
|
26
|
+
const DEFAULT_SETTLE_TIME_MS = parseInt(process.env.RELAY_SETTLE_TIME_MS ?? '500', 10);
|
|
27
|
+
const DEFAULT_MALFORMED_TIMEOUT_MS = parseInt(process.env.RELAY_MALFORMED_TIMEOUT_MS ?? '10000', 10);
|
|
28
|
+
const DEFAULT_RECONCILE_INTERVAL_MS = 30000;
|
|
29
|
+
const DEFAULT_MAX_MESSAGE_SIZE_BYTES = 1024 * 1024; // 1MB
|
|
30
|
+
const DEFAULT_MAX_ATTACHMENT_SIZE_BYTES = 10 * 1024 * 1024; // 10MB
|
|
31
|
+
const DEFAULT_CLEANUP_INTERVAL_MS = 60000;
|
|
32
|
+
const DEFAULT_ORPHANED_PENDING_AGE_MS = 30000;
|
|
33
|
+
const DEFAULT_ARCHIVE_RETENTION_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
34
|
+
// Files/directories to ignore
|
|
35
|
+
const IGNORE_PATTERNS = [
|
|
36
|
+
/^\./, // Hidden files
|
|
37
|
+
/^\.pending$/, // Pending marker
|
|
38
|
+
/\.tmp$/, // Temp files
|
|
39
|
+
/~$/, // Editor backups
|
|
40
|
+
];
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// RelayWatchdog Class
|
|
43
|
+
// ============================================================================
|
|
44
|
+
export class RelayWatchdog extends EventEmitter {
|
|
45
|
+
config;
|
|
46
|
+
relayPaths;
|
|
47
|
+
ledger;
|
|
48
|
+
watchers = new Map();
|
|
49
|
+
pendingFiles = new Map();
|
|
50
|
+
reconcileTimer;
|
|
51
|
+
cleanupTimer;
|
|
52
|
+
running = false;
|
|
53
|
+
constructor(config = {}) {
|
|
54
|
+
super();
|
|
55
|
+
this.relayPaths = config.relayPaths ?? getBaseRelayPaths();
|
|
56
|
+
this.config = {
|
|
57
|
+
relayPaths: this.relayPaths,
|
|
58
|
+
ledgerPath: config.ledgerPath ?? path.join(this.relayPaths.metaDir, 'ledger.sqlite'),
|
|
59
|
+
settleTimeMs: config.settleTimeMs ?? DEFAULT_SETTLE_TIME_MS,
|
|
60
|
+
malformedTimeoutMs: config.malformedTimeoutMs ?? DEFAULT_MALFORMED_TIMEOUT_MS,
|
|
61
|
+
reconcileIntervalMs: config.reconcileIntervalMs ?? DEFAULT_RECONCILE_INTERVAL_MS,
|
|
62
|
+
maxMessageSizeBytes: config.maxMessageSizeBytes ?? DEFAULT_MAX_MESSAGE_SIZE_BYTES,
|
|
63
|
+
maxAttachmentSizeBytes: config.maxAttachmentSizeBytes ?? DEFAULT_MAX_ATTACHMENT_SIZE_BYTES,
|
|
64
|
+
cleanupIntervalMs: config.cleanupIntervalMs ?? DEFAULT_CLEANUP_INTERVAL_MS,
|
|
65
|
+
orphanedPendingAgeMs: config.orphanedPendingAgeMs ?? DEFAULT_ORPHANED_PENDING_AGE_MS,
|
|
66
|
+
archiveRetentionMs: config.archiveRetentionMs ?? DEFAULT_ARCHIVE_RETENTION_MS,
|
|
67
|
+
debug: config.debug ?? false,
|
|
68
|
+
};
|
|
69
|
+
// Initialize ledger
|
|
70
|
+
this.ledger = new RelayLedger({
|
|
71
|
+
dbPath: this.config.ledgerPath,
|
|
72
|
+
archiveRetentionMs: this.config.archiveRetentionMs,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Resolve symlinks in relay paths for cloud/production workspace support
|
|
77
|
+
* This ensures we work with canonical paths even when directories are symlinked
|
|
78
|
+
*/
|
|
79
|
+
async resolveRelayPaths() {
|
|
80
|
+
const resolvePath = async (p) => {
|
|
81
|
+
try {
|
|
82
|
+
const resolved = await fs.promises.realpath(p);
|
|
83
|
+
if (resolved !== p) {
|
|
84
|
+
this.log(`Resolved symlink: ${p} -> ${resolved}`);
|
|
85
|
+
}
|
|
86
|
+
return resolved;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Path doesn't exist yet, keep original
|
|
90
|
+
return p;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
// Resolve all relay paths to canonical form
|
|
94
|
+
this.relayPaths = {
|
|
95
|
+
rootDir: await resolvePath(this.relayPaths.rootDir),
|
|
96
|
+
outboxDir: await resolvePath(this.relayPaths.outboxDir),
|
|
97
|
+
attachmentsDir: await resolvePath(this.relayPaths.attachmentsDir),
|
|
98
|
+
metaDir: await resolvePath(this.relayPaths.metaDir),
|
|
99
|
+
legacyOutboxDir: await resolvePath(this.relayPaths.legacyOutboxDir),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Start the watchdog
|
|
104
|
+
*/
|
|
105
|
+
async start() {
|
|
106
|
+
if (this.running)
|
|
107
|
+
return;
|
|
108
|
+
this.running = true;
|
|
109
|
+
this.log('Starting relay watchdog...');
|
|
110
|
+
// Resolve symlinks in relay paths for cloud/production workspace support
|
|
111
|
+
await this.resolveRelayPaths();
|
|
112
|
+
// Ensure directories exist
|
|
113
|
+
await this.ensureDirectories();
|
|
114
|
+
// Crash recovery: reset processing files and reconcile
|
|
115
|
+
const resetCount = this.ledger.resetProcessingFiles();
|
|
116
|
+
if (resetCount > 0) {
|
|
117
|
+
this.log(`Crash recovery: reset ${resetCount} processing files to pending`);
|
|
118
|
+
}
|
|
119
|
+
const reconcileResult = this.ledger.reconcileWithFilesystem();
|
|
120
|
+
if (reconcileResult.failed > 0) {
|
|
121
|
+
this.log(`Crash recovery: marked ${reconcileResult.failed} missing files as failed`);
|
|
122
|
+
}
|
|
123
|
+
// Initial scan to seed ledger
|
|
124
|
+
await this.fullReconciliation();
|
|
125
|
+
// Start root watcher for new agents
|
|
126
|
+
this.startRootWatcher();
|
|
127
|
+
// Start watchers for existing agents
|
|
128
|
+
await this.startAgentWatchers();
|
|
129
|
+
// Start periodic reconciliation
|
|
130
|
+
this.reconcileTimer = setInterval(() => {
|
|
131
|
+
this.fullReconciliation().catch(err => {
|
|
132
|
+
this.emit('error', err);
|
|
133
|
+
});
|
|
134
|
+
}, this.config.reconcileIntervalMs);
|
|
135
|
+
this.reconcileTimer.unref();
|
|
136
|
+
// Start cleanup timer
|
|
137
|
+
this.cleanupTimer = setInterval(() => {
|
|
138
|
+
this.cleanupOrphanedFiles();
|
|
139
|
+
this.ledger.cleanupArchivedRecords();
|
|
140
|
+
}, this.config.cleanupIntervalMs);
|
|
141
|
+
this.cleanupTimer.unref();
|
|
142
|
+
this.log('Relay watchdog started');
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Stop the watchdog
|
|
146
|
+
*/
|
|
147
|
+
async stop() {
|
|
148
|
+
if (!this.running)
|
|
149
|
+
return;
|
|
150
|
+
this.running = false;
|
|
151
|
+
this.log('Stopping relay watchdog...');
|
|
152
|
+
// Clear timers
|
|
153
|
+
if (this.reconcileTimer) {
|
|
154
|
+
clearInterval(this.reconcileTimer);
|
|
155
|
+
this.reconcileTimer = undefined;
|
|
156
|
+
}
|
|
157
|
+
if (this.cleanupTimer) {
|
|
158
|
+
clearInterval(this.cleanupTimer);
|
|
159
|
+
this.cleanupTimer = undefined;
|
|
160
|
+
}
|
|
161
|
+
// Clear pending file timers
|
|
162
|
+
for (const timer of this.pendingFiles.values()) {
|
|
163
|
+
clearTimeout(timer);
|
|
164
|
+
}
|
|
165
|
+
this.pendingFiles.clear();
|
|
166
|
+
// Close all watchers
|
|
167
|
+
for (const [dir, watcher] of this.watchers) {
|
|
168
|
+
watcher.close();
|
|
169
|
+
this.log(`Closed watcher for: ${dir}`);
|
|
170
|
+
}
|
|
171
|
+
this.watchers.clear();
|
|
172
|
+
// Close ledger
|
|
173
|
+
this.ledger.close();
|
|
174
|
+
this.log('Relay watchdog stopped');
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get ledger statistics
|
|
178
|
+
*/
|
|
179
|
+
getStats() {
|
|
180
|
+
return this.ledger.getStats();
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get pending file count
|
|
184
|
+
*/
|
|
185
|
+
getPendingCount() {
|
|
186
|
+
return this.ledger.getPendingFiles(1).length > 0 ? this.ledger.getStats().pending : 0;
|
|
187
|
+
}
|
|
188
|
+
// ==========================================================================
|
|
189
|
+
// Directory Management
|
|
190
|
+
// ==========================================================================
|
|
191
|
+
async ensureDirectories() {
|
|
192
|
+
const dirs = [
|
|
193
|
+
this.relayPaths.outboxDir,
|
|
194
|
+
this.relayPaths.attachmentsDir,
|
|
195
|
+
this.relayPaths.metaDir,
|
|
196
|
+
path.join(this.relayPaths.rootDir, 'archive'),
|
|
197
|
+
];
|
|
198
|
+
for (const dir of dirs) {
|
|
199
|
+
if (!fs.existsSync(dir)) {
|
|
200
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
201
|
+
this.log(`Created directory: ${dir}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// ==========================================================================
|
|
206
|
+
// File Watching
|
|
207
|
+
// ==========================================================================
|
|
208
|
+
startRootWatcher() {
|
|
209
|
+
const outboxDir = this.relayPaths.outboxDir;
|
|
210
|
+
if (!fs.existsSync(outboxDir)) {
|
|
211
|
+
this.log(`Outbox directory doesn't exist yet: ${outboxDir}`);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
const watcher = fs.watch(outboxDir, (eventType, filename) => {
|
|
216
|
+
if (!filename || this.shouldIgnore(filename))
|
|
217
|
+
return;
|
|
218
|
+
const agentDir = path.join(outboxDir, filename);
|
|
219
|
+
// Check if new agent directory was created
|
|
220
|
+
if (eventType === 'rename') {
|
|
221
|
+
this.checkAndWatchAgentDir(agentDir, filename);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
watcher.on('error', (err) => {
|
|
225
|
+
this.log(`Root watcher error: ${err.message}`);
|
|
226
|
+
this.emit('watcher:overflow', outboxDir);
|
|
227
|
+
// Trigger full reconciliation on watcher error
|
|
228
|
+
this.fullReconciliation().catch(e => this.emit('error', e));
|
|
229
|
+
});
|
|
230
|
+
this.watchers.set(outboxDir, watcher);
|
|
231
|
+
this.log(`Started root watcher: ${outboxDir}`);
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
this.log(`Failed to start root watcher: ${err.message}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async startAgentWatchers() {
|
|
238
|
+
const outboxDir = this.relayPaths.outboxDir;
|
|
239
|
+
if (!fs.existsSync(outboxDir))
|
|
240
|
+
return;
|
|
241
|
+
const entries = await fs.promises.readdir(outboxDir, { withFileTypes: true });
|
|
242
|
+
for (const entry of entries) {
|
|
243
|
+
if (entry.isDirectory() && !this.shouldIgnore(entry.name)) {
|
|
244
|
+
const agentDir = path.join(outboxDir, entry.name);
|
|
245
|
+
this.startAgentWatcher(agentDir, entry.name);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
checkAndWatchAgentDir(agentDir, agentName) {
|
|
250
|
+
try {
|
|
251
|
+
const stats = fs.statSync(agentDir);
|
|
252
|
+
if (stats.isDirectory() && !this.watchers.has(agentDir)) {
|
|
253
|
+
this.startAgentWatcher(agentDir, agentName);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
// Directory might not exist yet or was deleted
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
startAgentWatcher(agentDir, agentName) {
|
|
261
|
+
if (this.watchers.has(agentDir))
|
|
262
|
+
return;
|
|
263
|
+
try {
|
|
264
|
+
const watcher = fs.watch(agentDir, (eventType, filename) => {
|
|
265
|
+
if (!filename || this.shouldIgnore(filename))
|
|
266
|
+
return;
|
|
267
|
+
const filePath = path.join(agentDir, filename);
|
|
268
|
+
this.handleFileEvent(filePath, agentName, filename);
|
|
269
|
+
});
|
|
270
|
+
watcher.on('error', (err) => {
|
|
271
|
+
this.log(`Agent watcher error (${agentName}): ${err.message}`);
|
|
272
|
+
this.emit('watcher:overflow', agentDir);
|
|
273
|
+
// Remove failed watcher and trigger reconciliation
|
|
274
|
+
this.watchers.delete(agentDir);
|
|
275
|
+
this.fullReconciliation().catch(e => this.emit('error', e));
|
|
276
|
+
});
|
|
277
|
+
this.watchers.set(agentDir, watcher);
|
|
278
|
+
this.log(`Started agent watcher: ${agentDir}`);
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
this.log(`Failed to start agent watcher (${agentName}): ${err.message}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// ==========================================================================
|
|
285
|
+
// File Event Handling
|
|
286
|
+
// ==========================================================================
|
|
287
|
+
handleFileEvent(filePath, agentName, messageType) {
|
|
288
|
+
// Cancel any existing settle timer for this file
|
|
289
|
+
const existingTimer = this.pendingFiles.get(filePath);
|
|
290
|
+
if (existingTimer) {
|
|
291
|
+
clearTimeout(existingTimer);
|
|
292
|
+
}
|
|
293
|
+
// Start settle timer
|
|
294
|
+
const timer = setTimeout(() => {
|
|
295
|
+
this.pendingFiles.delete(filePath);
|
|
296
|
+
this.processDiscoveredFile(filePath, agentName, messageType).catch(err => {
|
|
297
|
+
this.emit('error', err);
|
|
298
|
+
});
|
|
299
|
+
}, this.config.settleTimeMs);
|
|
300
|
+
this.pendingFiles.set(filePath, timer);
|
|
301
|
+
}
|
|
302
|
+
async processDiscoveredFile(filePath, agentName, messageType) {
|
|
303
|
+
try {
|
|
304
|
+
// SECURITY: Check if the file itself is a symlink BEFORE resolving
|
|
305
|
+
// This prevents symlink attacks where an attacker creates a symlink
|
|
306
|
+
// pointing to a file outside the agent's outbox
|
|
307
|
+
try {
|
|
308
|
+
const lstats = await fs.promises.lstat(filePath);
|
|
309
|
+
if (lstats.isSymbolicLink()) {
|
|
310
|
+
this.log(`Rejected symlink file (security): ${filePath}`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// File doesn't exist - will be caught later
|
|
316
|
+
}
|
|
317
|
+
// Resolve symlinks in DIRECTORY path to get canonical path
|
|
318
|
+
// (for cloud workspaces where the outbox directory may be symlinked)
|
|
319
|
+
// Note: The file itself was already verified to NOT be a symlink above
|
|
320
|
+
let canonicalPath;
|
|
321
|
+
let originalPath;
|
|
322
|
+
try {
|
|
323
|
+
canonicalPath = await fs.promises.realpath(filePath);
|
|
324
|
+
// Only store original if it differs (directory was symlinked)
|
|
325
|
+
if (canonicalPath !== filePath) {
|
|
326
|
+
originalPath = filePath;
|
|
327
|
+
this.log(`Resolved directory symlink: ${filePath} -> ${canonicalPath}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
// File doesn't exist or can't resolve - use original
|
|
332
|
+
canonicalPath = filePath;
|
|
333
|
+
}
|
|
334
|
+
// Validate file exists and get stats (uses canonical path)
|
|
335
|
+
const validation = await this.validateFile(canonicalPath);
|
|
336
|
+
if (!validation.valid) {
|
|
337
|
+
this.log(`File validation failed (${canonicalPath}): ${validation.reason}`);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const stats = validation.stats;
|
|
341
|
+
// Check if already registered (by canonical path)
|
|
342
|
+
if (this.ledger.isFileRegistered(canonicalPath)) {
|
|
343
|
+
this.log(`File already registered: ${canonicalPath}`);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
// Calculate content hash for deduplication
|
|
347
|
+
const contentHash = await this.calculateFileHash(canonicalPath);
|
|
348
|
+
// Register in ledger with both paths
|
|
349
|
+
// sourcePath = canonical (resolved), symlinkPath = original (if symlinked)
|
|
350
|
+
const fileId = this.ledger.registerFile(canonicalPath, agentName, messageType, stats.size, contentHash, undefined, // fileMtimeNs
|
|
351
|
+
undefined, // fileInode
|
|
352
|
+
originalPath // symlinkPath (only set if it was a symlink)
|
|
353
|
+
);
|
|
354
|
+
const discoveredFile = {
|
|
355
|
+
path: canonicalPath,
|
|
356
|
+
agentName,
|
|
357
|
+
messageType,
|
|
358
|
+
size: stats.size,
|
|
359
|
+
mtime: stats.mtimeMs,
|
|
360
|
+
contentHash,
|
|
361
|
+
};
|
|
362
|
+
this.emit('file:discovered', discoveredFile);
|
|
363
|
+
this.log(`Discovered file: ${canonicalPath} (id: ${fileId})`);
|
|
364
|
+
// Attempt to process immediately
|
|
365
|
+
await this.processFile(fileId);
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
this.log(`Error processing discovered file (${filePath}): ${err.message}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// ==========================================================================
|
|
372
|
+
// File Validation
|
|
373
|
+
// ==========================================================================
|
|
374
|
+
async validateFile(filePath) {
|
|
375
|
+
try {
|
|
376
|
+
// Use lstat to detect symlinks (don't follow them)
|
|
377
|
+
const stats = await fs.promises.lstat(filePath);
|
|
378
|
+
// Reject symlinks (security)
|
|
379
|
+
if (stats.isSymbolicLink()) {
|
|
380
|
+
return { valid: false, reason: 'Symlinks not allowed' };
|
|
381
|
+
}
|
|
382
|
+
// Must be a regular file
|
|
383
|
+
if (!stats.isFile()) {
|
|
384
|
+
return { valid: false, reason: 'Not a regular file' };
|
|
385
|
+
}
|
|
386
|
+
// Skip 0-byte files
|
|
387
|
+
if (stats.size === 0) {
|
|
388
|
+
return { valid: false, reason: 'Empty file (0 bytes)' };
|
|
389
|
+
}
|
|
390
|
+
// Check size limits
|
|
391
|
+
if (stats.size > this.config.maxMessageSizeBytes) {
|
|
392
|
+
return { valid: false, reason: `File too large (${stats.size} > ${this.config.maxMessageSizeBytes})` };
|
|
393
|
+
}
|
|
394
|
+
// Re-stat to check stability (file size hasn't changed)
|
|
395
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
396
|
+
const stats2 = await fs.promises.lstat(filePath);
|
|
397
|
+
if (stats.size !== stats2.size || stats.mtimeMs !== stats2.mtimeMs) {
|
|
398
|
+
return { valid: false, reason: 'File still being written' };
|
|
399
|
+
}
|
|
400
|
+
return { valid: true, stats };
|
|
401
|
+
}
|
|
402
|
+
catch (err) {
|
|
403
|
+
if (err.code === 'ENOENT') {
|
|
404
|
+
return { valid: false, reason: 'File does not exist' };
|
|
405
|
+
}
|
|
406
|
+
return { valid: false, reason: err.message };
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
async calculateFileHash(filePath) {
|
|
410
|
+
const content = await fs.promises.readFile(filePath);
|
|
411
|
+
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
412
|
+
}
|
|
413
|
+
// ==========================================================================
|
|
414
|
+
// File Processing
|
|
415
|
+
// ==========================================================================
|
|
416
|
+
async processFile(fileId) {
|
|
417
|
+
// Atomically claim the file
|
|
418
|
+
const claimResult = this.ledger.claimFile(fileId);
|
|
419
|
+
if (!claimResult.success) {
|
|
420
|
+
this.log(`Failed to claim file ${fileId}: ${claimResult.reason}`);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const record = claimResult.record;
|
|
424
|
+
this.emit('file:processing', record);
|
|
425
|
+
try {
|
|
426
|
+
// Read file content
|
|
427
|
+
const content = await fs.promises.readFile(record.sourcePath, 'utf-8');
|
|
428
|
+
// Parse headers and body
|
|
429
|
+
const { headers, body } = this.parseFileContent(content);
|
|
430
|
+
const processedFile = {
|
|
431
|
+
fileId: record.fileId,
|
|
432
|
+
agentName: record.agentName,
|
|
433
|
+
messageType: record.messageType,
|
|
434
|
+
content,
|
|
435
|
+
headers,
|
|
436
|
+
body,
|
|
437
|
+
};
|
|
438
|
+
// Mark as delivered
|
|
439
|
+
this.ledger.markDelivered(fileId);
|
|
440
|
+
this.emit('file:delivered', processedFile);
|
|
441
|
+
// Archive the file
|
|
442
|
+
await this.archiveFile(record);
|
|
443
|
+
this.log(`Processed file: ${record.sourcePath} (id: ${fileId})`);
|
|
444
|
+
}
|
|
445
|
+
catch (err) {
|
|
446
|
+
this.log(`Error processing file ${fileId}: ${err.message}`);
|
|
447
|
+
this.ledger.markFailed(fileId, err.message);
|
|
448
|
+
this.emit('file:failed', record, err);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
parseFileContent(content) {
|
|
452
|
+
const headers = {};
|
|
453
|
+
const lines = content.split('\n');
|
|
454
|
+
let bodyStartIndex = 0;
|
|
455
|
+
// Parse headers until empty line
|
|
456
|
+
for (let i = 0; i < lines.length; i++) {
|
|
457
|
+
const line = lines[i];
|
|
458
|
+
// Empty line marks end of headers
|
|
459
|
+
if (line.trim() === '') {
|
|
460
|
+
bodyStartIndex = i + 1;
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
// Parse header: "KEY: value"
|
|
464
|
+
const colonIndex = line.indexOf(':');
|
|
465
|
+
if (colonIndex > 0) {
|
|
466
|
+
const key = line.slice(0, colonIndex).trim().toUpperCase();
|
|
467
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
468
|
+
headers[key] = value;
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
// No colon found, treat rest as body
|
|
472
|
+
bodyStartIndex = i;
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const body = lines.slice(bodyStartIndex).join('\n').trim();
|
|
477
|
+
return { headers, body };
|
|
478
|
+
}
|
|
479
|
+
// ==========================================================================
|
|
480
|
+
// File Archiving
|
|
481
|
+
// ==========================================================================
|
|
482
|
+
async archiveFile(record) {
|
|
483
|
+
const archiveDir = path.join(this.relayPaths.rootDir, 'archive', record.agentName, new Date().toISOString().slice(0, 10) // YYYY-MM-DD
|
|
484
|
+
);
|
|
485
|
+
await fs.promises.mkdir(archiveDir, { recursive: true });
|
|
486
|
+
const archivePath = path.join(archiveDir, `${record.fileId}-${record.messageType}`);
|
|
487
|
+
try {
|
|
488
|
+
// Move file to archive
|
|
489
|
+
await fs.promises.rename(record.sourcePath, archivePath);
|
|
490
|
+
this.ledger.markArchived(record.fileId, archivePath);
|
|
491
|
+
this.emit('file:archived', record, archivePath);
|
|
492
|
+
}
|
|
493
|
+
catch (err) {
|
|
494
|
+
// If rename fails (cross-device), copy and delete
|
|
495
|
+
if (err.code === 'EXDEV') {
|
|
496
|
+
await fs.promises.copyFile(record.sourcePath, archivePath);
|
|
497
|
+
await fs.promises.unlink(record.sourcePath);
|
|
498
|
+
this.ledger.markArchived(record.fileId, archivePath);
|
|
499
|
+
this.emit('file:archived', record, archivePath);
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
throw err;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
// ==========================================================================
|
|
507
|
+
// Reconciliation
|
|
508
|
+
// ==========================================================================
|
|
509
|
+
async fullReconciliation() {
|
|
510
|
+
const outboxDir = this.relayPaths.outboxDir;
|
|
511
|
+
let discovered = 0;
|
|
512
|
+
let failed = 0;
|
|
513
|
+
if (!fs.existsSync(outboxDir)) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
try {
|
|
517
|
+
// Scan all agent directories
|
|
518
|
+
const agents = await fs.promises.readdir(outboxDir, { withFileTypes: true });
|
|
519
|
+
for (const agent of agents) {
|
|
520
|
+
if (!agent.isDirectory() || this.shouldIgnore(agent.name))
|
|
521
|
+
continue;
|
|
522
|
+
const agentDir = path.join(outboxDir, agent.name);
|
|
523
|
+
// Ensure watcher exists for this agent
|
|
524
|
+
if (!this.watchers.has(agentDir)) {
|
|
525
|
+
this.startAgentWatcher(agentDir, agent.name);
|
|
526
|
+
}
|
|
527
|
+
// Scan agent's outbox
|
|
528
|
+
try {
|
|
529
|
+
const files = await fs.promises.readdir(agentDir, { withFileTypes: true });
|
|
530
|
+
for (const file of files) {
|
|
531
|
+
if (!file.isFile() || this.shouldIgnore(file.name))
|
|
532
|
+
continue;
|
|
533
|
+
const filePath = path.join(agentDir, file.name);
|
|
534
|
+
// Skip if already registered
|
|
535
|
+
if (this.ledger.isFileRegistered(filePath))
|
|
536
|
+
continue;
|
|
537
|
+
try {
|
|
538
|
+
await this.processDiscoveredFile(filePath, agent.name, file.name);
|
|
539
|
+
discovered++;
|
|
540
|
+
}
|
|
541
|
+
catch {
|
|
542
|
+
failed++;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
catch (err) {
|
|
547
|
+
this.log(`Failed to scan agent directory (${agent.name}): ${err.message}`);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
// Process any pending files from ledger
|
|
551
|
+
const pendingFiles = this.ledger.getPendingFiles();
|
|
552
|
+
for (const record of pendingFiles) {
|
|
553
|
+
await this.processFile(record.fileId);
|
|
554
|
+
}
|
|
555
|
+
this.emit('reconcile:complete', { discovered, failed });
|
|
556
|
+
}
|
|
557
|
+
catch (err) {
|
|
558
|
+
this.log(`Reconciliation error: ${err.message}`);
|
|
559
|
+
this.emit('error', err);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
// ==========================================================================
|
|
563
|
+
// Cleanup
|
|
564
|
+
// ==========================================================================
|
|
565
|
+
async cleanupOrphanedFiles() {
|
|
566
|
+
const outboxDir = this.relayPaths.outboxDir;
|
|
567
|
+
const now = Date.now();
|
|
568
|
+
if (!fs.existsSync(outboxDir))
|
|
569
|
+
return;
|
|
570
|
+
try {
|
|
571
|
+
const agents = await fs.promises.readdir(outboxDir, { withFileTypes: true });
|
|
572
|
+
for (const agent of agents) {
|
|
573
|
+
if (!agent.isDirectory())
|
|
574
|
+
continue;
|
|
575
|
+
const agentDir = path.join(outboxDir, agent.name);
|
|
576
|
+
const files = await fs.promises.readdir(agentDir);
|
|
577
|
+
for (const file of files) {
|
|
578
|
+
// Clean up .pending files older than threshold
|
|
579
|
+
if (file.endsWith('.pending')) {
|
|
580
|
+
const filePath = path.join(agentDir, file);
|
|
581
|
+
try {
|
|
582
|
+
const stats = await fs.promises.stat(filePath);
|
|
583
|
+
if (now - stats.mtimeMs > this.config.orphanedPendingAgeMs) {
|
|
584
|
+
await fs.promises.unlink(filePath);
|
|
585
|
+
this.log(`Cleaned up orphaned .pending file: ${filePath}`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
catch {
|
|
589
|
+
// File may have been deleted
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
catch (err) {
|
|
596
|
+
this.log(`Cleanup error: ${err.message}`);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
// ==========================================================================
|
|
600
|
+
// Utilities
|
|
601
|
+
// ==========================================================================
|
|
602
|
+
shouldIgnore(filename) {
|
|
603
|
+
return IGNORE_PATTERNS.some(pattern => pattern.test(filename));
|
|
604
|
+
}
|
|
605
|
+
log(message) {
|
|
606
|
+
if (this.config.debug) {
|
|
607
|
+
console.log(`[relay-watchdog] ${message}`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
//# sourceMappingURL=relay-watchdog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay-watchdog.js","sourceRoot":"","sources":["../src/relay-watchdog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,WAAW,EAA2C,MAAM,mBAAmB,CAAC;AACzF,OAAO,EAAE,iBAAiB,EAAmB,MAAM,uCAAuC,CAAC;AA4D3F,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E,MAAM,sBAAsB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,KAAK,EAAE,EAAE,CAAC,CAAC;AACvF,MAAM,4BAA4B,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,OAAO,EAAE,EAAE,CAAC,CAAC;AACrG,MAAM,6BAA6B,GAAG,KAAK,CAAC;AAC5C,MAAM,8BAA8B,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,MAAM;AAC1D,MAAM,iCAAiC,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AACnE,MAAM,2BAA2B,GAAG,KAAK,CAAC;AAC1C,MAAM,+BAA+B,GAAG,KAAK,CAAC;AAC9C,MAAM,4BAA4B,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,SAAS;AAEvE,8BAA8B;AAC9B,MAAM,eAAe,GAAG;IACtB,KAAK,EAAY,eAAe;IAChC,aAAa,EAAI,iBAAiB;IAClC,QAAQ,EAAS,aAAa;IAC9B,IAAI,EAAa,iBAAiB;CACnC,CAAC;AAEF,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E,MAAM,OAAO,aAAc,SAAQ,YAAY;IACrC,MAAM,CAA2B;IACjC,UAAU,CAAa;IACvB,MAAM,CAAc;IACpB,QAAQ,GAA8B,IAAI,GAAG,EAAE,CAAC;IAChD,YAAY,GAAgC,IAAI,GAAG,EAAE,CAAC;IACtD,cAAc,CAAkB;IAChC,YAAY,CAAkB;IAC9B,OAAO,GAAG,KAAK,CAAC;IAExB,YAAY,SAAyB,EAAE;QACrC,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,iBAAiB,EAAE,CAAC;QAE3D,IAAI,CAAC,MAAM,GAAG;YACZ,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,eAAe,CAAC;YACpF,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,sBAAsB;YAC3D,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,IAAI,4BAA4B;YAC7E,mBAAmB,EAAE,MAAM,CAAC,mBAAmB,IAAI,6BAA6B;YAChF,mBAAmB,EAAE,MAAM,CAAC,mBAAmB,IAAI,8BAA8B;YACjF,sBAAsB,EAAE,MAAM,CAAC,sBAAsB,IAAI,iCAAiC;YAC1F,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,IAAI,2BAA2B;YAC1E,oBAAoB,EAAE,MAAM,CAAC,oBAAoB,IAAI,+BAA+B;YACpF,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,IAAI,4BAA4B;YAC7E,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,KAAK;SAC7B,CAAC;QAEF,oBAAoB;QACpB,IAAI,CAAC,MAAM,GAAG,IAAI,WAAW,CAAC;YAC5B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU;YAC9B,kBAAkB,EAAE,IAAI,CAAC,MAAM,CAAC,kBAAkB;SACnD,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,iBAAiB;QAC7B,MAAM,WAAW,GAAG,KAAK,EAAE,CAAS,EAAmB,EAAE;YACvD,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC/C,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;oBACnB,IAAI,CAAC,GAAG,CAAC,qBAAqB,CAAC,OAAO,QAAQ,EAAE,CAAC,CAAC;gBACpD,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,CAAC;YAAC,MAAM,CAAC;gBACP,wCAAwC;gBACxC,OAAO,CAAC,CAAC;YACX,CAAC;QACH,CAAC,CAAC;QAEF,4CAA4C;QAC5C,IAAI,CAAC,UAAU,GAAG;YAChB,OAAO,EAAE,MAAM,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YACnD,SAAS,EAAE,MAAM,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YACvD,cAAc,EAAE,MAAM,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC;YACjE,OAAO,EAAE,MAAM,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YACnD,eAAe,EAAE,MAAM,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC;SACpE,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAEpB,IAAI,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAEvC,yEAAyE;QACzE,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAE/B,2BAA2B;QAC3B,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAE/B,uDAAuD;QACvD,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC;QACtD,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,yBAAyB,UAAU,8BAA8B,CAAC,CAAC;QAC9E,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC,uBAAuB,EAAE,CAAC;QAC9D,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,GAAG,CAAC,0BAA0B,eAAe,CAAC,MAAM,0BAA0B,CAAC,CAAC;QACvF,CAAC;QAED,8BAA8B;QAC9B,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAEhC,oCAAoC;QACpC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,qCAAqC;QACrC,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAEhC,gCAAgC;QAChC,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE;YACrC,IAAI,CAAC,kBAAkB,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;gBACpC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAC1B,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;QACpC,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAE5B,sBAAsB;QACtB,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE;YACnC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC5B,IAAI,CAAC,MAAM,CAAC,sBAAsB,EAAE,CAAC;QACvC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAClC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAE1B,IAAI,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QAErB,IAAI,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAEvC,eAAe;QACf,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QAClC,CAAC;QACD,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACjC,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;QAChC,CAAC;QAED,4BAA4B;QAC5B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC;YAC/C,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAE1B,qBAAqB;QACrB,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC3C,OAAO,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,GAAG,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QAEtB,eAAe;QACf,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAEpB,IAAI,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,eAAe;QACb,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACxF,CAAC;IAED,6EAA6E;IAC7E,uBAAuB;IACvB,6EAA6E;IAErE,KAAK,CAAC,iBAAiB;QAC7B,MAAM,IAAI,GAAG;YACX,IAAI,CAAC,UAAU,CAAC,SAAS;YACzB,IAAI,CAAC,UAAU,CAAC,cAAc;YAC9B,IAAI,CAAC,UAAU,CAAC,OAAO;YACvB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC;SAC9C,CAAC;QAEF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAClD,IAAI,CAAC,GAAG,CAAC,sBAAsB,GAAG,EAAE,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,gBAAgB;IAChB,6EAA6E;IAErE,gBAAgB;QACtB,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAE5C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,uCAAuC,SAAS,EAAE,CAAC,CAAC;YAC7D,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE;gBAC1D,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;oBAAE,OAAO;gBAErD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;gBAEhD,2CAA2C;gBAC3C,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;oBAC3B,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBACjD,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC1B,IAAI,CAAC,GAAG,CAAC,uBAAuB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC/C,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;gBACzC,+CAA+C;gBAC/C,IAAI,CAAC,kBAAkB,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;YAC9D,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YACtC,IAAI,CAAC,GAAG,CAAC,yBAAyB,SAAS,EAAE,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,iCAAiC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,kBAAkB;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAE5C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO;QAEtC,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAE9E,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAClD,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;IACH,CAAC;IAEO,qBAAqB,CAAC,QAAgB,EAAE,SAAiB;QAC/D,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACpC,IAAI,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACxD,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,+CAA+C;QACjD,CAAC;IACH,CAAC;IAEO,iBAAiB,CAAC,QAAgB,EAAE,SAAiB;QAC3D,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO;QAExC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE;gBACzD,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;oBAAE,OAAO;gBAErD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBAC/C,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;YACtD,CAAC,CAAC,CAAC;YAEH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC1B,IAAI,CAAC,GAAG,CAAC,wBAAwB,SAAS,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC/D,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;gBACxC,mDAAmD;gBACnD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAC/B,IAAI,CAAC,kBAAkB,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;YAC9D,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACrC,IAAI,CAAC,GAAG,CAAC,0BAA0B,QAAQ,EAAE,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,kCAAkC,SAAS,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,sBAAsB;IACtB,6EAA6E;IAErE,eAAe,CAAC,QAAgB,EAAE,SAAiB,EAAE,WAAmB;QAC9E,iDAAiD;QACjD,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACtD,IAAI,aAAa,EAAE,CAAC;YAClB,YAAY,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;QAED,qBAAqB;QACrB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACnC,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;gBACvE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAC1B,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAE7B,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,qBAAqB,CACjC,QAAgB,EAChB,SAAiB,EACjB,WAAmB;QAEnB,IAAI,CAAC;YACH,mEAAmE;YACnE,oEAAoE;YACpE,gDAAgD;YAChD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBACjD,IAAI,MAAM,CAAC,cAAc,EAAE,EAAE,CAAC;oBAC5B,IAAI,CAAC,GAAG,CAAC,qCAAqC,QAAQ,EAAE,CAAC,CAAC;oBAC1D,OAAO;gBACT,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,4CAA4C;YAC9C,CAAC;YAED,2DAA2D;YAC3D,qEAAqE;YACrE,uEAAuE;YACvE,IAAI,aAAqB,CAAC;YAC1B,IAAI,YAAgC,CAAC;YACrC,IAAI,CAAC;gBACH,aAAa,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBACrD,8DAA8D;gBAC9D,IAAI,aAAa,KAAK,QAAQ,EAAE,CAAC;oBAC/B,YAAY,GAAG,QAAQ,CAAC;oBACxB,IAAI,CAAC,GAAG,CAAC,+BAA+B,QAAQ,OAAO,aAAa,EAAE,CAAC,CAAC;gBAC1E,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,qDAAqD;gBACrD,aAAa,GAAG,QAAQ,CAAC;YAC3B,CAAC;YAED,2DAA2D;YAC3D,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;YAC1D,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBACtB,IAAI,CAAC,GAAG,CAAC,2BAA2B,aAAa,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC5E,OAAO;YACT,CAAC;YAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAM,CAAC;YAEhC,kDAAkD;YAClD,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,aAAa,CAAC,EAAE,CAAC;gBAChD,IAAI,CAAC,GAAG,CAAC,4BAA4B,aAAa,EAAE,CAAC,CAAC;gBACtD,OAAO;YACT,CAAC;YAED,2CAA2C;YAC3C,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;YAEhE,qCAAqC;YACrC,2EAA2E;YAC3E,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CACrC,aAAa,EACb,SAAS,EACT,WAAW,EACX,KAAK,CAAC,IAAI,EACV,WAAW,EACX,SAAS,EAAE,cAAc;YACzB,SAAS,EAAE,YAAY;YACvB,YAAY,CAAC,6CAA6C;aAC3D,CAAC;YAEF,MAAM,cAAc,GAAmB;gBACrC,IAAI,EAAE,aAAa;gBACnB,SAAS;gBACT,WAAW;gBACX,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,KAAK,EAAE,KAAK,CAAC,OAAO;gBACpB,WAAW;aACZ,CAAC;YAEF,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;YAC7C,IAAI,CAAC,GAAG,CAAC,oBAAoB,aAAa,SAAS,MAAM,GAAG,CAAC,CAAC;YAE9D,iCAAiC;YACjC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,qCAAqC,QAAQ,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,kBAAkB;IAClB,6EAA6E;IAErE,KAAK,CAAC,YAAY,CACxB,QAAgB;QAEhB,IAAI,CAAC;YACH,mDAAmD;YACnD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAEhD,6BAA6B;YAC7B,IAAI,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC3B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;YAC1D,CAAC;YAED,yBAAyB;YACzB,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;gBACpB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;YACxD,CAAC;YAED,oBAAoB;YACpB,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACrB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;YAC1D,CAAC;YAED,oBAAoB;YACpB,IAAI,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;gBACjD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,KAAK,CAAC,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,mBAAmB,GAAG,EAAE,CAAC;YACzG,CAAC;YAED,wDAAwD;YACxD,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;YACtD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAEjD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,EAAE,CAAC;YAC9D,CAAC;YAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAChC,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC1B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;YACzD,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;QAC/C,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAAC,QAAgB;QAC9C,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrD,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,6EAA6E;IAC7E,kBAAkB;IAClB,6EAA6E;IAErE,KAAK,CAAC,WAAW,CAAC,MAAc;QACtC,4BAA4B;QAC5B,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAElD,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;YACzB,IAAI,CAAC,GAAG,CAAC,wBAAwB,MAAM,KAAK,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;YAClE,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,WAAW,CAAC,MAAO,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QAErC,IAAI,CAAC;YACH,oBAAoB;YACpB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAEvE,yBAAyB;YACzB,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAEzD,MAAM,aAAa,GAAkB;gBACnC,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,OAAO;gBACP,OAAO;gBACP,IAAI;aACL,CAAC;YAEF,oBAAoB;YACpB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAClC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;YAE3C,mBAAmB;YACnB,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAE/B,IAAI,CAAC,GAAG,CAAC,mBAAmB,MAAM,CAAC,UAAU,SAAS,MAAM,GAAG,CAAC,CAAC;QACnE,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,yBAAyB,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAC5C,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAEO,gBAAgB,CAAC,OAAe;QACtC,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,cAAc,GAAG,CAAC,CAAC;QAEvB,iCAAiC;QACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAEtB,kCAAkC;YAClC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBACvB,cAAc,GAAG,CAAC,GAAG,CAAC,CAAC;gBACvB,MAAM;YACR,CAAC;YAED,6BAA6B;YAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACrC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;gBACnB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;gBAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAChD,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACN,qCAAqC;gBACrC,cAAc,GAAG,CAAC,CAAC;gBACnB,MAAM;YACR,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAE3D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,6EAA6E;IAC7E,iBAAiB;IACjB,6EAA6E;IAErE,KAAK,CAAC,WAAW,CAAC,MAAuB;QAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAC1B,IAAI,CAAC,UAAU,CAAC,OAAO,EACvB,SAAS,EACT,MAAM,CAAC,SAAS,EAChB,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa;SACpD,CAAC;QAEF,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEzD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QAEpF,IAAI,CAAC;YACH,uBAAuB;YACvB,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;YACzD,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;YACrD,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,kDAAkD;YAClD,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBACzB,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;gBAC3D,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gBAC5C,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;gBACrD,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;YAClD,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,iBAAiB;IACjB,6EAA6E;IAErE,KAAK,CAAC,kBAAkB;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAC5C,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,MAAM,GAAG,CAAC,CAAC;QAEf,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,6BAA6B;YAC7B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YAE7E,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAEpE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAElD,uCAAuC;gBACvC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACjC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC/C,CAAC;gBAED,sBAAsB;gBACtB,IAAI,CAAC;oBACH,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;oBAE3E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;wBACzB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;4BAAE,SAAS;wBAE7D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;wBAEhD,6BAA6B;wBAC7B,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC;4BAAE,SAAS;wBAErD,IAAI,CAAC;4BACH,MAAM,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;4BAClE,UAAU,EAAE,CAAC;wBACf,CAAC;wBAAC,MAAM,CAAC;4BACP,MAAM,EAAE,CAAC;wBACX,CAAC;oBACH,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAQ,EAAE,CAAC;oBAClB,IAAI,CAAC,GAAG,CAAC,mCAAmC,KAAK,CAAC,IAAI,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC7E,CAAC;YACH,CAAC;YAED,wCAAwC;YACxC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YACnD,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;gBAClC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACxC,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,yBAAyB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACjD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,UAAU;IACV,6EAA6E;IAErE,KAAK,CAAC,oBAAoB;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO;QAEtC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YAE7E,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;oBAAE,SAAS;gBAEnC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAClD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAElD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,+CAA+C;oBAC/C,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;wBAC3C,IAAI,CAAC;4BACH,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;4BAC/C,IAAI,GAAG,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC;gCAC3D,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gCACnC,IAAI,CAAC,GAAG,CAAC,sCAAsC,QAAQ,EAAE,CAAC,CAAC;4BAC7D,CAAC;wBACH,CAAC;wBAAC,MAAM,CAAC;4BACP,6BAA6B;wBAC/B,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,kBAAkB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,YAAY;IACZ,6EAA6E;IAErE,YAAY,CAAC,QAAgB;QACnC,OAAO,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjE,CAAC;IAEO,GAAG,CAAC,OAAe;QACzB,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;CACF"}
|