@intutic/sync-daemon 1.6.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.
Files changed (147) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/brainIndexer.d.ts +51 -0
  4. package/dist/brainIndexer.d.ts.map +1 -0
  5. package/dist/brainIndexer.js +148 -0
  6. package/dist/brainIndexer.js.map +1 -0
  7. package/dist/configReader.d.ts +46 -0
  8. package/dist/configReader.d.ts.map +1 -0
  9. package/dist/configReader.js +133 -0
  10. package/dist/configReader.js.map +1 -0
  11. package/dist/configWriter.d.ts +53 -0
  12. package/dist/configWriter.d.ts.map +1 -0
  13. package/dist/configWriter.js +503 -0
  14. package/dist/configWriter.js.map +1 -0
  15. package/dist/harness/aiderConfigMerger.d.ts +27 -0
  16. package/dist/harness/aiderConfigMerger.d.ts.map +1 -0
  17. package/dist/harness/aiderConfigMerger.js +139 -0
  18. package/dist/harness/aiderConfigMerger.js.map +1 -0
  19. package/dist/harness/antigravityHooks.d.ts +43 -0
  20. package/dist/harness/antigravityHooks.d.ts.map +1 -0
  21. package/dist/harness/antigravityHooks.js +193 -0
  22. package/dist/harness/antigravityHooks.js.map +1 -0
  23. package/dist/harness/claudeCodeHooks.d.ts +59 -0
  24. package/dist/harness/claudeCodeHooks.d.ts.map +1 -0
  25. package/dist/harness/claudeCodeHooks.js +414 -0
  26. package/dist/harness/claudeCodeHooks.js.map +1 -0
  27. package/dist/harness/claudeDesktopHooks.d.ts +30 -0
  28. package/dist/harness/claudeDesktopHooks.d.ts.map +1 -0
  29. package/dist/harness/claudeDesktopHooks.js +267 -0
  30. package/dist/harness/claudeDesktopHooks.js.map +1 -0
  31. package/dist/harness/clineHooks.d.ts +23 -0
  32. package/dist/harness/clineHooks.d.ts.map +1 -0
  33. package/dist/harness/clineHooks.js +169 -0
  34. package/dist/harness/clineHooks.js.map +1 -0
  35. package/dist/harness/continueHooks.d.ts +33 -0
  36. package/dist/harness/continueHooks.d.ts.map +1 -0
  37. package/dist/harness/continueHooks.js +124 -0
  38. package/dist/harness/continueHooks.js.map +1 -0
  39. package/dist/harness/cursorHooks.d.ts +27 -0
  40. package/dist/harness/cursorHooks.d.ts.map +1 -0
  41. package/dist/harness/cursorHooks.js +221 -0
  42. package/dist/harness/cursorHooks.js.map +1 -0
  43. package/dist/harness/cursorHooksJson.d.ts +29 -0
  44. package/dist/harness/cursorHooksJson.d.ts.map +1 -0
  45. package/dist/harness/cursorHooksJson.js +130 -0
  46. package/dist/harness/cursorHooksJson.js.map +1 -0
  47. package/dist/harness/gooseHardener.d.ts +39 -0
  48. package/dist/harness/gooseHardener.d.ts.map +1 -0
  49. package/dist/harness/gooseHardener.js +152 -0
  50. package/dist/harness/gooseHardener.js.map +1 -0
  51. package/dist/harness/gooseHooks.d.ts +24 -0
  52. package/dist/harness/gooseHooks.d.ts.map +1 -0
  53. package/dist/harness/gooseHooks.js +175 -0
  54. package/dist/harness/gooseHooks.js.map +1 -0
  55. package/dist/harness/gooseHooksWriter.d.ts +27 -0
  56. package/dist/harness/gooseHooksWriter.d.ts.map +1 -0
  57. package/dist/harness/gooseHooksWriter.js +147 -0
  58. package/dist/harness/gooseHooksWriter.js.map +1 -0
  59. package/dist/harness/hermesHooks.d.ts +31 -0
  60. package/dist/harness/hermesHooks.d.ts.map +1 -0
  61. package/dist/harness/hermesHooks.js +245 -0
  62. package/dist/harness/hermesHooks.js.map +1 -0
  63. package/dist/harness/mcpAutoWrite.d.ts +35 -0
  64. package/dist/harness/mcpAutoWrite.d.ts.map +1 -0
  65. package/dist/harness/mcpAutoWrite.js +334 -0
  66. package/dist/harness/mcpAutoWrite.js.map +1 -0
  67. package/dist/harness/n8nHooks.d.ts +28 -0
  68. package/dist/harness/n8nHooks.d.ts.map +1 -0
  69. package/dist/harness/n8nHooks.js +205 -0
  70. package/dist/harness/n8nHooks.js.map +1 -0
  71. package/dist/harness/openWebuiHooks.d.ts +31 -0
  72. package/dist/harness/openWebuiHooks.d.ts.map +1 -0
  73. package/dist/harness/openWebuiHooks.js +166 -0
  74. package/dist/harness/openWebuiHooks.js.map +1 -0
  75. package/dist/harness/openclawHooks.d.ts +34 -0
  76. package/dist/harness/openclawHooks.d.ts.map +1 -0
  77. package/dist/harness/openclawHooks.js +296 -0
  78. package/dist/harness/openclawHooks.js.map +1 -0
  79. package/dist/harness/openhandsHooks.d.ts +24 -0
  80. package/dist/harness/openhandsHooks.d.ts.map +1 -0
  81. package/dist/harness/openhandsHooks.js +178 -0
  82. package/dist/harness/openhandsHooks.js.map +1 -0
  83. package/dist/harness/openhandsHooksWriter.d.ts +23 -0
  84. package/dist/harness/openhandsHooksWriter.d.ts.map +1 -0
  85. package/dist/harness/openhandsHooksWriter.js +146 -0
  86. package/dist/harness/openhandsHooksWriter.js.map +1 -0
  87. package/dist/harness/piHooks.d.ts +32 -0
  88. package/dist/harness/piHooks.d.ts.map +1 -0
  89. package/dist/harness/piHooks.js +257 -0
  90. package/dist/harness/piHooks.js.map +1 -0
  91. package/dist/harness/rooCodeHooks.d.ts +31 -0
  92. package/dist/harness/rooCodeHooks.d.ts.map +1 -0
  93. package/dist/harness/rooCodeHooks.js +300 -0
  94. package/dist/harness/rooCodeHooks.js.map +1 -0
  95. package/dist/harness/windsurfHooks.d.ts +27 -0
  96. package/dist/harness/windsurfHooks.d.ts.map +1 -0
  97. package/dist/harness/windsurfHooks.js +202 -0
  98. package/dist/harness/windsurfHooks.js.map +1 -0
  99. package/dist/hashReporter.d.ts +33 -0
  100. package/dist/hashReporter.d.ts.map +1 -0
  101. package/dist/hashReporter.js +69 -0
  102. package/dist/hashReporter.js.map +1 -0
  103. package/dist/index.d.ts +43 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +43 -0
  106. package/dist/index.js.map +1 -0
  107. package/dist/integrityStore.d.ts +43 -0
  108. package/dist/integrityStore.d.ts.map +1 -0
  109. package/dist/integrityStore.js +94 -0
  110. package/dist/integrityStore.js.map +1 -0
  111. package/dist/lib/complianceProbes.d.ts +39 -0
  112. package/dist/lib/complianceProbes.d.ts.map +1 -0
  113. package/dist/lib/complianceProbes.js +115 -0
  114. package/dist/lib/complianceProbes.js.map +1 -0
  115. package/dist/lib/processPoller.d.ts +2 -0
  116. package/dist/lib/processPoller.d.ts.map +1 -0
  117. package/dist/lib/processPoller.js +87 -0
  118. package/dist/lib/processPoller.js.map +1 -0
  119. package/dist/lib/runtimeEnv.d.ts +49 -0
  120. package/dist/lib/runtimeEnv.d.ts.map +1 -0
  121. package/dist/lib/runtimeEnv.js +70 -0
  122. package/dist/lib/runtimeEnv.js.map +1 -0
  123. package/dist/skillWriter.d.ts +24 -0
  124. package/dist/skillWriter.d.ts.map +1 -0
  125. package/dist/skillWriter.js +112 -0
  126. package/dist/skillWriter.js.map +1 -0
  127. package/dist/syncLoop.d.ts +70 -0
  128. package/dist/syncLoop.d.ts.map +1 -0
  129. package/dist/syncLoop.js +546 -0
  130. package/dist/syncLoop.js.map +1 -0
  131. package/dist/trajectoryMonitor.d.ts +126 -0
  132. package/dist/trajectoryMonitor.d.ts.map +1 -0
  133. package/dist/trajectoryMonitor.js +291 -0
  134. package/dist/trajectoryMonitor.js.map +1 -0
  135. package/dist/watcher/driftWatcher.d.ts +25 -0
  136. package/dist/watcher/driftWatcher.d.ts.map +1 -0
  137. package/dist/watcher/driftWatcher.js +72 -0
  138. package/dist/watcher/driftWatcher.js.map +1 -0
  139. package/dist/watcher/settingsGuard.d.ts +32 -0
  140. package/dist/watcher/settingsGuard.d.ts.map +1 -0
  141. package/dist/watcher/settingsGuard.js +250 -0
  142. package/dist/watcher/settingsGuard.js.map +1 -0
  143. package/dist/wsClient.d.ts +49 -0
  144. package/dist/wsClient.d.ts.map +1 -0
  145. package/dist/wsClient.js +149 -0
  146. package/dist/wsClient.js.map +1 -0
  147. package/package.json +96 -0
@@ -0,0 +1,546 @@
1
+ /**
2
+ * syncLoop.ts — Core bidirectional sync loop.
3
+ *
4
+ * The sync loop is the heartbeat of the daemon. Each iteration:
5
+ * 1. Fetches config from the control plane (POST /api/v1/sync/config)
6
+ * 2. If configVersion > local → writes SOP content to harness files
7
+ * 3. Computes SHA-256 hashes of local config files
8
+ * 4. Reports hashes + status back to the control plane
9
+ * 5. Calls the onSync callback with the result
10
+ * 6. Sleeps for pollIntervalMs (AbortSignal-aware)
11
+ * 7. Repeats until signal.aborted
12
+ *
13
+ * Design: never crash on a single iteration failure. Errors are
14
+ * caught per-iteration, logged, and the loop continues.
15
+ *
16
+ * HLD §3.14 — Real-Time State Mirroring
17
+ * LLD #8 — Sync Daemon / CLI
18
+ *
19
+ * @module
20
+ */
21
+ import * as crypto from 'node:crypto';
22
+ import * as path from 'node:path';
23
+ import * as fs from 'node:fs';
24
+ import * as os from 'node:os';
25
+ import { newIso } from '@intutic/id';
26
+ import { writeConfigFiles, HARNESS_FILES, applyConfigEdits } from './configWriter.js';
27
+ import { computeFileHashes } from './hashReporter.js';
28
+ import { loadIntegrity, saveIntegrity } from './integrityStore.js';
29
+ import { drainHookEvents } from './harness/claudeCodeHooks.js';
30
+ import { writeRuntimeEnv } from './lib/runtimeEnv.js';
31
+ import { runComplianceProbes } from './lib/complianceProbes.js';
32
+ import { startWatcher } from './watcher/driftWatcher.js';
33
+ import { shouldCaptureThisIteration, captureAndUpload } from './configReader.js';
34
+ import { watch } from 'chokidar';
35
+ import Redis from 'ioredis';
36
+ import { TrajectoryMonitor } from './trajectoryMonitor.js';
37
+ // ─── Default constants ───────────────────────────────────────────────
38
+ const DEFAULT_POLL_INTERVAL_MS = 30_000;
39
+ // ─── Core sync loop ──────────────────────────────────────────────────
40
+ /**
41
+ * Start the sync loop. This function runs indefinitely until the
42
+ * AbortSignal is triggered or the process exits.
43
+ *
44
+ * @param options - Sync loop configuration.
45
+ */
46
+ export async function startSyncLoop(options) {
47
+ const { controlPlaneUrl, apiKey, workspaceId, workspaceRoot, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, onSync, signal, adapterId, } = options;
48
+ // Step -1: Start the Trajectory Monitor & Valkey Subscriber
49
+ let trajectoryMonitor = null;
50
+ let trajectorySubscriber = null;
51
+ if (process.env.VALKEY_URL || apiKey) {
52
+ const valkeyUrl = process.env.VALKEY_URL ?? 'redis://127.0.0.1:6379';
53
+ trajectoryMonitor = new TrajectoryMonitor({
54
+ valkeyUrl,
55
+ controlPlaneUrl,
56
+ apiKey,
57
+ windowMs: 300_000,
58
+ submitIntervalMs: 60_000,
59
+ });
60
+ try {
61
+ await trajectoryMonitor.start();
62
+ trajectorySubscriber = new Redis(valkeyUrl);
63
+ await trajectorySubscriber.psubscribe('trace:live:*');
64
+ trajectorySubscriber.on('pmessage', (pattern, channel, message) => {
65
+ try {
66
+ const event = JSON.parse(message);
67
+ trajectoryMonitor?.handleTraceEvent(event);
68
+ }
69
+ catch (err) {
70
+ // Non-blocking warning
71
+ console.warn('[sync-daemon] Failed to parse trajectory trace event:', err);
72
+ }
73
+ });
74
+ console.log('[sync-daemon] Trajectory monitor & subscriber started successfully');
75
+ }
76
+ catch (err) {
77
+ console.warn('[sync-daemon] Could not start trajectory monitor:', err);
78
+ }
79
+ }
80
+ // Tracking latest settings for use in drift handler
81
+ let latestSettings;
82
+ let latestSops = [];
83
+ let latestProxyUrl = '';
84
+ let latestHarnesses = [];
85
+ // Step 0: Write runtime env file (hook scripts source this for credentials)
86
+ // Runs once at startup and then again on every iteration.
87
+ try {
88
+ await writeRuntimeEnv({ controlPlaneUrl, apiKey, workspaceId });
89
+ }
90
+ catch (err) {
91
+ console.warn('[sync-daemon] Could not write runtime env file (non-fatal):', err);
92
+ }
93
+ // Try to sync offline traces back to PostgreSQL
94
+ try {
95
+ await syncOfflineTraces(controlPlaneUrl, apiKey);
96
+ }
97
+ catch (err) {
98
+ console.warn('[sync-daemon] Could not sync offline traces (non-fatal):', err);
99
+ }
100
+ // Set up FSEvents-driven hook event drain (replaces 30s fixed poll for drain step).
101
+ // Drains immediately when the hook-events.jsonl file changes (written by hook scripts).
102
+ // Falls back to a 60s safety-net interval for NFS/Docker mounts where inotify may not fire.
103
+ const hookEventsLog = `${workspaceRoot}/.intutic/events/hook-events.jsonl`;
104
+ let fsWatcher = null;
105
+ let drainSafetyTimer = null;
106
+ const runDrain = async () => {
107
+ try {
108
+ const drained = await drainHookEvents(workspaceRoot, controlPlaneUrl, apiKey);
109
+ if (drained > 0) {
110
+ console.log(`[sync-daemon] Drained ${drained} hook governance events to control plane`);
111
+ }
112
+ }
113
+ catch (err) {
114
+ console.warn('[sync-daemon] Hook event drain error (non-fatal):', err);
115
+ }
116
+ };
117
+ try {
118
+ fsWatcher = watch(hookEventsLog, { ignoreInitial: true, persistent: false });
119
+ fsWatcher.on('change', runDrain);
120
+ fsWatcher.on('add', runDrain);
121
+ }
122
+ catch {
123
+ // chokidar unavailable — rely on the 60s safety-net only
124
+ }
125
+ // 60-second safety-net drain poll (catches events missed by FSEvents on NFS/Docker)
126
+ drainSafetyTimer = setInterval(runDrain, 60_000);
127
+ signal?.addEventListener('abort', () => {
128
+ fsWatcher?.close();
129
+ if (drainSafetyTimer)
130
+ clearInterval(drainSafetyTimer);
131
+ trajectoryMonitor?.stop();
132
+ trajectorySubscriber?.disconnect();
133
+ }, { once: true });
134
+ // WS-5 — Drift watcher with onDriftDetected callback
135
+ // Wired once after first successful sync (when we have harness list).
136
+ // The callback fires a config_tamper governance event and immediately rewrites config.
137
+ let driftWatcher = null;
138
+ const onDriftDetected = async (changedPath) => {
139
+ // 1. Immediately rewrite config (if settings allow)
140
+ if (latestSettings?.bypassEnforcementTier !== 'alert-only') {
141
+ try {
142
+ await writeConfigFiles(workspaceRoot, latestSops, latestProxyUrl, latestHarnesses, workspaceId, latestSettings?.bypassEnforcementTier);
143
+ }
144
+ catch (err) {
145
+ console.warn('[sync-daemon] onDriftDetected: writeConfigFiles failed (non-fatal):', err);
146
+ }
147
+ }
148
+ // 2. Append config_tamper event to hook-events JSONL
149
+ try {
150
+ const tamperEntry = JSON.stringify({
151
+ event: 'config_tamper',
152
+ toolName: 'config_file',
153
+ reason: 'Harness config file modified outside sync-daemon',
154
+ workspaceId,
155
+ filePath: changedPath,
156
+ timestamp: new Date().toISOString(),
157
+ incidentId: crypto.createHash('sha1').update(changedPath + Date.now()).digest('hex').slice(0, 16),
158
+ }) + '\n';
159
+ const hookEventsJsonl = path.join(os.homedir(), '.intutic', 'events', 'hook-events.jsonl');
160
+ fs.appendFileSync(hookEventsJsonl, tamperEntry, { flag: 'a' });
161
+ }
162
+ catch (err) {
163
+ console.warn('[sync-daemon] onDriftDetected: failed to write tamper event (non-fatal):', err);
164
+ }
165
+ };
166
+ let iterationCount = 0;
167
+ while (!signal?.aborted) {
168
+ try {
169
+ // Refresh runtime env on every iteration (key rotation)
170
+ const result = await runSyncIteration({
171
+ controlPlaneUrl,
172
+ apiKey,
173
+ workspaceId,
174
+ workspaceRoot,
175
+ onSync,
176
+ adapterId,
177
+ });
178
+ // Update cached state for drift handler
179
+ if (result) {
180
+ latestSettings = result.settings;
181
+ latestSops = result.sops ?? [];
182
+ latestProxyUrl = result.proxyUrl ?? '';
183
+ latestHarnesses = result.harnesses ?? [];
184
+ await writeRuntimeEnv({
185
+ controlPlaneUrl,
186
+ apiKey,
187
+ workspaceId,
188
+ mcpProxyFailBehavior: result.settings?.mcpProxyFailBehavior,
189
+ mcpProxyMode: result.settings?.mcpProxyMode,
190
+ bypassEnforcementTier: result.settings?.bypassEnforcementTier,
191
+ });
192
+ }
193
+ // Start the drift watcher on first successful sync (once harnesses are known)
194
+ if (!driftWatcher && latestHarnesses.length > 0) {
195
+ try {
196
+ driftWatcher = startWatcher(workspaceRoot, latestHarnesses, onDriftDetected);
197
+ signal?.addEventListener('abort', () => driftWatcher?.stop(), { once: true });
198
+ }
199
+ catch (err) {
200
+ console.warn('[sync-daemon] Could not start drift watcher (non-fatal):', err);
201
+ }
202
+ }
203
+ // Run network compliance probes to check for proxy bypass
204
+ try {
205
+ const probeResults = await runComplianceProbes(workspaceId);
206
+ for (const res of probeResults) {
207
+ if (!res.contained && res.incident) {
208
+ const entry = JSON.stringify(res.incident) + '\n';
209
+ fs.appendFileSync(hookEventsLog, entry, { flag: 'a' });
210
+ }
211
+ }
212
+ }
213
+ catch (err) {
214
+ console.warn('[sync-daemon] Compliance probes failed (non-fatal):', err);
215
+ }
216
+ // 4b. Capture harness configs back to control plane (every Nth iteration)
217
+ if (shouldCaptureThisIteration(iterationCount)) {
218
+ try {
219
+ await captureAndUpload(controlPlaneUrl, apiKey, workspaceId, workspaceRoot, latestHarnesses);
220
+ }
221
+ catch (err) {
222
+ console.warn('[sync-daemon] Config capture failed (non-fatal):', err);
223
+ }
224
+ }
225
+ iterationCount++;
226
+ }
227
+ catch (err) {
228
+ console.error('[sync-daemon] iteration error:', err);
229
+ }
230
+ await sleep(pollIntervalMs, signal);
231
+ }
232
+ // Cleanup on exit
233
+ fsWatcher?.close();
234
+ driftWatcher?.stop();
235
+ if (drainSafetyTimer)
236
+ clearInterval(drainSafetyTimer);
237
+ }
238
+ /**
239
+ * Execute a single sync iteration:
240
+ * 1. Fetch config from control plane
241
+ * 2. Write config files if version is newer
242
+ * 3. Compute file hashes
243
+ * 4. Report status back to control plane
244
+ * 5. Update local integrity store
245
+ * 6. Call onSync callback
246
+ */
247
+ async function runSyncIteration(ctx) {
248
+ const { controlPlaneUrl, apiKey, workspaceId, workspaceRoot, onSync, adapterId } = ctx;
249
+ // 1. Fetch config from control plane
250
+ const config = await fetchConfig(controlPlaneUrl, apiKey, workspaceId);
251
+ // 2. Load local integrity store
252
+ const integrity = await loadIntegrity(workspaceRoot);
253
+ const localVersion = integrity?.configVersion ?? -1;
254
+ let sopsWritten = 0;
255
+ // 3. If remote version is newer → write config files
256
+ if (config.configVersion > localVersion) {
257
+ const harnesses = extractHarnesses(config);
258
+ const writeResult = await writeConfigFiles(workspaceRoot, config.sops, config.proxyUrl, harnesses);
259
+ sopsWritten = writeResult.filesWritten.length;
260
+ }
261
+ // 3b. Apply custom config edits from SkillOpt suggestions
262
+ if (config.appliedEdits && config.appliedEdits.length > 0) {
263
+ const forceApply = sopsWritten > 0;
264
+ const appliedSuggestionsPath = path.join(workspaceRoot, '.intutic', 'applied-suggestions.json');
265
+ let localAppliedIds = [];
266
+ try {
267
+ if (fs.existsSync(appliedSuggestionsPath)) {
268
+ localAppliedIds = JSON.parse(fs.readFileSync(appliedSuggestionsPath, 'utf-8'));
269
+ }
270
+ }
271
+ catch {
272
+ // ignore
273
+ }
274
+ const toApply = forceApply
275
+ ? config.appliedEdits
276
+ : config.appliedEdits.filter(edit => !localAppliedIds.includes(edit.suggestionId));
277
+ if (toApply.length > 0) {
278
+ try {
279
+ await applyConfigEdits(workspaceRoot, toApply, config.settings.bypassEnforcementTier);
280
+ const newlyAppliedIds = Array.from(new Set([...localAppliedIds, ...toApply.map(e => e.suggestionId)]));
281
+ try {
282
+ fs.mkdirSync(path.dirname(appliedSuggestionsPath), { recursive: true });
283
+ fs.writeFileSync(appliedSuggestionsPath, JSON.stringify(newlyAppliedIds, null, 2), 'utf-8');
284
+ }
285
+ catch {
286
+ // ignore
287
+ }
288
+ }
289
+ catch (err) {
290
+ console.warn('[sync-daemon] Failed to apply custom SkillOpt config edits:', err);
291
+ }
292
+ }
293
+ }
294
+ // 4. Compute file hashes for drift detection
295
+ const harnesses = extractHarnesses(config);
296
+ const canonicalHashes = buildCanonicalHashMap(config);
297
+ const fileHashes = await computeFileHashes(workspaceRoot, harnesses, canonicalHashes);
298
+ const driftCount = fileHashes.filter((f) => f.drifted).length;
299
+ // 5. Report status back to control plane per active harness
300
+ for (const harness of harnesses) {
301
+ const filename = HARNESS_FILES[harness];
302
+ if (!filename)
303
+ continue;
304
+ const harnessHashes = fileHashes.filter((f) => f.filePath === filename);
305
+ if (harnessHashes.length > 0) {
306
+ await reportStatus(controlPlaneUrl, apiKey, workspaceId, harness, harnessHashes);
307
+ }
308
+ }
309
+ // 6. Update local integrity store
310
+ const newFiles = {};
311
+ for (const fh of fileHashes) {
312
+ if (fh.localHash) {
313
+ newFiles[fh.filePath] = fh.localHash;
314
+ }
315
+ }
316
+ await saveIntegrity(workspaceRoot, {
317
+ lastSyncAt: newIso(),
318
+ configVersion: config.configVersion,
319
+ files: newFiles,
320
+ });
321
+ // 6b. ContextGraph Indexing Scan & Sync (WS1: LLD #16)
322
+ if (adapterId) {
323
+ try {
324
+ const { BrainIndexer } = await import('./brainIndexer.js');
325
+ const { loadContextIntegrity, saveContextIntegrity } = await import('./integrityStore.js');
326
+ const scanResult = await BrainIndexer.scanWorkspace(workspaceRoot);
327
+ const contextIntegrity = await loadContextIntegrity(workspaceRoot);
328
+ const delta = BrainIndexer.computeDelta(scanResult, contextIntegrity);
329
+ if (delta.upserted.length > 0 || delta.deleted.length > 0) {
330
+ const syncUrl = `${controlPlaneUrl}/api/v1/context/sync`;
331
+ const res = await fetch(syncUrl, {
332
+ method: 'POST',
333
+ headers: {
334
+ 'Content-Type': 'application/json',
335
+ Authorization: `Bearer ${apiKey}`,
336
+ },
337
+ body: JSON.stringify({
338
+ adapterId,
339
+ delta,
340
+ }),
341
+ signal: AbortSignal.timeout(15_000),
342
+ });
343
+ if (res.ok) {
344
+ const newContextFiles = {};
345
+ for (const [filePath, file] of Object.entries(scanResult.files)) {
346
+ newContextFiles[filePath] = file.hash;
347
+ }
348
+ await saveContextIntegrity(workspaceRoot, {
349
+ lastSyncAt: newIso(),
350
+ configVersion: config.configVersion,
351
+ files: newContextFiles,
352
+ });
353
+ }
354
+ else {
355
+ console.warn(`[sync-daemon] ContextGraph sync failed: ${res.status} ${res.statusText}`);
356
+ }
357
+ }
358
+ }
359
+ catch (err) {
360
+ console.warn('[sync-daemon] ContextGraph sync error:', err);
361
+ }
362
+ }
363
+ // 6c. Hook event drain is now FSEvents-driven (see startSyncLoop).
364
+ // No per-iteration drain needed here; the chokidar watcher handles it.
365
+ // 7. Invoke onSync callback
366
+ const result = {
367
+ configVersion: config.configVersion,
368
+ sopsWritten,
369
+ driftCount,
370
+ timestamp: newIso(),
371
+ settings: config.settings,
372
+ sops: config.sops,
373
+ proxyUrl: config.proxyUrl,
374
+ harnesses,
375
+ };
376
+ onSync?.(result);
377
+ return result;
378
+ }
379
+ // ─── HTTP helpers ────────────────────────────────────────────────────
380
+ /**
381
+ * Fetch the sync config from the control plane.
382
+ *
383
+ * POST /api/v1/sync/config
384
+ */
385
+ async function fetchConfig(controlPlaneUrl, apiKey, workspaceId) {
386
+ const url = `${controlPlaneUrl}/api/v1/sync/config`;
387
+ const res = await fetch(url, {
388
+ method: 'POST',
389
+ headers: {
390
+ 'Content-Type': 'application/json',
391
+ Authorization: `Bearer ${apiKey}`,
392
+ },
393
+ body: JSON.stringify({ workspaceId }),
394
+ signal: AbortSignal.timeout(15_000), // 15s request timeout
395
+ });
396
+ if (!res.ok) {
397
+ throw new Error(`[sync-daemon] fetchConfig failed: ${res.status} ${res.statusText}`);
398
+ }
399
+ return (await res.json());
400
+ }
401
+ /**
402
+ * Report file hashes and status back to the control plane.
403
+ *
404
+ * POST /api/v1/sync/sop-hash
405
+ */
406
+ async function reportStatus(controlPlaneUrl, apiKey, workspaceId, harnessType, files) {
407
+ const url = `${controlPlaneUrl}/api/v1/sync/sop-hash`;
408
+ const res = await fetch(url, {
409
+ method: 'POST',
410
+ headers: {
411
+ 'Content-Type': 'application/json',
412
+ Authorization: `Bearer ${apiKey}`,
413
+ },
414
+ body: JSON.stringify({
415
+ workspaceId,
416
+ harnessType,
417
+ files,
418
+ reportedAt: newIso(),
419
+ }),
420
+ signal: AbortSignal.timeout(15_000),
421
+ });
422
+ if (!res.ok) {
423
+ // Non-fatal: log but don't throw (control plane might be temporarily down)
424
+ console.warn(`[sync-daemon] reportStatus failed for ${harnessType}: ${res.status} ${res.statusText}`);
425
+ }
426
+ }
427
+ // ─── Helpers ─────────────────────────────────────────────────────────
428
+ /**
429
+ * Extract unique harness types from a sync config payload.
430
+ */
431
+ function extractHarnesses(config) {
432
+ const set = new Set();
433
+ for (const sop of config.sops) {
434
+ for (const h of sop.harnessTargets) {
435
+ set.add(h);
436
+ }
437
+ }
438
+ return [...set];
439
+ }
440
+ /**
441
+ * Build a filePath → canonical hash map from the config's SOPs.
442
+ * Uses the first canonical hash found for each file path.
443
+ */
444
+ function buildCanonicalHashMap(config) {
445
+ const map = {};
446
+ for (const sop of config.sops) {
447
+ // Each SOP has a single contentHash; in a multi-harness scenario
448
+ // the same hash applies to the content source (not the formatted output).
449
+ // For per-file canonical hashes, the control plane would need to
450
+ // provide them per-harness. For now we use sop.contentHash as a
451
+ // proxy keyed by sopId.
452
+ map[sop.sopId] = sop.contentHash;
453
+ }
454
+ return map;
455
+ }
456
+ /**
457
+ * Sleep for the given duration with AbortSignal support.
458
+ *
459
+ * Resolves immediately if the signal is already aborted.
460
+ * Otherwise, resolves after `ms` milliseconds or when the
461
+ * signal fires, whichever comes first.
462
+ */
463
+ function sleep(ms, signal) {
464
+ if (signal?.aborted)
465
+ return Promise.resolve();
466
+ return new Promise((resolve) => {
467
+ const timer = setTimeout(() => {
468
+ signal?.removeEventListener('abort', onAbort);
469
+ resolve();
470
+ }, ms);
471
+ function onAbort() {
472
+ clearTimeout(timer);
473
+ resolve();
474
+ }
475
+ signal?.addEventListener('abort', onAbort, { once: true });
476
+ });
477
+ }
478
+ export async function syncOfflineTraces(controlPlaneUrl, apiKey) {
479
+ const logsDir = path.join(os.homedir(), '.intutic', 'logs');
480
+ if (!fs.existsSync(logsDir))
481
+ return;
482
+ try {
483
+ const files = fs.readdirSync(logsDir);
484
+ const traceFiles = files.filter(f => f.startsWith('traces-') && f.endsWith('.jsonl'));
485
+ if (traceFiles.length === 0)
486
+ return;
487
+ for (const file of traceFiles) {
488
+ const originalPath = path.join(logsDir, file);
489
+ const syncingPath = originalPath + '.syncing';
490
+ // Rename to avoid write race conditions with the Rust proxy
491
+ try {
492
+ fs.renameSync(originalPath, syncingPath);
493
+ }
494
+ catch (renameErr) {
495
+ console.error(`[sync-daemon] Failed to lock/rename file ${file}:`, renameErr.message);
496
+ continue;
497
+ }
498
+ try {
499
+ const raw = fs.readFileSync(syncingPath, 'utf-8');
500
+ const lines = raw.split('\n').map(l => l.trim()).filter(Boolean);
501
+ if (lines.length === 0) {
502
+ // Empty file, just clean it up
503
+ fs.unlinkSync(syncingPath);
504
+ continue;
505
+ }
506
+ const traces = lines.map(line => JSON.parse(line));
507
+ console.log(`[sync-daemon] Found ${traces.length} offline traces to sync back in ${file}.`);
508
+ // Batch in groups of 100
509
+ const batchSize = 100;
510
+ for (let i = 0; i < traces.length; i += batchSize) {
511
+ const batch = traces.slice(i, i + batchSize);
512
+ const res = await fetch(`${controlPlaneUrl}/api/v1/traces/sync-back`, {
513
+ method: 'POST',
514
+ headers: {
515
+ 'Content-Type': 'application/json',
516
+ Authorization: `Bearer ${apiKey}`,
517
+ },
518
+ body: JSON.stringify({ traces: batch }),
519
+ });
520
+ if (!res.ok) {
521
+ throw new Error(`Sync-back API returned status ${res.status}`);
522
+ }
523
+ }
524
+ // Successfully synced this sharded file, delete it
525
+ fs.unlinkSync(syncingPath);
526
+ console.log(`[sync-daemon] Successfully synced ${traces.length} offline traces back from ${file}.`);
527
+ }
528
+ catch (err) {
529
+ console.error(`[sync-daemon] Failed to sync offline traces back from ${file}:`, err.message);
530
+ // Revert rename on failure to allow retry on next cycle
531
+ try {
532
+ if (fs.existsSync(syncingPath)) {
533
+ fs.renameSync(syncingPath, originalPath);
534
+ }
535
+ }
536
+ catch (revertErr) {
537
+ console.error(`[sync-daemon] Failed to revert rename for ${file}:`, revertErr.message);
538
+ }
539
+ }
540
+ }
541
+ }
542
+ catch (err) {
543
+ console.error(`[sync-daemon] Failed to scan offline traces directory:`, err.message);
544
+ }
545
+ }
546
+ //# sourceMappingURL=syncLoop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncLoop.js","sourceRoot":"","sources":["../src/syncLoop.ts"],"names":[],"mappings":"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"}