ultraclaude-agent 0.0.3
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/__tests__/config-windows.test.ts +93 -0
- package/__tests__/daemon.test.ts +166 -0
- package/__tests__/service-windows.test.ts +123 -0
- package/__tests__/sync-bugs.test.ts +246 -0
- package/__tests__/sync.test.ts +169 -0
- package/__tests__/usage-sync.test.ts +291 -0
- package/__tests__/version-check.test.ts +128 -0
- package/dist/auth.d.ts +10 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +105 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +196 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +181 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon.d.ts +27 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +214 -0
- package/dist/daemon.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +37 -0
- package/dist/logger.js.map +1 -0
- package/dist/service.d.ts +4 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +223 -0
- package/dist/service.js.map +1 -0
- package/dist/sync.d.ts +25 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +344 -0
- package/dist/sync.js.map +1 -0
- package/dist/usage-sync.d.ts +7 -0
- package/dist/usage-sync.d.ts.map +1 -0
- package/dist/usage-sync.js +208 -0
- package/dist/usage-sync.js.map +1 -0
- package/dist/watcher.d.ts +16 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +90 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +31 -0
- package/run.sh +28 -0
- package/src/auth.ts +127 -0
- package/src/cli.ts +235 -0
- package/src/config.ts +207 -0
- package/src/daemon.ts +264 -0
- package/src/index.ts +7 -0
- package/src/logger.ts +42 -0
- package/src/service.ts +237 -0
- package/src/sync.ts +473 -0
- package/src/usage-sync.ts +275 -0
- package/src/watcher.ts +106 -0
- package/tsconfig.build.json +6 -0
- package/tsconfig.json +8 -0
- package/tsconfig.tsbuildinfo +1 -0
package/dist/sync.js
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
// HTTP sync client: push sections/files to server, fetch manifest, manage offline queue
|
|
2
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
3
|
+
import { join, relative } from 'node:path';
|
|
4
|
+
import { parseMarkdownSections, computeContentHash, ok, err, } from '@ultra-claude/shared';
|
|
5
|
+
import { loadCredentials, getServerUrl } from './config.js';
|
|
6
|
+
import { logger } from './logger.js';
|
|
7
|
+
const QUEUE_CAP = 1000;
|
|
8
|
+
const RETRY_INTERVAL_MS = 5_000;
|
|
9
|
+
const MAX_FILE_SIZE = 1_000_000; // 1MB
|
|
10
|
+
// --- Module state ---
|
|
11
|
+
const queue = [];
|
|
12
|
+
const projectStates = new Map();
|
|
13
|
+
let retryTimer = null;
|
|
14
|
+
function getOrCreateState(projectId) {
|
|
15
|
+
let state = projectStates.get(projectId);
|
|
16
|
+
if (!state) {
|
|
17
|
+
state = { sectionHashes: new Map(), fileHashes: new Map(), needsManifestResync: false };
|
|
18
|
+
projectStates.set(projectId, state);
|
|
19
|
+
}
|
|
20
|
+
return state;
|
|
21
|
+
}
|
|
22
|
+
export async function apiRequest(method, path, body, extraHeaders) {
|
|
23
|
+
const creds = await loadCredentials();
|
|
24
|
+
if (!creds)
|
|
25
|
+
return err('NOT_LOGGED_IN', 'Not logged in — run `ultraclaude-dashboard-agent login` first');
|
|
26
|
+
const url = `${getServerUrl(creds)}${path}`;
|
|
27
|
+
const headers = {
|
|
28
|
+
Authorization: `Bearer ${creds.apiKey}`,
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
...extraHeaders,
|
|
31
|
+
};
|
|
32
|
+
const response = await fetch(url, {
|
|
33
|
+
method,
|
|
34
|
+
headers,
|
|
35
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
36
|
+
}).catch(() => null);
|
|
37
|
+
if (!response)
|
|
38
|
+
return err('NETWORK_ERROR', `Failed to reach ${url}`);
|
|
39
|
+
return ok(response);
|
|
40
|
+
}
|
|
41
|
+
export async function fetchManifest(projectId) {
|
|
42
|
+
const log = logger.child({ projectId, op: 'fetchManifest' });
|
|
43
|
+
const result = await apiRequest('GET', `/api/sync/manifest?projectId=${projectId}`);
|
|
44
|
+
if (!result.success)
|
|
45
|
+
return result;
|
|
46
|
+
if (!result.data.ok) {
|
|
47
|
+
log.error({ status: result.data.status }, 'Failed to fetch manifest');
|
|
48
|
+
return err('MANIFEST_FETCH_FAILED', `Manifest fetch failed: ${result.data.status}`);
|
|
49
|
+
}
|
|
50
|
+
const body = (await result.data.json());
|
|
51
|
+
// Flatten the nested manifest into ManifestEntry[]
|
|
52
|
+
const entries = [];
|
|
53
|
+
for (const [filePath, file] of Object.entries(body.data.files)) {
|
|
54
|
+
for (const [headingSlug, contentHash] of Object.entries(file.sections)) {
|
|
55
|
+
if (contentHash) {
|
|
56
|
+
entries.push({ filePath, headingSlug, contentHash });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return ok(entries);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Load manifest from server and populate local section hash cache.
|
|
64
|
+
*/
|
|
65
|
+
export async function loadManifestIntoCache(projectId) {
|
|
66
|
+
const state = getOrCreateState(projectId);
|
|
67
|
+
const result = await fetchManifest(projectId);
|
|
68
|
+
if (!result.success) {
|
|
69
|
+
logger.warn({ projectId, error: result.error, message: result.message }, 'Could not load manifest — will push all sections');
|
|
70
|
+
state.needsManifestResync = true;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
state.sectionHashes.clear();
|
|
74
|
+
for (const entry of result.data) {
|
|
75
|
+
state.sectionHashes.set(`${entry.filePath}#${entry.headingSlug}`, entry.contentHash);
|
|
76
|
+
}
|
|
77
|
+
state.needsManifestResync = false;
|
|
78
|
+
logger.info({ projectId, entries: result.data.length }, 'Manifest loaded');
|
|
79
|
+
}
|
|
80
|
+
// --- Push sections ---
|
|
81
|
+
/**
|
|
82
|
+
* Parse a markdown file and push only changed sections to the server.
|
|
83
|
+
* Returns the number of sections pushed.
|
|
84
|
+
*/
|
|
85
|
+
export async function syncMarkdownFile(projectId, projectPath, absoluteFilePath) {
|
|
86
|
+
const log = logger.child({ projectId, file: absoluteFilePath });
|
|
87
|
+
const fileStat = await stat(absoluteFilePath);
|
|
88
|
+
if (fileStat.size > MAX_FILE_SIZE) {
|
|
89
|
+
log.debug('Skipping file >1MB');
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
const content = await readFile(absoluteFilePath, 'utf8');
|
|
93
|
+
const docDir = join(projectPath, 'documentation');
|
|
94
|
+
const relativePath = relative(docDir, absoluteFilePath);
|
|
95
|
+
const fileHash = computeContentHash(content);
|
|
96
|
+
const sections = parseMarkdownSections(content);
|
|
97
|
+
const state = getOrCreateState(projectId);
|
|
98
|
+
// Diff against cached hashes
|
|
99
|
+
const changedSections = [];
|
|
100
|
+
const currentSlugs = new Set();
|
|
101
|
+
for (const section of sections) {
|
|
102
|
+
const key = `${relativePath}#${section.slug}`;
|
|
103
|
+
currentSlugs.add(key);
|
|
104
|
+
const cached = state.sectionHashes.get(key);
|
|
105
|
+
if (cached !== section.contentHash) {
|
|
106
|
+
changedSections.push(section);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Find removed slugs (sections that existed before but are gone now)
|
|
110
|
+
const removedSlugs = [];
|
|
111
|
+
for (const key of state.sectionHashes.keys()) {
|
|
112
|
+
if (key.startsWith(`${relativePath}#`) && !currentSlugs.has(key)) {
|
|
113
|
+
removedSlugs.push(key.split('#')[1]);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (changedSections.length === 0 && removedSlugs.length === 0) {
|
|
117
|
+
log.debug('No section changes detected');
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
// When we only have removals but no changed sections, we must include at least
|
|
121
|
+
// one current section to satisfy the server schema (sections.min(1)).
|
|
122
|
+
// Include the first existing section as a no-op upsert alongside the removals.
|
|
123
|
+
let sectionsToSend = changedSections;
|
|
124
|
+
if (sectionsToSend.length === 0 && removedSlugs.length > 0 && sections.length > 0) {
|
|
125
|
+
sectionsToSend = [sections[0]];
|
|
126
|
+
}
|
|
127
|
+
const payload = {
|
|
128
|
+
projectId,
|
|
129
|
+
file: relativePath,
|
|
130
|
+
file_hash: fileHash,
|
|
131
|
+
sections: sectionsToSend.map((s) => ({
|
|
132
|
+
slug: s.slug,
|
|
133
|
+
heading: s.heading || null,
|
|
134
|
+
level: s.level || null,
|
|
135
|
+
position: s.position,
|
|
136
|
+
content: s.content,
|
|
137
|
+
content_hash: s.contentHash,
|
|
138
|
+
})),
|
|
139
|
+
removedSlugs: removedSlugs.length > 0 ? removedSlugs : undefined,
|
|
140
|
+
};
|
|
141
|
+
const idempotencyKey = crypto.randomUUID();
|
|
142
|
+
const result = await apiRequest('POST', '/api/sync/sections', payload, {
|
|
143
|
+
'Idempotency-Key': idempotencyKey,
|
|
144
|
+
});
|
|
145
|
+
if (!result.success || !result.data.ok) {
|
|
146
|
+
const reason = !result.success ? result.message : `HTTP ${result.data.status}`;
|
|
147
|
+
log.warn({ error: reason }, 'Push failed — queueing for retry');
|
|
148
|
+
enqueue({ type: 'sections', projectId, filePath: relativePath, payload: JSON.stringify(payload), idempotencyKey });
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
// Update cache
|
|
152
|
+
for (const section of changedSections) {
|
|
153
|
+
state.sectionHashes.set(`${relativePath}#${section.slug}`, section.contentHash);
|
|
154
|
+
}
|
|
155
|
+
for (const slug of removedSlugs) {
|
|
156
|
+
state.sectionHashes.delete(`${relativePath}#${slug}`);
|
|
157
|
+
}
|
|
158
|
+
log.info({ changed: changedSections.length, removed: removedSlugs.length }, 'Sections pushed');
|
|
159
|
+
return changedSections.length;
|
|
160
|
+
}
|
|
161
|
+
// --- Push JSON files ---
|
|
162
|
+
export async function syncJsonFile(projectId, projectPath, absoluteFilePath) {
|
|
163
|
+
const log = logger.child({ projectId, file: absoluteFilePath });
|
|
164
|
+
const fileStat = await stat(absoluteFilePath);
|
|
165
|
+
if (fileStat.size > MAX_FILE_SIZE) {
|
|
166
|
+
log.debug('Skipping file >1MB');
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
const content = await readFile(absoluteFilePath, 'utf8');
|
|
170
|
+
const docDir = join(projectPath, 'documentation');
|
|
171
|
+
const relativePath = relative(docDir, absoluteFilePath);
|
|
172
|
+
const hash = computeContentHash(content);
|
|
173
|
+
const state = getOrCreateState(projectId);
|
|
174
|
+
if (state.fileHashes.get(relativePath) === hash) {
|
|
175
|
+
log.debug('No file changes detected');
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
const payload = {
|
|
179
|
+
projectId,
|
|
180
|
+
files: [
|
|
181
|
+
{
|
|
182
|
+
path: relativePath,
|
|
183
|
+
content,
|
|
184
|
+
hash,
|
|
185
|
+
size: fileStat.size,
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
};
|
|
189
|
+
const idempotencyKey = crypto.randomUUID();
|
|
190
|
+
const result = await apiRequest('POST', '/api/sync/files', payload, {
|
|
191
|
+
'Idempotency-Key': idempotencyKey,
|
|
192
|
+
});
|
|
193
|
+
if (!result.success || !result.data.ok) {
|
|
194
|
+
const reason = !result.success ? result.message : `HTTP ${result.data.status}`;
|
|
195
|
+
log.warn({ error: reason }, 'File push failed — queueing for retry');
|
|
196
|
+
enqueue({ type: 'file', projectId, filePath: relativePath, payload: JSON.stringify(payload), idempotencyKey });
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
state.fileHashes.set(relativePath, hash);
|
|
200
|
+
log.info('File pushed');
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
// --- Sync a single file (auto-detect type) ---
|
|
204
|
+
export async function syncFile(projectId, projectPath, absoluteFilePath) {
|
|
205
|
+
if (absoluteFilePath.endsWith('.md')) {
|
|
206
|
+
await syncMarkdownFile(projectId, projectPath, absoluteFilePath);
|
|
207
|
+
}
|
|
208
|
+
else if (absoluteFilePath.endsWith('.json')) {
|
|
209
|
+
await syncJsonFile(projectId, projectPath, absoluteFilePath);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// --- Project creation ---
|
|
213
|
+
export async function createProjectOnServer(name, slug) {
|
|
214
|
+
const log = logger.child({ op: 'createProject', name });
|
|
215
|
+
const idempotencyKey = crypto.randomUUID();
|
|
216
|
+
const result = await apiRequest('POST', '/api/projects', { name, slug }, {
|
|
217
|
+
'Idempotency-Key': idempotencyKey,
|
|
218
|
+
});
|
|
219
|
+
if (!result.success) {
|
|
220
|
+
log.error({ error: result.error, message: result.message }, 'Failed to create project');
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
if (!result.data.ok) {
|
|
224
|
+
log.error({ status: result.data.status }, 'Failed to create project');
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const data = (await result.data.json());
|
|
228
|
+
log.info({ projectId: data.data.id }, 'Project created on server');
|
|
229
|
+
return data.data;
|
|
230
|
+
}
|
|
231
|
+
// --- Snapshots ---
|
|
232
|
+
export async function createSnapshot(projectId, name, commitHash, trigger = 'manual') {
|
|
233
|
+
const log = logger.child({ projectId, op: 'snapshot' });
|
|
234
|
+
const result = await apiRequest('POST', '/api/sync/snapshot', {
|
|
235
|
+
projectId,
|
|
236
|
+
name,
|
|
237
|
+
commitHash,
|
|
238
|
+
trigger,
|
|
239
|
+
});
|
|
240
|
+
if (!result.success) {
|
|
241
|
+
log.error({ error: result.error, message: result.message }, 'Failed to create snapshot');
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
if (!result.data.ok) {
|
|
245
|
+
log.error({ status: result.data.status }, 'Failed to create snapshot');
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
log.info({ name }, 'Snapshot created');
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
// --- Initial sync for a project ---
|
|
252
|
+
export async function initialSync(projectId, projectPath) {
|
|
253
|
+
const log = logger.child({ projectId, op: 'initialSync' });
|
|
254
|
+
log.info('Starting initial sync');
|
|
255
|
+
await loadManifestIntoCache(projectId);
|
|
256
|
+
// Walk documentation/ and sync all files
|
|
257
|
+
const { glob } = await import('node:fs/promises');
|
|
258
|
+
const docDir = join(projectPath, 'documentation');
|
|
259
|
+
try {
|
|
260
|
+
for await (const entry of glob(join(docDir, '**/*'), {})) {
|
|
261
|
+
const fileStat = await stat(entry).catch(() => null);
|
|
262
|
+
if (!fileStat?.isFile())
|
|
263
|
+
continue;
|
|
264
|
+
if (fileStat.size > MAX_FILE_SIZE)
|
|
265
|
+
continue;
|
|
266
|
+
await syncFile(projectId, projectPath, entry);
|
|
267
|
+
}
|
|
268
|
+
log.info('Initial sync complete');
|
|
269
|
+
}
|
|
270
|
+
catch (err) {
|
|
271
|
+
log.warn({ err }, 'Initial sync incomplete — will catch up via watcher');
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// --- Memory queue ---
|
|
275
|
+
function enqueue(item) {
|
|
276
|
+
if (queue.length >= QUEUE_CAP) {
|
|
277
|
+
const dropped = queue.shift(); // Drop oldest
|
|
278
|
+
// Flag the DROPPED item's project for resync — that's the project that lost data
|
|
279
|
+
const droppedState = projectStates.get(dropped.projectId);
|
|
280
|
+
if (droppedState)
|
|
281
|
+
droppedState.needsManifestResync = true;
|
|
282
|
+
logger.warn({ droppedProject: dropped.projectId }, 'Queue overflow — dropped oldest item, manifest resync needed');
|
|
283
|
+
}
|
|
284
|
+
queue.push(item);
|
|
285
|
+
ensureRetryTimer();
|
|
286
|
+
}
|
|
287
|
+
async function flushQueue() {
|
|
288
|
+
if (queue.length === 0)
|
|
289
|
+
return;
|
|
290
|
+
const log = logger.child({ op: 'flushQueue', queueSize: queue.length });
|
|
291
|
+
log.info('Flushing offline queue');
|
|
292
|
+
// Check if any projects need manifest resync
|
|
293
|
+
for (const [projectId, state] of projectStates) {
|
|
294
|
+
if (state.needsManifestResync) {
|
|
295
|
+
await loadManifestIntoCache(projectId);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// Try to send queued items
|
|
299
|
+
const remaining = [];
|
|
300
|
+
for (const item of queue) {
|
|
301
|
+
const endpoint = item.type === 'sections' ? '/api/sync/sections' : '/api/sync/files';
|
|
302
|
+
const result = await apiRequest('POST', endpoint, JSON.parse(item.payload), {
|
|
303
|
+
'Idempotency-Key': item.idempotencyKey,
|
|
304
|
+
});
|
|
305
|
+
if (!result.success) {
|
|
306
|
+
if (result.error === 'NOT_LOGGED_IN')
|
|
307
|
+
break;
|
|
308
|
+
remaining.push(item);
|
|
309
|
+
break; // Network error — server still down, stop trying
|
|
310
|
+
}
|
|
311
|
+
if (!result.data.ok && result.data.status >= 500) {
|
|
312
|
+
remaining.push(item);
|
|
313
|
+
}
|
|
314
|
+
// 4xx errors are dropped (permanent failures)
|
|
315
|
+
}
|
|
316
|
+
queue.length = 0;
|
|
317
|
+
queue.push(...remaining);
|
|
318
|
+
if (queue.length === 0) {
|
|
319
|
+
stopRetryTimer();
|
|
320
|
+
log.info('Queue drained');
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function ensureRetryTimer() {
|
|
324
|
+
if (retryTimer)
|
|
325
|
+
return;
|
|
326
|
+
retryTimer = setInterval(() => {
|
|
327
|
+
flushQueue().catch((err) => logger.error({ err }, 'Queue flush error'));
|
|
328
|
+
}, RETRY_INTERVAL_MS);
|
|
329
|
+
}
|
|
330
|
+
function stopRetryTimer() {
|
|
331
|
+
if (retryTimer) {
|
|
332
|
+
clearInterval(retryTimer);
|
|
333
|
+
retryTimer = null;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
export function getQueueSize() {
|
|
337
|
+
return queue.length;
|
|
338
|
+
}
|
|
339
|
+
export function stopSync() {
|
|
340
|
+
stopRetryTimer();
|
|
341
|
+
queue.length = 0;
|
|
342
|
+
projectStates.clear();
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=sync.js.map
|
package/dist/sync.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,EAAE,EACF,GAAG,GAIJ,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,SAAS,GAAG,IAAI,CAAC;AACvB,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAChC,MAAM,aAAa,GAAG,SAAS,CAAC,CAAC,MAAM;AAoBvC,uBAAuB;AAEvB,MAAM,KAAK,GAAgB,EAAE,CAAC;AAC9B,MAAM,aAAa,GAAG,IAAI,GAAG,EAA4B,CAAC;AAC1D,IAAI,UAAU,GAA0C,IAAI,CAAC;AAE7D,SAAS,gBAAgB,CAAC,SAAiB;IACzC,IAAI,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,EAAE,aAAa,EAAE,IAAI,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,GAAG,EAAE,EAAE,mBAAmB,EAAE,KAAK,EAAE,CAAC;QACxF,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAc,EACd,IAAY,EACZ,IAAc,EACd,YAAqC;IAErC,MAAM,KAAK,GAAG,MAAM,eAAe,EAAE,CAAC;IACtC,IAAI,CAAC,KAAK;QAAE,OAAO,GAAG,CAAC,eAAe,EAAE,+DAA+D,CAAC,CAAC;IAEzG,MAAM,GAAG,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC;IAC5C,MAAM,OAAO,GAA2B;QACtC,aAAa,EAAE,UAAU,KAAK,CAAC,MAAM,EAAE;QACvC,cAAc,EAAE,kBAAkB;QAClC,GAAG,YAAY;KAChB,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM;QACN,OAAO;QACP,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;KAC9C,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAErB,IAAI,CAAC,QAAQ;QAAE,OAAO,GAAG,CAAC,eAAe,EAAE,mBAAmB,GAAG,EAAE,CAAC,CAAC;IACrE,OAAO,EAAE,CAAC,QAAQ,CAAC,CAAC;AACtB,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB;IACnD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC;IAE7D,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,gCAAgC,SAAS,EAAE,CAAC,CAAC;IACpF,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC;IAEnC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACpB,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,0BAA0B,CAAC,CAAC;QACtE,OAAO,GAAG,CAAC,uBAAuB,EAAE,0BAA0B,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAErC,CAAC;IAEF,mDAAmD;IACnD,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/D,KAAK,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvE,IAAI,WAAW,EAAE,CAAC;gBAChB,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,SAAiB;IAC3D,MAAM,KAAK,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,SAAS,CAAC,CAAC;IAE9C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,EAAE,kDAAkD,CAAC,CAAC;QAC7H,KAAK,CAAC,mBAAmB,GAAG,IAAI,CAAC;QACjC,OAAO;IACT,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;IAC5B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IACvF,CAAC;IACD,KAAK,CAAC,mBAAmB,GAAG,KAAK,CAAC;IAClC,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC;AAC7E,CAAC;AAED,wBAAwB;AAExB;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,SAAiB,EACjB,WAAmB,EACnB,gBAAwB;IAExB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAEhE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC9C,IAAI,QAAQ,CAAC,IAAI,GAAG,aAAa,EAAE,CAAC;QAClC,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACxD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAE7C,MAAM,QAAQ,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAE1C,6BAA6B;IAC7B,MAAM,eAAe,GAAkB,EAAE,CAAC;IAC1C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IAEvC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,GAAG,YAAY,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QAC9C,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,MAAM,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC;YACnC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,qEAAqE;IACrE,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC;QAC7C,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,YAAY,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACjE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9D,GAAG,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACzC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,+EAA+E;IAC/E,sEAAsE;IACtE,+EAA+E;IAC/E,IAAI,cAAc,GAAG,eAAe,CAAC;IACrC,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClF,cAAc,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,OAAO,GAAG;QACd,SAAS;QACT,IAAI,EAAE,YAAY;QAClB,SAAS,EAAE,QAAQ;QACnB,QAAQ,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,IAAI;YAC1B,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI;YACtB,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,YAAY,EAAE,CAAC,CAAC,WAAW;SAC5B,CAAC,CAAC;QACH,YAAY,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;KACjE,CAAC;IAEF,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAE3C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,oBAAoB,EAAE,OAAO,EAAE;QACrE,iBAAiB,EAAE,cAAc;KAClC,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAC/E,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,kCAAkC,CAAC,CAAC;QAChE,OAAO,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;QACnH,OAAO,CAAC,CAAC;IACX,CAAC;IAED,eAAe;IACf,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;QACtC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,YAAY,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAClF,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,YAAY,IAAI,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC;IAC/F,OAAO,eAAe,CAAC,MAAM,CAAC;AAChC,CAAC;AAED,0BAA0B;AAE1B,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,SAAiB,EACjB,WAAmB,EACnB,gBAAwB;IAExB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAEhE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC9C,IAAI,QAAQ,CAAC,IAAI,GAAG,aAAa,EAAE,CAAC;QAClC,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAEzC,MAAM,KAAK,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC;QAChD,GAAG,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QACtC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG;QACd,SAAS;QACT,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,YAAY;gBAClB,OAAO;gBACP,IAAI;gBACJ,IAAI,EAAE,QAAQ,CAAC,IAAI;aACpB;SACF;KACF,CAAC;IAEF,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAE3C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE;QAClE,iBAAiB,EAAE,cAAc;KAClC,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAC/E,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,uCAAuC,CAAC,CAAC;QACrE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;QAC/G,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACxB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gDAAgD;AAEhD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,SAAiB,EACjB,WAAmB,EACnB,gBAAwB;IAExB,IAAI,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,gBAAgB,CAAC,SAAS,EAAE,WAAW,EAAE,gBAAgB,CAAC,CAAC;IACnE,CAAC;SAAM,IAAI,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9C,MAAM,YAAY,CAAC,SAAS,EAAE,WAAW,EAAE,gBAAgB,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC;AAED,2BAA2B;AAE3B,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,IAAY,EACZ,IAAY;IAEZ,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAE3C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;QACvE,iBAAiB,EAAE,cAAc;KAClC,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,GAAG,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,EAAE,0BAA0B,CAAC,CAAC;QACxF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACpB,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,0BAA0B,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAA6B,CAAC;IACpE,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,2BAA2B,CAAC,CAAC;IACnE,OAAO,IAAI,CAAC,IAAI,CAAC;AACnB,CAAC;AAED,oBAAoB;AAEpB,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,SAAiB,EACjB,IAAY,EACZ,UAAmB,EACnB,UAAkB,QAAQ;IAE1B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IAExD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,oBAAoB,EAAE;QAC5D,SAAS;QACT,IAAI;QACJ,UAAU;QACV,OAAO;KACR,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,GAAG,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,EAAE,2BAA2B,CAAC,CAAC;QACzF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACpB,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,2BAA2B,CAAC,CAAC;QACvE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,kBAAkB,CAAC,CAAC;IACvC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,qCAAqC;AAErC,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAiB,EACjB,WAAmB;IAEnB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,aAAa,EAAE,CAAC,CAAC;IAC3D,GAAG,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAElC,MAAM,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAEvC,yCAAyC;IACzC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAElD,IAAI,CAAC;QACH,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACzD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACrD,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE;gBAAE,SAAS;YAClC,IAAI,QAAQ,CAAC,IAAI,GAAG,aAAa;gBAAE,SAAS;YAE5C,MAAM,QAAQ,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;QAChD,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,qDAAqD,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED,uBAAuB;AAEvB,SAAS,OAAO,CAAC,IAAe;IAC9B,IAAI,KAAK,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC,CAAC,cAAc;QAC9C,iFAAiF;QACjF,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC1D,IAAI,YAAY;YAAE,YAAY,CAAC,mBAAmB,GAAG,IAAI,CAAC;QAC1D,MAAM,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,8DAA8D,CAAC,CAAC;IACrH,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,gBAAgB,EAAE,CAAC;AACrB,CAAC;AAED,KAAK,UAAU,UAAU;IACvB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAE/B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACxE,GAAG,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAEnC,6CAA6C;IAC7C,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,aAAa,EAAE,CAAC;QAC/C,IAAI,KAAK,CAAC,mBAAmB,EAAE,CAAC;YAC9B,MAAM,qBAAqB,CAAC,SAAS,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAED,2BAA2B;IAC3B,MAAM,SAAS,GAAgB,EAAE,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,iBAAiB,CAAC;QACrF,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;YAC1E,iBAAiB,EAAE,IAAI,CAAC,cAAc;SACvC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,IAAI,MAAM,CAAC,KAAK,KAAK,eAAe;gBAAE,MAAM;YAC5C,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,MAAM,CAAC,iDAAiD;QAC1D,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;YACjD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QACD,8CAA8C;IAChD,CAAC;IAED,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACjB,KAAK,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;IAEzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,cAAc,EAAE,CAAC;QACjB,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB;IACvB,IAAI,UAAU;QAAE,OAAO;IACvB,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5B,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAC1E,CAAC,EAAE,iBAAiB,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,cAAc;IACrB,IAAI,UAAU,EAAE,CAAC;QACf,aAAa,CAAC,UAAU,CAAC,CAAC;QAC1B,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,cAAc,EAAE,CAAC;IACjB,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACjB,aAAa,CAAC,KAAK,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function syncUsageData(): Promise<void>;
|
|
2
|
+
export interface UsageWatcher {
|
|
3
|
+
close(): Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
export declare function startUsageWatcher(): UsageWatcher;
|
|
6
|
+
export declare function stopUsageWatcher(): void;
|
|
7
|
+
//# sourceMappingURL=usage-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-sync.d.ts","sourceRoot":"","sources":["../src/usage-sync.ts"],"names":[],"mappings":"AAkMA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAanD;AAID,MAAM,WAAW,YAAY;IAC3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,iBAAiB,IAAI,YAAY,CA4ChD;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAavC"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// Usage file watcher: watch usage-status.json and accounts/ registry, push merged data to server
|
|
2
|
+
import chokidar from 'chokidar';
|
|
3
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { apiRequest } from './sync.js';
|
|
7
|
+
import { logger } from './logger.js';
|
|
8
|
+
// --- Paths ---
|
|
9
|
+
const ULTRA_DIR = join(homedir(), '.claude', 'ultra');
|
|
10
|
+
const USAGE_STATUS_PATH = join(ULTRA_DIR, 'usage-status.json');
|
|
11
|
+
const ACCOUNTS_DIR = join(ULTRA_DIR, 'accounts');
|
|
12
|
+
// --- Module state ---
|
|
13
|
+
const DEBOUNCE_MS = 1_000;
|
|
14
|
+
const RETRY_INTERVAL_MS = 5_000;
|
|
15
|
+
let watcher = null;
|
|
16
|
+
let debounceTimer = null;
|
|
17
|
+
let retryTimer = null;
|
|
18
|
+
let pendingRetryPayload = null;
|
|
19
|
+
// --- JSON reading helpers ---
|
|
20
|
+
async function readJsonFile(filePath) {
|
|
21
|
+
try {
|
|
22
|
+
const content = await readFile(filePath, 'utf8');
|
|
23
|
+
if (!content.trim())
|
|
24
|
+
return null;
|
|
25
|
+
return JSON.parse(content);
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
if (e.code === 'ENOENT')
|
|
29
|
+
return null;
|
|
30
|
+
logger.warn({ err: e, filePath }, 'Failed to read/parse JSON file');
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function readAccountsRegistry() {
|
|
35
|
+
const accounts = new Map();
|
|
36
|
+
let files;
|
|
37
|
+
try {
|
|
38
|
+
files = await readdir(ACCOUNTS_DIR);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
if (e.code === 'ENOENT')
|
|
42
|
+
return accounts;
|
|
43
|
+
logger.warn({ err: e, dir: ACCOUNTS_DIR }, 'Failed to read accounts directory');
|
|
44
|
+
return accounts;
|
|
45
|
+
}
|
|
46
|
+
const jsonFiles = files.filter((f) => f.endsWith('.json'));
|
|
47
|
+
await Promise.all(jsonFiles.map(async (file) => {
|
|
48
|
+
const data = await readJsonFile(join(ACCOUNTS_DIR, file));
|
|
49
|
+
if (data?.account_id) {
|
|
50
|
+
accounts.set(data.account_id, data);
|
|
51
|
+
}
|
|
52
|
+
}));
|
|
53
|
+
return accounts;
|
|
54
|
+
}
|
|
55
|
+
// --- Build payload ---
|
|
56
|
+
async function buildUsagePayload() {
|
|
57
|
+
const log = logger.child({ op: 'buildUsagePayload' });
|
|
58
|
+
const usageStatus = await readJsonFile(USAGE_STATUS_PATH);
|
|
59
|
+
if (!usageStatus?.accounts || Object.keys(usageStatus.accounts).length === 0) {
|
|
60
|
+
log.debug('No usage data to push');
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const registry = await readAccountsRegistry();
|
|
64
|
+
const accounts = {};
|
|
65
|
+
for (const [accountId, usage] of Object.entries(usageStatus.accounts)) {
|
|
66
|
+
const accountInfo = registry.get(accountId);
|
|
67
|
+
accounts[accountId] = {
|
|
68
|
+
account_id: accountId,
|
|
69
|
+
email: accountInfo?.email,
|
|
70
|
+
org_name: accountInfo?.orgName,
|
|
71
|
+
subscription_type: accountInfo?.subscriptionType,
|
|
72
|
+
rate_limits: {
|
|
73
|
+
five_hour: {
|
|
74
|
+
used_percentage: usage.rate_limits.five_hour.used_percentage,
|
|
75
|
+
resets_at: usage.rate_limits.five_hour.resets_at,
|
|
76
|
+
},
|
|
77
|
+
seven_day: {
|
|
78
|
+
used_percentage: usage.rate_limits.seven_day.used_percentage,
|
|
79
|
+
resets_at: usage.rate_limits.seven_day.resets_at,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
updated_at: usage.updated_at,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return { accounts };
|
|
86
|
+
}
|
|
87
|
+
// --- Push to server ---
|
|
88
|
+
async function pushUsageData(payload) {
|
|
89
|
+
const log = logger.child({ op: 'pushUsageData', accountCount: Object.keys(payload.accounts).length });
|
|
90
|
+
const result = await apiRequest('POST', '/api/sync/usage', payload);
|
|
91
|
+
if (!result.success) {
|
|
92
|
+
if (result.error === 'NOT_LOGGED_IN') {
|
|
93
|
+
log.error({ error: result.error }, 'Cannot push usage — not logged in');
|
|
94
|
+
return true; // Permanent failure — don't retry (per error-handling.md)
|
|
95
|
+
}
|
|
96
|
+
log.warn({ error: result.message }, 'Usage push failed — will retry');
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (!result.data.ok) {
|
|
100
|
+
const status = result.data.status;
|
|
101
|
+
if (status >= 400 && status < 500) {
|
|
102
|
+
log.error({ status }, 'Usage push rejected by server — not retrying');
|
|
103
|
+
return true; // Don't retry 4xx (permanent failure)
|
|
104
|
+
}
|
|
105
|
+
log.warn({ status }, 'Usage push failed — will retry');
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
log.info('Usage data pushed');
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
// --- Retry mechanism ---
|
|
112
|
+
function startRetryTimer() {
|
|
113
|
+
if (retryTimer)
|
|
114
|
+
return;
|
|
115
|
+
retryTimer = setInterval(() => {
|
|
116
|
+
if (!pendingRetryPayload) {
|
|
117
|
+
stopRetryTimer();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
pushUsageData(pendingRetryPayload)
|
|
121
|
+
.then((success) => {
|
|
122
|
+
if (success) {
|
|
123
|
+
pendingRetryPayload = null;
|
|
124
|
+
stopRetryTimer();
|
|
125
|
+
logger.info('Usage retry succeeded');
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
.catch((err) => {
|
|
129
|
+
logger.error({ err }, 'Usage retry error');
|
|
130
|
+
});
|
|
131
|
+
}, RETRY_INTERVAL_MS);
|
|
132
|
+
}
|
|
133
|
+
function stopRetryTimer() {
|
|
134
|
+
if (retryTimer) {
|
|
135
|
+
clearInterval(retryTimer);
|
|
136
|
+
retryTimer = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// --- Core sync function ---
|
|
140
|
+
export async function syncUsageData() {
|
|
141
|
+
const payload = await buildUsagePayload();
|
|
142
|
+
if (!payload)
|
|
143
|
+
return;
|
|
144
|
+
// Fresh data supersedes any pending retry
|
|
145
|
+
pendingRetryPayload = null;
|
|
146
|
+
stopRetryTimer();
|
|
147
|
+
const success = await pushUsageData(payload);
|
|
148
|
+
if (!success) {
|
|
149
|
+
pendingRetryPayload = payload;
|
|
150
|
+
startRetryTimer();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export function startUsageWatcher() {
|
|
154
|
+
const log = logger.child({ op: 'usageWatcher' });
|
|
155
|
+
function scheduleSyncUsage() {
|
|
156
|
+
if (debounceTimer)
|
|
157
|
+
clearTimeout(debounceTimer);
|
|
158
|
+
debounceTimer = setTimeout(() => {
|
|
159
|
+
debounceTimer = null;
|
|
160
|
+
syncUsageData().catch((err) => {
|
|
161
|
+
log.error({ err }, 'Usage sync failed');
|
|
162
|
+
});
|
|
163
|
+
}, DEBOUNCE_MS);
|
|
164
|
+
}
|
|
165
|
+
watcher = chokidar.watch([USAGE_STATUS_PATH, ACCOUNTS_DIR], {
|
|
166
|
+
persistent: true,
|
|
167
|
+
ignoreInitial: true,
|
|
168
|
+
awaitWriteFinish: {
|
|
169
|
+
stabilityThreshold: 200,
|
|
170
|
+
pollInterval: 100,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
watcher
|
|
174
|
+
.on('add', scheduleSyncUsage)
|
|
175
|
+
.on('change', scheduleSyncUsage)
|
|
176
|
+
.on('error', (err) => log.error({ err }, 'Usage watcher error'));
|
|
177
|
+
log.info({ paths: [USAGE_STATUS_PATH, ACCOUNTS_DIR] }, 'Usage watcher started');
|
|
178
|
+
return {
|
|
179
|
+
async close() {
|
|
180
|
+
if (debounceTimer) {
|
|
181
|
+
clearTimeout(debounceTimer);
|
|
182
|
+
debounceTimer = null;
|
|
183
|
+
}
|
|
184
|
+
stopRetryTimer();
|
|
185
|
+
pendingRetryPayload = null;
|
|
186
|
+
if (watcher) {
|
|
187
|
+
await watcher.close();
|
|
188
|
+
watcher = null;
|
|
189
|
+
}
|
|
190
|
+
log.info('Usage watcher stopped');
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
export function stopUsageWatcher() {
|
|
195
|
+
if (debounceTimer) {
|
|
196
|
+
clearTimeout(debounceTimer);
|
|
197
|
+
debounceTimer = null;
|
|
198
|
+
}
|
|
199
|
+
stopRetryTimer();
|
|
200
|
+
pendingRetryPayload = null;
|
|
201
|
+
if (watcher) {
|
|
202
|
+
watcher.close().catch((err) => {
|
|
203
|
+
logger.error({ err }, 'Error closing usage watcher');
|
|
204
|
+
});
|
|
205
|
+
watcher = null;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=usage-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-sync.js","sourceRoot":"","sources":["../src/usage-sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface ProjectWatcherOptions {
|
|
2
|
+
projectId: string;
|
|
3
|
+
projectPath: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ProjectWatcher {
|
|
6
|
+
projectId: string;
|
|
7
|
+
projectPath: string;
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Start watching a project's documentation/ directory for changes.
|
|
12
|
+
* Also watches .git/HEAD for branch switches (triggers full resync).
|
|
13
|
+
*/
|
|
14
|
+
export declare function startProjectWatcher(options: ProjectWatcherOptions): ProjectWatcher;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAQA,UAAU,qBAAqB;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CAkFlF"}
|