@karmaniverous/jeeves-watcher 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -8,6 +8,7 @@ var radash = require('radash');
8
8
  var node_crypto = require('node:crypto');
9
9
  var node_fs = require('node:fs');
10
10
  var ignore = require('ignore');
11
+ var node_url = require('node:url');
11
12
  var jsonmap = require('@karmaniverous/jsonmap');
12
13
  var Handlebars = require('handlebars');
13
14
  var dayjs = require('dayjs');
@@ -791,7 +792,7 @@ async function loadCustomMapHelpers(paths, configDir) {
791
792
  const merged = {};
792
793
  for (const p of paths) {
793
794
  const resolved = node_path.resolve(configDir, p);
794
- const mod = (await import(resolved));
795
+ const mod = (await import(node_url.pathToFileURL(resolved).href));
795
796
  const fns = typeof mod.default === 'object' && mod.default !== null
796
797
  ? mod.default
797
798
  : mod;
@@ -1039,7 +1040,7 @@ function createHandlebarsInstance() {
1039
1040
  async function loadCustomHelpers(hbs, paths, configDir) {
1040
1041
  for (const p of paths) {
1041
1042
  const resolved = node_path.resolve(configDir, p);
1042
- const mod = (await import(resolved));
1043
+ const mod = (await import(node_url.pathToFileURL(resolved).href));
1043
1044
  if (typeof mod.default === 'function') {
1044
1045
  mod.default(hbs);
1045
1046
  }
@@ -3,6 +3,7 @@ import { Command } from '@commander-js/extra-typings';
3
3
  import { dirname, resolve, relative, join, extname, basename } from 'node:path';
4
4
  import { existsSync, statSync, readdirSync, readFileSync } from 'node:fs';
5
5
  import ignore from 'ignore';
6
+ import { pathToFileURL } from 'node:url';
6
7
  import { JsonMap, jsonMapMapSchema } from '@karmaniverous/jsonmap';
7
8
  import { get, capitalize, title, camel, snake, dash, isEqual, omit } from 'radash';
8
9
  import Handlebars from 'handlebars';
@@ -376,7 +377,7 @@ async function loadCustomMapHelpers(paths, configDir) {
376
377
  const merged = {};
377
378
  for (const p of paths) {
378
379
  const resolved = resolve(configDir, p);
379
- const mod = (await import(resolved));
380
+ const mod = (await import(pathToFileURL(resolved).href));
380
381
  const fns = typeof mod.default === 'object' && mod.default !== null
381
382
  ? mod.default
382
383
  : mod;
@@ -624,7 +625,7 @@ function createHandlebarsInstance() {
624
625
  async function loadCustomHelpers(hbs, paths, configDir) {
625
626
  for (const p of paths) {
626
627
  const resolved = resolve(configDir, p);
627
- const mod = (await import(resolved));
628
+ const mod = (await import(pathToFileURL(resolved).href));
628
629
  if (typeof mod.default === 'function') {
629
630
  mod.default(hbs);
630
631
  }
@@ -1,4 +1,4 @@
1
- (function (exports, Fastify, promises, node_path, picomatch, radash, node_crypto, node_fs, ignore, jsonmap, Handlebars, dayjs, hastUtilToMdast, mdastUtilFromAdf, mdastUtilToMarkdown, rehypeParse, unified, chokidar, cosmiconfig, zod, googleGenai, pino, uuid, cheerio, yaml, mammoth, Ajv, addFormats, textsplitters, jsClientRest) {
1
+ (function (exports, Fastify, promises, node_path, picomatch, radash, node_crypto, node_fs, ignore, node_url, jsonmap, Handlebars, dayjs, hastUtilToMdast, mdastUtilFromAdf, mdastUtilToMarkdown, rehypeParse, unified, chokidar, cosmiconfig, zod, googleGenai, pino, uuid, cheerio, yaml, mammoth, Ajv, addFormats, textsplitters, jsClientRest) {
2
2
  'use strict';
3
3
 
4
4
  function _interopNamespaceDefault(e) {
@@ -762,7 +762,7 @@
762
762
  const merged = {};
763
763
  for (const p of paths) {
764
764
  const resolved = node_path.resolve(configDir, p);
765
- const mod = (await import(resolved));
765
+ const mod = (await import(node_url.pathToFileURL(resolved).href));
766
766
  const fns = typeof mod.default === 'object' && mod.default !== null
767
767
  ? mod.default
768
768
  : mod;
@@ -1010,7 +1010,7 @@
1010
1010
  async function loadCustomHelpers(hbs, paths, configDir) {
1011
1011
  for (const p of paths) {
1012
1012
  const resolved = node_path.resolve(configDir, p);
1013
- const mod = (await import(resolved));
1013
+ const mod = (await import(node_url.pathToFileURL(resolved).href));
1014
1014
  if (typeof mod.default === 'function') {
1015
1015
  mod.default(hbs);
1016
1016
  }
@@ -3312,4 +3312,4 @@
3312
3312
  exports.watchConfigSchema = watchConfigSchema;
3313
3313
  exports.writeMetadata = writeMetadata;
3314
3314
 
3315
- })(this["jeeves-watcher"] = this["jeeves-watcher"] || {}, Fastify, promises, node_path, picomatch, radash, node_crypto, node_fs, ignore, jsonmap, Handlebars, dayjs, hastUtilToMdast, mdastUtilFromAdf, mdastUtilToMarkdown, rehypeParse, unified, chokidar, cosmiconfig, zod, googleGenai, pino, uuid, cheerio, yaml, mammoth, Ajv, addFormats, textsplitters, jsClientRest);
3315
+ })(this["jeeves-watcher"] = this["jeeves-watcher"] || {}, Fastify, promises, node_path, picomatch, radash, node_crypto, node_fs, ignore, node_url, jsonmap, Handlebars, dayjs, hastUtilToMdast, mdastUtilFromAdf, mdastUtilToMarkdown, rehypeParse, unified, chokidar, cosmiconfig, zod, googleGenai, pino, uuid, cheerio, yaml, mammoth, Ajv, addFormats, textsplitters, jsClientRest);
@@ -1 +1 @@
1
- !function(e,t,r,i,n,o,s,a,c,l,h,d,u,f,g,p,m,y,w,b,v,M,S,F,k,x,P,z,j,C){"use strict";function E(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var i=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,i.get?i:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var R=E(F);function T(e){if(e instanceof Error)return e;if("string"==typeof e)return new Error(e);const t=String("object"==typeof e&&null!==e&&"message"in e?e.message:e),r=new Error(t);return r.cause=e,r}function D(e){const t=e.replace(/\\/g,"/"),r=t.search(/[*?\[]/);if(-1===r)return i.resolve(e);const n=t.slice(0,r),o=n.endsWith("/")?n.slice(0,-1):i.dirname(n);return i.resolve(o)}async function*A(e){let t;try{t=(await r.readdir(e,{withFileTypes:!0})).map((e=>({name:e.name,isDirectory:e.isDirectory()})))}catch{return}for(const n of t){const t=i.resolve(e,n.name);if(n.isDirectory)yield*A(t);else try{(await r.stat(t)).isFile()&&(yield t)}catch{}}}async function I(e,t,r,i){const o=await async function(e,t=[]){const r=e.map((e=>e.replace(/\\/g,"/"))),i=t.map((e=>e.replace(/\\/g,"/"))),o=n(r,{dot:!0}),s=i.length?n(i,{dot:!0}):()=>!1,a=Array.from(new Set(e.map(D))),c=new Set;for(const e of a)for await(const t of A(e)){const e=t.replace(/\\/g,"/");s(e)||o(e)&&c.add(t)}return Array.from(c)}(e,t);for(const e of o)await r[i](e);return o.length}function W(e,t=!1){let r=e.replace(/\\/g,"/").toLowerCase();return t&&(r=r.replace(/^([a-z]):/,((e,t)=>t))),r}function N(e,t){const r=W(e,!0),n=s.createHash("sha256").update(r,"utf8").digest("hex");return i.join(t,`${n}.meta.json`)}async function H(e,t){try{const i=await r.readFile(N(e,t),"utf8");return JSON.parse(i)}catch{return null}}async function O(e,t,n){const o=N(e,t);await r.mkdir(i.dirname(o),{recursive:!0}),await r.writeFile(o,JSON.stringify(n,null,2),"utf8")}async function L(e,t){try{await r.rm(N(e,t))}catch{}}const _=["file_path","chunk_index","total_chunks","content_hash","chunk_text"];function $(e){const{processor:r,vectorStore:i,embeddingProvider:n,logger:s,config:a}=e,c=t({logger:!1});var l;return c.get("/status",(l={vectorStore:i,config:a},async()=>{const e=await l.vectorStore.getCollectionInfo();return{status:"ok",uptime:process.uptime(),collection:{name:l.config.vectorStore.collectionName,pointCount:e.pointCount,dimensions:e.dimensions},payloadFields:e.payloadFields}})),c.post("/metadata",function(e){return async(t,r)=>{try{const{path:r,metadata:i}=t.body;return await e.processor.processMetadataUpdate(r,i),{ok:!0}}catch(t){return e.logger.error({err:T(t)},"Metadata update failed"),r.status(500).send({error:"Internal server error"})}}}({processor:r,logger:s})),c.post("/search",function(e){return async(t,r)=>{try{const{query:r,limit:i=10,filter:n}=t.body,o=await e.embeddingProvider.embed([r]);return await e.vectorStore.search(o[0],i,n)}catch(t){return e.logger.error({err:T(t)},"Search failed"),r.status(500).send({error:"Internal server error"})}}}({embeddingProvider:n,vectorStore:i,logger:s})),c.post("/reindex",function(e){return async(t,r)=>{try{const t=await I(e.config.watch.paths,e.config.watch.ignored,e.processor,"processFile");return await r.status(200).send({ok:!0,filesIndexed:t})}catch(t){return e.logger.error({err:T(t)},"Reindex failed"),await r.status(500).send({error:"Internal server error"})}}}({config:a,processor:r,logger:s})),c.post("/rebuild-metadata",function(e){return async(t,r)=>{try{const t=e.config.metadataDir??".jeeves-metadata",i=[..._];for await(const r of e.vectorStore.scroll()){const e=r.payload,n=e.file_path;if("string"!=typeof n||0===n.length)continue;const s=o.omit(e,i);await O(n,t,s)}return await r.status(200).send({ok:!0})}catch(t){return e.logger.error({err:T(t)},"Rebuild metadata failed"),await r.status(500).send({error:"Internal server error"})}}}({config:a,vectorStore:i,logger:s})),c.post("/config-reindex",function(e){return async(t,r)=>{try{const i=t.body.scope??"rules";return(async()=>{try{if("rules"===i){const t=await I(e.config.watch.paths,e.config.watch.ignored,e.processor,"processRulesUpdate");e.logger.info({scope:i,filesProcessed:t},"Config reindex (rules) completed")}else{const t=await I(e.config.watch.paths,e.config.watch.ignored,e.processor,"processFile");e.logger.info({scope:i,filesProcessed:t},"Config reindex (full) completed")}}catch(t){e.logger.error({err:T(t),scope:i},"Config reindex failed")}})(),await r.status(200).send({status:"started",scope:i})}catch(t){return e.logger.error({err:T(t)},"Config reindex request failed"),await r.status(500).send({error:"Internal server error"})}}}({config:a,processor:r,logger:s})),c}function q(e){let t=i.resolve(e);const r=i.resolve("/");for(;t!==r;){if(a.existsSync(i.join(t,".git"))&&a.statSync(i.join(t,".git")).isDirectory())return t;const e=i.dirname(t);if(e===t)break;t=e}}function Q(e){const t=i.resolve(e);try{return a.statSync(t).isDirectory()?t:i.dirname(t)}catch{}const r=/[*?[{]/.exec(e);if(!r)return;const n=e.slice(0,r.index).trim(),o=0===n.length?".":n.endsWith("/")||n.endsWith("\\")?n:i.dirname(n),s=i.resolve(o);return a.existsSync(s)?s:void 0}function G(e){const t=[],r=i.join(e,".gitignore");let n;a.existsSync(r)&&t.push(r);try{n=a.readdirSync(e)}catch{return t}for(const r of n){if(".git"===r||"node_modules"===r)continue;const n=i.join(e,r);try{a.statSync(n).isDirectory()&&t.push(...G(n))}catch{}}return t}function J(e){const t=a.readFileSync(e,"utf8");return c().add(t)}class B{repos=new Map;constructor(e){this.scan(e)}scan(e){this.repos.clear();const t=new Set;for(const r of e){const e=Q(r);if(!e)continue;if(t.has(e))continue;t.add(e);const n=q(e);if(!n)continue;if(this.repos.has(n))continue;const o=G(n).map((e=>({dir:i.dirname(e),ig:J(e)})));o.sort(((e,t)=>t.dir.length-e.dir.length)),this.repos.set(n,{root:n,entries:o})}}isIgnored(e){const t=i.resolve(e);for(const[,e]of this.repos){const r=i.relative(e.root,t);if(!(r.startsWith("..")||r.startsWith(i.resolve("/"))||/^[a-zA-Z]:/.test(r)))for(const r of e.entries){const e=i.relative(r.dir,t);if(e.startsWith("..")||/^[a-zA-Z]:/.test(e))continue;const n=e.replace(/\\/g,"/");if(r.ig.ignores(n))return!0}}return!1}invalidate(e){const t=i.resolve(e),r=i.dirname(t);for(const[,e]of this.repos){if(!i.relative(e.root,r).startsWith(".."))return e.entries=e.entries.filter((e=>e.dir!==r)),void(a.existsSync(t)&&(e.entries.push({dir:r,ig:J(t)}),e.entries.sort(((e,t)=>t.dir.length-e.dir.length))))}const n=q(r);if(n&&a.existsSync(t)){const e=[{dir:r,ig:J(t)}];if(this.repos.has(n)){const t=this.repos.get(n);t.entries.push(e[0]),t.entries.sort(((e,t)=>t.dir.length-e.dir.length))}else this.repos.set(n,{root:n,entries:e})}}}function K(e,t){return"string"!=typeof e?e:e.replace(/\$\{([^}]+)\}/g,((e,r)=>{const i=o.get(t,r);return null==i?"":"string"==typeof i?i:JSON.stringify(i)}))}function U(e,t){const r={};for(const[i,n]of Object.entries(e))r[i]=K(n,t);return r}async function V(e,t){const r={};for(const n of e){const e=i.resolve(t,n),o=await import(e),s="object"==typeof o.default&&null!==o.default?o.default:o;for(const[e,t]of Object.entries(s))"function"==typeof t&&(r[e]=t)}return r}async function Y(e,t,r,n,s,c,h){const d=function(e,t){const r=new Map,n=t=>{const n=e?i.resolve(e,t):t;if(!r.has(n)){const e=a.readFileSync(n,"utf-8");r.set(n,JSON.parse(e))}return r.get(n)};return{split:(e,t)=>e.split(t),slice:(e,t,r)=>e.slice(t,r),join:(e,t)=>e.join(t),toLowerCase:e=>e.toLowerCase(),replace:(e,t,r)=>e.replace(t,r),get:(e,t)=>o.get(e,t),lookupJson:(e,t,r)=>{const i=n(e)[t];return null==i?null:r?o.get(i,r)??null:i},mapLookup:(e,t,r)=>{if(!Array.isArray(t))return[];const i=n(e),s=[];for(const e of t){if("string"!=typeof e)continue;const t=i[e];if(null==t)continue;const n=o.get(t,r);if(null!=n)if(Array.isArray(n))for(const e of n)s.push(e);else s.push(n)}return s},...t}}(c,h);let u={},f=null;const g=n??console;for(const[n,{rule:o,validate:h}]of e.entries())if(h(t)){const e=U(o.set,t);if(u={...u,...e},o.map){let e;if("string"==typeof o.map){if(o.map.endsWith(".json")&&c)try{const t=i.resolve(c,o.map),r=a.readFileSync(t,"utf-8");e=JSON.parse(r)}catch(e){g.warn(`Failed to load map file "${o.map}": ${e instanceof Error?e.message:String(e)}`);continue}else if(e=r?.[o.map],!e){g.warn(`Map reference "${o.map}" not found in named maps. Skipping map transformation.`);continue}}else e=o.map;try{const r=new l.JsonMap(e,d),i=await r.transform(t);i&&"object"==typeof i&&!Array.isArray(i)?u={...u,...i}:g.warn("JsonMap transformation did not return an object; skipping merge.")}catch(e){g.warn(`JsonMap transformation failed: ${e instanceof Error?e.message:String(e)}`)}}if(o.template&&s){const e=`rule-${String(n)}`,r={...t.json??{},...t,...u};try{const t=s.render(e,r);t&&t.trim()?f=t:g.warn(`Template for rule ${String(n)} rendered empty output. Falling back to raw content.`)}catch(e){g.warn(`Template render failed for rule ${String(n)}: ${e instanceof Error?e.message:String(e)}. Falling back to raw content.`)}}}return{metadata:u,renderedContent:f}}const Z=m.unified().use(p,{fragment:!0});function X(e){e.registerHelper("adfToMarkdown",(function(t){if(!t||"object"!=typeof t)return"";try{const r=f.fromADF(t);return new e.SafeString(g.toMarkdown(r).trim())}catch{return"\x3c!-- ADF conversion failed --\x3e"}})),e.registerHelper("markdownify",(function(t){if("string"!=typeof t||!t.trim())return"";try{const r=Z.parse(t),i=u.toMdast(r);return new e.SafeString(g.toMarkdown(i).trim())}catch{return"\x3c!-- HTML conversion failed --\x3e"}})),e.registerHelper("dateFormat",(function(e,t){if(null==e)return"";const r="string"==typeof t?t:"YYYY-MM-DD";return d(e).format(r)})),e.registerHelper("join",(function(e,t){if(!Array.isArray(e))return"";const r="string"==typeof t?t:", ";return e.join(r)})),e.registerHelper("pluck",(function(e,t){return Array.isArray(e)&&"string"==typeof t?e.map((e=>e&&"object"==typeof e?e[t]:void 0)):[]})),e.registerHelper("lowercase",(e=>"string"==typeof e?e.toLowerCase():"")),e.registerHelper("uppercase",(e=>"string"==typeof e?e.toUpperCase():"")),e.registerHelper("capitalize",(e=>"string"==typeof e?o.capitalize(e):"")),e.registerHelper("title",(e=>"string"==typeof e?o.title(e):"")),e.registerHelper("camel",(e=>"string"==typeof e?o.camel(e):"")),e.registerHelper("snake",(e=>"string"==typeof e?o.snake(e):"")),e.registerHelper("dash",(e=>"string"==typeof e?o.dash(e):"")),e.registerHelper("default",(function(e,t){return e??t??""})),e.registerHelper("eq",(function(e,t){return o.isEqual(e,t)})),e.registerHelper("json",(function(t){return new e.SafeString(JSON.stringify(t,null,2))}))}function ee(e,t,r,n=new Set){if(e.endsWith(".hbs")||e.endsWith(".handlebars"))return a.readFileSync(i.resolve(r,e),"utf-8");if(void 0!==t?.[e]){if(n.has(e))throw new Error(`Circular template reference detected: ${e}`);return n.add(e),ee(t[e],t,r,n)}return e}function te(){const e=h.create();return X(e),e}async function re(e,t,r){for(const n of t){const t=i.resolve(r,n),o=await import(t);"function"==typeof o.default&&o.default(e)}}class ie{hbs;compiled=new Map;constructor(e){this.hbs=e}compile(e,t){const r=this.hbs.compile(t);return this.compiled.set(e,r),r}get(e){return this.compiled.get(e)}render(e,t){const r=this.compiled.get(e);return r?r(t):null}}async function ne(e,t,r,i){if(0===e.filter((e=>e.template)).length)return;const n=te();r?.length&&i&&await re(n,r,i);const o=new ie(n);for(const[r,n]of e.entries()){if(!n.template)continue;const e=ee(n.template,t,i??".");o.compile(`rule-${String(r)}`,e)}return o}class oe{options;watcher;debounce;constructor(e){this.options=e}start(){this.options.enabled&&(this.watcher=y.watch(this.options.configPath,{ignoreInitial:!0}),this.watcher.on("change",(()=>{this.debounce&&clearTimeout(this.debounce),this.debounce=setTimeout((()=>{this.options.onChange()}),this.options.debounceMs)})),this.watcher.on("error",(e=>{this.options.logger.error({err:T(e)},"Config watcher error")})),this.options.logger.info({configPath:this.options.configPath,debounceMs:this.options.debounceMs},"Config watcher started"))}async stop(){this.debounce&&(clearTimeout(this.debounce),this.debounce=void 0),this.watcher&&(await this.watcher.close(),this.watcher=void 0)}}const se={metadataDir:".jeeves-watcher",shutdownTimeoutMs:1e4},ae={enabled:!0,debounceMs:1e3},ce={host:"127.0.0.1",port:3456},le={level:"info"},he={debounceMs:300,stabilityThresholdMs:500,usePolling:!1,pollIntervalMs:1e3,respectGitignore:!0},de={chunkSize:1e3,chunkOverlap:200,dimensions:3072,rateLimitPerMinute:300,concurrency:5},ue=b.z.object({paths:b.z.array(b.z.string()).min(1).describe('Glob patterns for files to watch (e.g., "**/*.md"). At least one required.'),ignored:b.z.array(b.z.string()).optional().describe('Glob patterns to exclude from watching (e.g., "**/node_modules/**").'),pollIntervalMs:b.z.number().optional().describe("Polling interval in milliseconds when usePolling is enabled."),usePolling:b.z.boolean().optional().describe("Use polling instead of native file system events (for network drives)."),debounceMs:b.z.number().optional().describe("Debounce delay in milliseconds for file change events."),stabilityThresholdMs:b.z.number().optional().describe("Time in milliseconds a file must remain unchanged before processing."),respectGitignore:b.z.boolean().optional().describe("Skip files ignored by .gitignore in git repositories. Only applies to repos with a .git directory. Default: true.")}),fe=b.z.object({enabled:b.z.boolean().optional().describe("Enable automatic reloading when config file changes."),debounceMs:b.z.number().optional().describe("Debounce delay in milliseconds for config file change detection.")}),ge=b.z.object({provider:b.z.string().default("gemini").describe('Embedding provider name (e.g., "gemini", "openai").'),model:b.z.string().default("gemini-embedding-001").describe('Embedding model identifier (e.g., "gemini-embedding-001", "text-embedding-3-small").'),chunkSize:b.z.number().optional().describe("Maximum chunk size in characters for text splitting."),chunkOverlap:b.z.number().optional().describe("Character overlap between consecutive chunks."),dimensions:b.z.number().optional().describe("Embedding vector dimensions (must match model output)."),apiKey:b.z.string().optional().describe("API key for embedding provider (supports ${ENV_VAR} substitution)."),rateLimitPerMinute:b.z.number().optional().describe("Maximum embedding API requests per minute (rate limiting)."),concurrency:b.z.number().optional().describe("Maximum concurrent embedding requests.")}),pe=b.z.object({url:b.z.string().describe('Qdrant server URL (e.g., "http://localhost:6333").'),collectionName:b.z.string().describe("Qdrant collection name for vector storage."),apiKey:b.z.string().optional().describe("Qdrant API key for authentication (supports ${ENV_VAR} substitution).")}),me=b.z.object({host:b.z.string().optional().describe('Host address for API server (e.g., "127.0.0.1", "0.0.0.0").'),port:b.z.number().optional().describe("Port for API server (e.g., 3456).")}),ye=b.z.object({level:b.z.string().optional().describe("Logging level (trace, debug, info, warn, error, fatal)."),file:b.z.string().optional().describe("Path to log file (logs to stdout if omitted).")}),we=b.z.object({match:b.z.record(b.z.string(),b.z.unknown()).describe("JSON Schema object to match against file attributes."),set:b.z.record(b.z.string(),b.z.unknown()).describe("Metadata fields to set when match succeeds."),map:b.z.union([l.jsonMapMapSchema,b.z.string()]).optional().describe("JsonMap transformation (inline definition, named map reference, or .json file path)."),template:b.z.string().optional().describe("Handlebars content template (inline string, named ref, or .hbs/.handlebars file path).")}),be=b.z.object({watch:ue.describe("File system watch configuration."),configWatch:fe.optional().describe("Configuration file watch settings."),embedding:ge.describe("Embedding model configuration."),vectorStore:pe.describe("Qdrant vector store configuration."),metadataDir:b.z.string().optional().describe("Directory for persisted metadata sidecar files."),api:me.optional().describe("API server configuration."),extractors:b.z.record(b.z.string(),b.z.unknown()).optional().describe("Extractor configurations keyed by name."),inferenceRules:b.z.array(we).optional().describe("Rules for inferring metadata from file attributes."),maps:b.z.record(b.z.string(),l.jsonMapMapSchema.or(b.z.string())).optional().describe("Reusable named JsonMap transformations (inline definition or .json file path resolved relative to config directory)."),templates:b.z.record(b.z.string(),b.z.string()).optional().describe("Named reusable Handlebars templates (inline strings or .hbs/.handlebars file paths)."),templateHelpers:b.z.object({paths:b.z.array(b.z.string()).optional().describe("File paths to custom helper modules.")}).optional().describe("Custom Handlebars helper registration."),mapHelpers:b.z.object({paths:b.z.array(b.z.string()).optional().describe("File paths to JS modules exporting functions to merge into the JsonMap lib.")}).optional().describe("Custom JsonMap lib function registration."),logging:ye.optional().describe("Logging configuration."),shutdownTimeoutMs:b.z.number().optional().describe("Timeout in milliseconds for graceful shutdown."),maxRetries:b.z.number().optional().describe("Maximum consecutive system-level failures before triggering fatal error. Default: Infinity."),maxBackoffMs:b.z.number().optional().describe("Maximum backoff delay in milliseconds for system errors. Default: 60000.")}),ve=/\$\{([^}]+)\}/g;function Me(e){if("string"==typeof e)return function(e){return e.replace(ve,((e,t)=>{const r=process.env[t];return void 0===r?e:r}))}(e);if(Array.isArray(e))return e.map((e=>Me(e)));if(null!==e&&"object"==typeof e){const t={};for(const[r,i]of Object.entries(e))t[r]=Me(i);return t}return e}const Se="jeeves-watcher";async function Fe(e){const t=w.cosmiconfig(Se),r=e?await t.load(e):await t.search();if(!r||r.isEmpty)throw new Error("No jeeves-watcher configuration found. Create a .jeeves-watcherrc or jeeves-watcher.config.{js,ts,json,yaml} file.");try{const e=be.parse(r.config);if(e.maps){const t=i.dirname(r.filepath);for(const[r,n]of Object.entries(e.maps))if("string"==typeof n){const o=i.resolve(t,n),s=a.readFileSync(o,"utf-8");e.maps[r]=JSON.parse(s)}}return Me((n=e,{...se,...n,watch:{...he,...n.watch},configWatch:{...ae,...n.configWatch},embedding:{...de,...n.embedding},api:{...ce,...n.api},logging:{...le,...n.logging}}))}catch(e){if(e instanceof b.ZodError){const t=e.issues.map((e=>`${e.path.join(".")}: ${e.message}`)).join("; ");throw new Error(`Invalid jeeves-watcher configuration: ${t}`)}throw e}var n}function ke(e){return e||{warn(e,t){t?console.warn(e,t):console.warn(e)}}}function xe(e,t){return e<=0?Promise.resolve():new Promise(((r,i)=>{const n=setTimeout((()=>{s(),r()}),e),o=()=>{s(),i(new Error("Retry sleep aborted"))},s=()=>{clearTimeout(n),t&&t.removeEventListener("abort",o)};if(t){if(t.aborted)return void o();t.addEventListener("abort",o,{once:!0})}}))}function Pe(e,t,r,i=0){const n=Math.max(0,e-1),o=Math.min(r,t*2**n),s=i>0?1+Math.random()*i:1;return Math.round(o*s)}async function ze(e,t){const r=Math.max(1,t.attempts);let i;for(let n=1;n<=r;n++)try{return await e(n)}catch(e){i=e;if(n>=r)break;const o=Pe(n,t.baseDelayMs,t.maxDelayMs,t.jitter);t.onRetry?.({attempt:n,attempts:r,delayMs:o,error:e}),await xe(o,t.signal)}throw i}const je=new Map([["mock",function(e){return function(e){return{dimensions:e,embed:t=>Promise.resolve(t.map((t=>{const r=s.createHash("sha256").update(t,"utf8").digest(),i=[];for(let t=0;t<e;t++){const e=r[t%r.length];i.push(e/127.5-1)}return i})))}}(e.dimensions??768)}],["gemini",function(e,t){if(!e.apiKey)throw new Error("Gemini embedding provider requires config.embedding.apiKey");const r=e.dimensions??3072,i=ke(t),n=new v.GoogleGenerativeAIEmbeddings({apiKey:e.apiKey,model:e.model});return{dimensions:r,async embed(t){const o=await ze((async r=>(r>1&&i.warn({attempt:r,provider:"gemini",model:e.model},"Retrying embedding request"),n.embedDocuments(t))),{attempts:5,baseDelayMs:500,maxDelayMs:1e4,jitter:.2,onRetry:({attempt:t,delayMs:r,error:n})=>{i.warn({attempt:t,delayMs:r,provider:"gemini",model:e.model,err:T(n)},"Embedding call failed; will retry")}});for(const e of o)if(e.length!==r)throw new Error(`Gemini embedding returned invalid dimensions: expected ${String(r)}, got ${String(e.length)}`);return o}}}]]);function Ce(e,t){const r=je.get(e.provider);if(!r)throw new Error(`Unsupported embedding provider: ${e.provider}`);return r(e,t)}function Ee(e){const t=e?.level??"info";if(e?.file){const r=M.transport({target:"pino/file",options:{destination:e.file,mkdir:!0}});return M({level:t},r)}return M({level:t})}function Re(e){return s.createHash("sha256").update(e,"utf8").digest("hex")}const Te="6a6f686e-6761-4c74-ad6a-656576657321";function De(e,t){const r=void 0!==t?`${W(e)}#${String(t)}`:W(e);return S.v5(r,Te)}const Ae=["content","body","text","snippet","subject","description","summary","transcript"];function Ie(e){if(!e||"object"!=typeof e)return JSON.stringify(e);const t=e;for(const e of Ae){const r=t[e];if("string"==typeof r&&r.trim())return r}return JSON.stringify(e)}async function We(e){const t=await r.readFile(e,"utf8"),{frontmatter:i,body:n}=function(e){const t=e.replace(/^\uFEFF/,"");if(!/^\s*---/.test(t))return{body:e};const r=/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/m.exec(t);if(!r)return{body:e};const[,i,n]=r,o=k.load(i);return{frontmatter:o&&"object"==typeof o&&!Array.isArray(o)?o:void 0,body:n}}(t);return{text:n,frontmatter:i}}async function Ne(e){return{text:(await r.readFile(e,"utf8")).replace(/^\uFEFF/,"")}}async function He(e){const t=await r.readFile(e,"utf8"),i=R.load(t.replace(/^\uFEFF/,""));i("script, style").remove();return{text:i("body").text().trim()||i.text().trim()}}const Oe=new Map([[".md",We],[".markdown",We],[".txt",Ne],[".text",Ne],[".json",async function(e){const t=await r.readFile(e,"utf8"),i=JSON.parse(t.replace(/^\uFEFF/,"")),n=i&&"object"==typeof i&&!Array.isArray(i)?i:void 0;return{text:Ie(i),json:n}}],[".pdf",async function(e){const t=await r.readFile(e),i=new Uint8Array(t),{extractText:n}=await import("unpdf"),{text:o}=await n(i);return{text:Array.isArray(o)?o.join("\n\n"):o}}],[".docx",async function(e){const t=await r.readFile(e);return{text:(await x.extractRawText({buffer:t})).value}}],[".html",He],[".htm",He]]);async function Le(e,t){const r=Oe.get(t.toLowerCase());return r?r(e):Ne(e)}function _e(e,t,r,n){const o=e.replace(/\\/g,"/"),s={file:{path:o,directory:i.dirname(o).replace(/\\/g,"/"),filename:i.basename(o),extension:i.extname(o),sizeBytes:t.size,modified:t.mtime.toISOString()}};return r&&(s.frontmatter=r),n&&(s.json=n),s}function $e(e){const t=function(){const e=new P({allErrors:!0});return z(e),e.addKeyword({keyword:"glob",type:"string",schemaType:"string",validate:(e,t)=>n.isMatch(t,e)}),e}();return e.map(((e,r)=>({rule:e,validate:t.compile({$id:`rule-${String(r)}`,...e.match})})))}async function qe(e,t,n,o,s,a,c,l){const h=i.extname(e),d=await r.stat(e),u=await Le(e,h),f=_e(e,d,u.frontmatter,u.json),{metadata:g,renderedContent:p}=await Y(t,f,o,s,a,c,l),m=await H(e,n);return{inferred:g,enrichment:m,metadata:{...g,...m??{}},attributes:f,extracted:u,renderedContent:p}}function Qe(e,t){const r=[];for(let i=0;i<t;i++)r.push(De(e,i));return r}function Ge(e,t=1){if(!e)return t;const r=e.total_chunks;return"number"==typeof r?r:t}class Je{config;embeddingProvider;vectorStore;compiledRules;logger;templateEngine;constructor(e,t,r,i,n,o){this.config=e,this.embeddingProvider=t,this.vectorStore=r,this.compiledRules=i,this.logger=n,this.templateEngine=o}async processFile(e){try{const t=i.extname(e),{metadata:r,extracted:n,renderedContent:o}=await qe(e,this.compiledRules,this.config.metadataDir,this.config.maps,this.logger,this.templateEngine,this.config.configDir,this.config.customMapLib),s=o??n.text;if(!s.trim())return void this.logger.debug({filePath:e},"Skipping empty file");const a=Re(s),c=De(e,0),l=await this.vectorStore.getPayload(c);if(l&&l.content_hash===a)return void this.logger.debug({filePath:e},"Content unchanged, skipping");const h=Ge(l),d=this.config.chunkSize??1e3,u=function(e,t,r){const i=e.toLowerCase();return".md"===i||".markdown"===i?new j.MarkdownTextSplitter({chunkSize:t,chunkOverlap:r}):new j.RecursiveCharacterTextSplitter({chunkSize:t,chunkOverlap:r})}(t,d,this.config.chunkOverlap??200),f=await u.splitText(s),g=await this.embeddingProvider.embed(f),p=f.map(((t,i)=>({id:De(e,i),vector:g[i],payload:{...r,file_path:e.replace(/\\/g,"/"),chunk_index:i,total_chunks:f.length,content_hash:a,chunk_text:t}})));if(await this.vectorStore.upsert(p),h>f.length){const t=Qe(e,h).slice(f.length);await this.vectorStore.delete(t)}this.logger.info({filePath:e,chunks:f.length},"File processed successfully")}catch(t){this.logger.error({filePath:e,err:T(t)},"Failed to process file")}}async deleteFile(e){try{const t=De(e,0),r=await this.vectorStore.getPayload(t),i=Qe(e,Ge(r));await this.vectorStore.delete(i),await L(e,this.config.metadataDir),this.logger.info({filePath:e},"File deleted from index")}catch(t){this.logger.error({filePath:e,err:T(t)},"Failed to delete file")}}async processMetadataUpdate(e,t){try{const r={...await H(e,this.config.metadataDir)??{},...t};await O(e,this.config.metadataDir,r);const i=De(e,0),n=await this.vectorStore.getPayload(i);if(!n)return null;const o=Ge(n),s=Qe(e,o);return await this.vectorStore.setPayload(s,r),this.logger.info({filePath:e,chunks:o},"Metadata updated"),r}catch(t){return this.logger.error({filePath:e,err:T(t)},"Failed to update metadata"),null}}async processRulesUpdate(e){try{const t=De(e,0),r=await this.vectorStore.getPayload(t);if(!r)return this.logger.debug({filePath:e},"File not indexed, skipping"),null;const{metadata:i}=await qe(e,this.compiledRules,this.config.metadataDir,this.config.maps,this.logger,this.templateEngine,this.config.configDir,this.config.customMapLib),n=Ge(r),o=Qe(e,n);return await this.vectorStore.setPayload(o,i),this.logger.info({filePath:e,chunks:n},"Rules re-applied"),i}catch(t){return this.logger.error({filePath:e,err:T(t)},"Failed to re-apply rules"),null}}updateRules(e,t,r){this.compiledRules=e,t&&(this.templateEngine=t),void 0!==r&&(this.config={...this.config,customMapLib:r}),this.logger.info({rules:e.length},"Inference rules updated")}}class Be{debounceMs;concurrency;rateLimitPerMinute;started=!1;active=0;debounceTimers=new Map;latestByKey=new Map;normalQueue=[];lowQueue=[];tokens;lastRefillMs=Date.now();drainWaiters=[];constructor(e){this.debounceMs=e.debounceMs,this.concurrency=e.concurrency,this.rateLimitPerMinute=e.rateLimitPerMinute,this.tokens=this.rateLimitPerMinute??Number.POSITIVE_INFINITY}enqueue(e,t){const r=`${e.priority}:${e.path}`;this.latestByKey.set(r,{event:e,fn:t});const i=this.debounceTimers.get(r);i&&clearTimeout(i);const n=setTimeout((()=>{this.debounceTimers.delete(r);const e=this.latestByKey.get(r);e&&(this.latestByKey.delete(r),this.push(e),this.pump())}),this.debounceMs);this.debounceTimers.set(r,n)}process(){this.started=!0,this.pump()}async drain(){this.isIdle()||await new Promise((e=>{this.drainWaiters.push(e)}))}push(e){"low"===e.event.priority?this.lowQueue.push(e):this.normalQueue.push(e)}refillTokens(e){if(void 0===this.rateLimitPerMinute)return;const t=Math.max(0,e-this.lastRefillMs)*(this.rateLimitPerMinute/6e4);this.tokens=Math.min(this.rateLimitPerMinute,this.tokens+t),this.lastRefillMs=e}takeToken(){const e=Date.now();return this.refillTokens(e),!(this.tokens<1)&&(this.tokens-=1,!0)}nextItem(){return this.normalQueue.shift()??this.lowQueue.shift()}pump(){if(this.started){for(;this.active<this.concurrency;){const e=this.nextItem();if(!e)break;if(!this.takeToken()){"low"===e.event.priority?this.lowQueue.unshift(e):this.normalQueue.unshift(e),setTimeout((()=>{this.pump()}),250);break}this.active+=1,Promise.resolve().then((()=>e.fn(e.event))).finally((()=>{this.active-=1,this.pump(),this.maybeResolveDrain()}))}this.maybeResolveDrain()}}isIdle(){return 0===this.active&&0===this.normalQueue.length&&0===this.lowQueue.length&&0===this.debounceTimers.size&&0===this.latestByKey.size}maybeResolveDrain(){if(!this.isIdle())return;const e=this.drainWaiters;this.drainWaiters=[];for(const t of e)t()}}function Ke(e){return null==e?"keyword":"number"==typeof e?Number.isInteger(e)?"integer":"float":"boolean"==typeof e?"bool":Array.isArray(e)?"keyword[]":"string"==typeof e&&e.length>256?"text":"keyword"}class Ue{client;collectionName;dims;log;constructor(e,t,r){this.client=new C.QdrantClient({url:e.url,apiKey:e.apiKey,checkCompatibility:!1}),this.collectionName=e.collectionName,this.dims=t,this.log=ke(r)}async ensureCollection(){try{const e=await this.client.getCollections();e.collections.some((e=>e.name===this.collectionName))||await this.client.createCollection(this.collectionName,{vectors:{size:this.dims,distance:"Cosine"}})}catch(e){throw new Error(`Failed to ensure collection "${this.collectionName}": ${String(e)}`)}}async upsert(e){0!==e.length&&await ze((async t=>{t>1&&this.log.warn({attempt:t,operation:"qdrant.upsert",points:e.length},"Retrying Qdrant upsert"),await this.client.upsert(this.collectionName,{wait:!0,points:e.map((e=>({id:e.id,vector:e.vector,payload:e.payload})))})}),{attempts:5,baseDelayMs:500,maxDelayMs:1e4,jitter:.2,onRetry:({attempt:e,delayMs:t,error:r})=>{this.log.warn({attempt:e,delayMs:t,operation:"qdrant.upsert",err:T(r)},"Qdrant upsert failed; will retry")}})}async delete(e){0!==e.length&&await ze((async t=>{t>1&&this.log.warn({attempt:t,operation:"qdrant.delete",ids:e.length},"Retrying Qdrant delete"),await this.client.delete(this.collectionName,{wait:!0,points:e})}),{attempts:5,baseDelayMs:500,maxDelayMs:1e4,jitter:.2,onRetry:({attempt:e,delayMs:t,error:r})=>{this.log.warn({attempt:e,delayMs:t,operation:"qdrant.delete",err:T(r)},"Qdrant delete failed; will retry")}})}async setPayload(e,t){0!==e.length&&await this.client.setPayload(this.collectionName,{wait:!0,points:e,payload:t})}async getPayload(e){try{const t=await this.client.retrieve(this.collectionName,{ids:[e],with_payload:!0,with_vector:!1});return 0===t.length?null:t[0].payload}catch{return null}}async getCollectionInfo(){const e=await this.client.getCollection(this.collectionName),t=e.points_count??0,r=e.config.params.vectors,i=void 0!==r&&"size"in r?r.size:0,n={},o=Object.entries(e.payload_schema);if(o.length>0)for(const[e,t]of o)n[e]={type:t.data_type??"unknown"};else t>0&&await this.discoverPayloadFields(n);return{pointCount:t,dimensions:i,payloadFields:n}}async discoverPayloadFields(e,t=100){const r=await this.client.scroll(this.collectionName,{limit:t,with_payload:!0,with_vector:!1});for(const t of r.points){const r=t.payload;if(r)for(const[t,i]of Object.entries(r))t in e||(e[t]={type:Ke(i)})}}async search(e,t,r){return(await this.client.search(this.collectionName,{vector:e,limit:t,with_payload:!0,...r?{filter:r}:{}})).map((e=>({id:String(e.id),score:e.score,payload:e.payload})))}async*scroll(e,t=100){let r;for(;;){const i=await this.client.scroll(this.collectionName,{limit:t,with_payload:!0,with_vector:!1,...e?{filter:e}:{},...void 0!==r?{offset:r}:{}});for(const e of i.points)yield{id:String(e.id),payload:e.payload};const n=i.next_page_offset;if(null==n)break;if("string"!=typeof n&&"number"!=typeof n)break;r=n}}}class Ve{consecutiveFailures=0;maxRetries;maxBackoffMs;baseDelayMs;onFatalError;logger;constructor(e){this.maxRetries=e.maxRetries??Number.POSITIVE_INFINITY,this.maxBackoffMs=e.maxBackoffMs??6e4,this.baseDelayMs=e.baseDelayMs??1e3,this.onFatalError=e.onFatalError,this.logger=e.logger}recordSuccess(){this.consecutiveFailures>0&&this.logger.info({previousFailures:this.consecutiveFailures},"System health recovered"),this.consecutiveFailures=0}recordFailure(e){if(this.consecutiveFailures+=1,this.logger.error({consecutiveFailures:this.consecutiveFailures,maxRetries:this.maxRetries,err:T(e)},"System-level failure recorded"),this.consecutiveFailures>=this.maxRetries){if(this.logger.fatal({consecutiveFailures:this.consecutiveFailures},"Maximum retries exceeded, triggering fatal error"),this.onFatalError)return this.onFatalError(e),!1;throw e instanceof Error?e:new Error(`Fatal system error: ${String(e)}`)}return!0}get currentBackoffMs(){if(0===this.consecutiveFailures)return 0;const e=Math.max(0,this.consecutiveFailures-1);return Math.min(this.maxBackoffMs,this.baseDelayMs*2**e)}async backoff(e){const t=this.currentBackoffMs;t<=0||(this.logger.warn({delayMs:t,consecutiveFailures:this.consecutiveFailures},"Backing off before next attempt"),await new Promise(((r,i)=>{const n=setTimeout((()=>{s(),r()}),t),o=()=>{s(),i(new Error("Backoff aborted"))},s=()=>{clearTimeout(n),e&&e.removeEventListener("abort",o)};if(e){if(e.aborted)return void o();e.addEventListener("abort",o,{once:!0})}})))}get failures(){return this.consecutiveFailures}}function Ye(e){const t=e.replace(/\\/g,"/").split("/"),r=[];for(const e of t){if(/[*?{[\]]/.test(e))break;r.push(e)}return r.join("/")||"."}function Ze(e){const t=function(e){const t=e.map((e=>e.replace(/\\/g,"/").toLowerCase()));return[...new Set(t)].sort().filter(((e,t,r)=>{const i=e.endsWith("/")?e:e+"/";return!r.some((t=>t!==e&&i.startsWith(t+"/")))}))}(e.map(Ye)),r=function(e){const t=e.map((e=>e.replace(/\\/g,"/"))),r=n(t,{dot:!0,nocase:!0});return e=>{const t=e.replace(/\\/g,"/");return r(t)}}(e);return{roots:t,matches:r}}class Xe{config;queue;processor;logger;health;gitignoreFilter;globMatches;watcher;constructor(e,t,r,i,n={}){this.config=e,this.queue=t,this.processor=r,this.logger=i,this.gitignoreFilter=n.gitignoreFilter,this.globMatches=()=>!0;const o={maxRetries:n.maxRetries,maxBackoffMs:n.maxBackoffMs,onFatalError:n.onFatalError,logger:i};this.health=new Ve(o)}start(){const{roots:e,matches:t}=Ze(this.config.paths);this.globMatches=t;const r=this.config.ignored?function(e){return e.map((e=>{if("string"!=typeof e)return e;const t=e.replace(/\\/g,"/"),r=n(t,{dot:!0,nocase:!0});return e=>{const t=e.replace(/\\/g,"/");return r(t)}}))}(this.config.ignored):void 0;this.watcher=y.watch(e,{ignored:r,usePolling:this.config.usePolling,interval:this.config.pollIntervalMs,awaitWriteFinish:!!this.config.stabilityThresholdMs&&{stabilityThreshold:this.config.stabilityThresholdMs},ignoreInitial:!1}),this.watcher.on("add",(e=>{this.handleGitignoreChange(e),this.globMatches(e)&&(this.isGitignored(e)||(this.logger.debug({path:e},"File added"),this.queue.enqueue({type:"create",path:e,priority:"normal"},(()=>this.wrapProcessing((()=>this.processor.processFile(e)))))))})),this.watcher.on("change",(e=>{this.handleGitignoreChange(e),this.globMatches(e)&&(this.isGitignored(e)||(this.logger.debug({path:e},"File changed"),this.queue.enqueue({type:"modify",path:e,priority:"normal"},(()=>this.wrapProcessing((()=>this.processor.processFile(e)))))))})),this.watcher.on("unlink",(e=>{this.handleGitignoreChange(e),this.globMatches(e)&&(this.isGitignored(e)||(this.logger.debug({path:e},"File removed"),this.queue.enqueue({type:"delete",path:e,priority:"normal"},(()=>this.wrapProcessing((()=>this.processor.deleteFile(e)))))))})),this.watcher.on("error",(e=>{this.logger.error({err:T(e)},"Watcher error"),this.health.recordFailure(e)})),this.queue.process(),this.logger.info({paths:this.config.paths},"Filesystem watcher started")}async stop(){this.watcher&&(await this.watcher.close(),this.watcher=void 0,this.logger.info("Filesystem watcher stopped"))}get systemHealth(){return this.health}isGitignored(e){if(!this.gitignoreFilter)return!1;const t=this.gitignoreFilter.isIgnored(e);return t&&this.logger.debug({path:e},"Skipping gitignored file"),t}handleGitignoreChange(e){this.gitignoreFilter&&e.endsWith(".gitignore")&&(this.logger.info({path:e},"Gitignore file changed, refreshing filter"),this.gitignoreFilter.invalidate(e))}async wrapProcessing(e){try{await this.health.backoff(),await e(),this.health.recordSuccess()}catch(e){this.health.recordFailure(e)||await this.stop()}}}const et={loadConfig:Fe,createLogger:Ee,createEmbeddingProvider:Ce,createVectorStoreClient:(e,t,r)=>new Ue(e,t,r),compileRules:$e,createDocumentProcessor:(e,t,r,i,n,o)=>new Je(e,t,r,i,n,o),createEventQueue:e=>new Be(e),createFileSystemWatcher:(e,t,r,i,n)=>new Xe(e,t,r,i,n),createApiServer:$};class tt{config;configPath;factories;runtimeOptions;logger;watcher;queue;server;processor;configWatcher;constructor(e,t,r={},i={}){this.config=e,this.configPath=t,this.factories={...et,...r},this.runtimeOptions=i}async start(){const e=this.factories.createLogger(this.config.logging);this.logger=e;const{embeddingProvider:t,vectorStore:r}=await this.initEmbeddingAndStore(e),n=this.factories.compileRules(this.config.inferenceRules??[]),o=this.configPath?i.dirname(this.configPath):".",s=await ne(this.config.inferenceRules??[],this.config.templates,this.config.templateHelpers?.paths,o),a=this.config.mapHelpers?.paths?.length&&o?await V(this.config.mapHelpers.paths,o):void 0,c=this.factories.createDocumentProcessor({metadataDir:this.config.metadataDir??".jeeves-metadata",chunkSize:this.config.embedding.chunkSize,chunkOverlap:this.config.embedding.chunkOverlap,maps:this.config.maps,configDir:o,customMapLib:a},t,r,n,e,s);this.processor=c,this.queue=this.factories.createEventQueue({debounceMs:this.config.watch.debounceMs??2e3,concurrency:this.config.embedding.concurrency??5,rateLimitPerMinute:this.config.embedding.rateLimitPerMinute}),this.watcher=this.createWatcher(this.queue,c,e),this.server=await this.startApiServer(c,r,t,e),this.watcher.start(),this.startConfigWatch(),e.info("jeeves-watcher started")}async stop(){if(await this.stopConfigWatch(),this.watcher&&await this.watcher.stop(),this.queue){const e=this.config.shutdownTimeoutMs??1e4;await Promise.race([this.queue.drain().then((()=>!0)),new Promise((t=>{setTimeout((()=>{t(!1)}),e)}))])||this.logger?.warn({timeoutMs:e},"Queue drain timeout hit, forcing shutdown")}this.server&&await this.server.close(),this.logger?.info("jeeves-watcher stopped")}async initEmbeddingAndStore(e){let t;try{t=this.factories.createEmbeddingProvider(this.config.embedding,e)}catch(t){throw e.fatal({err:T(t)},"Failed to create embedding provider"),t}const r=this.factories.createVectorStoreClient(this.config.vectorStore,t.dimensions,e);return await r.ensureCollection(),{embeddingProvider:t,vectorStore:r}}createWatcher(e,t,r){const i=this.config.watch.respectGitignore??!0?new B(this.config.watch.paths):void 0;return this.factories.createFileSystemWatcher(this.config.watch,e,t,r,{maxRetries:this.config.maxRetries,maxBackoffMs:this.config.maxBackoffMs,onFatalError:this.runtimeOptions.onFatalError,gitignoreFilter:i})}async startApiServer(e,t,r,i){const n=this.factories.createApiServer({processor:e,vectorStore:t,embeddingProvider:r,queue:this.queue,config:this.config,logger:i});return await n.listen({host:this.config.api?.host??"127.0.0.1",port:this.config.api?.port??3456}),n}startConfigWatch(){const e=this.logger;if(!e)return;const t=this.config.configWatch?.enabled??!0;t&&this.configPath?(this.configWatcher=new oe({configPath:this.configPath,enabled:t,debounceMs:this.config.configWatch?.debounceMs??1e4,logger:e,onChange:async()=>this.reloadConfig()}),this.configWatcher.start()):this.configPath||e.debug("Config watch enabled, but no config path was provided")}async stopConfigWatch(){this.configWatcher&&(await this.configWatcher.stop(),this.configWatcher=void 0)}async reloadConfig(){const e=this.logger,t=this.processor;if(e&&t&&this.configPath){e.info({configPath:this.configPath},"Config change detected, reloading...");try{const r=await this.factories.loadConfig(this.configPath);this.config=r;const n=this.factories.compileRules(r.inferenceRules??[]),o=i.dirname(this.configPath),s=await ne(r.inferenceRules??[],r.templates,r.templateHelpers?.paths,o),a=r.mapHelpers?.paths?.length&&o?await V(r.mapHelpers.paths,o):void 0;t.updateRules(n,s,a),e.info({configPath:this.configPath,rules:n.length},"Config reloaded")}catch(t){e.error({err:T(t)},"Failed to reload config")}}}}e.DocumentProcessor=Je,e.EventQueue=Be,e.FileSystemWatcher=Xe,e.GitignoreFilter=B,e.JeevesWatcher=tt,e.SystemHealth=Ve,e.TemplateEngine=ie,e.VectorStoreClient=Ue,e.apiConfigSchema=me,e.applyRules=Y,e.buildAttributes=_e,e.buildTemplateEngine=ne,e.compileRules=$e,e.configWatchConfigSchema=fe,e.contentHash=Re,e.createApiServer=$,e.createEmbeddingProvider=Ce,e.createHandlebarsInstance=te,e.createLogger=Ee,e.deleteMetadata=L,e.embeddingConfigSchema=ge,e.extractText=Le,e.inferenceRuleSchema=we,e.jeevesWatcherConfigSchema=be,e.loadConfig=Fe,e.loadCustomHelpers=re,e.loggingConfigSchema=ye,e.metadataPath=N,e.pointId=De,e.readMetadata=H,e.registerBuiltinHelpers=X,e.resolveTemplateSource=ee,e.startFromConfig=async function(e){const t=await Fe(e),r=new tt(t,e);return function(e){const t=async()=>{await e(),process.exit(0)};process.on("SIGTERM",(()=>{t()})),process.on("SIGINT",(()=>{t()}))}((()=>r.stop())),await r.start(),r},e.vectorStoreConfigSchema=pe,e.watchConfigSchema=ue,e.writeMetadata=O}(this["jeeves-watcher"]=this["jeeves-watcher"]||{},Fastify,promises,node_path,picomatch,radash,node_crypto,node_fs,ignore,jsonmap,Handlebars,dayjs,hastUtilToMdast,mdastUtilFromAdf,mdastUtilToMarkdown,rehypeParse,unified,chokidar,cosmiconfig,zod,googleGenai,pino,uuid,cheerio,yaml,mammoth,Ajv,addFormats,textsplitters,jsClientRest);
1
+ !function(e,t,r,i,n,o,s,a,c,l,h,d,u,f,g,p,m,y,w,b,v,M,S,F,k,x,P,z,j,C,E){"use strict";function R(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var i=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,i.get?i:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var T=R(k);function D(e){if(e instanceof Error)return e;if("string"==typeof e)return new Error(e);const t=String("object"==typeof e&&null!==e&&"message"in e?e.message:e),r=new Error(t);return r.cause=e,r}function A(e){const t=e.replace(/\\/g,"/"),r=t.search(/[*?\[]/);if(-1===r)return i.resolve(e);const n=t.slice(0,r),o=n.endsWith("/")?n.slice(0,-1):i.dirname(n);return i.resolve(o)}async function*I(e){let t;try{t=(await r.readdir(e,{withFileTypes:!0})).map((e=>({name:e.name,isDirectory:e.isDirectory()})))}catch{return}for(const n of t){const t=i.resolve(e,n.name);if(n.isDirectory)yield*I(t);else try{(await r.stat(t)).isFile()&&(yield t)}catch{}}}async function W(e,t,r,i){const o=await async function(e,t=[]){const r=e.map((e=>e.replace(/\\/g,"/"))),i=t.map((e=>e.replace(/\\/g,"/"))),o=n(r,{dot:!0}),s=i.length?n(i,{dot:!0}):()=>!1,a=Array.from(new Set(e.map(A))),c=new Set;for(const e of a)for await(const t of I(e)){const e=t.replace(/\\/g,"/");s(e)||o(e)&&c.add(t)}return Array.from(c)}(e,t);for(const e of o)await r[i](e);return o.length}function N(e,t=!1){let r=e.replace(/\\/g,"/").toLowerCase();return t&&(r=r.replace(/^([a-z]):/,((e,t)=>t))),r}function H(e,t){const r=N(e,!0),n=s.createHash("sha256").update(r,"utf8").digest("hex");return i.join(t,`${n}.meta.json`)}async function O(e,t){try{const i=await r.readFile(H(e,t),"utf8");return JSON.parse(i)}catch{return null}}async function L(e,t,n){const o=H(e,t);await r.mkdir(i.dirname(o),{recursive:!0}),await r.writeFile(o,JSON.stringify(n,null,2),"utf8")}async function _(e,t){try{await r.rm(H(e,t))}catch{}}const $=["file_path","chunk_index","total_chunks","content_hash","chunk_text"];function q(e){const{processor:r,vectorStore:i,embeddingProvider:n,logger:s,config:a}=e,c=t({logger:!1});var l;return c.get("/status",(l={vectorStore:i,config:a},async()=>{const e=await l.vectorStore.getCollectionInfo();return{status:"ok",uptime:process.uptime(),collection:{name:l.config.vectorStore.collectionName,pointCount:e.pointCount,dimensions:e.dimensions},payloadFields:e.payloadFields}})),c.post("/metadata",function(e){return async(t,r)=>{try{const{path:r,metadata:i}=t.body;return await e.processor.processMetadataUpdate(r,i),{ok:!0}}catch(t){return e.logger.error({err:D(t)},"Metadata update failed"),r.status(500).send({error:"Internal server error"})}}}({processor:r,logger:s})),c.post("/search",function(e){return async(t,r)=>{try{const{query:r,limit:i=10,filter:n}=t.body,o=await e.embeddingProvider.embed([r]);return await e.vectorStore.search(o[0],i,n)}catch(t){return e.logger.error({err:D(t)},"Search failed"),r.status(500).send({error:"Internal server error"})}}}({embeddingProvider:n,vectorStore:i,logger:s})),c.post("/reindex",function(e){return async(t,r)=>{try{const t=await W(e.config.watch.paths,e.config.watch.ignored,e.processor,"processFile");return await r.status(200).send({ok:!0,filesIndexed:t})}catch(t){return e.logger.error({err:D(t)},"Reindex failed"),await r.status(500).send({error:"Internal server error"})}}}({config:a,processor:r,logger:s})),c.post("/rebuild-metadata",function(e){return async(t,r)=>{try{const t=e.config.metadataDir??".jeeves-metadata",i=[...$];for await(const r of e.vectorStore.scroll()){const e=r.payload,n=e.file_path;if("string"!=typeof n||0===n.length)continue;const s=o.omit(e,i);await L(n,t,s)}return await r.status(200).send({ok:!0})}catch(t){return e.logger.error({err:D(t)},"Rebuild metadata failed"),await r.status(500).send({error:"Internal server error"})}}}({config:a,vectorStore:i,logger:s})),c.post("/config-reindex",function(e){return async(t,r)=>{try{const i=t.body.scope??"rules";return(async()=>{try{if("rules"===i){const t=await W(e.config.watch.paths,e.config.watch.ignored,e.processor,"processRulesUpdate");e.logger.info({scope:i,filesProcessed:t},"Config reindex (rules) completed")}else{const t=await W(e.config.watch.paths,e.config.watch.ignored,e.processor,"processFile");e.logger.info({scope:i,filesProcessed:t},"Config reindex (full) completed")}}catch(t){e.logger.error({err:D(t),scope:i},"Config reindex failed")}})(),await r.status(200).send({status:"started",scope:i})}catch(t){return e.logger.error({err:D(t)},"Config reindex request failed"),await r.status(500).send({error:"Internal server error"})}}}({config:a,processor:r,logger:s})),c}function Q(e){let t=i.resolve(e);const r=i.resolve("/");for(;t!==r;){if(a.existsSync(i.join(t,".git"))&&a.statSync(i.join(t,".git")).isDirectory())return t;const e=i.dirname(t);if(e===t)break;t=e}}function G(e){const t=i.resolve(e);try{return a.statSync(t).isDirectory()?t:i.dirname(t)}catch{}const r=/[*?[{]/.exec(e);if(!r)return;const n=e.slice(0,r.index).trim(),o=0===n.length?".":n.endsWith("/")||n.endsWith("\\")?n:i.dirname(n),s=i.resolve(o);return a.existsSync(s)?s:void 0}function J(e){const t=[],r=i.join(e,".gitignore");let n;a.existsSync(r)&&t.push(r);try{n=a.readdirSync(e)}catch{return t}for(const r of n){if(".git"===r||"node_modules"===r)continue;const n=i.join(e,r);try{a.statSync(n).isDirectory()&&t.push(...J(n))}catch{}}return t}function B(e){const t=a.readFileSync(e,"utf8");return c().add(t)}class K{repos=new Map;constructor(e){this.scan(e)}scan(e){this.repos.clear();const t=new Set;for(const r of e){const e=G(r);if(!e)continue;if(t.has(e))continue;t.add(e);const n=Q(e);if(!n)continue;if(this.repos.has(n))continue;const o=J(n).map((e=>({dir:i.dirname(e),ig:B(e)})));o.sort(((e,t)=>t.dir.length-e.dir.length)),this.repos.set(n,{root:n,entries:o})}}isIgnored(e){const t=i.resolve(e);for(const[,e]of this.repos){const r=i.relative(e.root,t);if(!(r.startsWith("..")||r.startsWith(i.resolve("/"))||/^[a-zA-Z]:/.test(r)))for(const r of e.entries){const e=i.relative(r.dir,t);if(e.startsWith("..")||/^[a-zA-Z]:/.test(e))continue;const n=e.replace(/\\/g,"/");if(r.ig.ignores(n))return!0}}return!1}invalidate(e){const t=i.resolve(e),r=i.dirname(t);for(const[,e]of this.repos){if(!i.relative(e.root,r).startsWith(".."))return e.entries=e.entries.filter((e=>e.dir!==r)),void(a.existsSync(t)&&(e.entries.push({dir:r,ig:B(t)}),e.entries.sort(((e,t)=>t.dir.length-e.dir.length))))}const n=Q(r);if(n&&a.existsSync(t)){const e=[{dir:r,ig:B(t)}];if(this.repos.has(n)){const t=this.repos.get(n);t.entries.push(e[0]),t.entries.sort(((e,t)=>t.dir.length-e.dir.length))}else this.repos.set(n,{root:n,entries:e})}}}function U(e,t){return"string"!=typeof e?e:e.replace(/\$\{([^}]+)\}/g,((e,r)=>{const i=o.get(t,r);return null==i?"":"string"==typeof i?i:JSON.stringify(i)}))}function V(e,t){const r={};for(const[i,n]of Object.entries(e))r[i]=U(n,t);return r}async function Y(e,t){const r={};for(const n of e){const e=i.resolve(t,n),o=await import(l.pathToFileURL(e).href),s="object"==typeof o.default&&null!==o.default?o.default:o;for(const[e,t]of Object.entries(s))"function"==typeof t&&(r[e]=t)}return r}async function Z(e,t,r,n,s,c,l){const d=function(e,t){const r=new Map,n=t=>{const n=e?i.resolve(e,t):t;if(!r.has(n)){const e=a.readFileSync(n,"utf-8");r.set(n,JSON.parse(e))}return r.get(n)};return{split:(e,t)=>e.split(t),slice:(e,t,r)=>e.slice(t,r),join:(e,t)=>e.join(t),toLowerCase:e=>e.toLowerCase(),replace:(e,t,r)=>e.replace(t,r),get:(e,t)=>o.get(e,t),lookupJson:(e,t,r)=>{const i=n(e)[t];return null==i?null:r?o.get(i,r)??null:i},mapLookup:(e,t,r)=>{if(!Array.isArray(t))return[];const i=n(e),s=[];for(const e of t){if("string"!=typeof e)continue;const t=i[e];if(null==t)continue;const n=o.get(t,r);if(null!=n)if(Array.isArray(n))for(const e of n)s.push(e);else s.push(n)}return s},...t}}(c,l);let u={},f=null;const g=n??console;for(const[n,{rule:o,validate:l}]of e.entries())if(l(t)){const e=V(o.set,t);if(u={...u,...e},o.map){let e;if("string"==typeof o.map){if(o.map.endsWith(".json")&&c)try{const t=i.resolve(c,o.map),r=a.readFileSync(t,"utf-8");e=JSON.parse(r)}catch(e){g.warn(`Failed to load map file "${o.map}": ${e instanceof Error?e.message:String(e)}`);continue}else if(e=r?.[o.map],!e){g.warn(`Map reference "${o.map}" not found in named maps. Skipping map transformation.`);continue}}else e=o.map;try{const r=new h.JsonMap(e,d),i=await r.transform(t);i&&"object"==typeof i&&!Array.isArray(i)?u={...u,...i}:g.warn("JsonMap transformation did not return an object; skipping merge.")}catch(e){g.warn(`JsonMap transformation failed: ${e instanceof Error?e.message:String(e)}`)}}if(o.template&&s){const e=`rule-${String(n)}`,r={...t.json??{},...t,...u};try{const t=s.render(e,r);t&&t.trim()?f=t:g.warn(`Template for rule ${String(n)} rendered empty output. Falling back to raw content.`)}catch(e){g.warn(`Template render failed for rule ${String(n)}: ${e instanceof Error?e.message:String(e)}. Falling back to raw content.`)}}}return{metadata:u,renderedContent:f}}const X=y.unified().use(m,{fragment:!0});function ee(e){e.registerHelper("adfToMarkdown",(function(t){if(!t||"object"!=typeof t)return"";try{const r=g.fromADF(t);return new e.SafeString(p.toMarkdown(r).trim())}catch{return"\x3c!-- ADF conversion failed --\x3e"}})),e.registerHelper("markdownify",(function(t){if("string"!=typeof t||!t.trim())return"";try{const r=X.parse(t),i=f.toMdast(r);return new e.SafeString(p.toMarkdown(i).trim())}catch{return"\x3c!-- HTML conversion failed --\x3e"}})),e.registerHelper("dateFormat",(function(e,t){if(null==e)return"";const r="string"==typeof t?t:"YYYY-MM-DD";return u(e).format(r)})),e.registerHelper("join",(function(e,t){if(!Array.isArray(e))return"";const r="string"==typeof t?t:", ";return e.join(r)})),e.registerHelper("pluck",(function(e,t){return Array.isArray(e)&&"string"==typeof t?e.map((e=>e&&"object"==typeof e?e[t]:void 0)):[]})),e.registerHelper("lowercase",(e=>"string"==typeof e?e.toLowerCase():"")),e.registerHelper("uppercase",(e=>"string"==typeof e?e.toUpperCase():"")),e.registerHelper("capitalize",(e=>"string"==typeof e?o.capitalize(e):"")),e.registerHelper("title",(e=>"string"==typeof e?o.title(e):"")),e.registerHelper("camel",(e=>"string"==typeof e?o.camel(e):"")),e.registerHelper("snake",(e=>"string"==typeof e?o.snake(e):"")),e.registerHelper("dash",(e=>"string"==typeof e?o.dash(e):"")),e.registerHelper("default",(function(e,t){return e??t??""})),e.registerHelper("eq",(function(e,t){return o.isEqual(e,t)})),e.registerHelper("json",(function(t){return new e.SafeString(JSON.stringify(t,null,2))}))}function te(e,t,r,n=new Set){if(e.endsWith(".hbs")||e.endsWith(".handlebars"))return a.readFileSync(i.resolve(r,e),"utf-8");if(void 0!==t?.[e]){if(n.has(e))throw new Error(`Circular template reference detected: ${e}`);return n.add(e),te(t[e],t,r,n)}return e}function re(){const e=d.create();return ee(e),e}async function ie(e,t,r){for(const n of t){const t=i.resolve(r,n),o=await import(l.pathToFileURL(t).href);"function"==typeof o.default&&o.default(e)}}class ne{hbs;compiled=new Map;constructor(e){this.hbs=e}compile(e,t){const r=this.hbs.compile(t);return this.compiled.set(e,r),r}get(e){return this.compiled.get(e)}render(e,t){const r=this.compiled.get(e);return r?r(t):null}}async function oe(e,t,r,i){if(0===e.filter((e=>e.template)).length)return;const n=re();r?.length&&i&&await ie(n,r,i);const o=new ne(n);for(const[r,n]of e.entries()){if(!n.template)continue;const e=te(n.template,t,i??".");o.compile(`rule-${String(r)}`,e)}return o}class se{options;watcher;debounce;constructor(e){this.options=e}start(){this.options.enabled&&(this.watcher=w.watch(this.options.configPath,{ignoreInitial:!0}),this.watcher.on("change",(()=>{this.debounce&&clearTimeout(this.debounce),this.debounce=setTimeout((()=>{this.options.onChange()}),this.options.debounceMs)})),this.watcher.on("error",(e=>{this.options.logger.error({err:D(e)},"Config watcher error")})),this.options.logger.info({configPath:this.options.configPath,debounceMs:this.options.debounceMs},"Config watcher started"))}async stop(){this.debounce&&(clearTimeout(this.debounce),this.debounce=void 0),this.watcher&&(await this.watcher.close(),this.watcher=void 0)}}const ae={metadataDir:".jeeves-watcher",shutdownTimeoutMs:1e4},ce={enabled:!0,debounceMs:1e3},le={host:"127.0.0.1",port:3456},he={level:"info"},de={debounceMs:300,stabilityThresholdMs:500,usePolling:!1,pollIntervalMs:1e3,respectGitignore:!0},ue={chunkSize:1e3,chunkOverlap:200,dimensions:3072,rateLimitPerMinute:300,concurrency:5},fe=v.z.object({paths:v.z.array(v.z.string()).min(1).describe('Glob patterns for files to watch (e.g., "**/*.md"). At least one required.'),ignored:v.z.array(v.z.string()).optional().describe('Glob patterns to exclude from watching (e.g., "**/node_modules/**").'),pollIntervalMs:v.z.number().optional().describe("Polling interval in milliseconds when usePolling is enabled."),usePolling:v.z.boolean().optional().describe("Use polling instead of native file system events (for network drives)."),debounceMs:v.z.number().optional().describe("Debounce delay in milliseconds for file change events."),stabilityThresholdMs:v.z.number().optional().describe("Time in milliseconds a file must remain unchanged before processing."),respectGitignore:v.z.boolean().optional().describe("Skip files ignored by .gitignore in git repositories. Only applies to repos with a .git directory. Default: true.")}),ge=v.z.object({enabled:v.z.boolean().optional().describe("Enable automatic reloading when config file changes."),debounceMs:v.z.number().optional().describe("Debounce delay in milliseconds for config file change detection.")}),pe=v.z.object({provider:v.z.string().default("gemini").describe('Embedding provider name (e.g., "gemini", "openai").'),model:v.z.string().default("gemini-embedding-001").describe('Embedding model identifier (e.g., "gemini-embedding-001", "text-embedding-3-small").'),chunkSize:v.z.number().optional().describe("Maximum chunk size in characters for text splitting."),chunkOverlap:v.z.number().optional().describe("Character overlap between consecutive chunks."),dimensions:v.z.number().optional().describe("Embedding vector dimensions (must match model output)."),apiKey:v.z.string().optional().describe("API key for embedding provider (supports ${ENV_VAR} substitution)."),rateLimitPerMinute:v.z.number().optional().describe("Maximum embedding API requests per minute (rate limiting)."),concurrency:v.z.number().optional().describe("Maximum concurrent embedding requests.")}),me=v.z.object({url:v.z.string().describe('Qdrant server URL (e.g., "http://localhost:6333").'),collectionName:v.z.string().describe("Qdrant collection name for vector storage."),apiKey:v.z.string().optional().describe("Qdrant API key for authentication (supports ${ENV_VAR} substitution).")}),ye=v.z.object({host:v.z.string().optional().describe('Host address for API server (e.g., "127.0.0.1", "0.0.0.0").'),port:v.z.number().optional().describe("Port for API server (e.g., 3456).")}),we=v.z.object({level:v.z.string().optional().describe("Logging level (trace, debug, info, warn, error, fatal)."),file:v.z.string().optional().describe("Path to log file (logs to stdout if omitted).")}),be=v.z.object({match:v.z.record(v.z.string(),v.z.unknown()).describe("JSON Schema object to match against file attributes."),set:v.z.record(v.z.string(),v.z.unknown()).describe("Metadata fields to set when match succeeds."),map:v.z.union([h.jsonMapMapSchema,v.z.string()]).optional().describe("JsonMap transformation (inline definition, named map reference, or .json file path)."),template:v.z.string().optional().describe("Handlebars content template (inline string, named ref, or .hbs/.handlebars file path).")}),ve=v.z.object({watch:fe.describe("File system watch configuration."),configWatch:ge.optional().describe("Configuration file watch settings."),embedding:pe.describe("Embedding model configuration."),vectorStore:me.describe("Qdrant vector store configuration."),metadataDir:v.z.string().optional().describe("Directory for persisted metadata sidecar files."),api:ye.optional().describe("API server configuration."),extractors:v.z.record(v.z.string(),v.z.unknown()).optional().describe("Extractor configurations keyed by name."),inferenceRules:v.z.array(be).optional().describe("Rules for inferring metadata from file attributes."),maps:v.z.record(v.z.string(),h.jsonMapMapSchema.or(v.z.string())).optional().describe("Reusable named JsonMap transformations (inline definition or .json file path resolved relative to config directory)."),templates:v.z.record(v.z.string(),v.z.string()).optional().describe("Named reusable Handlebars templates (inline strings or .hbs/.handlebars file paths)."),templateHelpers:v.z.object({paths:v.z.array(v.z.string()).optional().describe("File paths to custom helper modules.")}).optional().describe("Custom Handlebars helper registration."),mapHelpers:v.z.object({paths:v.z.array(v.z.string()).optional().describe("File paths to JS modules exporting functions to merge into the JsonMap lib.")}).optional().describe("Custom JsonMap lib function registration."),logging:we.optional().describe("Logging configuration."),shutdownTimeoutMs:v.z.number().optional().describe("Timeout in milliseconds for graceful shutdown."),maxRetries:v.z.number().optional().describe("Maximum consecutive system-level failures before triggering fatal error. Default: Infinity."),maxBackoffMs:v.z.number().optional().describe("Maximum backoff delay in milliseconds for system errors. Default: 60000.")}),Me=/\$\{([^}]+)\}/g;function Se(e){if("string"==typeof e)return function(e){return e.replace(Me,((e,t)=>{const r=process.env[t];return void 0===r?e:r}))}(e);if(Array.isArray(e))return e.map((e=>Se(e)));if(null!==e&&"object"==typeof e){const t={};for(const[r,i]of Object.entries(e))t[r]=Se(i);return t}return e}const Fe="jeeves-watcher";async function ke(e){const t=b.cosmiconfig(Fe),r=e?await t.load(e):await t.search();if(!r||r.isEmpty)throw new Error("No jeeves-watcher configuration found. Create a .jeeves-watcherrc or jeeves-watcher.config.{js,ts,json,yaml} file.");try{const e=ve.parse(r.config);if(e.maps){const t=i.dirname(r.filepath);for(const[r,n]of Object.entries(e.maps))if("string"==typeof n){const o=i.resolve(t,n),s=a.readFileSync(o,"utf-8");e.maps[r]=JSON.parse(s)}}return Se((n=e,{...ae,...n,watch:{...de,...n.watch},configWatch:{...ce,...n.configWatch},embedding:{...ue,...n.embedding},api:{...le,...n.api},logging:{...he,...n.logging}}))}catch(e){if(e instanceof v.ZodError){const t=e.issues.map((e=>`${e.path.join(".")}: ${e.message}`)).join("; ");throw new Error(`Invalid jeeves-watcher configuration: ${t}`)}throw e}var n}function xe(e){return e||{warn(e,t){t?console.warn(e,t):console.warn(e)}}}function Pe(e,t){return e<=0?Promise.resolve():new Promise(((r,i)=>{const n=setTimeout((()=>{s(),r()}),e),o=()=>{s(),i(new Error("Retry sleep aborted"))},s=()=>{clearTimeout(n),t&&t.removeEventListener("abort",o)};if(t){if(t.aborted)return void o();t.addEventListener("abort",o,{once:!0})}}))}function ze(e,t,r,i=0){const n=Math.max(0,e-1),o=Math.min(r,t*2**n),s=i>0?1+Math.random()*i:1;return Math.round(o*s)}async function je(e,t){const r=Math.max(1,t.attempts);let i;for(let n=1;n<=r;n++)try{return await e(n)}catch(e){i=e;if(n>=r)break;const o=ze(n,t.baseDelayMs,t.maxDelayMs,t.jitter);t.onRetry?.({attempt:n,attempts:r,delayMs:o,error:e}),await Pe(o,t.signal)}throw i}const Ce=new Map([["mock",function(e){return function(e){return{dimensions:e,embed:t=>Promise.resolve(t.map((t=>{const r=s.createHash("sha256").update(t,"utf8").digest(),i=[];for(let t=0;t<e;t++){const e=r[t%r.length];i.push(e/127.5-1)}return i})))}}(e.dimensions??768)}],["gemini",function(e,t){if(!e.apiKey)throw new Error("Gemini embedding provider requires config.embedding.apiKey");const r=e.dimensions??3072,i=xe(t),n=new M.GoogleGenerativeAIEmbeddings({apiKey:e.apiKey,model:e.model});return{dimensions:r,async embed(t){const o=await je((async r=>(r>1&&i.warn({attempt:r,provider:"gemini",model:e.model},"Retrying embedding request"),n.embedDocuments(t))),{attempts:5,baseDelayMs:500,maxDelayMs:1e4,jitter:.2,onRetry:({attempt:t,delayMs:r,error:n})=>{i.warn({attempt:t,delayMs:r,provider:"gemini",model:e.model,err:D(n)},"Embedding call failed; will retry")}});for(const e of o)if(e.length!==r)throw new Error(`Gemini embedding returned invalid dimensions: expected ${String(r)}, got ${String(e.length)}`);return o}}}]]);function Ee(e,t){const r=Ce.get(e.provider);if(!r)throw new Error(`Unsupported embedding provider: ${e.provider}`);return r(e,t)}function Re(e){const t=e?.level??"info";if(e?.file){const r=S.transport({target:"pino/file",options:{destination:e.file,mkdir:!0}});return S({level:t},r)}return S({level:t})}function Te(e){return s.createHash("sha256").update(e,"utf8").digest("hex")}const De="6a6f686e-6761-4c74-ad6a-656576657321";function Ae(e,t){const r=void 0!==t?`${N(e)}#${String(t)}`:N(e);return F.v5(r,De)}const Ie=["content","body","text","snippet","subject","description","summary","transcript"];function We(e){if(!e||"object"!=typeof e)return JSON.stringify(e);const t=e;for(const e of Ie){const r=t[e];if("string"==typeof r&&r.trim())return r}return JSON.stringify(e)}async function Ne(e){const t=await r.readFile(e,"utf8"),{frontmatter:i,body:n}=function(e){const t=e.replace(/^\uFEFF/,"");if(!/^\s*---/.test(t))return{body:e};const r=/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/m.exec(t);if(!r)return{body:e};const[,i,n]=r,o=x.load(i);return{frontmatter:o&&"object"==typeof o&&!Array.isArray(o)?o:void 0,body:n}}(t);return{text:n,frontmatter:i}}async function He(e){return{text:(await r.readFile(e,"utf8")).replace(/^\uFEFF/,"")}}async function Oe(e){const t=await r.readFile(e,"utf8"),i=T.load(t.replace(/^\uFEFF/,""));i("script, style").remove();return{text:i("body").text().trim()||i.text().trim()}}const Le=new Map([[".md",Ne],[".markdown",Ne],[".txt",He],[".text",He],[".json",async function(e){const t=await r.readFile(e,"utf8"),i=JSON.parse(t.replace(/^\uFEFF/,"")),n=i&&"object"==typeof i&&!Array.isArray(i)?i:void 0;return{text:We(i),json:n}}],[".pdf",async function(e){const t=await r.readFile(e),i=new Uint8Array(t),{extractText:n}=await import("unpdf"),{text:o}=await n(i);return{text:Array.isArray(o)?o.join("\n\n"):o}}],[".docx",async function(e){const t=await r.readFile(e);return{text:(await P.extractRawText({buffer:t})).value}}],[".html",Oe],[".htm",Oe]]);async function _e(e,t){const r=Le.get(t.toLowerCase());return r?r(e):He(e)}function $e(e,t,r,n){const o=e.replace(/\\/g,"/"),s={file:{path:o,directory:i.dirname(o).replace(/\\/g,"/"),filename:i.basename(o),extension:i.extname(o),sizeBytes:t.size,modified:t.mtime.toISOString()}};return r&&(s.frontmatter=r),n&&(s.json=n),s}function qe(e){const t=function(){const e=new z({allErrors:!0});return j(e),e.addKeyword({keyword:"glob",type:"string",schemaType:"string",validate:(e,t)=>n.isMatch(t,e)}),e}();return e.map(((e,r)=>({rule:e,validate:t.compile({$id:`rule-${String(r)}`,...e.match})})))}async function Qe(e,t,n,o,s,a,c,l){const h=i.extname(e),d=await r.stat(e),u=await _e(e,h),f=$e(e,d,u.frontmatter,u.json),{metadata:g,renderedContent:p}=await Z(t,f,o,s,a,c,l),m=await O(e,n);return{inferred:g,enrichment:m,metadata:{...g,...m??{}},attributes:f,extracted:u,renderedContent:p}}function Ge(e,t){const r=[];for(let i=0;i<t;i++)r.push(Ae(e,i));return r}function Je(e,t=1){if(!e)return t;const r=e.total_chunks;return"number"==typeof r?r:t}class Be{config;embeddingProvider;vectorStore;compiledRules;logger;templateEngine;constructor(e,t,r,i,n,o){this.config=e,this.embeddingProvider=t,this.vectorStore=r,this.compiledRules=i,this.logger=n,this.templateEngine=o}async processFile(e){try{const t=i.extname(e),{metadata:r,extracted:n,renderedContent:o}=await Qe(e,this.compiledRules,this.config.metadataDir,this.config.maps,this.logger,this.templateEngine,this.config.configDir,this.config.customMapLib),s=o??n.text;if(!s.trim())return void this.logger.debug({filePath:e},"Skipping empty file");const a=Te(s),c=Ae(e,0),l=await this.vectorStore.getPayload(c);if(l&&l.content_hash===a)return void this.logger.debug({filePath:e},"Content unchanged, skipping");const h=Je(l),d=this.config.chunkSize??1e3,u=function(e,t,r){const i=e.toLowerCase();return".md"===i||".markdown"===i?new C.MarkdownTextSplitter({chunkSize:t,chunkOverlap:r}):new C.RecursiveCharacterTextSplitter({chunkSize:t,chunkOverlap:r})}(t,d,this.config.chunkOverlap??200),f=await u.splitText(s),g=await this.embeddingProvider.embed(f),p=f.map(((t,i)=>({id:Ae(e,i),vector:g[i],payload:{...r,file_path:e.replace(/\\/g,"/"),chunk_index:i,total_chunks:f.length,content_hash:a,chunk_text:t}})));if(await this.vectorStore.upsert(p),h>f.length){const t=Ge(e,h).slice(f.length);await this.vectorStore.delete(t)}this.logger.info({filePath:e,chunks:f.length},"File processed successfully")}catch(t){this.logger.error({filePath:e,err:D(t)},"Failed to process file")}}async deleteFile(e){try{const t=Ae(e,0),r=await this.vectorStore.getPayload(t),i=Ge(e,Je(r));await this.vectorStore.delete(i),await _(e,this.config.metadataDir),this.logger.info({filePath:e},"File deleted from index")}catch(t){this.logger.error({filePath:e,err:D(t)},"Failed to delete file")}}async processMetadataUpdate(e,t){try{const r={...await O(e,this.config.metadataDir)??{},...t};await L(e,this.config.metadataDir,r);const i=Ae(e,0),n=await this.vectorStore.getPayload(i);if(!n)return null;const o=Je(n),s=Ge(e,o);return await this.vectorStore.setPayload(s,r),this.logger.info({filePath:e,chunks:o},"Metadata updated"),r}catch(t){return this.logger.error({filePath:e,err:D(t)},"Failed to update metadata"),null}}async processRulesUpdate(e){try{const t=Ae(e,0),r=await this.vectorStore.getPayload(t);if(!r)return this.logger.debug({filePath:e},"File not indexed, skipping"),null;const{metadata:i}=await Qe(e,this.compiledRules,this.config.metadataDir,this.config.maps,this.logger,this.templateEngine,this.config.configDir,this.config.customMapLib),n=Je(r),o=Ge(e,n);return await this.vectorStore.setPayload(o,i),this.logger.info({filePath:e,chunks:n},"Rules re-applied"),i}catch(t){return this.logger.error({filePath:e,err:D(t)},"Failed to re-apply rules"),null}}updateRules(e,t,r){this.compiledRules=e,t&&(this.templateEngine=t),void 0!==r&&(this.config={...this.config,customMapLib:r}),this.logger.info({rules:e.length},"Inference rules updated")}}class Ke{debounceMs;concurrency;rateLimitPerMinute;started=!1;active=0;debounceTimers=new Map;latestByKey=new Map;normalQueue=[];lowQueue=[];tokens;lastRefillMs=Date.now();drainWaiters=[];constructor(e){this.debounceMs=e.debounceMs,this.concurrency=e.concurrency,this.rateLimitPerMinute=e.rateLimitPerMinute,this.tokens=this.rateLimitPerMinute??Number.POSITIVE_INFINITY}enqueue(e,t){const r=`${e.priority}:${e.path}`;this.latestByKey.set(r,{event:e,fn:t});const i=this.debounceTimers.get(r);i&&clearTimeout(i);const n=setTimeout((()=>{this.debounceTimers.delete(r);const e=this.latestByKey.get(r);e&&(this.latestByKey.delete(r),this.push(e),this.pump())}),this.debounceMs);this.debounceTimers.set(r,n)}process(){this.started=!0,this.pump()}async drain(){this.isIdle()||await new Promise((e=>{this.drainWaiters.push(e)}))}push(e){"low"===e.event.priority?this.lowQueue.push(e):this.normalQueue.push(e)}refillTokens(e){if(void 0===this.rateLimitPerMinute)return;const t=Math.max(0,e-this.lastRefillMs)*(this.rateLimitPerMinute/6e4);this.tokens=Math.min(this.rateLimitPerMinute,this.tokens+t),this.lastRefillMs=e}takeToken(){const e=Date.now();return this.refillTokens(e),!(this.tokens<1)&&(this.tokens-=1,!0)}nextItem(){return this.normalQueue.shift()??this.lowQueue.shift()}pump(){if(this.started){for(;this.active<this.concurrency;){const e=this.nextItem();if(!e)break;if(!this.takeToken()){"low"===e.event.priority?this.lowQueue.unshift(e):this.normalQueue.unshift(e),setTimeout((()=>{this.pump()}),250);break}this.active+=1,Promise.resolve().then((()=>e.fn(e.event))).finally((()=>{this.active-=1,this.pump(),this.maybeResolveDrain()}))}this.maybeResolveDrain()}}isIdle(){return 0===this.active&&0===this.normalQueue.length&&0===this.lowQueue.length&&0===this.debounceTimers.size&&0===this.latestByKey.size}maybeResolveDrain(){if(!this.isIdle())return;const e=this.drainWaiters;this.drainWaiters=[];for(const t of e)t()}}function Ue(e){return null==e?"keyword":"number"==typeof e?Number.isInteger(e)?"integer":"float":"boolean"==typeof e?"bool":Array.isArray(e)?"keyword[]":"string"==typeof e&&e.length>256?"text":"keyword"}class Ve{client;collectionName;dims;log;constructor(e,t,r){this.client=new E.QdrantClient({url:e.url,apiKey:e.apiKey,checkCompatibility:!1}),this.collectionName=e.collectionName,this.dims=t,this.log=xe(r)}async ensureCollection(){try{const e=await this.client.getCollections();e.collections.some((e=>e.name===this.collectionName))||await this.client.createCollection(this.collectionName,{vectors:{size:this.dims,distance:"Cosine"}})}catch(e){throw new Error(`Failed to ensure collection "${this.collectionName}": ${String(e)}`)}}async upsert(e){0!==e.length&&await je((async t=>{t>1&&this.log.warn({attempt:t,operation:"qdrant.upsert",points:e.length},"Retrying Qdrant upsert"),await this.client.upsert(this.collectionName,{wait:!0,points:e.map((e=>({id:e.id,vector:e.vector,payload:e.payload})))})}),{attempts:5,baseDelayMs:500,maxDelayMs:1e4,jitter:.2,onRetry:({attempt:e,delayMs:t,error:r})=>{this.log.warn({attempt:e,delayMs:t,operation:"qdrant.upsert",err:D(r)},"Qdrant upsert failed; will retry")}})}async delete(e){0!==e.length&&await je((async t=>{t>1&&this.log.warn({attempt:t,operation:"qdrant.delete",ids:e.length},"Retrying Qdrant delete"),await this.client.delete(this.collectionName,{wait:!0,points:e})}),{attempts:5,baseDelayMs:500,maxDelayMs:1e4,jitter:.2,onRetry:({attempt:e,delayMs:t,error:r})=>{this.log.warn({attempt:e,delayMs:t,operation:"qdrant.delete",err:D(r)},"Qdrant delete failed; will retry")}})}async setPayload(e,t){0!==e.length&&await this.client.setPayload(this.collectionName,{wait:!0,points:e,payload:t})}async getPayload(e){try{const t=await this.client.retrieve(this.collectionName,{ids:[e],with_payload:!0,with_vector:!1});return 0===t.length?null:t[0].payload}catch{return null}}async getCollectionInfo(){const e=await this.client.getCollection(this.collectionName),t=e.points_count??0,r=e.config.params.vectors,i=void 0!==r&&"size"in r?r.size:0,n={},o=Object.entries(e.payload_schema);if(o.length>0)for(const[e,t]of o)n[e]={type:t.data_type??"unknown"};else t>0&&await this.discoverPayloadFields(n);return{pointCount:t,dimensions:i,payloadFields:n}}async discoverPayloadFields(e,t=100){const r=await this.client.scroll(this.collectionName,{limit:t,with_payload:!0,with_vector:!1});for(const t of r.points){const r=t.payload;if(r)for(const[t,i]of Object.entries(r))t in e||(e[t]={type:Ue(i)})}}async search(e,t,r){return(await this.client.search(this.collectionName,{vector:e,limit:t,with_payload:!0,...r?{filter:r}:{}})).map((e=>({id:String(e.id),score:e.score,payload:e.payload})))}async*scroll(e,t=100){let r;for(;;){const i=await this.client.scroll(this.collectionName,{limit:t,with_payload:!0,with_vector:!1,...e?{filter:e}:{},...void 0!==r?{offset:r}:{}});for(const e of i.points)yield{id:String(e.id),payload:e.payload};const n=i.next_page_offset;if(null==n)break;if("string"!=typeof n&&"number"!=typeof n)break;r=n}}}class Ye{consecutiveFailures=0;maxRetries;maxBackoffMs;baseDelayMs;onFatalError;logger;constructor(e){this.maxRetries=e.maxRetries??Number.POSITIVE_INFINITY,this.maxBackoffMs=e.maxBackoffMs??6e4,this.baseDelayMs=e.baseDelayMs??1e3,this.onFatalError=e.onFatalError,this.logger=e.logger}recordSuccess(){this.consecutiveFailures>0&&this.logger.info({previousFailures:this.consecutiveFailures},"System health recovered"),this.consecutiveFailures=0}recordFailure(e){if(this.consecutiveFailures+=1,this.logger.error({consecutiveFailures:this.consecutiveFailures,maxRetries:this.maxRetries,err:D(e)},"System-level failure recorded"),this.consecutiveFailures>=this.maxRetries){if(this.logger.fatal({consecutiveFailures:this.consecutiveFailures},"Maximum retries exceeded, triggering fatal error"),this.onFatalError)return this.onFatalError(e),!1;throw e instanceof Error?e:new Error(`Fatal system error: ${String(e)}`)}return!0}get currentBackoffMs(){if(0===this.consecutiveFailures)return 0;const e=Math.max(0,this.consecutiveFailures-1);return Math.min(this.maxBackoffMs,this.baseDelayMs*2**e)}async backoff(e){const t=this.currentBackoffMs;t<=0||(this.logger.warn({delayMs:t,consecutiveFailures:this.consecutiveFailures},"Backing off before next attempt"),await new Promise(((r,i)=>{const n=setTimeout((()=>{s(),r()}),t),o=()=>{s(),i(new Error("Backoff aborted"))},s=()=>{clearTimeout(n),e&&e.removeEventListener("abort",o)};if(e){if(e.aborted)return void o();e.addEventListener("abort",o,{once:!0})}})))}get failures(){return this.consecutiveFailures}}function Ze(e){const t=e.replace(/\\/g,"/").split("/"),r=[];for(const e of t){if(/[*?{[\]]/.test(e))break;r.push(e)}return r.join("/")||"."}function Xe(e){const t=function(e){const t=e.map((e=>e.replace(/\\/g,"/").toLowerCase()));return[...new Set(t)].sort().filter(((e,t,r)=>{const i=e.endsWith("/")?e:e+"/";return!r.some((t=>t!==e&&i.startsWith(t+"/")))}))}(e.map(Ze)),r=function(e){const t=e.map((e=>e.replace(/\\/g,"/"))),r=n(t,{dot:!0,nocase:!0});return e=>{const t=e.replace(/\\/g,"/");return r(t)}}(e);return{roots:t,matches:r}}class et{config;queue;processor;logger;health;gitignoreFilter;globMatches;watcher;constructor(e,t,r,i,n={}){this.config=e,this.queue=t,this.processor=r,this.logger=i,this.gitignoreFilter=n.gitignoreFilter,this.globMatches=()=>!0;const o={maxRetries:n.maxRetries,maxBackoffMs:n.maxBackoffMs,onFatalError:n.onFatalError,logger:i};this.health=new Ye(o)}start(){const{roots:e,matches:t}=Xe(this.config.paths);this.globMatches=t;const r=this.config.ignored?function(e){return e.map((e=>{if("string"!=typeof e)return e;const t=e.replace(/\\/g,"/"),r=n(t,{dot:!0,nocase:!0});return e=>{const t=e.replace(/\\/g,"/");return r(t)}}))}(this.config.ignored):void 0;this.watcher=w.watch(e,{ignored:r,usePolling:this.config.usePolling,interval:this.config.pollIntervalMs,awaitWriteFinish:!!this.config.stabilityThresholdMs&&{stabilityThreshold:this.config.stabilityThresholdMs},ignoreInitial:!1}),this.watcher.on("add",(e=>{this.handleGitignoreChange(e),this.globMatches(e)&&(this.isGitignored(e)||(this.logger.debug({path:e},"File added"),this.queue.enqueue({type:"create",path:e,priority:"normal"},(()=>this.wrapProcessing((()=>this.processor.processFile(e)))))))})),this.watcher.on("change",(e=>{this.handleGitignoreChange(e),this.globMatches(e)&&(this.isGitignored(e)||(this.logger.debug({path:e},"File changed"),this.queue.enqueue({type:"modify",path:e,priority:"normal"},(()=>this.wrapProcessing((()=>this.processor.processFile(e)))))))})),this.watcher.on("unlink",(e=>{this.handleGitignoreChange(e),this.globMatches(e)&&(this.isGitignored(e)||(this.logger.debug({path:e},"File removed"),this.queue.enqueue({type:"delete",path:e,priority:"normal"},(()=>this.wrapProcessing((()=>this.processor.deleteFile(e)))))))})),this.watcher.on("error",(e=>{this.logger.error({err:D(e)},"Watcher error"),this.health.recordFailure(e)})),this.queue.process(),this.logger.info({paths:this.config.paths},"Filesystem watcher started")}async stop(){this.watcher&&(await this.watcher.close(),this.watcher=void 0,this.logger.info("Filesystem watcher stopped"))}get systemHealth(){return this.health}isGitignored(e){if(!this.gitignoreFilter)return!1;const t=this.gitignoreFilter.isIgnored(e);return t&&this.logger.debug({path:e},"Skipping gitignored file"),t}handleGitignoreChange(e){this.gitignoreFilter&&e.endsWith(".gitignore")&&(this.logger.info({path:e},"Gitignore file changed, refreshing filter"),this.gitignoreFilter.invalidate(e))}async wrapProcessing(e){try{await this.health.backoff(),await e(),this.health.recordSuccess()}catch(e){this.health.recordFailure(e)||await this.stop()}}}const tt={loadConfig:ke,createLogger:Re,createEmbeddingProvider:Ee,createVectorStoreClient:(e,t,r)=>new Ve(e,t,r),compileRules:qe,createDocumentProcessor:(e,t,r,i,n,o)=>new Be(e,t,r,i,n,o),createEventQueue:e=>new Ke(e),createFileSystemWatcher:(e,t,r,i,n)=>new et(e,t,r,i,n),createApiServer:q};class rt{config;configPath;factories;runtimeOptions;logger;watcher;queue;server;processor;configWatcher;constructor(e,t,r={},i={}){this.config=e,this.configPath=t,this.factories={...tt,...r},this.runtimeOptions=i}async start(){const e=this.factories.createLogger(this.config.logging);this.logger=e;const{embeddingProvider:t,vectorStore:r}=await this.initEmbeddingAndStore(e),n=this.factories.compileRules(this.config.inferenceRules??[]),o=this.configPath?i.dirname(this.configPath):".",s=await oe(this.config.inferenceRules??[],this.config.templates,this.config.templateHelpers?.paths,o),a=this.config.mapHelpers?.paths?.length&&o?await Y(this.config.mapHelpers.paths,o):void 0,c=this.factories.createDocumentProcessor({metadataDir:this.config.metadataDir??".jeeves-metadata",chunkSize:this.config.embedding.chunkSize,chunkOverlap:this.config.embedding.chunkOverlap,maps:this.config.maps,configDir:o,customMapLib:a},t,r,n,e,s);this.processor=c,this.queue=this.factories.createEventQueue({debounceMs:this.config.watch.debounceMs??2e3,concurrency:this.config.embedding.concurrency??5,rateLimitPerMinute:this.config.embedding.rateLimitPerMinute}),this.watcher=this.createWatcher(this.queue,c,e),this.server=await this.startApiServer(c,r,t,e),this.watcher.start(),this.startConfigWatch(),e.info("jeeves-watcher started")}async stop(){if(await this.stopConfigWatch(),this.watcher&&await this.watcher.stop(),this.queue){const e=this.config.shutdownTimeoutMs??1e4;await Promise.race([this.queue.drain().then((()=>!0)),new Promise((t=>{setTimeout((()=>{t(!1)}),e)}))])||this.logger?.warn({timeoutMs:e},"Queue drain timeout hit, forcing shutdown")}this.server&&await this.server.close(),this.logger?.info("jeeves-watcher stopped")}async initEmbeddingAndStore(e){let t;try{t=this.factories.createEmbeddingProvider(this.config.embedding,e)}catch(t){throw e.fatal({err:D(t)},"Failed to create embedding provider"),t}const r=this.factories.createVectorStoreClient(this.config.vectorStore,t.dimensions,e);return await r.ensureCollection(),{embeddingProvider:t,vectorStore:r}}createWatcher(e,t,r){const i=this.config.watch.respectGitignore??!0?new K(this.config.watch.paths):void 0;return this.factories.createFileSystemWatcher(this.config.watch,e,t,r,{maxRetries:this.config.maxRetries,maxBackoffMs:this.config.maxBackoffMs,onFatalError:this.runtimeOptions.onFatalError,gitignoreFilter:i})}async startApiServer(e,t,r,i){const n=this.factories.createApiServer({processor:e,vectorStore:t,embeddingProvider:r,queue:this.queue,config:this.config,logger:i});return await n.listen({host:this.config.api?.host??"127.0.0.1",port:this.config.api?.port??3456}),n}startConfigWatch(){const e=this.logger;if(!e)return;const t=this.config.configWatch?.enabled??!0;t&&this.configPath?(this.configWatcher=new se({configPath:this.configPath,enabled:t,debounceMs:this.config.configWatch?.debounceMs??1e4,logger:e,onChange:async()=>this.reloadConfig()}),this.configWatcher.start()):this.configPath||e.debug("Config watch enabled, but no config path was provided")}async stopConfigWatch(){this.configWatcher&&(await this.configWatcher.stop(),this.configWatcher=void 0)}async reloadConfig(){const e=this.logger,t=this.processor;if(e&&t&&this.configPath){e.info({configPath:this.configPath},"Config change detected, reloading...");try{const r=await this.factories.loadConfig(this.configPath);this.config=r;const n=this.factories.compileRules(r.inferenceRules??[]),o=i.dirname(this.configPath),s=await oe(r.inferenceRules??[],r.templates,r.templateHelpers?.paths,o),a=r.mapHelpers?.paths?.length&&o?await Y(r.mapHelpers.paths,o):void 0;t.updateRules(n,s,a),e.info({configPath:this.configPath,rules:n.length},"Config reloaded")}catch(t){e.error({err:D(t)},"Failed to reload config")}}}}e.DocumentProcessor=Be,e.EventQueue=Ke,e.FileSystemWatcher=et,e.GitignoreFilter=K,e.JeevesWatcher=rt,e.SystemHealth=Ye,e.TemplateEngine=ne,e.VectorStoreClient=Ve,e.apiConfigSchema=ye,e.applyRules=Z,e.buildAttributes=$e,e.buildTemplateEngine=oe,e.compileRules=qe,e.configWatchConfigSchema=ge,e.contentHash=Te,e.createApiServer=q,e.createEmbeddingProvider=Ee,e.createHandlebarsInstance=re,e.createLogger=Re,e.deleteMetadata=_,e.embeddingConfigSchema=pe,e.extractText=_e,e.inferenceRuleSchema=be,e.jeevesWatcherConfigSchema=ve,e.loadConfig=ke,e.loadCustomHelpers=ie,e.loggingConfigSchema=we,e.metadataPath=H,e.pointId=Ae,e.readMetadata=O,e.registerBuiltinHelpers=ee,e.resolveTemplateSource=te,e.startFromConfig=async function(e){const t=await ke(e),r=new rt(t,e);return function(e){const t=async()=>{await e(),process.exit(0)};process.on("SIGTERM",(()=>{t()})),process.on("SIGINT",(()=>{t()}))}((()=>r.stop())),await r.start(),r},e.vectorStoreConfigSchema=me,e.watchConfigSchema=fe,e.writeMetadata=L}(this["jeeves-watcher"]=this["jeeves-watcher"]||{},Fastify,promises,node_path,picomatch,radash,node_crypto,node_fs,ignore,node_url,jsonmap,Handlebars,dayjs,hastUtilToMdast,mdastUtilFromAdf,mdastUtilToMarkdown,rehypeParse,unified,chokidar,cosmiconfig,zod,googleGenai,pino,uuid,cheerio,yaml,mammoth,Ajv,addFormats,textsplitters,jsClientRest);
package/dist/mjs/index.js CHANGED
@@ -6,6 +6,7 @@ import { omit, get, capitalize, title, camel, snake, dash, isEqual } from 'radas
6
6
  import { createHash } from 'node:crypto';
7
7
  import { existsSync, statSync, readdirSync, readFileSync } from 'node:fs';
8
8
  import ignore from 'ignore';
9
+ import { pathToFileURL } from 'node:url';
9
10
  import { JsonMap, jsonMapMapSchema } from '@karmaniverous/jsonmap';
10
11
  import Handlebars from 'handlebars';
11
12
  import dayjs from 'dayjs';
@@ -770,7 +771,7 @@ async function loadCustomMapHelpers(paths, configDir) {
770
771
  const merged = {};
771
772
  for (const p of paths) {
772
773
  const resolved = resolve(configDir, p);
773
- const mod = (await import(resolved));
774
+ const mod = (await import(pathToFileURL(resolved).href));
774
775
  const fns = typeof mod.default === 'object' && mod.default !== null
775
776
  ? mod.default
776
777
  : mod;
@@ -1018,7 +1019,7 @@ function createHandlebarsInstance() {
1018
1019
  async function loadCustomHelpers(hbs, paths, configDir) {
1019
1020
  for (const p of paths) {
1020
1021
  const resolved = resolve(configDir, p);
1021
- const mod = (await import(resolved));
1022
+ const mod = (await import(pathToFileURL(resolved).href));
1022
1023
  if (typeof mod.default === 'function') {
1023
1024
  mod.default(hbs);
1024
1025
  }
package/package.json CHANGED
@@ -185,5 +185,5 @@
185
185
  },
186
186
  "type": "module",
187
187
  "types": "dist/index.d.ts",
188
- "version": "0.4.3"
188
+ "version": "0.4.4"
189
189
  }