@karmaniverous/jeeves-watcher 0.4.0 → 0.4.1

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
@@ -2767,6 +2767,34 @@ function resolveWatchPaths(globs) {
2767
2767
  const matches = buildGlobMatcher(globs);
2768
2768
  return { roots, matches };
2769
2769
  }
2770
+ /**
2771
+ * Convert ignored glob patterns to picomatch matcher functions.
2772
+ *
2773
+ * Chokidar v5 replaced the external `anymatch` dependency with an inline
2774
+ * implementation that does **exact string equality** for string matchers,
2775
+ * breaking glob-based `ignored` patterns. This function converts glob strings
2776
+ * to picomatch functions that chokidar's `createPattern` passes through
2777
+ * unchanged (`typeof matcher === 'function'`).
2778
+ *
2779
+ * Non-string entries (functions, RegExps) are passed through as-is.
2780
+ *
2781
+ * @param ignored - Array of ignored patterns (globs, functions, RegExps).
2782
+ * @returns Array with glob strings replaced by picomatch matcher functions.
2783
+ */
2784
+ function resolveIgnored(ignored) {
2785
+ return ignored.map((entry) => {
2786
+ if (typeof entry !== 'string')
2787
+ return entry;
2788
+ // If the string contains glob characters, convert to a picomatch function.
2789
+ // Literal strings (exact paths) are also converted for consistent matching.
2790
+ const normalizedPattern = entry.replace(/\\/g, '/');
2791
+ const matcher = picomatch(normalizedPattern, { dot: true, nocase: true });
2792
+ return (filePath) => {
2793
+ const normalized = filePath.replace(/\\/g, '/');
2794
+ return matcher(normalized);
2795
+ };
2796
+ });
2797
+ }
2770
2798
 
2771
2799
  /**
2772
2800
  * @module watcher
@@ -2818,8 +2846,14 @@ class FileSystemWatcher {
2818
2846
  // filter emitted events against the original globs via picomatch.
2819
2847
  const { roots, matches } = resolveWatchPaths(this.config.paths);
2820
2848
  this.globMatches = matches;
2849
+ // Chokidar v5's inline anymatch does exact string equality for string
2850
+ // matchers, breaking glob-based ignored patterns. Convert to picomatch
2851
+ // functions that chokidar passes through as-is.
2852
+ const ignored = this.config.ignored
2853
+ ? resolveIgnored(this.config.ignored)
2854
+ : undefined;
2821
2855
  this.watcher = chokidar.watch(roots, {
2822
- ignored: this.config.ignored,
2856
+ ignored,
2823
2857
  usePolling: this.config.usePolling,
2824
2858
  interval: this.config.pollIntervalMs,
2825
2859
  awaitWriteFinish: this.config.stabilityThresholdMs
@@ -2769,6 +2769,34 @@ function resolveWatchPaths(globs) {
2769
2769
  const matches = buildGlobMatcher(globs);
2770
2770
  return { roots, matches };
2771
2771
  }
2772
+ /**
2773
+ * Convert ignored glob patterns to picomatch matcher functions.
2774
+ *
2775
+ * Chokidar v5 replaced the external `anymatch` dependency with an inline
2776
+ * implementation that does **exact string equality** for string matchers,
2777
+ * breaking glob-based `ignored` patterns. This function converts glob strings
2778
+ * to picomatch functions that chokidar's `createPattern` passes through
2779
+ * unchanged (`typeof matcher === 'function'`).
2780
+ *
2781
+ * Non-string entries (functions, RegExps) are passed through as-is.
2782
+ *
2783
+ * @param ignored - Array of ignored patterns (globs, functions, RegExps).
2784
+ * @returns Array with glob strings replaced by picomatch matcher functions.
2785
+ */
2786
+ function resolveIgnored(ignored) {
2787
+ return ignored.map((entry) => {
2788
+ if (typeof entry !== 'string')
2789
+ return entry;
2790
+ // If the string contains glob characters, convert to a picomatch function.
2791
+ // Literal strings (exact paths) are also converted for consistent matching.
2792
+ const normalizedPattern = entry.replace(/\\/g, '/');
2793
+ const matcher = picomatch(normalizedPattern, { dot: true, nocase: true });
2794
+ return (filePath) => {
2795
+ const normalized = filePath.replace(/\\/g, '/');
2796
+ return matcher(normalized);
2797
+ };
2798
+ });
2799
+ }
2772
2800
 
2773
2801
  /**
2774
2802
  * @module watcher
@@ -2820,8 +2848,14 @@ class FileSystemWatcher {
2820
2848
  // filter emitted events against the original globs via picomatch.
2821
2849
  const { roots, matches } = resolveWatchPaths(this.config.paths);
2822
2850
  this.globMatches = matches;
2851
+ // Chokidar v5's inline anymatch does exact string equality for string
2852
+ // matchers, breaking glob-based ignored patterns. Convert to picomatch
2853
+ // functions that chokidar passes through as-is.
2854
+ const ignored = this.config.ignored
2855
+ ? resolveIgnored(this.config.ignored)
2856
+ : undefined;
2823
2857
  this.watcher = chokidar.watch(roots, {
2824
- ignored: this.config.ignored,
2858
+ ignored,
2825
2859
  usePolling: this.config.usePolling,
2826
2860
  interval: this.config.pollIntervalMs,
2827
2861
  awaitWriteFinish: this.config.stabilityThresholdMs
@@ -2738,6 +2738,34 @@
2738
2738
  const matches = buildGlobMatcher(globs);
2739
2739
  return { roots, matches };
2740
2740
  }
2741
+ /**
2742
+ * Convert ignored glob patterns to picomatch matcher functions.
2743
+ *
2744
+ * Chokidar v5 replaced the external `anymatch` dependency with an inline
2745
+ * implementation that does **exact string equality** for string matchers,
2746
+ * breaking glob-based `ignored` patterns. This function converts glob strings
2747
+ * to picomatch functions that chokidar's `createPattern` passes through
2748
+ * unchanged (`typeof matcher === 'function'`).
2749
+ *
2750
+ * Non-string entries (functions, RegExps) are passed through as-is.
2751
+ *
2752
+ * @param ignored - Array of ignored patterns (globs, functions, RegExps).
2753
+ * @returns Array with glob strings replaced by picomatch matcher functions.
2754
+ */
2755
+ function resolveIgnored(ignored) {
2756
+ return ignored.map((entry) => {
2757
+ if (typeof entry !== 'string')
2758
+ return entry;
2759
+ // If the string contains glob characters, convert to a picomatch function.
2760
+ // Literal strings (exact paths) are also converted for consistent matching.
2761
+ const normalizedPattern = entry.replace(/\\/g, '/');
2762
+ const matcher = picomatch(normalizedPattern, { dot: true, nocase: true });
2763
+ return (filePath) => {
2764
+ const normalized = filePath.replace(/\\/g, '/');
2765
+ return matcher(normalized);
2766
+ };
2767
+ });
2768
+ }
2741
2769
 
2742
2770
  /**
2743
2771
  * @module watcher
@@ -2789,8 +2817,14 @@
2789
2817
  // filter emitted events against the original globs via picomatch.
2790
2818
  const { roots, matches } = resolveWatchPaths(this.config.paths);
2791
2819
  this.globMatches = matches;
2820
+ // Chokidar v5's inline anymatch does exact string equality for string
2821
+ // matchers, breaking glob-based ignored patterns. Convert to picomatch
2822
+ // functions that chokidar passes through as-is.
2823
+ const ignored = this.config.ignored
2824
+ ? resolveIgnored(this.config.ignored)
2825
+ : undefined;
2792
2826
  this.watcher = chokidar.watch(roots, {
2793
- ignored: this.config.ignored,
2827
+ ignored,
2794
2828
  usePolling: this.config.usePolling,
2795
2829
  interval: this.config.pollIntervalMs,
2796
2830
  awaitWriteFinish: this.config.stabilityThresholdMs
@@ -1 +1 @@
1
- !function(e,t,r,i,n,o,s,a,c,l,h,d,u,g,f,p,m,y,w,b,v,M,S,F,x,k,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*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 A(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 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 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 _(e,t){try{const i=await r.readFile(N(e,t),"utf8");return JSON.parse(i)}catch{return null}}async function H(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 O(e,t){try{await r.rm(N(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: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 A(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 H(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 A(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 A(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 L(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 B(e){const t=a.readFileSync(e,"utf8");return c().add(t)}class J{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=L(e);if(!n)continue;if(this.repos.has(n))continue;const o=G(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=L(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})}}}const K=p.unified().use(f,{fragment:!0});function U(e){e.registerHelper("adfToMarkdown",(function(t){if(!t||"object"!=typeof t)return"";try{const r=u.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=K.parse(t),i=d.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 h(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 V(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),V(t[e],t,r,n)}return e}function Y(){const e=l.create();return U(e),e}async function Z(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 X{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 ee(e,t,r,i){if(0===e.filter((e=>e.template)).length)return;const n=Y();r?.length&&i&&await Z(n,r,i);const o=new X(n);for(const[r,n]of e.entries()){if(!n.template)continue;const e=V(n.template,t,i??".");o.compile(`rule-${String(r)}`,e)}return o}class te{options;watcher;debounce;constructor(e){this.options=e}start(){this.options.enabled&&(this.watcher=m.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 re={metadataDir:".jeeves-watcher",shutdownTimeoutMs:1e4},ie={enabled:!0,debounceMs:1e3},ne={host:"127.0.0.1",port:3456},oe={level:"info"},se={debounceMs:300,stabilityThresholdMs:500,usePolling:!1,pollIntervalMs:1e3,respectGitignore:!0},ae={chunkSize:1e3,chunkOverlap:200,dimensions:3072,rateLimitPerMinute:300,concurrency:5},ce=w.z.object({paths:w.z.array(w.z.string()).min(1).describe('Glob patterns for files to watch (e.g., "**/*.md"). At least one required.'),ignored:w.z.array(w.z.string()).optional().describe('Glob patterns to exclude from watching (e.g., "**/node_modules/**").'),pollIntervalMs:w.z.number().optional().describe("Polling interval in milliseconds when usePolling is enabled."),usePolling:w.z.boolean().optional().describe("Use polling instead of native file system events (for network drives)."),debounceMs:w.z.number().optional().describe("Debounce delay in milliseconds for file change events."),stabilityThresholdMs:w.z.number().optional().describe("Time in milliseconds a file must remain unchanged before processing."),respectGitignore:w.z.boolean().optional().describe("Skip files ignored by .gitignore in git repositories. Only applies to repos with a .git directory. Default: true.")}),le=w.z.object({enabled:w.z.boolean().optional().describe("Enable automatic reloading when config file changes."),debounceMs:w.z.number().optional().describe("Debounce delay in milliseconds for config file change detection.")}),he=w.z.object({provider:w.z.string().default("gemini").describe('Embedding provider name (e.g., "gemini", "openai").'),model:w.z.string().default("gemini-embedding-001").describe('Embedding model identifier (e.g., "gemini-embedding-001", "text-embedding-3-small").'),chunkSize:w.z.number().optional().describe("Maximum chunk size in characters for text splitting."),chunkOverlap:w.z.number().optional().describe("Character overlap between consecutive chunks."),dimensions:w.z.number().optional().describe("Embedding vector dimensions (must match model output)."),apiKey:w.z.string().optional().describe("API key for embedding provider (supports ${ENV_VAR} substitution)."),rateLimitPerMinute:w.z.number().optional().describe("Maximum embedding API requests per minute (rate limiting)."),concurrency:w.z.number().optional().describe("Maximum concurrent embedding requests.")}),de=w.z.object({url:w.z.string().describe('Qdrant server URL (e.g., "http://localhost:6333").'),collectionName:w.z.string().describe("Qdrant collection name for vector storage."),apiKey:w.z.string().optional().describe("Qdrant API key for authentication (supports ${ENV_VAR} substitution).")}),ue=w.z.object({host:w.z.string().optional().describe('Host address for API server (e.g., "127.0.0.1", "0.0.0.0").'),port:w.z.number().optional().describe("Port for API server (e.g., 3456).")}),ge=w.z.object({level:w.z.string().optional().describe("Logging level (trace, debug, info, warn, error, fatal)."),file:w.z.string().optional().describe("Path to log file (logs to stdout if omitted).")}),fe=w.z.object({match:w.z.record(w.z.string(),w.z.unknown()).describe("JSON Schema object to match against file attributes."),set:w.z.record(w.z.string(),w.z.unknown()).describe("Metadata fields to set when match succeeds."),map:w.z.union([b.jsonMapMapSchema,w.z.string()]).optional().describe("JsonMap transformation (inline definition, named map reference, or .json file path)."),template:w.z.string().optional().describe("Handlebars content template (inline string, named ref, or .hbs/.handlebars file path).")}),pe=w.z.object({watch:ce.describe("File system watch configuration."),configWatch:le.optional().describe("Configuration file watch settings."),embedding:he.describe("Embedding model configuration."),vectorStore:de.describe("Qdrant vector store configuration."),metadataDir:w.z.string().optional().describe("Directory for persisted metadata sidecar files."),api:ue.optional().describe("API server configuration."),extractors:w.z.record(w.z.string(),w.z.unknown()).optional().describe("Extractor configurations keyed by name."),inferenceRules:w.z.array(fe).optional().describe("Rules for inferring metadata from file attributes."),maps:w.z.record(w.z.string(),b.jsonMapMapSchema).optional().describe("Reusable named JsonMap transformations."),templates:w.z.record(w.z.string(),w.z.string()).optional().describe("Named reusable Handlebars templates (inline strings or .hbs/.handlebars file paths)."),templateHelpers:w.z.object({paths:w.z.array(w.z.string()).optional().describe("File paths to custom helper modules.")}).optional().describe("Custom Handlebars helper registration."),logging:ge.optional().describe("Logging configuration."),shutdownTimeoutMs:w.z.number().optional().describe("Timeout in milliseconds for graceful shutdown."),maxRetries:w.z.number().optional().describe("Maximum consecutive system-level failures before triggering fatal error. Default: Infinity."),maxBackoffMs:w.z.number().optional().describe("Maximum backoff delay in milliseconds for system errors. Default: 60000.")}),me=/\$\{([^}]+)\}/g;function ye(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=>ye(e)));if(null!==e&&"object"==typeof e){const t={};for(const[r,i]of Object.entries(e))t[r]=ye(i);return t}return e}const we="jeeves-watcher";async function be(e){const t=y.cosmiconfig(we),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=pe.parse(r.config);return ye((i=e,{...re,...i,watch:{...se,...i.watch},configWatch:{...ie,...i.configWatch},embedding:{...ae,...i.embedding},api:{...ne,...i.api},logging:{...oe,...i.logging}}))}catch(e){if(e instanceof w.ZodError){const t=e.issues.map((e=>`${e.path.join(".")}: ${e.message}`)).join("; ");throw new Error(`Invalid jeeves-watcher configuration: ${t}`)}throw e}var i}function ve(e){return e||{warn(e,t){t?console.warn(e,t):console.warn(e)}}}function Me(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 Se(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 Fe(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=Se(n,t.baseDelayMs,t.maxDelayMs,t.jitter);t.onRetry?.({attempt:n,attempts:r,delayMs:o,error:e}),await Me(o,t.signal)}throw i}const xe=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=ve(t),n=new v.GoogleGenerativeAIEmbeddings({apiKey:e.apiKey,model:e.model});return{dimensions:r,async embed(t){const o=await Fe((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 ke(e,t){const r=xe.get(e.provider);if(!r)throw new Error(`Unsupported embedding provider: ${e.provider}`);return r(e,t)}function Pe(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 ze(e){return s.createHash("sha256").update(e,"utf8").digest("hex")}const je="6a6f686e-6761-4c74-ad6a-656576657321";function Ce(e,t){const r=void 0!==t?`${W(e)}#${String(t)}`:W(e);return S.v5(r,je)}const Ee=["content","body","text","snippet","subject","description","summary","transcript"];function Re(e){if(!e||"object"!=typeof e)return JSON.stringify(e);const t=e;for(const e of Ee){const r=t[e];if("string"==typeof r&&r.trim())return r}return JSON.stringify(e)}async function Te(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 De(e){return{text:(await r.readFile(e,"utf8")).replace(/^\uFEFF/,"")}}async function Ie(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 Ae=new Map([[".md",Te],[".markdown",Te],[".txt",De],[".text",De],[".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:Re(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 k.extractRawText({buffer:t})).value}}],[".html",Ie],[".htm",Ie]]);async function We(e,t){const r=Ae.get(t.toLowerCase());return r?r(e):De(e)}function Ne(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 _e(e,t){const r={};for(const[i,n]of Object.entries(e))r[i]=Ne(n,t);return r}async function He(e,t,r,n,s,c){const l={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)};let h={},d=null;const u=n??console;for(const[n,{rule:o,validate:g}]of e.entries())if(g(t)){const e=_e(o.set,t);if(h={...h,...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){u.warn(`Failed to load map file "${o.map}": ${e instanceof Error?e.message:String(e)}`);continue}else if(e=r?.[o.map],!e){u.warn(`Map reference "${o.map}" not found in named maps. Skipping map transformation.`);continue}}else e=o.map;try{const r=new b.JsonMap(e,l),i=await r.transform(t);i&&"object"==typeof i&&!Array.isArray(i)?h={...h,...i}:u.warn("JsonMap transformation did not return an object; skipping merge.")}catch(e){u.warn(`JsonMap transformation failed: ${e instanceof Error?e.message:String(e)}`)}}if(o.template&&s){const e=`rule-${String(n)}`,r={...t.json??{},...t,...h};try{const t=s.render(e,r);t&&t.trim()?d=t:u.warn(`Template for rule ${String(n)} rendered empty output. Falling back to raw content.`)}catch(e){u.warn(`Template render failed for rule ${String(n)}: ${e instanceof Error?e.message:String(e)}. Falling back to raw content.`)}}}return{metadata:h,renderedContent:d}}function Oe(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){const l=i.extname(e),h=await r.stat(e),d=await We(e,l),u=Oe(e,h,d.frontmatter,d.json),{metadata:g,renderedContent:f}=await He(t,u,o,s,a,c),p=await _(e,n);return{inferred:g,enrichment:p,metadata:{...g,...p??{}},attributes:u,extracted:d,renderedContent:f}}function Le(e,t){const r=[];for(let i=0;i<t;i++)r.push(Ce(e,i));return r}function Qe(e,t=1){if(!e)return t;const r=e.total_chunks;return"number"==typeof r?r:t}class Ge{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),s=o??n.text;if(!s.trim())return void this.logger.debug({filePath:e},"Skipping empty file");const a=ze(s),c=Ce(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=Qe(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),g=await u.splitText(s),f=await this.embeddingProvider.embed(g),p=g.map(((t,i)=>({id:Ce(e,i),vector:f[i],payload:{...r,file_path:e.replace(/\\/g,"/"),chunk_index:i,total_chunks:g.length,content_hash:a,chunk_text:t}})));if(await this.vectorStore.upsert(p),h>g.length){const t=Le(e,h).slice(g.length);await this.vectorStore.delete(t)}this.logger.info({filePath:e,chunks:g.length},"File processed successfully")}catch(t){this.logger.error({filePath:e,err:T(t)},"Failed to process file")}}async deleteFile(e){try{const t=Ce(e,0),r=await this.vectorStore.getPayload(t),i=Le(e,Qe(r));await this.vectorStore.delete(i),await O(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 _(e,this.config.metadataDir)??{},...t};await H(e,this.config.metadataDir,r);const i=Ce(e,0),n=await this.vectorStore.getPayload(i);if(!n)return null;const o=Qe(n),s=Le(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=Ce(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),n=Qe(r),o=Le(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){this.compiledRules=e,t&&(this.templateEngine=t),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 Je(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 Ke{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=ve(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 Fe((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 Fe((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:Je(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 Ue{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 Ve(e){const t=e.replace(/\\/g,"/").split("/"),r=[];for(const e of t){if(/[*?{[\]]/.test(e))break;r.push(e)}return r.join("/")||"."}function Ye(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(Ve)),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 Ze{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 Ue(o)}start(){const{roots:e,matches:t}=Ye(this.config.paths);this.globMatches=t,this.watcher=m.watch(e,{ignored:this.config.ignored,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 Xe={loadConfig:be,createLogger:Pe,createEmbeddingProvider:ke,createVectorStoreClient:(e,t,r)=>new Ke(e,t,r),compileRules:$e,createDocumentProcessor:(e,t,r,i,n,o)=>new Ge(e,t,r,i,n,o),createEventQueue:e=>new Be(e),createFileSystemWatcher:(e,t,r,i,n)=>new Ze(e,t,r,i,n),createApiServer:q};class et{config;configPath;factories;runtimeOptions;logger;watcher;queue;server;processor;configWatcher;constructor(e,t,r={},i={}){this.config=e,this.configPath=t,this.factories={...Xe,...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 ee(this.config.inferenceRules??[],this.config.templates,this.config.templateHelpers?.paths,o),a=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},t,r,n,e,s);this.processor=a,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,a,e),this.server=await this.startApiServer(a,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 J(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 te({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 ee(r.inferenceRules??[],r.templates,r.templateHelpers?.paths,o);t.updateRules(n,s),e.info({configPath:this.configPath,rules:n.length},"Config reloaded")}catch(t){e.error({err:T(t)},"Failed to reload config")}}}}e.DocumentProcessor=Ge,e.EventQueue=Be,e.FileSystemWatcher=Ze,e.GitignoreFilter=J,e.JeevesWatcher=et,e.SystemHealth=Ue,e.TemplateEngine=X,e.VectorStoreClient=Ke,e.apiConfigSchema=ue,e.applyRules=He,e.buildAttributes=Oe,e.buildTemplateEngine=ee,e.compileRules=$e,e.configWatchConfigSchema=le,e.contentHash=ze,e.createApiServer=q,e.createEmbeddingProvider=ke,e.createHandlebarsInstance=Y,e.createLogger=Pe,e.deleteMetadata=O,e.embeddingConfigSchema=he,e.extractText=We,e.inferenceRuleSchema=fe,e.jeevesWatcherConfigSchema=pe,e.loadConfig=be,e.loadCustomHelpers=Z,e.loggingConfigSchema=ge,e.metadataPath=N,e.pointId=Ce,e.readMetadata=_,e.registerBuiltinHelpers=U,e.resolveTemplateSource=V,e.startFromConfig=async function(e){const t=await be(e),r=new et(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=de,e.watchConfigSchema=ce,e.writeMetadata=H}(this["jeeves-watcher"]=this["jeeves-watcher"]||{},Fastify,promises,node_path,picomatch,radash,node_crypto,node_fs,ignore,Handlebars,dayjs,hastUtilToMdast,mdastUtilFromAdf,mdastUtilToMarkdown,rehypeParse,unified,chokidar,cosmiconfig,zod,jsonmap,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,g,f,p,m,y,w,b,v,M,S,F,x,k,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*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 A(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 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 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 _(e,t){try{const i=await r.readFile(N(e,t),"utf8");return JSON.parse(i)}catch{return null}}async function H(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 O(e,t){try{await r.rm(N(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: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 A(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 H(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 A(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 A(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 L(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 B(e){const t=a.readFileSync(e,"utf8");return c().add(t)}class J{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=L(e);if(!n)continue;if(this.repos.has(n))continue;const o=G(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=L(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})}}}const K=p.unified().use(f,{fragment:!0});function U(e){e.registerHelper("adfToMarkdown",(function(t){if(!t||"object"!=typeof t)return"";try{const r=u.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=K.parse(t),i=d.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 h(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 V(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),V(t[e],t,r,n)}return e}function Y(){const e=l.create();return U(e),e}async function Z(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 X{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 ee(e,t,r,i){if(0===e.filter((e=>e.template)).length)return;const n=Y();r?.length&&i&&await Z(n,r,i);const o=new X(n);for(const[r,n]of e.entries()){if(!n.template)continue;const e=V(n.template,t,i??".");o.compile(`rule-${String(r)}`,e)}return o}class te{options;watcher;debounce;constructor(e){this.options=e}start(){this.options.enabled&&(this.watcher=m.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 re={metadataDir:".jeeves-watcher",shutdownTimeoutMs:1e4},ie={enabled:!0,debounceMs:1e3},ne={host:"127.0.0.1",port:3456},oe={level:"info"},se={debounceMs:300,stabilityThresholdMs:500,usePolling:!1,pollIntervalMs:1e3,respectGitignore:!0},ae={chunkSize:1e3,chunkOverlap:200,dimensions:3072,rateLimitPerMinute:300,concurrency:5},ce=w.z.object({paths:w.z.array(w.z.string()).min(1).describe('Glob patterns for files to watch (e.g., "**/*.md"). At least one required.'),ignored:w.z.array(w.z.string()).optional().describe('Glob patterns to exclude from watching (e.g., "**/node_modules/**").'),pollIntervalMs:w.z.number().optional().describe("Polling interval in milliseconds when usePolling is enabled."),usePolling:w.z.boolean().optional().describe("Use polling instead of native file system events (for network drives)."),debounceMs:w.z.number().optional().describe("Debounce delay in milliseconds for file change events."),stabilityThresholdMs:w.z.number().optional().describe("Time in milliseconds a file must remain unchanged before processing."),respectGitignore:w.z.boolean().optional().describe("Skip files ignored by .gitignore in git repositories. Only applies to repos with a .git directory. Default: true.")}),le=w.z.object({enabled:w.z.boolean().optional().describe("Enable automatic reloading when config file changes."),debounceMs:w.z.number().optional().describe("Debounce delay in milliseconds for config file change detection.")}),he=w.z.object({provider:w.z.string().default("gemini").describe('Embedding provider name (e.g., "gemini", "openai").'),model:w.z.string().default("gemini-embedding-001").describe('Embedding model identifier (e.g., "gemini-embedding-001", "text-embedding-3-small").'),chunkSize:w.z.number().optional().describe("Maximum chunk size in characters for text splitting."),chunkOverlap:w.z.number().optional().describe("Character overlap between consecutive chunks."),dimensions:w.z.number().optional().describe("Embedding vector dimensions (must match model output)."),apiKey:w.z.string().optional().describe("API key for embedding provider (supports ${ENV_VAR} substitution)."),rateLimitPerMinute:w.z.number().optional().describe("Maximum embedding API requests per minute (rate limiting)."),concurrency:w.z.number().optional().describe("Maximum concurrent embedding requests.")}),de=w.z.object({url:w.z.string().describe('Qdrant server URL (e.g., "http://localhost:6333").'),collectionName:w.z.string().describe("Qdrant collection name for vector storage."),apiKey:w.z.string().optional().describe("Qdrant API key for authentication (supports ${ENV_VAR} substitution).")}),ue=w.z.object({host:w.z.string().optional().describe('Host address for API server (e.g., "127.0.0.1", "0.0.0.0").'),port:w.z.number().optional().describe("Port for API server (e.g., 3456).")}),ge=w.z.object({level:w.z.string().optional().describe("Logging level (trace, debug, info, warn, error, fatal)."),file:w.z.string().optional().describe("Path to log file (logs to stdout if omitted).")}),fe=w.z.object({match:w.z.record(w.z.string(),w.z.unknown()).describe("JSON Schema object to match against file attributes."),set:w.z.record(w.z.string(),w.z.unknown()).describe("Metadata fields to set when match succeeds."),map:w.z.union([b.jsonMapMapSchema,w.z.string()]).optional().describe("JsonMap transformation (inline definition, named map reference, or .json file path)."),template:w.z.string().optional().describe("Handlebars content template (inline string, named ref, or .hbs/.handlebars file path).")}),pe=w.z.object({watch:ce.describe("File system watch configuration."),configWatch:le.optional().describe("Configuration file watch settings."),embedding:he.describe("Embedding model configuration."),vectorStore:de.describe("Qdrant vector store configuration."),metadataDir:w.z.string().optional().describe("Directory for persisted metadata sidecar files."),api:ue.optional().describe("API server configuration."),extractors:w.z.record(w.z.string(),w.z.unknown()).optional().describe("Extractor configurations keyed by name."),inferenceRules:w.z.array(fe).optional().describe("Rules for inferring metadata from file attributes."),maps:w.z.record(w.z.string(),b.jsonMapMapSchema).optional().describe("Reusable named JsonMap transformations."),templates:w.z.record(w.z.string(),w.z.string()).optional().describe("Named reusable Handlebars templates (inline strings or .hbs/.handlebars file paths)."),templateHelpers:w.z.object({paths:w.z.array(w.z.string()).optional().describe("File paths to custom helper modules.")}).optional().describe("Custom Handlebars helper registration."),logging:ge.optional().describe("Logging configuration."),shutdownTimeoutMs:w.z.number().optional().describe("Timeout in milliseconds for graceful shutdown."),maxRetries:w.z.number().optional().describe("Maximum consecutive system-level failures before triggering fatal error. Default: Infinity."),maxBackoffMs:w.z.number().optional().describe("Maximum backoff delay in milliseconds for system errors. Default: 60000.")}),me=/\$\{([^}]+)\}/g;function ye(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=>ye(e)));if(null!==e&&"object"==typeof e){const t={};for(const[r,i]of Object.entries(e))t[r]=ye(i);return t}return e}const we="jeeves-watcher";async function be(e){const t=y.cosmiconfig(we),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=pe.parse(r.config);return ye((i=e,{...re,...i,watch:{...se,...i.watch},configWatch:{...ie,...i.configWatch},embedding:{...ae,...i.embedding},api:{...ne,...i.api},logging:{...oe,...i.logging}}))}catch(e){if(e instanceof w.ZodError){const t=e.issues.map((e=>`${e.path.join(".")}: ${e.message}`)).join("; ");throw new Error(`Invalid jeeves-watcher configuration: ${t}`)}throw e}var i}function ve(e){return e||{warn(e,t){t?console.warn(e,t):console.warn(e)}}}function Me(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 Se(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 Fe(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=Se(n,t.baseDelayMs,t.maxDelayMs,t.jitter);t.onRetry?.({attempt:n,attempts:r,delayMs:o,error:e}),await Me(o,t.signal)}throw i}const xe=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=ve(t),n=new v.GoogleGenerativeAIEmbeddings({apiKey:e.apiKey,model:e.model});return{dimensions:r,async embed(t){const o=await Fe((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 ke(e,t){const r=xe.get(e.provider);if(!r)throw new Error(`Unsupported embedding provider: ${e.provider}`);return r(e,t)}function Pe(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 ze(e){return s.createHash("sha256").update(e,"utf8").digest("hex")}const je="6a6f686e-6761-4c74-ad6a-656576657321";function Ce(e,t){const r=void 0!==t?`${W(e)}#${String(t)}`:W(e);return S.v5(r,je)}const Ee=["content","body","text","snippet","subject","description","summary","transcript"];function Re(e){if(!e||"object"!=typeof e)return JSON.stringify(e);const t=e;for(const e of Ee){const r=t[e];if("string"==typeof r&&r.trim())return r}return JSON.stringify(e)}async function Te(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 De(e){return{text:(await r.readFile(e,"utf8")).replace(/^\uFEFF/,"")}}async function Ie(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 Ae=new Map([[".md",Te],[".markdown",Te],[".txt",De],[".text",De],[".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:Re(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 k.extractRawText({buffer:t})).value}}],[".html",Ie],[".htm",Ie]]);async function We(e,t){const r=Ae.get(t.toLowerCase());return r?r(e):De(e)}function Ne(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 _e(e,t){const r={};for(const[i,n]of Object.entries(e))r[i]=Ne(n,t);return r}async function He(e,t,r,n,s,c){const l={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)};let h={},d=null;const u=n??console;for(const[n,{rule:o,validate:g}]of e.entries())if(g(t)){const e=_e(o.set,t);if(h={...h,...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){u.warn(`Failed to load map file "${o.map}": ${e instanceof Error?e.message:String(e)}`);continue}else if(e=r?.[o.map],!e){u.warn(`Map reference "${o.map}" not found in named maps. Skipping map transformation.`);continue}}else e=o.map;try{const r=new b.JsonMap(e,l),i=await r.transform(t);i&&"object"==typeof i&&!Array.isArray(i)?h={...h,...i}:u.warn("JsonMap transformation did not return an object; skipping merge.")}catch(e){u.warn(`JsonMap transformation failed: ${e instanceof Error?e.message:String(e)}`)}}if(o.template&&s){const e=`rule-${String(n)}`,r={...t.json??{},...t,...h};try{const t=s.render(e,r);t&&t.trim()?d=t:u.warn(`Template for rule ${String(n)} rendered empty output. Falling back to raw content.`)}catch(e){u.warn(`Template render failed for rule ${String(n)}: ${e instanceof Error?e.message:String(e)}. Falling back to raw content.`)}}}return{metadata:h,renderedContent:d}}function Oe(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){const l=i.extname(e),h=await r.stat(e),d=await We(e,l),u=Oe(e,h,d.frontmatter,d.json),{metadata:g,renderedContent:f}=await He(t,u,o,s,a,c),p=await _(e,n);return{inferred:g,enrichment:p,metadata:{...g,...p??{}},attributes:u,extracted:d,renderedContent:f}}function Le(e,t){const r=[];for(let i=0;i<t;i++)r.push(Ce(e,i));return r}function Qe(e,t=1){if(!e)return t;const r=e.total_chunks;return"number"==typeof r?r:t}class Ge{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),s=o??n.text;if(!s.trim())return void this.logger.debug({filePath:e},"Skipping empty file");const a=ze(s),c=Ce(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=Qe(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),g=await u.splitText(s),f=await this.embeddingProvider.embed(g),p=g.map(((t,i)=>({id:Ce(e,i),vector:f[i],payload:{...r,file_path:e.replace(/\\/g,"/"),chunk_index:i,total_chunks:g.length,content_hash:a,chunk_text:t}})));if(await this.vectorStore.upsert(p),h>g.length){const t=Le(e,h).slice(g.length);await this.vectorStore.delete(t)}this.logger.info({filePath:e,chunks:g.length},"File processed successfully")}catch(t){this.logger.error({filePath:e,err:T(t)},"Failed to process file")}}async deleteFile(e){try{const t=Ce(e,0),r=await this.vectorStore.getPayload(t),i=Le(e,Qe(r));await this.vectorStore.delete(i),await O(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 _(e,this.config.metadataDir)??{},...t};await H(e,this.config.metadataDir,r);const i=Ce(e,0),n=await this.vectorStore.getPayload(i);if(!n)return null;const o=Qe(n),s=Le(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=Ce(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),n=Qe(r),o=Le(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){this.compiledRules=e,t&&(this.templateEngine=t),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 Je(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 Ke{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=ve(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 Fe((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 Fe((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:Je(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 Ue{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 Ve(e){const t=e.replace(/\\/g,"/").split("/"),r=[];for(const e of t){if(/[*?{[\]]/.test(e))break;r.push(e)}return r.join("/")||"."}function Ye(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(Ve)),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 Ze{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 Ue(o)}start(){const{roots:e,matches:t}=Ye(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=m.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 Xe={loadConfig:be,createLogger:Pe,createEmbeddingProvider:ke,createVectorStoreClient:(e,t,r)=>new Ke(e,t,r),compileRules:$e,createDocumentProcessor:(e,t,r,i,n,o)=>new Ge(e,t,r,i,n,o),createEventQueue:e=>new Be(e),createFileSystemWatcher:(e,t,r,i,n)=>new Ze(e,t,r,i,n),createApiServer:q};class et{config;configPath;factories;runtimeOptions;logger;watcher;queue;server;processor;configWatcher;constructor(e,t,r={},i={}){this.config=e,this.configPath=t,this.factories={...Xe,...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 ee(this.config.inferenceRules??[],this.config.templates,this.config.templateHelpers?.paths,o),a=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},t,r,n,e,s);this.processor=a,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,a,e),this.server=await this.startApiServer(a,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 J(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 te({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 ee(r.inferenceRules??[],r.templates,r.templateHelpers?.paths,o);t.updateRules(n,s),e.info({configPath:this.configPath,rules:n.length},"Config reloaded")}catch(t){e.error({err:T(t)},"Failed to reload config")}}}}e.DocumentProcessor=Ge,e.EventQueue=Be,e.FileSystemWatcher=Ze,e.GitignoreFilter=J,e.JeevesWatcher=et,e.SystemHealth=Ue,e.TemplateEngine=X,e.VectorStoreClient=Ke,e.apiConfigSchema=ue,e.applyRules=He,e.buildAttributes=Oe,e.buildTemplateEngine=ee,e.compileRules=$e,e.configWatchConfigSchema=le,e.contentHash=ze,e.createApiServer=q,e.createEmbeddingProvider=ke,e.createHandlebarsInstance=Y,e.createLogger=Pe,e.deleteMetadata=O,e.embeddingConfigSchema=he,e.extractText=We,e.inferenceRuleSchema=fe,e.jeevesWatcherConfigSchema=pe,e.loadConfig=be,e.loadCustomHelpers=Z,e.loggingConfigSchema=ge,e.metadataPath=N,e.pointId=Ce,e.readMetadata=_,e.registerBuiltinHelpers=U,e.resolveTemplateSource=V,e.startFromConfig=async function(e){const t=await be(e),r=new et(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=de,e.watchConfigSchema=ce,e.writeMetadata=H}(this["jeeves-watcher"]=this["jeeves-watcher"]||{},Fastify,promises,node_path,picomatch,radash,node_crypto,node_fs,ignore,Handlebars,dayjs,hastUtilToMdast,mdastUtilFromAdf,mdastUtilToMarkdown,rehypeParse,unified,chokidar,cosmiconfig,zod,jsonmap,googleGenai,pino,uuid,cheerio,yaml,mammoth,Ajv,addFormats,textsplitters,jsClientRest);
package/dist/mjs/index.js CHANGED
@@ -2746,6 +2746,34 @@ function resolveWatchPaths(globs) {
2746
2746
  const matches = buildGlobMatcher(globs);
2747
2747
  return { roots, matches };
2748
2748
  }
2749
+ /**
2750
+ * Convert ignored glob patterns to picomatch matcher functions.
2751
+ *
2752
+ * Chokidar v5 replaced the external `anymatch` dependency with an inline
2753
+ * implementation that does **exact string equality** for string matchers,
2754
+ * breaking glob-based `ignored` patterns. This function converts glob strings
2755
+ * to picomatch functions that chokidar's `createPattern` passes through
2756
+ * unchanged (`typeof matcher === 'function'`).
2757
+ *
2758
+ * Non-string entries (functions, RegExps) are passed through as-is.
2759
+ *
2760
+ * @param ignored - Array of ignored patterns (globs, functions, RegExps).
2761
+ * @returns Array with glob strings replaced by picomatch matcher functions.
2762
+ */
2763
+ function resolveIgnored(ignored) {
2764
+ return ignored.map((entry) => {
2765
+ if (typeof entry !== 'string')
2766
+ return entry;
2767
+ // If the string contains glob characters, convert to a picomatch function.
2768
+ // Literal strings (exact paths) are also converted for consistent matching.
2769
+ const normalizedPattern = entry.replace(/\\/g, '/');
2770
+ const matcher = picomatch(normalizedPattern, { dot: true, nocase: true });
2771
+ return (filePath) => {
2772
+ const normalized = filePath.replace(/\\/g, '/');
2773
+ return matcher(normalized);
2774
+ };
2775
+ });
2776
+ }
2749
2777
 
2750
2778
  /**
2751
2779
  * @module watcher
@@ -2797,8 +2825,14 @@ class FileSystemWatcher {
2797
2825
  // filter emitted events against the original globs via picomatch.
2798
2826
  const { roots, matches } = resolveWatchPaths(this.config.paths);
2799
2827
  this.globMatches = matches;
2828
+ // Chokidar v5's inline anymatch does exact string equality for string
2829
+ // matchers, breaking glob-based ignored patterns. Convert to picomatch
2830
+ // functions that chokidar passes through as-is.
2831
+ const ignored = this.config.ignored
2832
+ ? resolveIgnored(this.config.ignored)
2833
+ : undefined;
2800
2834
  this.watcher = chokidar.watch(roots, {
2801
- ignored: this.config.ignored,
2835
+ ignored,
2802
2836
  usePolling: this.config.usePolling,
2803
2837
  interval: this.config.pollIntervalMs,
2804
2838
  awaitWriteFinish: this.config.stabilityThresholdMs
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.0"
188
+ "version": "0.4.1"
189
189
  }