@karmaniverous/jeeves-watcher 0.10.0 → 0.11.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.
@@ -4268,7 +4268,8 @@ function createStatusHandler(deps) {
4268
4268
 
4269
4269
  /**
4270
4270
  * @module api/handlers/walk
4271
- * Fastify route handler for POST /walk. Walks watched filesystem paths with caller-provided glob intersection.
4271
+ * Fastify route handler for POST /walk. Enumerates watched files matching caller-provided globs
4272
+ * using chokidar's in-memory file list (zero filesystem I/O).
4272
4273
  */
4273
4274
  /**
4274
4275
  * Create handler for POST /walk.
@@ -4284,8 +4285,22 @@ function createWalkHandler(deps) {
4284
4285
  message: 'The "globs" field is required and must be a non-empty string array.',
4285
4286
  });
4286
4287
  }
4287
- const isGitignored = createIsGitignored(deps.gitignoreFilter);
4288
- const paths = await listFilesFromWatchRoots(deps.watchPaths, deps.watchIgnored, globs, isGitignored);
4288
+ if (!deps.fileSystemWatcher) {
4289
+ return await reply.status(503).send({
4290
+ error: 'Watcher unavailable',
4291
+ message: 'Filesystem watcher is not initialized.',
4292
+ });
4293
+ }
4294
+ if (!deps.fileSystemWatcher.isReady) {
4295
+ return await reply.status(503).send({
4296
+ error: 'Scan in progress',
4297
+ message: 'Initial filesystem scan is still active. Try again after scan completes.',
4298
+ });
4299
+ }
4300
+ const watchedFiles = deps.fileSystemWatcher.getWatchedFiles();
4301
+ const normGlobs = globs.map((g) => normalizeSlashes(g));
4302
+ const matchGlobs = picomatch(normGlobs, { dot: true, nocase: true });
4303
+ const paths = watchedFiles.filter((f) => matchGlobs(normalizeSlashes(f)));
4289
4304
  const scannedRoots = getWatchRootBases(deps.watchPaths);
4290
4305
  return {
4291
4306
  paths,
@@ -4719,8 +4734,7 @@ function createApiServer(options) {
4719
4734
  }));
4720
4735
  app.post('/walk', createWalkHandler({
4721
4736
  watchPaths: config.watch.paths,
4722
- watchIgnored: config.watch.ignored ?? [],
4723
- gitignoreFilter,
4737
+ fileSystemWatcher: options.fileSystemWatcher,
4724
4738
  logger,
4725
4739
  }));
4726
4740
  app.post('/search', createSearchHandler({
@@ -6785,6 +6799,33 @@ class FileSystemWatcher {
6785
6799
  this.queue.process();
6786
6800
  this.logger.info({ paths: this.config.paths }, 'Filesystem watcher started');
6787
6801
  }
6802
+ /**
6803
+ * Get the list of all currently watched file paths (absolute).
6804
+ *
6805
+ * Uses chokidar's in-memory `getWatched()` state — no filesystem I/O.
6806
+ * Returns an empty array if the watcher hasn't been started.
6807
+ */
6808
+ getWatchedFiles() {
6809
+ if (!this.watcher)
6810
+ return [];
6811
+ const watched = this.watcher.getWatched();
6812
+ const files = [];
6813
+ for (const [dir, entries] of Object.entries(watched)) {
6814
+ for (const entry of entries) {
6815
+ const full = join(dir, entry);
6816
+ files.push(full);
6817
+ }
6818
+ }
6819
+ return files;
6820
+ }
6821
+ /**
6822
+ * Whether the initial filesystem scan is complete.
6823
+ */
6824
+ get isReady() {
6825
+ return this.initialScanTracker
6826
+ ? !this.initialScanTracker.getStatus().active
6827
+ : true;
6828
+ }
6788
6829
  /**
6789
6830
  * Stop the filesystem watcher.
6790
6831
  */
@@ -7032,6 +7073,7 @@ class JeevesWatcher {
7032
7073
  gitignoreFilter: this.gitignoreFilter,
7033
7074
  version: this.version,
7034
7075
  initialScanTracker: this.initialScanTracker,
7076
+ fileSystemWatcher: this.watcher,
7035
7077
  });
7036
7078
  await server.listen({
7037
7079
  host: this.config.api?.host ?? '127.0.0.1',
package/dist/index.d.ts CHANGED
@@ -1303,107 +1303,6 @@ declare class InitialScanTracker {
1303
1303
  getStatus(): InitialScanStatus;
1304
1304
  }
1305
1305
 
1306
- /**
1307
- * @module api/ReindexTracker
1308
- * Tracks reindex operation state for status reporting. Single instance shared across handlers.
1309
- */
1310
- /** Reindex status snapshot for API consumers. */
1311
- interface ReindexStatus {
1312
- /** Whether a reindex operation is currently in progress. */
1313
- active: boolean;
1314
- /** The active reindex scope (when {@link active} is true). */
1315
- scope?: string;
1316
- /** ISO 8601 timestamp when the current reindex started (when {@link active} is true). */
1317
- startedAt?: string;
1318
- /** Number of files processed so far (when {@link active} is true). */
1319
- filesProcessed?: number;
1320
- /** Total number of files to process (when {@link active} is true). */
1321
- totalFiles?: number;
1322
- }
1323
- /**
1324
- * Tracks the state of reindex operations.
1325
- */
1326
- declare class ReindexTracker {
1327
- private _active;
1328
- private _scope?;
1329
- private _startedAt?;
1330
- private _filesProcessed;
1331
- private _totalFiles;
1332
- /** Mark a reindex as started. */
1333
- start(scope: string): void;
1334
- /** Set the total number of files to process. */
1335
- setTotal(total: number): void;
1336
- /** Increment the processed file count. */
1337
- incrementProcessed(): void;
1338
- /** Mark the current reindex as complete. */
1339
- complete(): void;
1340
- /** Get current reindex status. */
1341
- getStatus(): ReindexStatus;
1342
- }
1343
-
1344
- /**
1345
- * @module api
1346
- * Fastify API server factory. Registers all route handlers and returns an unstarted server instance.
1347
- */
1348
-
1349
- /**
1350
- * Options for {@link createApiServer}.
1351
- */
1352
- interface ApiServerOptions {
1353
- /** The document processor. */
1354
- processor: DocumentProcessorInterface;
1355
- /** The vector store client. */
1356
- vectorStore: VectorStoreClient;
1357
- /** The embedding provider. */
1358
- embeddingProvider: EmbeddingProvider;
1359
- /** The event queue. */
1360
- queue: EventQueue;
1361
- /** The application configuration. */
1362
- config: JeevesWatcherConfig;
1363
- /** The logger instance. */
1364
- logger: pino.Logger;
1365
- /** The issues manager. */
1366
- issuesManager: IssuesManager;
1367
- /** The values manager. */
1368
- valuesManager: ValuesManager;
1369
- /** The reindex tracker (optional, created if not provided). */
1370
- reindexTracker?: ReindexTracker;
1371
- /** Path to the config file on disk. */
1372
- configPath: string;
1373
- /** Helper introspection for merged document. */
1374
- helperIntrospection?: AllHelpersIntrospection;
1375
- /** Virtual rule store for externally registered inference rules. */
1376
- virtualRuleStore?: VirtualRuleStore;
1377
- /** Gitignore filter for reindex path validation. */
1378
- gitignoreFilter?: GitignoreFilter;
1379
- /** Service version string for /status endpoint. */
1380
- version?: string;
1381
- /** Initial scan tracker for /status visibility. */
1382
- initialScanTracker?: InitialScanTracker;
1383
- }
1384
- /**
1385
- * Create the Fastify API server with all routes registered.
1386
- *
1387
- * The returned instance is not yet listening — call `server.listen()` to start.
1388
- *
1389
- * @param options - The server options.
1390
- * @returns A configured Fastify instance.
1391
- */
1392
- declare function createApiServer(options: ApiServerOptions): FastifyInstance;
1393
-
1394
- /**
1395
- * @module logger
1396
- * Creates pino logger instances. I/O: optionally writes logs to file via pino/file transport. Defaults to stdout at info level.
1397
- */
1398
-
1399
- /**
1400
- * Create a pino logger instance.
1401
- *
1402
- * @param config - Optional logging configuration.
1403
- * @returns A configured pino logger.
1404
- */
1405
- declare function createLogger(config?: LoggingConfig): pino.Logger;
1406
-
1407
1306
  /**
1408
1307
  * @module health
1409
1308
  * Tracks consecutive system-level failures and applies exponential backoff.
@@ -1505,6 +1404,17 @@ declare class FileSystemWatcher {
1505
1404
  * Start watching the filesystem and processing events.
1506
1405
  */
1507
1406
  start(): void;
1407
+ /**
1408
+ * Get the list of all currently watched file paths (absolute).
1409
+ *
1410
+ * Uses chokidar's in-memory `getWatched()` state — no filesystem I/O.
1411
+ * Returns an empty array if the watcher hasn't been started.
1412
+ */
1413
+ getWatchedFiles(): string[];
1414
+ /**
1415
+ * Whether the initial filesystem scan is complete.
1416
+ */
1417
+ get isReady(): boolean;
1508
1418
  /**
1509
1419
  * Stop the filesystem watcher.
1510
1420
  */
@@ -1529,6 +1439,109 @@ declare class FileSystemWatcher {
1529
1439
  private wrapProcessing;
1530
1440
  }
1531
1441
 
1442
+ /**
1443
+ * @module api/ReindexTracker
1444
+ * Tracks reindex operation state for status reporting. Single instance shared across handlers.
1445
+ */
1446
+ /** Reindex status snapshot for API consumers. */
1447
+ interface ReindexStatus {
1448
+ /** Whether a reindex operation is currently in progress. */
1449
+ active: boolean;
1450
+ /** The active reindex scope (when {@link active} is true). */
1451
+ scope?: string;
1452
+ /** ISO 8601 timestamp when the current reindex started (when {@link active} is true). */
1453
+ startedAt?: string;
1454
+ /** Number of files processed so far (when {@link active} is true). */
1455
+ filesProcessed?: number;
1456
+ /** Total number of files to process (when {@link active} is true). */
1457
+ totalFiles?: number;
1458
+ }
1459
+ /**
1460
+ * Tracks the state of reindex operations.
1461
+ */
1462
+ declare class ReindexTracker {
1463
+ private _active;
1464
+ private _scope?;
1465
+ private _startedAt?;
1466
+ private _filesProcessed;
1467
+ private _totalFiles;
1468
+ /** Mark a reindex as started. */
1469
+ start(scope: string): void;
1470
+ /** Set the total number of files to process. */
1471
+ setTotal(total: number): void;
1472
+ /** Increment the processed file count. */
1473
+ incrementProcessed(): void;
1474
+ /** Mark the current reindex as complete. */
1475
+ complete(): void;
1476
+ /** Get current reindex status. */
1477
+ getStatus(): ReindexStatus;
1478
+ }
1479
+
1480
+ /**
1481
+ * @module api
1482
+ * Fastify API server factory. Registers all route handlers and returns an unstarted server instance.
1483
+ */
1484
+
1485
+ /**
1486
+ * Options for {@link createApiServer}.
1487
+ */
1488
+ interface ApiServerOptions {
1489
+ /** The document processor. */
1490
+ processor: DocumentProcessorInterface;
1491
+ /** The vector store client. */
1492
+ vectorStore: VectorStoreClient;
1493
+ /** The embedding provider. */
1494
+ embeddingProvider: EmbeddingProvider;
1495
+ /** The event queue. */
1496
+ queue: EventQueue;
1497
+ /** The application configuration. */
1498
+ config: JeevesWatcherConfig;
1499
+ /** The logger instance. */
1500
+ logger: pino.Logger;
1501
+ /** The issues manager. */
1502
+ issuesManager: IssuesManager;
1503
+ /** The values manager. */
1504
+ valuesManager: ValuesManager;
1505
+ /** The reindex tracker (optional, created if not provided). */
1506
+ reindexTracker?: ReindexTracker;
1507
+ /** Path to the config file on disk. */
1508
+ configPath: string;
1509
+ /** Helper introspection for merged document. */
1510
+ helperIntrospection?: AllHelpersIntrospection;
1511
+ /** Virtual rule store for externally registered inference rules. */
1512
+ virtualRuleStore?: VirtualRuleStore;
1513
+ /** Gitignore filter for reindex path validation. */
1514
+ gitignoreFilter?: GitignoreFilter;
1515
+ /** Service version string for /status endpoint. */
1516
+ version?: string;
1517
+ /** Initial scan tracker for /status visibility. */
1518
+ initialScanTracker?: InitialScanTracker;
1519
+ /** Filesystem watcher instance for /walk endpoint (in-memory file list). */
1520
+ fileSystemWatcher?: FileSystemWatcher;
1521
+ }
1522
+ /**
1523
+ * Create the Fastify API server with all routes registered.
1524
+ *
1525
+ * The returned instance is not yet listening — call `server.listen()` to start.
1526
+ *
1527
+ * @param options - The server options.
1528
+ * @returns A configured Fastify instance.
1529
+ */
1530
+ declare function createApiServer(options: ApiServerOptions): FastifyInstance;
1531
+
1532
+ /**
1533
+ * @module logger
1534
+ * Creates pino logger instances. I/O: optionally writes logs to file via pino/file transport. Defaults to stdout at info level.
1535
+ */
1536
+
1537
+ /**
1538
+ * Create a pino logger instance.
1539
+ *
1540
+ * @param config - Optional logging configuration.
1541
+ * @returns A configured pino logger.
1542
+ */
1543
+ declare function createLogger(config?: LoggingConfig): pino.Logger;
1544
+
1532
1545
  /**
1533
1546
  * @module app/factories
1534
1547
  * Component factory interfaces and defaults for {@link JeevesWatcher}. Override in tests to inject mocks.
package/dist/index.js CHANGED
@@ -3890,7 +3890,8 @@ function createStatusHandler(deps) {
3890
3890
 
3891
3891
  /**
3892
3892
  * @module api/handlers/walk
3893
- * Fastify route handler for POST /walk. Walks watched filesystem paths with caller-provided glob intersection.
3893
+ * Fastify route handler for POST /walk. Enumerates watched files matching caller-provided globs
3894
+ * using chokidar's in-memory file list (zero filesystem I/O).
3894
3895
  */
3895
3896
  /**
3896
3897
  * Create handler for POST /walk.
@@ -3906,8 +3907,22 @@ function createWalkHandler(deps) {
3906
3907
  message: 'The "globs" field is required and must be a non-empty string array.',
3907
3908
  });
3908
3909
  }
3909
- const isGitignored = createIsGitignored(deps.gitignoreFilter);
3910
- const paths = await listFilesFromWatchRoots(deps.watchPaths, deps.watchIgnored, globs, isGitignored);
3910
+ if (!deps.fileSystemWatcher) {
3911
+ return await reply.status(503).send({
3912
+ error: 'Watcher unavailable',
3913
+ message: 'Filesystem watcher is not initialized.',
3914
+ });
3915
+ }
3916
+ if (!deps.fileSystemWatcher.isReady) {
3917
+ return await reply.status(503).send({
3918
+ error: 'Scan in progress',
3919
+ message: 'Initial filesystem scan is still active. Try again after scan completes.',
3920
+ });
3921
+ }
3922
+ const watchedFiles = deps.fileSystemWatcher.getWatchedFiles();
3923
+ const normGlobs = globs.map((g) => normalizeSlashes(g));
3924
+ const matchGlobs = picomatch(normGlobs, { dot: true, nocase: true });
3925
+ const paths = watchedFiles.filter((f) => matchGlobs(normalizeSlashes(f)));
3911
3926
  const scannedRoots = getWatchRootBases(deps.watchPaths);
3912
3927
  return {
3913
3928
  paths,
@@ -4477,8 +4492,7 @@ function createApiServer(options) {
4477
4492
  }));
4478
4493
  app.post('/walk', createWalkHandler({
4479
4494
  watchPaths: config.watch.paths,
4480
- watchIgnored: config.watch.ignored ?? [],
4481
- gitignoreFilter,
4495
+ fileSystemWatcher: options.fileSystemWatcher,
4482
4496
  logger,
4483
4497
  }));
4484
4498
  app.post('/search', createSearchHandler({
@@ -6763,6 +6777,33 @@ class FileSystemWatcher {
6763
6777
  this.queue.process();
6764
6778
  this.logger.info({ paths: this.config.paths }, 'Filesystem watcher started');
6765
6779
  }
6780
+ /**
6781
+ * Get the list of all currently watched file paths (absolute).
6782
+ *
6783
+ * Uses chokidar's in-memory `getWatched()` state — no filesystem I/O.
6784
+ * Returns an empty array if the watcher hasn't been started.
6785
+ */
6786
+ getWatchedFiles() {
6787
+ if (!this.watcher)
6788
+ return [];
6789
+ const watched = this.watcher.getWatched();
6790
+ const files = [];
6791
+ for (const [dir, entries] of Object.entries(watched)) {
6792
+ for (const entry of entries) {
6793
+ const full = join(dir, entry);
6794
+ files.push(full);
6795
+ }
6796
+ }
6797
+ return files;
6798
+ }
6799
+ /**
6800
+ * Whether the initial filesystem scan is complete.
6801
+ */
6802
+ get isReady() {
6803
+ return this.initialScanTracker
6804
+ ? !this.initialScanTracker.getStatus().active
6805
+ : true;
6806
+ }
6766
6807
  /**
6767
6808
  * Stop the filesystem watcher.
6768
6809
  */
@@ -7010,6 +7051,7 @@ class JeevesWatcher {
7010
7051
  gitignoreFilter: this.gitignoreFilter,
7011
7052
  version: this.version,
7012
7053
  initialScanTracker: this.initialScanTracker,
7054
+ fileSystemWatcher: this.watcher,
7013
7055
  });
7014
7056
  await server.listen({
7015
7057
  host: this.config.api?.host ?? '127.0.0.1',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karmaniverous/jeeves-watcher",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "author": "Jason Williscroft",
5
5
  "description": "Filesystem watcher that keeps a Qdrant vector store in sync with document changes",
6
6
  "license": "BSD-3-Clause",
@@ -121,6 +121,7 @@
121
121
  },
122
122
  "auto-changelog": {
123
123
  "output": "CHANGELOG.md",
124
+ "tagPrefix": "service/",
124
125
  "unreleased": true,
125
126
  "commitLimit": false,
126
127
  "hideCredit": true