claude-mem-lite 3.3.0 → 3.3.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.
@@ -10,7 +10,7 @@
10
10
  "plugins": [
11
11
  {
12
12
  "name": "claude-mem-lite",
13
- "version": "3.3.0",
13
+ "version": "3.3.1",
14
14
  "source": "./",
15
15
  "description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark)."
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "3.3.0",
3
+ "version": "3.3.1",
4
4
  "description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
5
5
  "author": {
6
6
  "name": "sdsrss"
package/haiku-client.mjs CHANGED
@@ -6,12 +6,84 @@
6
6
  // overridable via OPENROUTER_MODEL
7
7
 
8
8
  import { execFileSync, spawn } from 'child_process';
9
+ import http from 'node:http';
10
+ import https from 'node:https';
11
+ import tls from 'node:tls';
9
12
  import { readFileSync } from 'fs';
10
13
  import { join } from 'path';
11
14
  import { randomUUID } from 'crypto';
12
15
  import { debugLog, debugCatch, parseJsonFromLLM } from './utils.mjs';
13
16
  import { DB_DIR } from './schema.mjs';
14
17
 
18
+ // ─── Proxy support (native fetch ignores HTTP(S)_PROXY) ──────────────────────
19
+ //
20
+ // Node's global fetch (undici) does NOT honour HTTP(S)_PROXY env vars, and
21
+ // undici's ProxyAgent isn't importable without adding a dependency. In an env
22
+ // that requires a local proxy to reach external APIs (e.g.
23
+ // HTTPS_PROXY=http://127.0.0.1:PORT), a direct fetch to openrouter.ai
24
+ // hangs/times out. We tunnel HTTPS through the HTTP CONNECT proxy using built-ins
25
+ // only. No proxy var (or a NO_PROXY host) → null → callers keep native fetch,
26
+ // unchanged (zero behaviour change when no proxy is configured).
27
+ function httpConnectProxyFor(targetUrl) {
28
+ const proxy = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy;
29
+ if (!proxy || !/^https?:\/\//.test(proxy)) return null; // socks5 ALL_PROXY not supported here
30
+ try {
31
+ const host = new URL(targetUrl).hostname;
32
+ const noProxy = (process.env.NO_PROXY || process.env.no_proxy || '').split(',').map((s) => s.trim()).filter(Boolean);
33
+ if (noProxy.some((n) => n === host || (n.startsWith('.') && host.endsWith(n.slice(1))))) return null;
34
+ return proxy;
35
+ } catch {
36
+ return null;
37
+ }
38
+ }
39
+
40
+ // fetch-compatible (subset) POST over an HTTP CONNECT tunnel: returns
41
+ // { ok, status, json(), text() }. Rejects on connect/timeout/socket error so the
42
+ // caller's try/catch degrades to the CLI exactly as a failed fetch would.
43
+ function postViaConnectProxy(proxy, url, { headers = {}, body = '', timeout = 20000 }) {
44
+ return new Promise((resolve, reject) => {
45
+ const p = new URL(proxy);
46
+ const t = new URL(url);
47
+ const port = Number(t.port) || 443;
48
+ let settled = false;
49
+ const finish = (fn, arg) => { if (!settled) { settled = true; fn(arg); } };
50
+ const connReq = http.request({
51
+ host: p.hostname,
52
+ port: Number(p.port) || 80,
53
+ method: 'CONNECT',
54
+ path: `${t.hostname}:${port}`,
55
+ headers: { Host: `${t.hostname}:${port}` },
56
+ });
57
+ connReq.setTimeout(timeout, () => connReq.destroy(new Error('proxy CONNECT timeout')));
58
+ connReq.on('error', (e) => finish(reject, e));
59
+ connReq.on('connect', (res, socket) => {
60
+ if (res.statusCode !== 200) {
61
+ socket.destroy();
62
+ return finish(reject, new Error(`proxy CONNECT ${res.statusCode}`));
63
+ }
64
+ const req = https.request(
65
+ url,
66
+ { method: 'POST', headers, createConnection: () => tls.connect({ socket, servername: t.hostname }) },
67
+ (resp) => {
68
+ let data = '';
69
+ resp.setEncoding('utf8');
70
+ resp.on('data', (c) => (data += c));
71
+ resp.on('end', () => finish(resolve, {
72
+ ok: resp.statusCode >= 200 && resp.statusCode < 300,
73
+ status: resp.statusCode,
74
+ json: () => JSON.parse(data),
75
+ text: () => data,
76
+ }));
77
+ }
78
+ );
79
+ req.setTimeout(timeout, () => req.destroy(new Error('proxy request timeout')));
80
+ req.on('error', (e) => finish(reject, e));
81
+ req.end(body);
82
+ });
83
+ connReq.end();
84
+ });
85
+ }
86
+
15
87
  // ─── Model Resolution ────────────────────────────────────────────────────────
16
88
 
17
89
  // CLI name → API model ID mapping
@@ -493,17 +565,20 @@ async function callOpenRouterAPI(prompt, tier, { timeout, maxTokens, temperature
493
565
  if (system) messages.push({ role: 'system', content: system });
494
566
  messages.push({ role: 'user', content: user });
495
567
 
496
- const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
497
- method: 'POST',
498
- headers: {
499
- 'Content-Type': 'application/json',
500
- 'Authorization': `Bearer ${apiKey}`,
501
- // Optional OpenRouter attribution headers (ignored by the API if absent).
502
- 'X-Title': 'claude-mem-lite',
503
- },
504
- body: JSON.stringify({ model, max_tokens: maxTokens, temperature, messages }),
505
- signal: controller.signal,
506
- });
568
+ const url = 'https://openrouter.ai/api/v1/chat/completions';
569
+ const reqHeaders = {
570
+ 'Content-Type': 'application/json',
571
+ 'Authorization': `Bearer ${apiKey}`,
572
+ // Optional OpenRouter attribution headers (ignored by the API if absent).
573
+ 'X-Title': 'claude-mem-lite',
574
+ };
575
+ const reqBody = JSON.stringify({ model, max_tokens: maxTokens, temperature, messages });
576
+ // Native fetch ignores HTTP(S)_PROXY; when a proxy is configured, tunnel the
577
+ // request through it — a direct fetch to openrouter.ai times out behind one.
578
+ const proxy = httpConnectProxyFor(url);
579
+ const res = proxy
580
+ ? await postViaConnectProxy(proxy, url, { headers: reqHeaders, body: reqBody, timeout })
581
+ : await fetch(url, { method: 'POST', headers: reqHeaders, body: reqBody, signal: controller.signal });
507
582
 
508
583
  if (!res.ok) {
509
584
  debugLog('WARN', `${tier}-openrouter`, `HTTP ${res.status}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "3.3.0",
3
+ "version": "3.3.1",
4
4
  "description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",