@karmaniverous/jeeves-watcher 0.9.3 → 0.9.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.
@@ -25,6 +25,7 @@ import { JSONPath } from 'jsonpath-plus';
25
25
  import { createHash } from 'node:crypto';
26
26
  import crypto from 'crypto';
27
27
  import { cosmiconfig } from 'cosmiconfig';
28
+ import https from 'node:https';
28
29
  import pino from 'pino';
29
30
  import { v5 } from 'uuid';
30
31
  import * as cheerio from 'cheerio';
@@ -3897,8 +3898,40 @@ function getLogger(logger) {
3897
3898
  /**
3898
3899
  * @module embedding/geminiProvider
3899
3900
  * Gemini embedding provider using the Google Generative AI REST API directly.
3901
+ * Uses node:https with a keep-alive agent for reliable performance in
3902
+ * long-running processes (avoids undici/fetch event-loop contention).
3900
3903
  */
3901
3904
  const GEMINI_API_BASE = 'https://generativelanguage.googleapis.com/v1beta';
3905
+ /** Persistent HTTPS agent for connection reuse. */
3906
+ const agent = new https.Agent({ keepAlive: true });
3907
+ /** Make an HTTPS POST request using node:https (bypasses undici/fetch). */
3908
+ function httpsPost(url, body) {
3909
+ return new Promise((resolve, reject) => {
3910
+ const parsed = new URL(url);
3911
+ const req = https.request({
3912
+ hostname: parsed.hostname,
3913
+ path: parsed.pathname + parsed.search,
3914
+ method: 'POST',
3915
+ agent,
3916
+ headers: {
3917
+ 'Content-Type': 'application/json',
3918
+ 'Content-Length': Buffer.byteLength(body),
3919
+ },
3920
+ }, (res) => {
3921
+ const chunks = [];
3922
+ res.on('data', (chunk) => chunks.push(chunk));
3923
+ res.on('end', () => {
3924
+ resolve({
3925
+ status: res.statusCode ?? 0,
3926
+ body: Buffer.concat(chunks).toString('utf8'),
3927
+ });
3928
+ });
3929
+ });
3930
+ req.on('error', reject);
3931
+ req.write(body);
3932
+ req.end();
3933
+ });
3934
+ }
3902
3935
  /**
3903
3936
  * Create a Gemini embedding provider using the Google Generative AI REST API.
3904
3937
  *
@@ -3927,16 +3960,11 @@ function createGeminiProvider(config, logger) {
3927
3960
  model: `models/${model}`,
3928
3961
  content: { parts: [{ text }] },
3929
3962
  }));
3930
- const response = await fetch(url, {
3931
- method: 'POST',
3932
- headers: { 'Content-Type': 'application/json' },
3933
- body: JSON.stringify({ requests }),
3934
- });
3935
- if (!response.ok) {
3936
- const body = await response.text();
3937
- throw new Error(`Gemini API error ${String(response.status)}: ${body}`);
3963
+ const response = await httpsPost(url, JSON.stringify({ requests }));
3964
+ if (response.status < 200 || response.status >= 300) {
3965
+ throw new Error(`Gemini API error ${String(response.status)}: ${response.body}`);
3938
3966
  }
3939
- const data = (await response.json());
3967
+ const data = JSON.parse(response.body);
3940
3968
  return data.embeddings.map((e) => e.values);
3941
3969
  }, {
3942
3970
  attempts: 5,
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ import { createHash } from 'node:crypto';
22
22
  import crypto from 'crypto';
23
23
  import chokidar from 'chokidar';
24
24
  import { cosmiconfig } from 'cosmiconfig';
25
+ import https from 'node:https';
25
26
  import pino from 'pino';
26
27
  import { v5 } from 'uuid';
27
28
  import * as cheerio from 'cheerio';
@@ -3873,8 +3874,40 @@ function getLogger(logger) {
3873
3874
  /**
3874
3875
  * @module embedding/geminiProvider
3875
3876
  * Gemini embedding provider using the Google Generative AI REST API directly.
3877
+ * Uses node:https with a keep-alive agent for reliable performance in
3878
+ * long-running processes (avoids undici/fetch event-loop contention).
3876
3879
  */
3877
3880
  const GEMINI_API_BASE = 'https://generativelanguage.googleapis.com/v1beta';
3881
+ /** Persistent HTTPS agent for connection reuse. */
3882
+ const agent = new https.Agent({ keepAlive: true });
3883
+ /** Make an HTTPS POST request using node:https (bypasses undici/fetch). */
3884
+ function httpsPost(url, body) {
3885
+ return new Promise((resolve, reject) => {
3886
+ const parsed = new URL(url);
3887
+ const req = https.request({
3888
+ hostname: parsed.hostname,
3889
+ path: parsed.pathname + parsed.search,
3890
+ method: 'POST',
3891
+ agent,
3892
+ headers: {
3893
+ 'Content-Type': 'application/json',
3894
+ 'Content-Length': Buffer.byteLength(body),
3895
+ },
3896
+ }, (res) => {
3897
+ const chunks = [];
3898
+ res.on('data', (chunk) => chunks.push(chunk));
3899
+ res.on('end', () => {
3900
+ resolve({
3901
+ status: res.statusCode ?? 0,
3902
+ body: Buffer.concat(chunks).toString('utf8'),
3903
+ });
3904
+ });
3905
+ });
3906
+ req.on('error', reject);
3907
+ req.write(body);
3908
+ req.end();
3909
+ });
3910
+ }
3878
3911
  /**
3879
3912
  * Create a Gemini embedding provider using the Google Generative AI REST API.
3880
3913
  *
@@ -3903,16 +3936,11 @@ function createGeminiProvider(config, logger) {
3903
3936
  model: `models/${model}`,
3904
3937
  content: { parts: [{ text }] },
3905
3938
  }));
3906
- const response = await fetch(url, {
3907
- method: 'POST',
3908
- headers: { 'Content-Type': 'application/json' },
3909
- body: JSON.stringify({ requests }),
3910
- });
3911
- if (!response.ok) {
3912
- const body = await response.text();
3913
- throw new Error(`Gemini API error ${String(response.status)}: ${body}`);
3939
+ const response = await httpsPost(url, JSON.stringify({ requests }));
3940
+ if (response.status < 200 || response.status >= 300) {
3941
+ throw new Error(`Gemini API error ${String(response.status)}: ${response.body}`);
3914
3942
  }
3915
- const data = (await response.json());
3943
+ const data = JSON.parse(response.body);
3916
3944
  return data.embeddings.map((e) => e.values);
3917
3945
  }, {
3918
3946
  attempts: 5,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karmaniverous/jeeves-watcher",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "author": "Jason Williscroft",
5
5
  "description": "Filesystem watcher that keeps a Qdrant vector store in sync with document changes",
6
6
  "license": "BSD-3-Clause",