gsd-agent 1.0.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/README.md +221 -0
- package/bin/cli.js +313 -0
- package/dist/auth-flow.d.ts +50 -0
- package/dist/auth-flow.d.ts.map +1 -0
- package/dist/auth-flow.js +233 -0
- package/dist/auth-flow.js.map +1 -0
- package/dist/auth.d.ts +42 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +117 -0
- package/dist/auth.js.map +1 -0
- package/dist/command-executor.d.ts +44 -0
- package/dist/command-executor.d.ts.map +1 -0
- package/dist/command-executor.js +193 -0
- package/dist/command-executor.js.map +1 -0
- package/dist/command-executor.test.d.ts +8 -0
- package/dist/command-executor.test.d.ts.map +1 -0
- package/dist/command-executor.test.js +87 -0
- package/dist/command-executor.test.js.map +1 -0
- package/dist/command-queue.d.ts +44 -0
- package/dist/command-queue.d.ts.map +1 -0
- package/dist/command-queue.js +184 -0
- package/dist/command-queue.js.map +1 -0
- package/dist/command-queue.test.d.ts +7 -0
- package/dist/command-queue.test.d.ts.map +1 -0
- package/dist/command-queue.test.js +220 -0
- package/dist/command-queue.test.js.map +1 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +103 -0
- package/dist/config.js.map +1 -0
- package/dist/conflict-resolver.d.ts +43 -0
- package/dist/conflict-resolver.d.ts.map +1 -0
- package/dist/conflict-resolver.js +91 -0
- package/dist/conflict-resolver.js.map +1 -0
- package/dist/conflict-resolver.test.d.ts +7 -0
- package/dist/conflict-resolver.test.d.ts.map +1 -0
- package/dist/conflict-resolver.test.js +123 -0
- package/dist/conflict-resolver.test.js.map +1 -0
- package/dist/discovery.d.ts +59 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +180 -0
- package/dist/discovery.js.map +1 -0
- package/dist/discovery.test.d.ts +8 -0
- package/dist/discovery.test.d.ts.map +1 -0
- package/dist/discovery.test.js +132 -0
- package/dist/discovery.test.js.map +1 -0
- package/dist/hash.d.ts +20 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +35 -0
- package/dist/hash.js.map +1 -0
- package/dist/hash.test.d.ts +7 -0
- package/dist/hash.test.d.ts.map +1 -0
- package/dist/hash.test.js +58 -0
- package/dist/hash.test.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +202 -0
- package/dist/index.js.map +1 -0
- package/dist/integration.test.d.ts +8 -0
- package/dist/integration.test.d.ts.map +1 -0
- package/dist/integration.test.js +37 -0
- package/dist/integration.test.js.map +1 -0
- package/dist/logger.d.ts +68 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +159 -0
- package/dist/logger.js.map +1 -0
- package/dist/output-streamer.d.ts +27 -0
- package/dist/output-streamer.d.ts.map +1 -0
- package/dist/output-streamer.js +71 -0
- package/dist/output-streamer.js.map +1 -0
- package/dist/output-streamer.test.d.ts +7 -0
- package/dist/output-streamer.test.d.ts.map +1 -0
- package/dist/output-streamer.test.js +90 -0
- package/dist/output-streamer.test.js.map +1 -0
- package/dist/realtime-subscriber.d.ts +63 -0
- package/dist/realtime-subscriber.d.ts.map +1 -0
- package/dist/realtime-subscriber.js +201 -0
- package/dist/realtime-subscriber.js.map +1 -0
- package/dist/realtime-subscriber.test.d.ts +7 -0
- package/dist/realtime-subscriber.test.d.ts.map +1 -0
- package/dist/realtime-subscriber.test.js +183 -0
- package/dist/realtime-subscriber.test.js.map +1 -0
- package/dist/reconnection-manager.d.ts +88 -0
- package/dist/reconnection-manager.d.ts.map +1 -0
- package/dist/reconnection-manager.js +229 -0
- package/dist/reconnection-manager.js.map +1 -0
- package/dist/reconnection-manager.test.d.ts +8 -0
- package/dist/reconnection-manager.test.d.ts.map +1 -0
- package/dist/reconnection-manager.test.js +151 -0
- package/dist/reconnection-manager.test.js.map +1 -0
- package/dist/remote-sync-handler.d.ts +61 -0
- package/dist/remote-sync-handler.d.ts.map +1 -0
- package/dist/remote-sync-handler.js +197 -0
- package/dist/remote-sync-handler.js.map +1 -0
- package/dist/remote-sync-handler.test.d.ts +7 -0
- package/dist/remote-sync-handler.test.d.ts.map +1 -0
- package/dist/remote-sync-handler.test.js +212 -0
- package/dist/remote-sync-handler.test.js.map +1 -0
- package/dist/retry.d.ts +35 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +63 -0
- package/dist/retry.js.map +1 -0
- package/dist/retry.test.d.ts +5 -0
- package/dist/retry.test.d.ts.map +1 -0
- package/dist/retry.test.js +84 -0
- package/dist/retry.test.js.map +1 -0
- package/dist/storage-client.d.ts +69 -0
- package/dist/storage-client.d.ts.map +1 -0
- package/dist/storage-client.js +168 -0
- package/dist/storage-client.js.map +1 -0
- package/dist/storage-client.test.d.ts +7 -0
- package/dist/storage-client.test.d.ts.map +1 -0
- package/dist/storage-client.test.js +126 -0
- package/dist/storage-client.test.js.map +1 -0
- package/dist/supabase.d.ts +82 -0
- package/dist/supabase.d.ts.map +1 -0
- package/dist/supabase.js +341 -0
- package/dist/supabase.js.map +1 -0
- package/dist/supabase.test.d.ts +7 -0
- package/dist/supabase.test.d.ts.map +1 -0
- package/dist/supabase.test.js +273 -0
- package/dist/supabase.test.js.map +1 -0
- package/dist/sync-engine.d.ts +84 -0
- package/dist/sync-engine.d.ts.map +1 -0
- package/dist/sync-engine.js +251 -0
- package/dist/sync-engine.js.map +1 -0
- package/dist/sync-engine.test.d.ts +7 -0
- package/dist/sync-engine.test.d.ts.map +1 -0
- package/dist/sync-engine.test.js +241 -0
- package/dist/sync-engine.test.js.map +1 -0
- package/dist/sync-state.d.ts +82 -0
- package/dist/sync-state.d.ts.map +1 -0
- package/dist/sync-state.js +145 -0
- package/dist/sync-state.js.map +1 -0
- package/dist/sync-state.test.d.ts +7 -0
- package/dist/sync-state.test.d.ts.map +1 -0
- package/dist/sync-state.test.js +129 -0
- package/dist/sync-state.test.js.map +1 -0
- package/dist/types.d.ts +148 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/types.test.d.ts +7 -0
- package/dist/types.test.d.ts.map +1 -0
- package/dist/types.test.js +73 -0
- package/dist/types.test.js.map +1 -0
- package/dist/watcher.d.ts +55 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +214 -0
- package/dist/watcher.js.map +1 -0
- package/dist/watcher.test.d.ts +8 -0
- package/dist/watcher.test.d.ts.map +1 -0
- package/dist/watcher.test.js +164 -0
- package/dist/watcher.test.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for SupabaseClient
|
|
3
|
+
*
|
|
4
|
+
* Covers database operations and Storage integration for large files.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
7
|
+
import { SupabaseClient } from './supabase.js';
|
|
8
|
+
describe('SupabaseClient', () => {
|
|
9
|
+
let mockConfig;
|
|
10
|
+
let mockLogger;
|
|
11
|
+
let mockStorageClient;
|
|
12
|
+
let supabaseClient;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
mockConfig = {
|
|
15
|
+
supabase_url: 'https://test.supabase.co',
|
|
16
|
+
supabase_key: 'test-key',
|
|
17
|
+
watch_dirs: [],
|
|
18
|
+
ignored_patterns: [],
|
|
19
|
+
debounce_ms: 300,
|
|
20
|
+
batch_size: 50,
|
|
21
|
+
log_level: 'INFO',
|
|
22
|
+
log_file: '/tmp/test.log',
|
|
23
|
+
log_rotation_days: 7
|
|
24
|
+
};
|
|
25
|
+
mockLogger = {
|
|
26
|
+
error: vi.fn(),
|
|
27
|
+
warn: vi.fn(),
|
|
28
|
+
info: vi.fn(),
|
|
29
|
+
debug: vi.fn()
|
|
30
|
+
};
|
|
31
|
+
mockStorageClient = {
|
|
32
|
+
uploadFile: vi.fn(),
|
|
33
|
+
downloadFile: vi.fn(),
|
|
34
|
+
deleteFile: vi.fn(),
|
|
35
|
+
getBucketInfo: vi.fn()
|
|
36
|
+
};
|
|
37
|
+
// Note: createSupabaseClient will be updated to accept StorageClient
|
|
38
|
+
// For now, we'll test the class directly
|
|
39
|
+
});
|
|
40
|
+
describe('syncFile with Storage integration', () => {
|
|
41
|
+
it('should use database for files ≤200KB', async () => {
|
|
42
|
+
const mockClient = {
|
|
43
|
+
from: vi.fn().mockReturnValue({
|
|
44
|
+
upsert: vi.fn().mockResolvedValue({ error: null })
|
|
45
|
+
})
|
|
46
|
+
};
|
|
47
|
+
const client = new SupabaseClient(mockConfig, mockLogger, mockStorageClient, 'test-user-id');
|
|
48
|
+
// @ts-ignore - accessing private field for testing
|
|
49
|
+
client.client = mockClient;
|
|
50
|
+
const event = {
|
|
51
|
+
id: 'file-1',
|
|
52
|
+
workspace_id: 'workspace-1',
|
|
53
|
+
file_path: '.planning/STATE.md',
|
|
54
|
+
event_type: 'change',
|
|
55
|
+
content_hash: 'hash123',
|
|
56
|
+
content: 'small content',
|
|
57
|
+
source: 'cli',
|
|
58
|
+
timestamp: '2026-03-27T01:00:00Z',
|
|
59
|
+
metadata: {
|
|
60
|
+
size: 100 * 1024, // 100KB
|
|
61
|
+
mtime: '2026-03-27T01:00:00Z',
|
|
62
|
+
agent_version: '0.1.0'
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const result = await client.syncFile(event);
|
|
66
|
+
expect(result.success).toBe(true);
|
|
67
|
+
expect(mockClient.from).toHaveBeenCalledWith('files');
|
|
68
|
+
expect(mockStorageClient.uploadFile).not.toHaveBeenCalled();
|
|
69
|
+
});
|
|
70
|
+
it('should use Storage for files >200KB', async () => {
|
|
71
|
+
const mockClient = {
|
|
72
|
+
from: vi.fn().mockReturnValue({
|
|
73
|
+
upsert: vi.fn().mockResolvedValue({ error: null })
|
|
74
|
+
})
|
|
75
|
+
};
|
|
76
|
+
const client = new SupabaseClient(mockConfig, mockLogger, mockStorageClient, 'test-user-id');
|
|
77
|
+
// @ts-ignore - accessing private field for testing
|
|
78
|
+
client.client = mockClient;
|
|
79
|
+
const largeContent = 'x'.repeat(250 * 1024); // 250KB
|
|
80
|
+
const event = {
|
|
81
|
+
id: 'file-2',
|
|
82
|
+
workspace_id: 'workspace-1',
|
|
83
|
+
file_path: '.planning/LARGE.md',
|
|
84
|
+
event_type: 'change',
|
|
85
|
+
content_hash: 'hash456',
|
|
86
|
+
content: largeContent,
|
|
87
|
+
source: 'cli',
|
|
88
|
+
timestamp: '2026-03-27T01:00:00Z',
|
|
89
|
+
metadata: {
|
|
90
|
+
size: 250 * 1024, // 250KB
|
|
91
|
+
mtime: '2026-03-27T01:00:00Z',
|
|
92
|
+
agent_version: '0.1.0'
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
vi.mocked(mockStorageClient.uploadFile).mockResolvedValue({
|
|
96
|
+
success: true,
|
|
97
|
+
storage_url: 'https://storage.example.com/workspace-1/.planning/LARGE.md'
|
|
98
|
+
});
|
|
99
|
+
const result = await client.syncFile(event);
|
|
100
|
+
expect(result.success).toBe(true);
|
|
101
|
+
expect(mockStorageClient.uploadFile).toHaveBeenCalledWith('workspace-1', '.planning/LARGE.md', largeContent);
|
|
102
|
+
});
|
|
103
|
+
it('should store storage_url in database for large files', async () => {
|
|
104
|
+
const mockUpsert = vi.fn().mockResolvedValue({ error: null });
|
|
105
|
+
const mockClient = {
|
|
106
|
+
from: vi.fn().mockReturnValue({
|
|
107
|
+
upsert: mockUpsert
|
|
108
|
+
})
|
|
109
|
+
};
|
|
110
|
+
const client = new SupabaseClient(mockConfig, mockLogger, mockStorageClient, 'test-user-id');
|
|
111
|
+
// @ts-ignore - accessing private field for testing
|
|
112
|
+
client.client = mockClient;
|
|
113
|
+
const largeContent = 'x'.repeat(250 * 1024); // 250KB
|
|
114
|
+
const storageUrl = 'https://storage.example.com/workspace-1/.planning/LARGE.md';
|
|
115
|
+
const event = {
|
|
116
|
+
id: 'file-3',
|
|
117
|
+
workspace_id: 'workspace-1',
|
|
118
|
+
file_path: '.planning/LARGE.md',
|
|
119
|
+
event_type: 'change',
|
|
120
|
+
content_hash: 'hash789',
|
|
121
|
+
content: largeContent,
|
|
122
|
+
source: 'cli',
|
|
123
|
+
timestamp: '2026-03-27T01:00:00Z',
|
|
124
|
+
metadata: {
|
|
125
|
+
size: 250 * 1024,
|
|
126
|
+
mtime: '2026-03-27T01:00:00Z',
|
|
127
|
+
agent_version: '0.1.0'
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
vi.mocked(mockStorageClient.uploadFile).mockResolvedValue({
|
|
131
|
+
success: true,
|
|
132
|
+
storage_url: storageUrl
|
|
133
|
+
});
|
|
134
|
+
await client.syncFile(event);
|
|
135
|
+
expect(mockUpsert).toHaveBeenCalledWith({
|
|
136
|
+
id: 'file-3',
|
|
137
|
+
workspace_id: 'workspace-1',
|
|
138
|
+
file_path: '.planning/LARGE.md',
|
|
139
|
+
content: null,
|
|
140
|
+
content_hash: 'hash789',
|
|
141
|
+
size: 250 * 1024,
|
|
142
|
+
updated_at: '2026-03-27T01:00:00Z',
|
|
143
|
+
storage_url: storageUrl
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
it('should set content=null in database for large files', async () => {
|
|
147
|
+
const mockUpsert = vi.fn().mockResolvedValue({ error: null });
|
|
148
|
+
const mockClient = {
|
|
149
|
+
from: vi.fn().mockReturnValue({
|
|
150
|
+
upsert: mockUpsert
|
|
151
|
+
})
|
|
152
|
+
};
|
|
153
|
+
const client = new SupabaseClient(mockConfig, mockLogger, mockStorageClient, 'test-user-id');
|
|
154
|
+
// @ts-ignore - accessing private field for testing
|
|
155
|
+
client.client = mockClient;
|
|
156
|
+
const largeContent = 'x'.repeat(250 * 1024);
|
|
157
|
+
const event = {
|
|
158
|
+
id: 'file-4',
|
|
159
|
+
workspace_id: 'workspace-1',
|
|
160
|
+
file_path: '.planning/LARGE.md',
|
|
161
|
+
event_type: 'change',
|
|
162
|
+
content_hash: 'hash999',
|
|
163
|
+
content: largeContent,
|
|
164
|
+
source: 'cli',
|
|
165
|
+
timestamp: '2026-03-27T01:00:00Z',
|
|
166
|
+
metadata: {
|
|
167
|
+
size: 250 * 1024,
|
|
168
|
+
mtime: '2026-03-27T01:00:00Z',
|
|
169
|
+
agent_version: '0.1.0'
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
vi.mocked(mockStorageClient.uploadFile).mockResolvedValue({
|
|
173
|
+
success: true,
|
|
174
|
+
storage_url: 'https://storage.example.com/file'
|
|
175
|
+
});
|
|
176
|
+
await client.syncFile(event);
|
|
177
|
+
const upsertCall = mockUpsert.mock.calls[0][0];
|
|
178
|
+
expect(upsertCall.content).toBeNull();
|
|
179
|
+
});
|
|
180
|
+
it('should fall back to database if Storage upload fails and size <200KB', async () => {
|
|
181
|
+
const mockUpsert = vi.fn().mockResolvedValue({ error: null });
|
|
182
|
+
const mockClient = {
|
|
183
|
+
from: vi.fn().mockReturnValue({
|
|
184
|
+
upsert: mockUpsert
|
|
185
|
+
})
|
|
186
|
+
};
|
|
187
|
+
const client = new SupabaseClient(mockConfig, mockLogger, mockStorageClient, 'test-user-id');
|
|
188
|
+
// @ts-ignore - accessing private field for testing
|
|
189
|
+
client.client = mockClient;
|
|
190
|
+
const content = 'x'.repeat(150 * 1024); // 150KB (under 200KB threshold)
|
|
191
|
+
const event = {
|
|
192
|
+
id: 'file-5',
|
|
193
|
+
workspace_id: 'workspace-1',
|
|
194
|
+
file_path: '.planning/MEDIUM.md',
|
|
195
|
+
event_type: 'change',
|
|
196
|
+
content_hash: 'hash111',
|
|
197
|
+
content: content,
|
|
198
|
+
source: 'cli',
|
|
199
|
+
timestamp: '2026-03-27T01:00:00Z',
|
|
200
|
+
metadata: {
|
|
201
|
+
size: 150 * 1024,
|
|
202
|
+
mtime: '2026-03-27T01:00:00Z',
|
|
203
|
+
agent_version: '0.1.0'
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
// Storage upload fails
|
|
207
|
+
vi.mocked(mockStorageClient.uploadFile).mockResolvedValue({
|
|
208
|
+
success: false,
|
|
209
|
+
error: 'Storage error'
|
|
210
|
+
});
|
|
211
|
+
const result = await client.syncFile(event);
|
|
212
|
+
// Should fall back to database since size < 200KB
|
|
213
|
+
expect(result.success).toBe(true);
|
|
214
|
+
const upsertCall = mockUpsert.mock.calls[0][0];
|
|
215
|
+
expect(upsertCall.content).toBe(content);
|
|
216
|
+
expect(upsertCall.storage_url).toBeUndefined();
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
describe('syncFileBatch with Storage integration', () => {
|
|
220
|
+
it('should split large and small files into separate processing paths', async () => {
|
|
221
|
+
const mockUpsert = vi.fn().mockResolvedValue({ error: null });
|
|
222
|
+
const mockClient = {
|
|
223
|
+
from: vi.fn().mockReturnValue({
|
|
224
|
+
upsert: mockUpsert
|
|
225
|
+
})
|
|
226
|
+
};
|
|
227
|
+
const client = new SupabaseClient(mockConfig, mockLogger, mockStorageClient, 'test-user-id');
|
|
228
|
+
// @ts-ignore - accessing private field for testing
|
|
229
|
+
client.client = mockClient;
|
|
230
|
+
const smallFile = {
|
|
231
|
+
id: 'small-1',
|
|
232
|
+
workspace_id: 'workspace-1',
|
|
233
|
+
file_path: '.planning/SMALL.md',
|
|
234
|
+
event_type: 'change',
|
|
235
|
+
content_hash: 'hash-small',
|
|
236
|
+
content: 'small',
|
|
237
|
+
source: 'cli',
|
|
238
|
+
timestamp: '2026-03-27T01:00:00Z',
|
|
239
|
+
metadata: {
|
|
240
|
+
size: 50 * 1024, // 50KB
|
|
241
|
+
mtime: '2026-03-27T01:00:00Z',
|
|
242
|
+
agent_version: '0.1.0'
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const largeFile = {
|
|
246
|
+
id: 'large-1',
|
|
247
|
+
workspace_id: 'workspace-1',
|
|
248
|
+
file_path: '.planning/LARGE.md',
|
|
249
|
+
event_type: 'change',
|
|
250
|
+
content_hash: 'hash-large',
|
|
251
|
+
content: 'x'.repeat(250 * 1024),
|
|
252
|
+
source: 'cli',
|
|
253
|
+
timestamp: '2026-03-27T01:00:00Z',
|
|
254
|
+
metadata: {
|
|
255
|
+
size: 250 * 1024, // 250KB
|
|
256
|
+
mtime: '2026-03-27T01:00:00Z',
|
|
257
|
+
agent_version: '0.1.0'
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
vi.mocked(mockStorageClient.uploadFile).mockResolvedValue({
|
|
261
|
+
success: true,
|
|
262
|
+
storage_url: 'https://storage.example.com/file'
|
|
263
|
+
});
|
|
264
|
+
const result = await client.syncFileBatch([smallFile, largeFile]);
|
|
265
|
+
expect(result.success).toBe(true);
|
|
266
|
+
// Small file goes to database with content
|
|
267
|
+
// Large file goes to Storage first, then database with storage_url
|
|
268
|
+
expect(mockStorageClient.uploadFile).toHaveBeenCalledTimes(1);
|
|
269
|
+
expect(mockUpsert).toHaveBeenCalledTimes(2);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
//# sourceMappingURL=supabase.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabase.test.js","sourceRoot":"","sources":["../src/supabase.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Engine for GSD Agent
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates filesystem watching and cloud sync with batching, debouncing,
|
|
5
|
+
* priority queue management, and event storm detection.
|
|
6
|
+
*/
|
|
7
|
+
import { FileWatcher } from './watcher.js';
|
|
8
|
+
import { SupabaseClient } from './supabase.js';
|
|
9
|
+
import { Logger } from './logger.js';
|
|
10
|
+
import type { SyncConfig, AgentState } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* SyncEngine orchestrates sync between filesystem and Supabase
|
|
13
|
+
*
|
|
14
|
+
* Features:
|
|
15
|
+
* - Batches multiple file changes into single transactions
|
|
16
|
+
* - Debounces rapid changes to prevent rate limit exhaustion
|
|
17
|
+
* - Prioritizes STATE.md and ROADMAP.md changes
|
|
18
|
+
* - Tracks synced file hashes to avoid duplicate syncs
|
|
19
|
+
* - Detects event storms (>100 events/sec) and pauses
|
|
20
|
+
* - Sends heartbeat to Supabase every 30 seconds
|
|
21
|
+
* - Flushes pending changes on graceful shutdown
|
|
22
|
+
*/
|
|
23
|
+
export declare class SyncEngine {
|
|
24
|
+
private watcher;
|
|
25
|
+
private supabase;
|
|
26
|
+
private config;
|
|
27
|
+
private logger;
|
|
28
|
+
private state;
|
|
29
|
+
private debounceTimer;
|
|
30
|
+
private heartbeatTimer;
|
|
31
|
+
private syncedHashes;
|
|
32
|
+
private eventCount;
|
|
33
|
+
private eventWindowStart;
|
|
34
|
+
private isStormPaused;
|
|
35
|
+
constructor(watcher: FileWatcher, supabase: SupabaseClient, config: SyncConfig, logger: Logger);
|
|
36
|
+
/**
|
|
37
|
+
* Start sync engine and subscribe to watcher events
|
|
38
|
+
*/
|
|
39
|
+
start(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Stop sync engine and flush pending changes
|
|
42
|
+
*/
|
|
43
|
+
stop(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Get current agent state for monitoring
|
|
46
|
+
*/
|
|
47
|
+
getState(): AgentState;
|
|
48
|
+
/**
|
|
49
|
+
* Enqueue file change event for sync
|
|
50
|
+
*
|
|
51
|
+
* Implements:
|
|
52
|
+
* - Duplicate detection via content hash
|
|
53
|
+
* - Priority queue (STATE.md and ROADMAP.md first)
|
|
54
|
+
* - Debouncing to batch rapid changes
|
|
55
|
+
*/
|
|
56
|
+
private enqueue;
|
|
57
|
+
/**
|
|
58
|
+
* Flush sync queue to Supabase
|
|
59
|
+
*
|
|
60
|
+
* Implements:
|
|
61
|
+
* - Batch size limiting (max batch_size files per batch)
|
|
62
|
+
* - Retry on failure (re-queue failed events)
|
|
63
|
+
* - Automatic continuation if more events in queue
|
|
64
|
+
*/
|
|
65
|
+
private flush;
|
|
66
|
+
/**
|
|
67
|
+
* Check for event storm and pause if detected
|
|
68
|
+
*
|
|
69
|
+
* Event storm = >100 events per second
|
|
70
|
+
* When detected, pause for 5 seconds and log warning
|
|
71
|
+
*
|
|
72
|
+
* @returns True if currently in storm pause
|
|
73
|
+
*/
|
|
74
|
+
private checkEventStorm;
|
|
75
|
+
/**
|
|
76
|
+
* Send heartbeat to Supabase to maintain connection
|
|
77
|
+
*/
|
|
78
|
+
private sendHeartbeat;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Factory function to create sync engine
|
|
82
|
+
*/
|
|
83
|
+
export declare function createSyncEngine(watcher: FileWatcher, supabase: SupabaseClient, config: SyncConfig, logger: Logger): SyncEngine;
|
|
84
|
+
//# sourceMappingURL=sync-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-engine.d.ts","sourceRoot":"","sources":["../src/sync-engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,KAAK,EAAE,UAAU,EAAmB,UAAU,EAAE,MAAM,YAAY,CAAA;AAEzE;;;;;;;;;;;GAWG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAQ;IAGtB,OAAO,CAAC,KAAK,CAKZ;IAGD,OAAO,CAAC,aAAa,CAA8B;IAGnD,OAAO,CAAC,cAAc,CAA8B;IAGpD,OAAO,CAAC,YAAY,CAAiC;IAGrD,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,aAAa,CAAQ;gBAG3B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM;IAQhB;;OAEG;IACH,KAAK,IAAI,IAAI;IAqBb;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B3B;;OAEG;IACH,QAAQ,IAAI,UAAU;IAStB;;;;;;;OAOG;YACW,OAAO;IA6BrB;;;;;;;OAOG;YACW,KAAK;IAqDnB;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAkCvB;;OAEG;YACW,aAAa;CAa5B;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM,GACb,UAAU,CAEZ"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Engine for GSD Agent
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates filesystem watching and cloud sync with batching, debouncing,
|
|
5
|
+
* priority queue management, and event storm detection.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* SyncEngine orchestrates sync between filesystem and Supabase
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Batches multiple file changes into single transactions
|
|
12
|
+
* - Debounces rapid changes to prevent rate limit exhaustion
|
|
13
|
+
* - Prioritizes STATE.md and ROADMAP.md changes
|
|
14
|
+
* - Tracks synced file hashes to avoid duplicate syncs
|
|
15
|
+
* - Detects event storms (>100 events/sec) and pauses
|
|
16
|
+
* - Sends heartbeat to Supabase every 30 seconds
|
|
17
|
+
* - Flushes pending changes on graceful shutdown
|
|
18
|
+
*/
|
|
19
|
+
export class SyncEngine {
|
|
20
|
+
watcher;
|
|
21
|
+
supabase;
|
|
22
|
+
config;
|
|
23
|
+
logger;
|
|
24
|
+
// Internal state
|
|
25
|
+
state = {
|
|
26
|
+
workspaces: new Map(),
|
|
27
|
+
sync_queue: [],
|
|
28
|
+
is_syncing: false,
|
|
29
|
+
last_heartbeat: new Date().toISOString()
|
|
30
|
+
};
|
|
31
|
+
// Debounce timer
|
|
32
|
+
debounceTimer = null;
|
|
33
|
+
// Heartbeat timer
|
|
34
|
+
heartbeatTimer = null;
|
|
35
|
+
// Track synced file hashes to avoid duplicates
|
|
36
|
+
syncedHashes = new Map();
|
|
37
|
+
// Event storm detection
|
|
38
|
+
eventCount = 0;
|
|
39
|
+
eventWindowStart = Date.now();
|
|
40
|
+
isStormPaused = false;
|
|
41
|
+
constructor(watcher, supabase, config, logger) {
|
|
42
|
+
this.watcher = watcher;
|
|
43
|
+
this.supabase = supabase;
|
|
44
|
+
this.config = config;
|
|
45
|
+
this.logger = logger;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Start sync engine and subscribe to watcher events
|
|
49
|
+
*/
|
|
50
|
+
start() {
|
|
51
|
+
// Subscribe to file change events
|
|
52
|
+
this.watcher.on('change', async (event) => {
|
|
53
|
+
if (this.checkEventStorm()) {
|
|
54
|
+
// Drop events during storm
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
await this.enqueue(event);
|
|
58
|
+
});
|
|
59
|
+
// Start watcher
|
|
60
|
+
this.watcher.start();
|
|
61
|
+
// Start heartbeat timer (every 30s)
|
|
62
|
+
this.heartbeatTimer = setInterval(() => {
|
|
63
|
+
void this.sendHeartbeat();
|
|
64
|
+
}, 30000);
|
|
65
|
+
this.logger.info('Sync engine started');
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Stop sync engine and flush pending changes
|
|
69
|
+
*/
|
|
70
|
+
async stop() {
|
|
71
|
+
this.logger.info('Stopping sync engine, flushing pending changes');
|
|
72
|
+
// Stop watcher
|
|
73
|
+
this.watcher.stop();
|
|
74
|
+
// Stop heartbeat
|
|
75
|
+
if (this.heartbeatTimer) {
|
|
76
|
+
clearInterval(this.heartbeatTimer);
|
|
77
|
+
this.heartbeatTimer = null;
|
|
78
|
+
}
|
|
79
|
+
// Clear debounce timer
|
|
80
|
+
if (this.debounceTimer) {
|
|
81
|
+
clearTimeout(this.debounceTimer);
|
|
82
|
+
this.debounceTimer = null;
|
|
83
|
+
}
|
|
84
|
+
// Flush remaining queue
|
|
85
|
+
while (this.state.sync_queue.length > 0) {
|
|
86
|
+
await this.flush();
|
|
87
|
+
}
|
|
88
|
+
this.logger.info('Sync engine stopped');
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get current agent state for monitoring
|
|
92
|
+
*/
|
|
93
|
+
getState() {
|
|
94
|
+
return {
|
|
95
|
+
workspaces: new Map(this.state.workspaces),
|
|
96
|
+
sync_queue: [...this.state.sync_queue],
|
|
97
|
+
is_syncing: this.state.is_syncing,
|
|
98
|
+
last_heartbeat: this.state.last_heartbeat
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Enqueue file change event for sync
|
|
103
|
+
*
|
|
104
|
+
* Implements:
|
|
105
|
+
* - Duplicate detection via content hash
|
|
106
|
+
* - Priority queue (STATE.md and ROADMAP.md first)
|
|
107
|
+
* - Debouncing to batch rapid changes
|
|
108
|
+
*/
|
|
109
|
+
async enqueue(event) {
|
|
110
|
+
// Skip if already synced with same hash (duplicate detection)
|
|
111
|
+
const key = `${event.workspace_id}:${event.file_path}`;
|
|
112
|
+
if (this.syncedHashes.get(key) === event.content_hash) {
|
|
113
|
+
this.logger.debug('Skipping duplicate sync', { file_path: event.file_path });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Add to appropriate queue based on priority
|
|
117
|
+
if (event.file_path.endsWith('STATE.md') || event.file_path.endsWith('ROADMAP.md')) {
|
|
118
|
+
// High priority - add to front
|
|
119
|
+
this.state.sync_queue.unshift(event);
|
|
120
|
+
this.logger.debug('Enqueued high priority file', { file_path: event.file_path });
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Normal priority - add to back
|
|
124
|
+
this.state.sync_queue.push(event);
|
|
125
|
+
this.logger.debug('Enqueued file', { file_path: event.file_path });
|
|
126
|
+
}
|
|
127
|
+
// Debounce: reset timer on each new event
|
|
128
|
+
if (this.debounceTimer) {
|
|
129
|
+
clearTimeout(this.debounceTimer);
|
|
130
|
+
}
|
|
131
|
+
this.debounceTimer = setTimeout(() => {
|
|
132
|
+
void this.flush();
|
|
133
|
+
}, this.config.debounce_ms);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Flush sync queue to Supabase
|
|
137
|
+
*
|
|
138
|
+
* Implements:
|
|
139
|
+
* - Batch size limiting (max batch_size files per batch)
|
|
140
|
+
* - Retry on failure (re-queue failed events)
|
|
141
|
+
* - Automatic continuation if more events in queue
|
|
142
|
+
*/
|
|
143
|
+
async flush() {
|
|
144
|
+
if (this.state.is_syncing || this.state.sync_queue.length === 0) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
this.state.is_syncing = true;
|
|
148
|
+
// Take batch from queue (max batch_size)
|
|
149
|
+
const rawBatch = this.state.sync_queue.splice(0, this.config.batch_size);
|
|
150
|
+
// Deduplicate: keep only the latest event for each workspace_id:file_path
|
|
151
|
+
const deduped = new Map();
|
|
152
|
+
for (const event of rawBatch) {
|
|
153
|
+
const key = `${event.workspace_id}:${event.file_path}`;
|
|
154
|
+
deduped.set(key, event); // Later events overwrite earlier ones
|
|
155
|
+
}
|
|
156
|
+
const batch = Array.from(deduped.values());
|
|
157
|
+
try {
|
|
158
|
+
const result = await this.supabase.syncFileBatch(batch);
|
|
159
|
+
if (result.success) {
|
|
160
|
+
// Update synced hashes
|
|
161
|
+
for (const event of batch) {
|
|
162
|
+
const key = `${event.workspace_id}:${event.file_path}`;
|
|
163
|
+
this.syncedHashes.set(key, event.content_hash);
|
|
164
|
+
}
|
|
165
|
+
this.logger.info(`Synced ${batch.length} files`, { batch_size: batch.length });
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// Sync failed - re-queue at front for retry
|
|
169
|
+
this.logger.error('Sync failed, re-queuing batch', {
|
|
170
|
+
error: result.error,
|
|
171
|
+
batch_size: batch.length
|
|
172
|
+
});
|
|
173
|
+
this.state.sync_queue.unshift(...batch);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
this.logger.error('Sync failed with exception', { error, batch_size: batch.length });
|
|
178
|
+
// Re-queue failed events at front for retry
|
|
179
|
+
this.state.sync_queue.unshift(...batch);
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
this.state.is_syncing = false;
|
|
183
|
+
// If more events in queue, schedule next flush
|
|
184
|
+
if (this.state.sync_queue.length > 0) {
|
|
185
|
+
setTimeout(() => {
|
|
186
|
+
void this.flush();
|
|
187
|
+
}, 100);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Check for event storm and pause if detected
|
|
193
|
+
*
|
|
194
|
+
* Event storm = >100 events per second
|
|
195
|
+
* When detected, pause for 5 seconds and log warning
|
|
196
|
+
*
|
|
197
|
+
* @returns True if currently in storm pause
|
|
198
|
+
*/
|
|
199
|
+
checkEventStorm() {
|
|
200
|
+
const now = Date.now();
|
|
201
|
+
const elapsed = now - this.eventWindowStart;
|
|
202
|
+
if (elapsed >= 1000) {
|
|
203
|
+
// Reset window every second
|
|
204
|
+
this.eventCount = 0;
|
|
205
|
+
this.eventWindowStart = now;
|
|
206
|
+
this.isStormPaused = false;
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
this.eventCount++;
|
|
210
|
+
if (this.eventCount > 100 && !this.isStormPaused) {
|
|
211
|
+
this.logger.warn('Event storm detected, pausing for 5s', {
|
|
212
|
+
events_per_sec: this.eventCount
|
|
213
|
+
});
|
|
214
|
+
this.isStormPaused = true;
|
|
215
|
+
// Resume after 5 seconds
|
|
216
|
+
setTimeout(() => {
|
|
217
|
+
this.isStormPaused = false;
|
|
218
|
+
this.eventCount = 0;
|
|
219
|
+
this.eventWindowStart = Date.now();
|
|
220
|
+
this.logger.info('Event storm pause ended, resuming');
|
|
221
|
+
}, 5000);
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
return this.isStormPaused;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Send heartbeat to Supabase to maintain connection
|
|
228
|
+
*/
|
|
229
|
+
async sendHeartbeat() {
|
|
230
|
+
try {
|
|
231
|
+
const result = await this.supabase.heartbeat();
|
|
232
|
+
if (result.success) {
|
|
233
|
+
this.state.last_heartbeat = new Date().toISOString();
|
|
234
|
+
this.logger.debug('Heartbeat sent', { timestamp: this.state.last_heartbeat });
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
this.logger.error('Heartbeat failed', { error: result.error });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
this.logger.error('Heartbeat failed with exception', { error });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Factory function to create sync engine
|
|
247
|
+
*/
|
|
248
|
+
export function createSyncEngine(watcher, supabase, config, logger) {
|
|
249
|
+
return new SyncEngine(watcher, supabase, config, logger);
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=sync-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-engine.js","sourceRoot":"","sources":["../src/sync-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-engine.test.d.ts","sourceRoot":"","sources":["../src/sync-engine.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|