watchfix 0.2.2 → 0.4.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.
@@ -152,7 +152,7 @@ const formatStatusLine = (result, maxAttempts) => {
152
152
  return 'Status: resolved (issue already fixed)';
153
153
  }
154
154
  const retryInfo = result.attempts < maxAttempts ? ', will retry' : ', max attempts reached';
155
- return `Status: ${result.status} (attempt ${result.attempts} of ${maxAttempts}${retryInfo})`;
155
+ return `Status: ${result.status} (attempt ${result.attempts + 1} of ${maxAttempts}${retryInfo})`;
156
156
  };
157
157
  const formatFixOutcome = (result, verbosity, maxAttempts) => {
158
158
  if (verbosity === 'quiet') {
@@ -211,7 +211,7 @@ const checkDaemonConflict = (db, logger) => {
211
211
  if (!state) {
212
212
  return true;
213
213
  }
214
- if (!isOurProcess(state.pid, state.project_root)) {
214
+ if (!isOurProcess(state.pid)) {
215
215
  clearWatcherState(db);
216
216
  return true;
217
217
  }
@@ -27,6 +27,19 @@ logs:
27
27
  type: file
28
28
  path: ./logs/app.log
29
29
 
30
+ # Example NDJSON source (for structured JSON logs like Pino, Bunyan, Winston):
31
+ # - name: app-json
32
+ # type: file
33
+ # path: ./logs/app.ndjson
34
+ # format: ndjson
35
+ # ndjson:
36
+ # messageField: msg # Required: field containing log message
37
+ # timestampField: time # Optional: field with timestamp
38
+ # levelField: level # Optional: field with log level
39
+ # levelFilter: # Optional: only process these levels
40
+ # - error
41
+ # - fatal
42
+
30
43
  # Example docker source:
31
44
  # - name: api
32
45
  # type: docker
@@ -56,6 +56,9 @@ const formatAnalysis = (analysis) => {
56
56
  return ['Analysis (raw):', ` ${analysis}`];
57
57
  }
58
58
  const lines = ['Analysis:'];
59
+ if (analysis.category) {
60
+ lines.push(` Category: ${analysis.category}`);
61
+ }
59
62
  if (analysis.summary) {
60
63
  lines.push(` Summary: ${analysis.summary}`);
61
64
  }
@@ -73,6 +76,10 @@ const formatAnalysis = (analysis) => {
73
76
  lines.push(` - ${file}`);
74
77
  }
75
78
  }
79
+ if (analysis.remediation_guidance) {
80
+ lines.push(' Remediation guidance:');
81
+ lines.push(...analysis.remediation_guidance.split('\n').map((line) => ` ${line}`));
82
+ }
76
83
  if (analysis.confidence) {
77
84
  lines.push(` Confidence: ${analysis.confidence}`);
78
85
  }
@@ -13,6 +13,7 @@ const STATUS_ORDER = [
13
13
  'fixed',
14
14
  'failed',
15
15
  'ignored',
16
+ 'deferred',
16
17
  ];
17
18
  const buildDatabasePath = (rootDir) => path.join(rootDir, '.watchfix', 'errors.db');
18
19
  const getWatcherState = (db) => db.get('SELECT pid, started_at, autonomous, project_root, command_line FROM watcher_state WHERE id = 1');
@@ -62,6 +63,16 @@ const formatActionableErrors = (errors) => {
62
63
  }
63
64
  return lines;
64
65
  };
66
+ const formatDeferredErrors = (errors) => {
67
+ if (errors.length === 0) {
68
+ return ['Deferred errors: none'];
69
+ }
70
+ const lines = ['Deferred errors (manual action required):'];
71
+ for (const error of errors) {
72
+ lines.push(` #${error.id} ${error.errorType} (${error.source}): ${error.message}`);
73
+ }
74
+ return lines;
75
+ };
65
76
  export const statusCommand = async (options) => {
66
77
  const config = loadConfig(options.config);
67
78
  const dbPath = buildDatabasePath(config.project.root);
@@ -71,6 +82,7 @@ export const statusCommand = async (options) => {
71
82
  'Errors:',
72
83
  ...STATUS_ORDER.map((status) => ` ${status}: 0`),
73
84
  'Actionable errors: none',
85
+ 'Deferred errors: none',
74
86
  ];
75
87
  process.stdout.write(`${lines.join('\n')}\n`);
76
88
  return;
@@ -81,7 +93,7 @@ export const statusCommand = async (options) => {
81
93
  checkSchemaVersion(db);
82
94
  const lines = [];
83
95
  const state = getWatcherState(db);
84
- if (state && isOurProcess(state.pid, state.project_root)) {
96
+ if (state && isOurProcess(state.pid)) {
85
97
  const mode = state.autonomous ? 'autonomous' : 'manual';
86
98
  const uptime = formatUptime(state.started_at);
87
99
  lines.push(`Watcher: running (pid ${state.pid}, ${mode} mode, uptime ${uptime}).`);
@@ -102,6 +114,8 @@ export const statusCommand = async (options) => {
102
114
  }
103
115
  const actionable = getErrorsByStatus(db, ['pending', 'suggested']);
104
116
  lines.push(...formatActionableErrors(actionable));
117
+ const deferred = getErrorsByStatus(db, ['deferred']);
118
+ lines.push(...formatDeferredErrors(deferred));
105
119
  process.stdout.write(`${lines.join('\n')}\n`);
106
120
  }
107
121
  finally {
@@ -58,7 +58,7 @@ export const stopCommand = async (options) => {
58
58
  process.exitCode = EXIT_CODES.WATCHER_CONFLICT;
59
59
  return;
60
60
  }
61
- if (!isOurProcess(state.pid, state.project_root)) {
61
+ if (!isOurProcess(state.pid)) {
62
62
  process.stdout.write('Stale watcher state (process no longer exists).\n');
63
63
  clearWatcherState(db);
64
64
  process.exitCode = EXIT_CODES.WATCHER_CONFLICT;
@@ -31,7 +31,7 @@ const ensureWatcherAvailable = (db, projectRoot, logger) => {
31
31
  if (!existing) {
32
32
  return true;
33
33
  }
34
- if (isOurProcess(existing.pid, projectRoot)) {
34
+ if (isOurProcess(existing.pid)) {
35
35
  const mode = existing.autonomous ? 'autonomous' : 'manual';
36
36
  const message = `Watcher already running (pid ${existing.pid}, ${mode} mode). Use 'watchfix stop'.`;
37
37
  if (logger) {
@@ -1,29 +1,76 @@
1
1
  import { z } from 'zod';
2
2
  declare const durationSchema: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
3
+ declare const ndjsonConfigSchema: z.ZodObject<{
4
+ messageField: z.ZodString;
5
+ timestampField: z.ZodOptional<z.ZodString>;
6
+ levelField: z.ZodOptional<z.ZodString>;
7
+ levelFilter: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ messageField: string;
10
+ timestampField?: string | undefined;
11
+ levelField?: string | undefined;
12
+ levelFilter?: string[] | undefined;
13
+ }, {
14
+ messageField: string;
15
+ timestampField?: string | undefined;
16
+ levelField?: string | undefined;
17
+ levelFilter?: string[] | undefined;
18
+ }>;
3
19
  declare const fileSourceSchema: z.ZodObject<{
4
20
  name: z.ZodString;
5
21
  type: z.ZodLiteral<"file">;
6
22
  path: z.ZodString;
23
+ format: z.ZodOptional<z.ZodEnum<["text", "ndjson"]>>;
24
+ ndjson: z.ZodOptional<z.ZodObject<{
25
+ messageField: z.ZodString;
26
+ timestampField: z.ZodOptional<z.ZodString>;
27
+ levelField: z.ZodOptional<z.ZodString>;
28
+ levelFilter: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
29
+ }, "strip", z.ZodTypeAny, {
30
+ messageField: string;
31
+ timestampField?: string | undefined;
32
+ levelField?: string | undefined;
33
+ levelFilter?: string[] | undefined;
34
+ }, {
35
+ messageField: string;
36
+ timestampField?: string | undefined;
37
+ levelField?: string | undefined;
38
+ levelFilter?: string[] | undefined;
39
+ }>>;
7
40
  }, "strip", z.ZodTypeAny, {
8
41
  path: string;
9
- name: string;
10
42
  type: "file";
43
+ name: string;
44
+ ndjson?: {
45
+ messageField: string;
46
+ timestampField?: string | undefined;
47
+ levelField?: string | undefined;
48
+ levelFilter?: string[] | undefined;
49
+ } | undefined;
50
+ format?: "text" | "ndjson" | undefined;
11
51
  }, {
12
52
  path: string;
13
- name: string;
14
53
  type: "file";
54
+ name: string;
55
+ ndjson?: {
56
+ messageField: string;
57
+ timestampField?: string | undefined;
58
+ levelField?: string | undefined;
59
+ levelFilter?: string[] | undefined;
60
+ } | undefined;
61
+ format?: "text" | "ndjson" | undefined;
15
62
  }>;
16
63
  declare const dockerSourceSchema: z.ZodObject<{
17
64
  name: z.ZodString;
18
65
  type: z.ZodLiteral<"docker">;
19
66
  container: z.ZodString;
20
67
  }, "strip", z.ZodTypeAny, {
21
- name: string;
22
68
  type: "docker";
69
+ name: string;
23
70
  container: string;
24
71
  }, {
25
- name: string;
26
72
  type: "docker";
73
+ name: string;
27
74
  container: string;
28
75
  }>;
29
76
  declare const commandSourceSchema: z.ZodObject<{
@@ -32,13 +79,13 @@ declare const commandSourceSchema: z.ZodObject<{
32
79
  run: z.ZodString;
33
80
  interval: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
34
81
  }, "strip", z.ZodTypeAny, {
35
- name: string;
36
82
  type: "command";
83
+ name: string;
37
84
  run: string;
38
85
  interval: string;
39
86
  }, {
40
- name: string;
41
87
  type: "command";
88
+ name: string;
42
89
  run: string;
43
90
  interval: string;
44
91
  }>;
@@ -46,25 +93,56 @@ declare const logSourceSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
46
93
  name: z.ZodString;
47
94
  type: z.ZodLiteral<"file">;
48
95
  path: z.ZodString;
96
+ format: z.ZodOptional<z.ZodEnum<["text", "ndjson"]>>;
97
+ ndjson: z.ZodOptional<z.ZodObject<{
98
+ messageField: z.ZodString;
99
+ timestampField: z.ZodOptional<z.ZodString>;
100
+ levelField: z.ZodOptional<z.ZodString>;
101
+ levelFilter: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
102
+ }, "strip", z.ZodTypeAny, {
103
+ messageField: string;
104
+ timestampField?: string | undefined;
105
+ levelField?: string | undefined;
106
+ levelFilter?: string[] | undefined;
107
+ }, {
108
+ messageField: string;
109
+ timestampField?: string | undefined;
110
+ levelField?: string | undefined;
111
+ levelFilter?: string[] | undefined;
112
+ }>>;
49
113
  }, "strip", z.ZodTypeAny, {
50
114
  path: string;
51
- name: string;
52
115
  type: "file";
116
+ name: string;
117
+ ndjson?: {
118
+ messageField: string;
119
+ timestampField?: string | undefined;
120
+ levelField?: string | undefined;
121
+ levelFilter?: string[] | undefined;
122
+ } | undefined;
123
+ format?: "text" | "ndjson" | undefined;
53
124
  }, {
54
125
  path: string;
55
- name: string;
56
126
  type: "file";
127
+ name: string;
128
+ ndjson?: {
129
+ messageField: string;
130
+ timestampField?: string | undefined;
131
+ levelField?: string | undefined;
132
+ levelFilter?: string[] | undefined;
133
+ } | undefined;
134
+ format?: "text" | "ndjson" | undefined;
57
135
  }>, z.ZodObject<{
58
136
  name: z.ZodString;
59
137
  type: z.ZodLiteral<"docker">;
60
138
  container: z.ZodString;
61
139
  }, "strip", z.ZodTypeAny, {
62
- name: string;
63
140
  type: "docker";
141
+ name: string;
64
142
  container: string;
65
143
  }, {
66
- name: string;
67
144
  type: "docker";
145
+ name: string;
68
146
  container: string;
69
147
  }>, z.ZodObject<{
70
148
  name: z.ZodString;
@@ -72,13 +150,13 @@ declare const logSourceSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
72
150
  run: z.ZodString;
73
151
  interval: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
74
152
  }, "strip", z.ZodTypeAny, {
75
- name: string;
76
153
  type: "command";
154
+ name: string;
77
155
  run: string;
78
156
  interval: string;
79
157
  }, {
80
- name: string;
81
158
  type: "command";
159
+ name: string;
82
160
  run: string;
83
161
  interval: string;
84
162
  }>]>;
@@ -117,29 +195,60 @@ declare const configSchema: z.ZodObject<{
117
195
  stderr_is_progress?: boolean | undefined;
118
196
  }>;
119
197
  logs: z.ZodObject<{
120
- sources: z.ZodEffects<z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
198
+ sources: z.ZodEffects<z.ZodEffects<z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
121
199
  name: z.ZodString;
122
200
  type: z.ZodLiteral<"file">;
123
201
  path: z.ZodString;
202
+ format: z.ZodOptional<z.ZodEnum<["text", "ndjson"]>>;
203
+ ndjson: z.ZodOptional<z.ZodObject<{
204
+ messageField: z.ZodString;
205
+ timestampField: z.ZodOptional<z.ZodString>;
206
+ levelField: z.ZodOptional<z.ZodString>;
207
+ levelFilter: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
208
+ }, "strip", z.ZodTypeAny, {
209
+ messageField: string;
210
+ timestampField?: string | undefined;
211
+ levelField?: string | undefined;
212
+ levelFilter?: string[] | undefined;
213
+ }, {
214
+ messageField: string;
215
+ timestampField?: string | undefined;
216
+ levelField?: string | undefined;
217
+ levelFilter?: string[] | undefined;
218
+ }>>;
124
219
  }, "strip", z.ZodTypeAny, {
125
220
  path: string;
126
- name: string;
127
221
  type: "file";
222
+ name: string;
223
+ ndjson?: {
224
+ messageField: string;
225
+ timestampField?: string | undefined;
226
+ levelField?: string | undefined;
227
+ levelFilter?: string[] | undefined;
228
+ } | undefined;
229
+ format?: "text" | "ndjson" | undefined;
128
230
  }, {
129
231
  path: string;
130
- name: string;
131
232
  type: "file";
233
+ name: string;
234
+ ndjson?: {
235
+ messageField: string;
236
+ timestampField?: string | undefined;
237
+ levelField?: string | undefined;
238
+ levelFilter?: string[] | undefined;
239
+ } | undefined;
240
+ format?: "text" | "ndjson" | undefined;
132
241
  }>, z.ZodObject<{
133
242
  name: z.ZodString;
134
243
  type: z.ZodLiteral<"docker">;
135
244
  container: z.ZodString;
136
245
  }, "strip", z.ZodTypeAny, {
137
- name: string;
138
246
  type: "docker";
247
+ name: string;
139
248
  container: string;
140
249
  }, {
141
- name: string;
142
250
  type: "docker";
251
+ name: string;
143
252
  container: string;
144
253
  }>, z.ZodObject<{
145
254
  name: z.ZodString;
@@ -147,39 +256,93 @@ declare const configSchema: z.ZodObject<{
147
256
  run: z.ZodString;
148
257
  interval: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
149
258
  }, "strip", z.ZodTypeAny, {
150
- name: string;
151
259
  type: "command";
260
+ name: string;
152
261
  run: string;
153
262
  interval: string;
154
263
  }, {
155
- name: string;
156
264
  type: "command";
265
+ name: string;
157
266
  run: string;
158
267
  interval: string;
159
268
  }>]>, "many">, ({
160
269
  path: string;
161
- name: string;
162
270
  type: "file";
163
- } | {
164
271
  name: string;
272
+ ndjson?: {
273
+ messageField: string;
274
+ timestampField?: string | undefined;
275
+ levelField?: string | undefined;
276
+ levelFilter?: string[] | undefined;
277
+ } | undefined;
278
+ format?: "text" | "ndjson" | undefined;
279
+ } | {
165
280
  type: "docker";
281
+ name: string;
166
282
  container: string;
167
283
  } | {
168
- name: string;
169
284
  type: "command";
285
+ name: string;
170
286
  run: string;
171
287
  interval: string;
172
288
  })[], ({
173
289
  path: string;
174
- name: string;
175
290
  type: "file";
291
+ name: string;
292
+ ndjson?: {
293
+ messageField: string;
294
+ timestampField?: string | undefined;
295
+ levelField?: string | undefined;
296
+ levelFilter?: string[] | undefined;
297
+ } | undefined;
298
+ format?: "text" | "ndjson" | undefined;
299
+ } | {
300
+ type: "docker";
301
+ name: string;
302
+ container: string;
176
303
  } | {
304
+ type: "command";
305
+ name: string;
306
+ run: string;
307
+ interval: string;
308
+ })[]>, ({
309
+ path: string;
310
+ type: "file";
177
311
  name: string;
312
+ ndjson?: {
313
+ messageField: string;
314
+ timestampField?: string | undefined;
315
+ levelField?: string | undefined;
316
+ levelFilter?: string[] | undefined;
317
+ } | undefined;
318
+ format?: "text" | "ndjson" | undefined;
319
+ } | {
178
320
  type: "docker";
321
+ name: string;
179
322
  container: string;
180
323
  } | {
324
+ type: "command";
181
325
  name: string;
326
+ run: string;
327
+ interval: string;
328
+ })[], ({
329
+ path: string;
330
+ type: "file";
331
+ name: string;
332
+ ndjson?: {
333
+ messageField: string;
334
+ timestampField?: string | undefined;
335
+ levelField?: string | undefined;
336
+ levelFilter?: string[] | undefined;
337
+ } | undefined;
338
+ format?: "text" | "ndjson" | undefined;
339
+ } | {
340
+ type: "docker";
341
+ name: string;
342
+ container: string;
343
+ } | {
182
344
  type: "command";
345
+ name: string;
183
346
  run: string;
184
347
  interval: string;
185
348
  })[]>;
@@ -189,15 +352,22 @@ declare const configSchema: z.ZodObject<{
189
352
  }, "strip", z.ZodTypeAny, {
190
353
  sources: ({
191
354
  path: string;
192
- name: string;
193
355
  type: "file";
194
- } | {
195
356
  name: string;
357
+ ndjson?: {
358
+ messageField: string;
359
+ timestampField?: string | undefined;
360
+ levelField?: string | undefined;
361
+ levelFilter?: string[] | undefined;
362
+ } | undefined;
363
+ format?: "text" | "ndjson" | undefined;
364
+ } | {
196
365
  type: "docker";
366
+ name: string;
197
367
  container: string;
198
368
  } | {
199
- name: string;
200
369
  type: "command";
370
+ name: string;
201
371
  run: string;
202
372
  interval: string;
203
373
  })[];
@@ -207,15 +377,22 @@ declare const configSchema: z.ZodObject<{
207
377
  }, {
208
378
  sources: ({
209
379
  path: string;
210
- name: string;
211
380
  type: "file";
212
- } | {
213
381
  name: string;
382
+ ndjson?: {
383
+ messageField: string;
384
+ timestampField?: string | undefined;
385
+ levelField?: string | undefined;
386
+ levelFilter?: string[] | undefined;
387
+ } | undefined;
388
+ format?: "text" | "ndjson" | undefined;
389
+ } | {
214
390
  type: "docker";
391
+ name: string;
215
392
  container: string;
216
393
  } | {
217
- name: string;
218
394
  type: "command";
395
+ name: string;
219
396
  run: string;
220
397
  interval: string;
221
398
  })[];
@@ -261,10 +438,13 @@ declare const configSchema: z.ZodObject<{
261
438
  }>>;
262
439
  deduplication: z.ZodDefault<z.ZodObject<{
263
440
  fixed_grace_period: z.ZodDefault<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>>;
441
+ deferred_grace_period: z.ZodDefault<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>>;
264
442
  }, "strip", z.ZodTypeAny, {
265
443
  fixed_grace_period: string;
444
+ deferred_grace_period: string;
266
445
  }, {
267
446
  fixed_grace_period?: string | undefined;
447
+ deferred_grace_period?: string | undefined;
268
448
  }>>;
269
449
  patterns: z.ZodDefault<z.ZodObject<{
270
450
  match: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
@@ -292,15 +472,22 @@ declare const configSchema: z.ZodObject<{
292
472
  logs: {
293
473
  sources: ({
294
474
  path: string;
295
- name: string;
296
475
  type: "file";
297
- } | {
298
476
  name: string;
477
+ ndjson?: {
478
+ messageField: string;
479
+ timestampField?: string | undefined;
480
+ levelField?: string | undefined;
481
+ levelFilter?: string[] | undefined;
482
+ } | undefined;
483
+ format?: "text" | "ndjson" | undefined;
484
+ } | {
299
485
  type: "docker";
486
+ name: string;
300
487
  container: string;
301
488
  } | {
302
- name: string;
303
489
  type: "command";
490
+ name: string;
304
491
  run: string;
305
492
  interval: string;
306
493
  })[];
@@ -324,6 +511,7 @@ declare const configSchema: z.ZodObject<{
324
511
  };
325
512
  deduplication: {
326
513
  fixed_grace_period: string;
514
+ deferred_grace_period: string;
327
515
  };
328
516
  patterns: {
329
517
  ignore: string[];
@@ -345,15 +533,22 @@ declare const configSchema: z.ZodObject<{
345
533
  logs: {
346
534
  sources: ({
347
535
  path: string;
348
- name: string;
349
536
  type: "file";
350
- } | {
351
537
  name: string;
538
+ ndjson?: {
539
+ messageField: string;
540
+ timestampField?: string | undefined;
541
+ levelField?: string | undefined;
542
+ levelFilter?: string[] | undefined;
543
+ } | undefined;
544
+ format?: "text" | "ndjson" | undefined;
545
+ } | {
352
546
  type: "docker";
547
+ name: string;
353
548
  container: string;
354
549
  } | {
355
- name: string;
356
550
  type: "command";
551
+ name: string;
357
552
  run: string;
358
553
  interval: string;
359
554
  })[];
@@ -377,6 +572,7 @@ declare const configSchema: z.ZodObject<{
377
572
  } | undefined;
378
573
  deduplication?: {
379
574
  fixed_grace_period?: string | undefined;
575
+ deferred_grace_period?: string | undefined;
380
576
  } | undefined;
381
577
  patterns?: {
382
578
  ignore?: string[] | undefined;
@@ -384,5 +580,5 @@ declare const configSchema: z.ZodObject<{
384
580
  } | undefined;
385
581
  }>;
386
582
  type Config = z.infer<typeof configSchema>;
387
- export { commandSourceSchema, configSchema, dockerSourceSchema, durationSchema, fileSourceSchema, logSourceSchema, patternSchema, };
583
+ export { commandSourceSchema, configSchema, dockerSourceSchema, durationSchema, fileSourceSchema, logSourceSchema, ndjsonConfigSchema, patternSchema, };
388
584
  export type { Config };
@@ -17,10 +17,18 @@ const durationSchema = z
17
17
  };
18
18
  return amount * msByUnit[unit] <= MAX_DURATION_MS;
19
19
  }, 'Duration cannot exceed 24 hours');
20
+ const ndjsonConfigSchema = z.object({
21
+ messageField: z.string().min(1),
22
+ timestampField: z.string().min(1).optional(),
23
+ levelField: z.string().min(1).optional(),
24
+ levelFilter: z.array(z.string().min(1)).optional(),
25
+ });
20
26
  const fileSourceSchema = z.object({
21
27
  name: z.string().min(1),
22
28
  type: z.literal('file'),
23
29
  path: z.string().min(1),
30
+ format: z.enum(['text', 'ndjson']).optional(),
31
+ ndjson: ndjsonConfigSchema.optional(),
24
32
  });
25
33
  const dockerSourceSchema = z.object({
26
34
  name: z.string().min(1),
@@ -59,7 +67,15 @@ const configSchema = z.object({
59
67
  .refine((sources) => {
60
68
  const names = sources.map((source) => source.name);
61
69
  return names.length === new Set(names).size;
62
- }, { message: 'Log source names must be unique' }),
70
+ }, { message: 'Log source names must be unique' })
71
+ .refine((sources) => {
72
+ return sources.every((source) => {
73
+ if (source.type === 'file' && source.format === 'ndjson') {
74
+ return source.ndjson !== undefined;
75
+ }
76
+ return true;
77
+ });
78
+ }, { message: 'ndjson config is required when format is "ndjson"' }),
63
79
  context_lines_before: z.number().int().min(0).default(10),
64
80
  context_lines_after: z.number().int().min(0).default(5),
65
81
  max_line_buffer: z.number().int().min(100).default(10000),
@@ -92,6 +108,7 @@ const configSchema = z.object({
92
108
  deduplication: z
93
109
  .object({
94
110
  fixed_grace_period: durationSchema.default('10m'),
111
+ deferred_grace_period: durationSchema.default('1h'),
95
112
  })
96
113
  .default({}),
97
114
  patterns: z
@@ -101,4 +118,4 @@ const configSchema = z.object({
101
118
  })
102
119
  .default({}),
103
120
  });
104
- export { commandSourceSchema, configSchema, dockerSourceSchema, durationSchema, fileSourceSchema, logSourceSchema, patternSchema, };
121
+ export { commandSourceSchema, configSchema, dockerSourceSchema, durationSchema, fileSourceSchema, logSourceSchema, ndjsonConfigSchema, patternSchema, };
@@ -35,7 +35,7 @@ export type ErrorInsert = {
35
35
  createdAt?: string;
36
36
  updatedAt?: string;
37
37
  };
38
- export type ActivityAction = 'watcher_start' | 'watcher_stop' | 'error_detected' | 'error_deduplicated' | 'analysis_start' | 'analysis_complete' | 'analysis_failed' | 'analysis_timeout' | 'already_fixed_detected' | 'fix_start' | 'fix_complete' | 'fix_failed' | 'fix_timeout' | 'verification_start' | 'verification_pass' | 'verification_fail' | 'error_ignored' | 'lock_acquired' | 'lock_released' | 'lock_expired' | 'stale_recovery';
38
+ export type ActivityAction = 'watcher_start' | 'watcher_stop' | 'error_detected' | 'error_deduplicated' | 'analysis_start' | 'analysis_complete' | 'analysis_failed' | 'analysis_timeout' | 'already_fixed_detected' | 'error_deferred' | 'deferred_reanalyzed' | 'fix_start' | 'fix_complete' | 'fix_failed' | 'fix_timeout' | 'verification_start' | 'verification_pass' | 'verification_fail' | 'error_ignored' | 'lock_acquired' | 'lock_released' | 'lock_expired' | 'stale_recovery';
39
39
  export declare function insertError(db: Database, error: ErrorInsert): number;
40
40
  export declare function getError(db: Database, id: number): ErrorRecord | null;
41
41
  export declare function getErrorByHash(db: Database, hash: string): ErrorRecord | null;