@karmaniverous/jeeves-watcher 0.2.3 → 0.2.5

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.
@@ -1,4 +1,4 @@
1
- (function (exports, Fastify, promises, node_path, picomatch, radash, node_crypto, cosmiconfig, zod, jsonmap, googleGenai, pino, uuid, cheerio, yaml, mammoth, Ajv, addFormats, textsplitters, jsClientRest, chokidar) {
1
+ (function (exports, Fastify, promises, node_path, picomatch, radash, node_crypto, cosmiconfig, zod, jsonmap, googleGenai, node_fs, ignore, pino, uuid, cheerio, yaml, mammoth, Ajv, addFormats, textsplitters, jsClientRest, chokidar) {
2
2
  'use strict';
3
3
 
4
4
  function _interopNamespaceDefault(e) {
@@ -434,6 +434,7 @@
434
434
  stabilityThresholdMs: 500,
435
435
  usePolling: false,
436
436
  pollIntervalMs: 1000,
437
+ respectGitignore: true,
437
438
  };
438
439
  /** Default embedding configuration. */
439
440
  const EMBEDDING_DEFAULTS = {
@@ -478,6 +479,11 @@
478
479
  .number()
479
480
  .optional()
480
481
  .describe('Time in milliseconds a file must remain unchanged before processing.'),
482
+ /** Whether to respect .gitignore files when processing. */
483
+ respectGitignore: zod.z
484
+ .boolean()
485
+ .optional()
486
+ .describe('Skip files ignored by .gitignore in git repositories. Only applies to repos with a .git directory. Default: true.'),
481
487
  });
482
488
  /**
483
489
  * Configuration watch settings.
@@ -645,6 +651,16 @@
645
651
  .number()
646
652
  .optional()
647
653
  .describe('Timeout in milliseconds for graceful shutdown.'),
654
+ /** Maximum consecutive system-level failures before triggering fatal error. Default: Infinity. */
655
+ maxRetries: zod.z
656
+ .number()
657
+ .optional()
658
+ .describe('Maximum consecutive system-level failures before triggering fatal error. Default: Infinity.'),
659
+ /** Maximum backoff delay in milliseconds for system errors. Default: 60000. */
660
+ maxBackoffMs: zod.z
661
+ .number()
662
+ .optional()
663
+ .describe('Maximum backoff delay in milliseconds for system errors. Default: 60000.'),
648
664
  });
649
665
 
650
666
  /**
@@ -933,6 +949,212 @@
933
949
  return factory(config, logger);
934
950
  }
935
951
 
952
+ /**
953
+ * @module gitignore
954
+ * Processor-level gitignore filtering. Scans watched paths for `.gitignore` files in git repos, caches parsed patterns, and exposes `isIgnored()` for path checking.
955
+ */
956
+ /**
957
+ * Find the git repo root by walking up from `startDir` looking for `.git/`.
958
+ * Returns `undefined` if no repo is found.
959
+ */
960
+ function findRepoRoot(startDir) {
961
+ let dir = node_path.resolve(startDir);
962
+ const root = node_path.resolve('/');
963
+ while (dir !== root) {
964
+ if (node_fs.existsSync(node_path.join(dir, '.git')) &&
965
+ node_fs.statSync(node_path.join(dir, '.git')).isDirectory()) {
966
+ return dir;
967
+ }
968
+ const parent = node_path.dirname(dir);
969
+ if (parent === dir)
970
+ break;
971
+ dir = parent;
972
+ }
973
+ return undefined;
974
+ }
975
+ /**
976
+ * Convert a watch path (directory, file path, or glob) to a concrete directory
977
+ * that can be scanned for a repo root.
978
+ */
979
+ function watchPathToScanDir(watchPath) {
980
+ const absPath = node_path.resolve(watchPath);
981
+ try {
982
+ return node_fs.statSync(absPath).isDirectory() ? absPath : node_path.dirname(absPath);
983
+ }
984
+ catch {
985
+ // ignore
986
+ }
987
+ // If this is a glob, fall back to the non-glob prefix.
988
+ const globMatch = /[*?[{]/.exec(watchPath);
989
+ if (!globMatch)
990
+ return undefined;
991
+ const prefix = watchPath.slice(0, globMatch.index);
992
+ const trimmed = prefix.trim();
993
+ const baseDir = trimmed.length === 0
994
+ ? '.'
995
+ : trimmed.endsWith('/') || trimmed.endsWith('\\')
996
+ ? trimmed
997
+ : node_path.dirname(trimmed);
998
+ const resolved = node_path.resolve(baseDir);
999
+ if (!node_fs.existsSync(resolved))
1000
+ return undefined;
1001
+ return resolved;
1002
+ }
1003
+ /**
1004
+ * Recursively find all `.gitignore` files under `dir`.
1005
+ * Skips `.git` and `node_modules` directories for performance.
1006
+ */
1007
+ function findGitignoreFiles(dir) {
1008
+ const results = [];
1009
+ const gitignorePath = node_path.join(dir, '.gitignore');
1010
+ if (node_fs.existsSync(gitignorePath)) {
1011
+ results.push(gitignorePath);
1012
+ }
1013
+ let entries;
1014
+ try {
1015
+ entries = node_fs.readdirSync(dir);
1016
+ }
1017
+ catch {
1018
+ return results;
1019
+ }
1020
+ for (const entry of entries) {
1021
+ if (entry === '.git' || entry === 'node_modules')
1022
+ continue;
1023
+ const fullPath = node_path.join(dir, entry);
1024
+ try {
1025
+ if (node_fs.statSync(fullPath).isDirectory()) {
1026
+ results.push(...findGitignoreFiles(fullPath));
1027
+ }
1028
+ }
1029
+ catch {
1030
+ // Skip inaccessible entries
1031
+ }
1032
+ }
1033
+ return results;
1034
+ }
1035
+ /**
1036
+ * Parse a `.gitignore` file into an `ignore` instance.
1037
+ */
1038
+ function parseGitignore(gitignorePath) {
1039
+ const content = node_fs.readFileSync(gitignorePath, 'utf8');
1040
+ return ignore().add(content);
1041
+ }
1042
+ /**
1043
+ * Normalize a path to use forward slashes (required by `ignore` package).
1044
+ */
1045
+ function toForwardSlash(p) {
1046
+ return p.replace(/\\/g, '/');
1047
+ }
1048
+ /**
1049
+ * Processor-level gitignore filter. Checks file paths against the nearest
1050
+ * `.gitignore` chain in git repositories.
1051
+ */
1052
+ class GitignoreFilter {
1053
+ repos = new Map();
1054
+ /**
1055
+ * Create a GitignoreFilter by scanning watched paths for `.gitignore` files.
1056
+ *
1057
+ * @param watchPaths - Absolute paths being watched (directories or globs resolved to roots).
1058
+ */
1059
+ constructor(watchPaths) {
1060
+ this.scan(watchPaths);
1061
+ }
1062
+ /**
1063
+ * Scan paths for git repos and their `.gitignore` files.
1064
+ */
1065
+ scan(watchPaths) {
1066
+ this.repos.clear();
1067
+ const scannedDirs = new Set();
1068
+ for (const watchPath of watchPaths) {
1069
+ const scanDir = watchPathToScanDir(watchPath);
1070
+ if (!scanDir)
1071
+ continue;
1072
+ if (scannedDirs.has(scanDir))
1073
+ continue;
1074
+ scannedDirs.add(scanDir);
1075
+ const repoRoot = findRepoRoot(scanDir);
1076
+ if (!repoRoot)
1077
+ continue;
1078
+ if (this.repos.has(repoRoot))
1079
+ continue;
1080
+ const gitignoreFiles = findGitignoreFiles(repoRoot);
1081
+ const entries = gitignoreFiles.map((gf) => ({
1082
+ dir: node_path.dirname(gf),
1083
+ ig: parseGitignore(gf),
1084
+ }));
1085
+ // Sort deepest-first so nested `.gitignore` files are checked first
1086
+ entries.sort((a, b) => b.dir.length - a.dir.length);
1087
+ this.repos.set(repoRoot, { root: repoRoot, entries });
1088
+ }
1089
+ }
1090
+ /**
1091
+ * Check whether a file path is ignored by any applicable `.gitignore`.
1092
+ *
1093
+ * @param filePath - Absolute file path to check.
1094
+ * @returns `true` if the file should be ignored.
1095
+ */
1096
+ isIgnored(filePath) {
1097
+ const absPath = node_path.resolve(filePath);
1098
+ for (const [, repo] of this.repos) {
1099
+ // Check if file is within this repo
1100
+ const relToRepo = node_path.relative(repo.root, absPath);
1101
+ if (relToRepo.startsWith('..') || relToRepo.startsWith(node_path.resolve('/'))) {
1102
+ continue;
1103
+ }
1104
+ // Check each `.gitignore` entry (deepest-first)
1105
+ for (const entry of repo.entries) {
1106
+ const relToEntry = node_path.relative(entry.dir, absPath);
1107
+ if (relToEntry.startsWith('..'))
1108
+ continue;
1109
+ const normalized = toForwardSlash(relToEntry);
1110
+ if (entry.ig.ignores(normalized)) {
1111
+ return true;
1112
+ }
1113
+ }
1114
+ }
1115
+ return false;
1116
+ }
1117
+ /**
1118
+ * Invalidate and re-parse a specific `.gitignore` file.
1119
+ * Call when a `.gitignore` file is added, changed, or removed.
1120
+ *
1121
+ * @param gitignorePath - Absolute path to the `.gitignore` file that changed.
1122
+ */
1123
+ invalidate(gitignorePath) {
1124
+ const absPath = node_path.resolve(gitignorePath);
1125
+ const gitignoreDir = node_path.dirname(absPath);
1126
+ for (const [, repo] of this.repos) {
1127
+ const relToRepo = node_path.relative(repo.root, gitignoreDir);
1128
+ if (relToRepo.startsWith('..'))
1129
+ continue;
1130
+ // Remove old entry for this directory
1131
+ repo.entries = repo.entries.filter((e) => e.dir !== gitignoreDir);
1132
+ // Re-parse if file still exists
1133
+ if (node_fs.existsSync(absPath)) {
1134
+ repo.entries.push({ dir: gitignoreDir, ig: parseGitignore(absPath) });
1135
+ // Re-sort deepest-first
1136
+ repo.entries.sort((a, b) => b.dir.length - a.dir.length);
1137
+ }
1138
+ return;
1139
+ }
1140
+ // If not in any known repo, check if it's in a repo we haven't scanned
1141
+ const repoRoot = findRepoRoot(gitignoreDir);
1142
+ if (repoRoot && node_fs.existsSync(absPath)) {
1143
+ const entries = [
1144
+ { dir: gitignoreDir, ig: parseGitignore(absPath) },
1145
+ ];
1146
+ if (this.repos.has(repoRoot)) {
1147
+ const repo = this.repos.get(repoRoot);
1148
+ repo.entries.push(entries[0]);
1149
+ repo.entries.sort((a, b) => b.dir.length - a.dir.length);
1150
+ }
1151
+ else {
1152
+ this.repos.set(repoRoot, { root: repoRoot, entries });
1153
+ }
1154
+ }
1155
+ }
1156
+ }
1157
+
936
1158
  /**
937
1159
  * @module logger
938
1160
  * Creates pino logger instances. I/O: optionally writes logs to file via pino/file transport. Defaults to stdout at info level.
@@ -1049,11 +1271,11 @@
1049
1271
  }
1050
1272
  async function extractPlaintext(filePath) {
1051
1273
  const raw = await promises.readFile(filePath, 'utf8');
1052
- return { text: raw };
1274
+ return { text: raw.replace(/^\uFEFF/, '') };
1053
1275
  }
1054
1276
  async function extractJson(filePath) {
1055
1277
  const raw = await promises.readFile(filePath, 'utf8');
1056
- const parsed = JSON.parse(raw);
1278
+ const parsed = JSON.parse(raw.replace(/^\uFEFF/, ''));
1057
1279
  const json = parsed && typeof parsed === 'object' && !Array.isArray(parsed)
1058
1280
  ? parsed
1059
1281
  : undefined;
@@ -1075,7 +1297,7 @@
1075
1297
  }
1076
1298
  async function extractHtml(filePath) {
1077
1299
  const raw = await promises.readFile(filePath, 'utf8');
1078
- const $ = cheerio__namespace.load(raw);
1300
+ const $ = cheerio__namespace.load(raw.replace(/^\uFEFF/, ''));
1079
1301
  $('script, style').remove();
1080
1302
  const text = $('body').text().trim() || $.text().trim();
1081
1303
  return { text };
@@ -1905,6 +2127,112 @@
1905
2127
  }
1906
2128
  }
1907
2129
 
2130
+ /**
2131
+ * @module health
2132
+ * Tracks consecutive system-level failures and applies exponential backoff.
2133
+ * Triggers fatal error callback when maxRetries is exceeded.
2134
+ */
2135
+ /**
2136
+ * Tracks system health via consecutive failure count and exponential backoff.
2137
+ */
2138
+ class SystemHealth {
2139
+ consecutiveFailures = 0;
2140
+ maxRetries;
2141
+ maxBackoffMs;
2142
+ baseDelayMs;
2143
+ onFatalError;
2144
+ logger;
2145
+ constructor(options) {
2146
+ this.maxRetries = options.maxRetries ?? Number.POSITIVE_INFINITY;
2147
+ this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
2148
+ this.baseDelayMs = options.baseDelayMs ?? 1000;
2149
+ this.onFatalError = options.onFatalError;
2150
+ this.logger = options.logger;
2151
+ }
2152
+ /**
2153
+ * Record a successful system operation. Resets the failure counter.
2154
+ */
2155
+ recordSuccess() {
2156
+ if (this.consecutiveFailures > 0) {
2157
+ this.logger.info({ previousFailures: this.consecutiveFailures }, 'System health recovered');
2158
+ }
2159
+ this.consecutiveFailures = 0;
2160
+ }
2161
+ /**
2162
+ * Record a system-level failure. If maxRetries is exceeded, triggers fatal error.
2163
+ *
2164
+ * @param error - The error that occurred.
2165
+ * @returns Whether the watcher should continue (false = fatal).
2166
+ */
2167
+ recordFailure(error) {
2168
+ this.consecutiveFailures += 1;
2169
+ this.logger.error({
2170
+ consecutiveFailures: this.consecutiveFailures,
2171
+ maxRetries: this.maxRetries,
2172
+ err: normalizeError(error),
2173
+ }, 'System-level failure recorded');
2174
+ if (this.consecutiveFailures >= this.maxRetries) {
2175
+ this.logger.fatal({ consecutiveFailures: this.consecutiveFailures }, 'Maximum retries exceeded, triggering fatal error');
2176
+ if (this.onFatalError) {
2177
+ this.onFatalError(error);
2178
+ return false;
2179
+ }
2180
+ throw error instanceof Error
2181
+ ? error
2182
+ : new Error(`Fatal system error: ${String(error)}`);
2183
+ }
2184
+ return true;
2185
+ }
2186
+ /**
2187
+ * Compute the current backoff delay based on consecutive failures.
2188
+ *
2189
+ * @returns Delay in milliseconds.
2190
+ */
2191
+ get currentBackoffMs() {
2192
+ if (this.consecutiveFailures === 0)
2193
+ return 0;
2194
+ const exp = Math.max(0, this.consecutiveFailures - 1);
2195
+ return Math.min(this.maxBackoffMs, this.baseDelayMs * 2 ** exp);
2196
+ }
2197
+ /**
2198
+ * Sleep for the current backoff duration.
2199
+ *
2200
+ * @param signal - Optional abort signal.
2201
+ */
2202
+ async backoff(signal) {
2203
+ const delay = this.currentBackoffMs;
2204
+ if (delay <= 0)
2205
+ return;
2206
+ this.logger.warn({ delayMs: delay, consecutiveFailures: this.consecutiveFailures }, 'Backing off before next attempt');
2207
+ await new Promise((resolve, reject) => {
2208
+ const timer = setTimeout(() => {
2209
+ cleanup();
2210
+ resolve();
2211
+ }, delay);
2212
+ const onAbort = () => {
2213
+ cleanup();
2214
+ reject(new Error('Backoff aborted'));
2215
+ };
2216
+ const cleanup = () => {
2217
+ clearTimeout(timer);
2218
+ if (signal)
2219
+ signal.removeEventListener('abort', onAbort);
2220
+ };
2221
+ if (signal) {
2222
+ if (signal.aborted) {
2223
+ onAbort();
2224
+ return;
2225
+ }
2226
+ signal.addEventListener('abort', onAbort, { once: true });
2227
+ }
2228
+ });
2229
+ }
2230
+ /** Current consecutive failure count. */
2231
+ get failures() {
2232
+ return this.consecutiveFailures;
2233
+ }
2234
+ }
2235
+
1908
2236
  /**
1909
2237
  * @module watcher
1910
2238
  * Filesystem watcher wrapping chokidar. I/O: watches files/directories for add/change/unlink events, enqueues to processing queue.
@@ -1917,6 +2245,8 @@
1917
2245
  queue;
1918
2246
  processor;
1919
2247
  logger;
2248
+ health;
2249
+ gitignoreFilter;
1920
2250
  watcher;
1921
2251
  /**
1922
2252
  * Create a new FileSystemWatcher.
@@ -1925,12 +2255,21 @@
1925
2255
  * @param queue - The event queue.
1926
2256
  * @param processor - The document processor.
1927
2257
  * @param logger - The logger instance.
2258
+ * @param options - Optional health/fatal error options.
1928
2259
  */
1929
- constructor(config, queue, processor, logger) {
2260
+ constructor(config, queue, processor, logger, options = {}) {
1930
2261
  this.config = config;
1931
2262
  this.queue = queue;
1932
2263
  this.processor = processor;
1933
2264
  this.logger = logger;
2265
+ this.gitignoreFilter = options.gitignoreFilter;
2266
+ const healthOptions = {
2267
+ maxRetries: options.maxRetries,
2268
+ maxBackoffMs: options.maxBackoffMs,
2269
+ onFatalError: options.onFatalError,
2270
+ logger,
2271
+ };
2272
+ this.health = new SystemHealth(healthOptions);
1934
2273
  }
1935
2274
  /**
1936
2275
  * Start watching the filesystem and processing events.
@@ -1946,19 +2285,29 @@
1946
2285
  ignoreInitial: false,
1947
2286
  });
1948
2287
  this.watcher.on('add', (path) => {
2288
+ this.handleGitignoreChange(path);
2289
+ if (this.isGitignored(path))
2290
+ return;
1949
2291
  this.logger.debug({ path }, 'File added');
1950
- this.queue.enqueue({ type: 'create', path, priority: 'normal' }, () => this.processor.processFile(path));
2292
+ this.queue.enqueue({ type: 'create', path, priority: 'normal' }, () => this.wrapProcessing(() => this.processor.processFile(path)));
1951
2293
  });
1952
2294
  this.watcher.on('change', (path) => {
2295
+ this.handleGitignoreChange(path);
2296
+ if (this.isGitignored(path))
2297
+ return;
1953
2298
  this.logger.debug({ path }, 'File changed');
1954
- this.queue.enqueue({ type: 'modify', path, priority: 'normal' }, () => this.processor.processFile(path));
2299
+ this.queue.enqueue({ type: 'modify', path, priority: 'normal' }, () => this.wrapProcessing(() => this.processor.processFile(path)));
1955
2300
  });
1956
2301
  this.watcher.on('unlink', (path) => {
2302
+ this.handleGitignoreChange(path);
2303
+ if (this.isGitignored(path))
2304
+ return;
1957
2305
  this.logger.debug({ path }, 'File removed');
1958
- this.queue.enqueue({ type: 'delete', path, priority: 'normal' }, () => this.processor.deleteFile(path));
2306
+ this.queue.enqueue({ type: 'delete', path, priority: 'normal' }, () => this.wrapProcessing(() => this.processor.deleteFile(path)));
1959
2307
  });
1960
2308
  this.watcher.on('error', (error) => {
1961
2309
  this.logger.error({ err: normalizeError(error) }, 'Watcher error');
2310
+ this.health.recordFailure(error);
1962
2311
  });
1963
2312
  this.queue.process();
1964
2313
  this.logger.info({ paths: this.config.paths }, 'Filesystem watcher started');
@@ -1973,6 +2322,53 @@
1973
2322
  this.logger.info('Filesystem watcher stopped');
1974
2323
  }
1975
2324
  }
2325
+ /**
2326
+ * Get the system health tracker.
2327
+ */
2328
+ get systemHealth() {
2329
+ return this.health;
2330
+ }
2331
+ /**
2332
+ * Check if a path is gitignored and should be skipped.
2333
+ */
2334
+ isGitignored(path) {
2335
+ if (!this.gitignoreFilter)
2336
+ return false;
2337
+ const ignored = this.gitignoreFilter.isIgnored(path);
2338
+ if (ignored) {
2339
+ this.logger.debug({ path }, 'Skipping gitignored file');
2340
+ }
2341
+ return ignored;
2342
+ }
2343
+ /**
2344
+ * If the changed file is a `.gitignore`, invalidate the filter cache.
2345
+ */
2346
+ handleGitignoreChange(path) {
2347
+ if (!this.gitignoreFilter)
2348
+ return;
2349
+ if (path.endsWith('.gitignore')) {
2350
+ this.logger.info({ path }, 'Gitignore file changed, refreshing filter');
2351
+ this.gitignoreFilter.invalidate(path);
2352
+ }
2353
+ }
2354
+ /**
2355
+ * Wrap a processing operation with health tracking.
2356
+ * On success, resets the failure counter.
2357
+ * On failure, records the failure and applies backoff.
2358
+ */
2359
+ async wrapProcessing(fn) {
2360
+ try {
2361
+ await this.health.backoff();
2362
+ await fn();
2363
+ this.health.recordSuccess();
2364
+ }
2365
+ catch (error) {
2366
+ const shouldContinue = this.health.recordFailure(error);
2367
+ if (!shouldContinue) {
2368
+ await this.stop();
2369
+ }
2370
+ }
2371
+ }
1976
2372
  }
1977
2373
 
1978
2374
  /**
@@ -2048,7 +2444,7 @@
2048
2444
  compileRules,
2049
2445
  createDocumentProcessor: (config, embeddingProvider, vectorStore, compiledRules, logger) => new DocumentProcessor(config, embeddingProvider, vectorStore, compiledRules, logger),
2050
2446
  createEventQueue: (options) => new EventQueue(options),
2051
- createFileSystemWatcher: (config, queue, processor, logger) => new FileSystemWatcher(config, queue, processor, logger),
2447
+ createFileSystemWatcher: (config, queue, processor, logger, options) => new FileSystemWatcher(config, queue, processor, logger, options),
2052
2448
  createApiServer,
2053
2449
  };
2054
2450
  /**
@@ -2058,6 +2454,7 @@
2058
2454
  config;
2059
2455
  configPath;
2060
2456
  factories;
2457
+ runtimeOptions;
2061
2458
  logger;
2062
2459
  watcher;
2063
2460
  queue;
@@ -2070,11 +2467,13 @@
2070
2467
  * @param config - The application configuration.
2071
2468
  * @param configPath - Optional config file path to watch for changes.
2072
2469
  * @param factories - Optional component factories (for dependency injection).
2470
+ * @param runtimeOptions - Optional runtime-only options (e.g., onFatalError).
2073
2471
  */
2074
- constructor(config, configPath, factories = {}) {
2472
+ constructor(config, configPath, factories = {}, runtimeOptions = {}) {
2075
2473
  this.config = config;
2076
2474
  this.configPath = configPath;
2077
2475
  this.factories = { ...defaultFactories, ...factories };
2476
+ this.runtimeOptions = runtimeOptions;
2078
2477
  }
2079
2478
  /**
2080
2479
  * Start the watcher, API server, and all components.
@@ -2107,7 +2506,16 @@
2107
2506
  rateLimitPerMinute: this.config.embedding.rateLimitPerMinute,
2108
2507
  });
2109
2508
  this.queue = queue;
2110
- const watcher = this.factories.createFileSystemWatcher(this.config.watch, queue, processor, logger);
2509
+ const respectGitignore = this.config.watch.respectGitignore ?? true;
2510
+ const gitignoreFilter = respectGitignore
2511
+ ? new GitignoreFilter(this.config.watch.paths)
2512
+ : undefined;
2513
+ const watcher = this.factories.createFileSystemWatcher(this.config.watch, queue, processor, logger, {
2514
+ maxRetries: this.config.maxRetries,
2515
+ maxBackoffMs: this.config.maxBackoffMs,
2516
+ onFatalError: this.runtimeOptions.onFatalError,
2517
+ gitignoreFilter,
2518
+ });
2111
2519
  this.watcher = watcher;
2112
2520
  const server = this.factories.createApiServer({
2113
2521
  processor,
@@ -2215,7 +2623,9 @@
2215
2623
  exports.DocumentProcessor = DocumentProcessor;
2216
2624
  exports.EventQueue = EventQueue;
2217
2625
  exports.FileSystemWatcher = FileSystemWatcher;
2626
+ exports.GitignoreFilter = GitignoreFilter;
2218
2627
  exports.JeevesWatcher = JeevesWatcher;
2628
+ exports.SystemHealth = SystemHealth;
2219
2629
  exports.VectorStoreClient = VectorStoreClient;
2220
2630
  exports.apiConfigSchema = apiConfigSchema;
2221
2631
  exports.applyRules = applyRules;
@@ -2241,4 +2651,4 @@
2241
2651
  exports.watchConfigSchema = watchConfigSchema;
2242
2652
  exports.writeMetadata = writeMetadata;
2243
2653
 
2244
- })(this["jeeves-watcher"] = this["jeeves-watcher"] || {}, Fastify, promises, node_path, picomatch, radash, node_crypto, cosmiconfig, zod, jsonmap, googleGenai, pino, uuid, cheerio, yaml, mammoth, Ajv, addFormats, textsplitters, jsClientRest, chokidar);
2654
+ })(this["jeeves-watcher"] = this["jeeves-watcher"] || {}, Fastify, promises, node_path, picomatch, radash, node_crypto, cosmiconfig, zod, jsonmap, googleGenai, node_fs, ignore, pino, uuid, cheerio, yaml, mammoth, Ajv, addFormats, textsplitters, jsClientRest, chokidar);