claude-code-cache-fix 3.0.2 → 3.0.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.
@@ -1,6 +1,8 @@
1
1
  import https from "node:https";
2
2
  import http from "node:http";
3
3
  import { URL } from "node:url";
4
+ import { readFileSync } from "node:fs";
5
+ import { HttpProxyAgent, HttpsProxyAgent } from "hpagent";
4
6
  import config from "./config.mjs";
5
7
 
6
8
  const STRIP_REQUEST_HEADERS = new Set([
@@ -46,6 +48,105 @@ function filterResponseHeaders(rawHeaders) {
46
48
  return headers;
47
49
  }
48
50
 
51
+ // --- HTTP proxy and custom CA support ---
52
+
53
+ const _agents = new Map(); // cache key → Agent | null
54
+ const _loggedProxies = new Set(); // dedupe stderr "using proxy" lines per (url, isHTTPS)
55
+ let _warnedTlsDisabled = false;
56
+
57
+ function shouldBypassProxy(hostname) {
58
+ if (!config.noProxy) return false;
59
+ const list = config.noProxy.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
60
+ const host = hostname.toLowerCase();
61
+ for (const pattern of list) {
62
+ if (pattern === "*") return true;
63
+ if (pattern.startsWith(".")) {
64
+ // ".example.com" matches "foo.example.com" and "example.com"
65
+ const bare = pattern.slice(1);
66
+ if (host === bare || host.endsWith(pattern)) return true;
67
+ } else if (host === pattern || host.endsWith("." + pattern)) {
68
+ return true;
69
+ }
70
+ }
71
+ return false;
72
+ }
73
+
74
+ function loadCa() {
75
+ if (!config.caFile) return undefined;
76
+ try {
77
+ return readFileSync(config.caFile);
78
+ } catch (err) {
79
+ process.stderr.write(`[upstream] CACHE_FIX_PROXY_CA_FILE read failed: ${err.message}\n`);
80
+ return undefined;
81
+ }
82
+ }
83
+
84
+ // Pick the proxy URL for an upstream, matching curl/Python/Go semantics:
85
+ // https upstream → HTTPS_PROXY, falling back to HTTP_PROXY if unset
86
+ // http upstream → HTTP_PROXY only (HTTPS_PROXY does NOT apply to plain HTTP)
87
+ //
88
+ // Exported for direct unit testing — tests against the live forwardRequest path
89
+ // can't easily reload a fresh config across cases (config is a single module
90
+ // instance), so we expose the pure function for table-driven coverage.
91
+ export function selectProxyUrl(isHTTPS) {
92
+ if (isHTTPS) return config.httpsProxy || config.httpProxy || "";
93
+ return config.httpProxy || "";
94
+ }
95
+
96
+ function buildAgent(isHTTPS, proxyUrl) {
97
+ const ca = loadCa();
98
+ if (proxyUrl) {
99
+ const opts = {
100
+ keepAlive: true,
101
+ proxy: proxyUrl,
102
+ rejectUnauthorized: config.rejectUnauthorized,
103
+ ...(ca ? { ca } : {}),
104
+ };
105
+ return isHTTPS ? new HttpsProxyAgent(opts) : new HttpProxyAgent(opts);
106
+ }
107
+ // No proxy. Only build a custom agent when CA or insecure mode warrants it;
108
+ // otherwise return null so Node uses its global default agent (preserves the
109
+ // pre-change behavior end-to-end, including connection pooling).
110
+ if (ca || !config.rejectUnauthorized) {
111
+ if (isHTTPS) {
112
+ return new https.Agent({
113
+ keepAlive: true,
114
+ rejectUnauthorized: config.rejectUnauthorized,
115
+ ...(ca ? { ca } : {}),
116
+ });
117
+ }
118
+ return new http.Agent({ keepAlive: true });
119
+ }
120
+ return null;
121
+ }
122
+
123
+ function getAgent(isHTTPS, hostname) {
124
+ if (!_warnedTlsDisabled && !config.rejectUnauthorized) {
125
+ _warnedTlsDisabled = true;
126
+ process.stderr.write(
127
+ `[upstream] WARNING: TLS verification disabled (CACHE_FIX_PROXY_REJECT_UNAUTHORIZED=0). This is insecure!\n`
128
+ );
129
+ }
130
+
131
+ const bypass = shouldBypassProxy(hostname);
132
+ const proxyUrl = bypass ? "" : selectProxyUrl(isHTTPS);
133
+ const cacheKey = `${isHTTPS ? "https" : "http"}|${proxyUrl}|${config.caFile}|${config.rejectUnauthorized}`;
134
+
135
+ let agent = _agents.get(cacheKey);
136
+ if (agent === undefined) {
137
+ agent = buildAgent(isHTTPS, proxyUrl);
138
+ _agents.set(cacheKey, agent);
139
+ if (proxyUrl && !_loggedProxies.has(`${proxyUrl}|${isHTTPS}`)) {
140
+ _loggedProxies.add(`${proxyUrl}|${isHTTPS}`);
141
+ process.stderr.write(
142
+ `[upstream] using proxy ${proxyUrl} for ${isHTTPS ? "https" : "http"} upstream ` +
143
+ `(rejectUnauthorized=${config.rejectUnauthorized}, ca=${config.caFile || "default"})\n`
144
+ );
145
+ }
146
+ }
147
+ return agent;
148
+ }
149
+
49
150
  export function forwardRequest(clientReq, body, signal) {
50
151
  return new Promise((resolve, reject) => {
51
152
  const upstreamUrl = new URL(clientReq.url, config.upstream);
@@ -66,6 +167,7 @@ export function forwardRequest(clientReq, body, signal) {
66
167
  method: clientReq.method,
67
168
  headers,
68
169
  timeout: config.timeout,
170
+ agent: getAgent(isHTTPS, upstreamUrl.hostname),
69
171
  };
70
172
 
71
173
  const upstreamReq = transport.request(options, (upstreamRes) => {