@bubblebrain-ai/bubble 0.0.18 → 0.0.19
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.
|
@@ -13,4 +13,5 @@ export declare function createChatGptFetch(options?: ChatGptFetchOptions): ChatG
|
|
|
13
13
|
export declare function createChatGptDispatcher(env?: NodeJS.ProcessEnv, input?: RequestInfo | URL): Dispatcher | undefined;
|
|
14
14
|
export declare function withChatGptNetworkOptions(input: RequestInfo | URL, init: RequestInit | undefined, env?: NodeJS.ProcessEnv, dispatcher?: Dispatcher | undefined): RequestInitWithDispatcher;
|
|
15
15
|
export declare function normalizeChatGptNetworkError(error: unknown, env?: NodeJS.ProcessEnv): Error;
|
|
16
|
+
export declare function parseMacSystemProxyForUrl(output: string, url: URL): string | undefined;
|
|
16
17
|
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
1
2
|
import { readFileSync } from "node:fs";
|
|
2
3
|
import { delimiter } from "node:path";
|
|
3
4
|
import { rootCertificates } from "node:tls";
|
|
@@ -19,9 +20,8 @@ export function getChatGptFetch(env = process.env) {
|
|
|
19
20
|
export function createChatGptFetch(options = {}) {
|
|
20
21
|
const env = options.env ?? process.env;
|
|
21
22
|
const fetchImpl = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
22
|
-
const dispatcher = createChatGptDispatcher(env);
|
|
23
23
|
return async (input, init) => {
|
|
24
|
-
const requestInit = withChatGptNetworkOptions(input, init, env
|
|
24
|
+
const requestInit = withChatGptNetworkOptions(input, init, env);
|
|
25
25
|
try {
|
|
26
26
|
return await fetchImpl(input, requestInit);
|
|
27
27
|
}
|
|
@@ -34,9 +34,9 @@ export function createChatGptDispatcher(env = process.env, input) {
|
|
|
34
34
|
if (isBunRuntime())
|
|
35
35
|
return undefined;
|
|
36
36
|
const ca = loadExtraCaCertificates(env);
|
|
37
|
-
if (!hasProxyEnv(env) && ca.length === 0)
|
|
38
|
-
return undefined;
|
|
39
37
|
const proxy = input ? nodeProxyForUrl(input, env) : defaultNodeProxy(env);
|
|
38
|
+
if (!proxy && ca.length === 0)
|
|
39
|
+
return undefined;
|
|
40
40
|
const caOptions = ca.length > 0 ? { ca: [...rootCertificates, ...ca] } : undefined;
|
|
41
41
|
if (proxy) {
|
|
42
42
|
return new ProxyAgent({
|
|
@@ -73,41 +73,144 @@ export function normalizeChatGptNetworkError(error, env = process.env) {
|
|
|
73
73
|
: "This looks like a proxy or network transport failure.",
|
|
74
74
|
hasProxyEnv(env)
|
|
75
75
|
? "Bubble is using proxy environment variables for ChatGPT requests. Make sure NO_PROXY includes localhost,127.0.0.1."
|
|
76
|
-
:
|
|
76
|
+
: hasMacSystemProxy(env)
|
|
77
|
+
? "Bubble detected the macOS system proxy for ChatGPT requests. If it is stale, restart your proxy client or set BUBBLE_DISABLE_SYSTEM_PROXY=1 and configure HTTPS_PROXY explicitly."
|
|
78
|
+
: "If your network requires a proxy, set HTTPS_PROXY or HTTP_PROXY, and set NO_PROXY=localhost,127.0.0.1.",
|
|
77
79
|
"Do not disable TLS verification with NODE_TLS_REJECT_UNAUTHORIZED=0.",
|
|
78
80
|
`Original error: ${firstMeaningfulErrorMessage(error) || "unknown network error"}`,
|
|
79
81
|
].join(" ");
|
|
80
82
|
return new Error(message, { cause: error });
|
|
81
83
|
}
|
|
82
84
|
function hasProxyEnv(env) {
|
|
83
|
-
return Boolean(env.
|
|
85
|
+
return Boolean(env.BUBBLE_CHATGPT_PROXY ||
|
|
86
|
+
env.bubble_chatgpt_proxy ||
|
|
87
|
+
env.HTTPS_PROXY ||
|
|
88
|
+
env.https_proxy ||
|
|
89
|
+
env.HTTP_PROXY ||
|
|
90
|
+
env.http_proxy ||
|
|
91
|
+
env.ALL_PROXY ||
|
|
92
|
+
env.all_proxy);
|
|
84
93
|
}
|
|
85
94
|
function isBunRuntime() {
|
|
86
95
|
return typeof globalThis.Bun !== "undefined";
|
|
87
96
|
}
|
|
88
97
|
function bunProxyForUrl(input, env) {
|
|
98
|
+
return proxyForUrl(input, env);
|
|
99
|
+
}
|
|
100
|
+
function nodeProxyForUrl(input, env) {
|
|
101
|
+
return proxyForUrl(input, env);
|
|
102
|
+
}
|
|
103
|
+
function proxyForUrl(input, env) {
|
|
89
104
|
const url = urlFromInput(input);
|
|
90
105
|
if (!url || shouldBypassProxy(url, env))
|
|
91
106
|
return undefined;
|
|
107
|
+
const explicit = explicitProxyForUrl(url, env);
|
|
108
|
+
return explicit ?? macSystemProxyForUrl(url, env);
|
|
109
|
+
}
|
|
110
|
+
function defaultNodeProxy(env) {
|
|
111
|
+
return (env.BUBBLE_CHATGPT_PROXY ??
|
|
112
|
+
env.bubble_chatgpt_proxy ??
|
|
113
|
+
env.HTTPS_PROXY ??
|
|
114
|
+
env.https_proxy ??
|
|
115
|
+
env.HTTP_PROXY ??
|
|
116
|
+
env.http_proxy ??
|
|
117
|
+
env.ALL_PROXY ??
|
|
118
|
+
env.all_proxy ??
|
|
119
|
+
macSystemProxyForUrl(new URL("https://chatgpt.com/"), env));
|
|
120
|
+
}
|
|
121
|
+
function explicitProxyForUrl(url, env) {
|
|
122
|
+
const chatGptProxy = env.BUBBLE_CHATGPT_PROXY ?? env.bubble_chatgpt_proxy;
|
|
123
|
+
if (chatGptProxy)
|
|
124
|
+
return chatGptProxy;
|
|
92
125
|
const allProxy = env.ALL_PROXY ?? env.all_proxy;
|
|
93
126
|
if (url.protocol === "https:")
|
|
94
127
|
return env.HTTPS_PROXY ?? env.https_proxy ?? allProxy;
|
|
95
128
|
if (url.protocol === "http:")
|
|
96
129
|
return env.HTTP_PROXY ?? env.http_proxy ?? allProxy;
|
|
130
|
+
return allProxy;
|
|
131
|
+
}
|
|
132
|
+
function hasMacSystemProxy(env) {
|
|
133
|
+
return Boolean(macSystemProxyForUrl(new URL("https://chatgpt.com/"), env));
|
|
134
|
+
}
|
|
135
|
+
function macSystemProxyForUrl(url, env) {
|
|
136
|
+
if (process.platform !== "darwin" || truthyEnv(env.BUBBLE_DISABLE_SYSTEM_PROXY))
|
|
137
|
+
return undefined;
|
|
138
|
+
const output = readMacSystemProxy();
|
|
139
|
+
return output ? parseMacSystemProxyForUrl(output, url) : undefined;
|
|
140
|
+
}
|
|
141
|
+
export function parseMacSystemProxyForUrl(output, url) {
|
|
142
|
+
if (macProxyExceptionMatches(output, url.hostname, url.port))
|
|
143
|
+
return undefined;
|
|
144
|
+
const values = parseScutilProxyValues(output);
|
|
145
|
+
if (url.protocol === "https:") {
|
|
146
|
+
return formatMacProxy(values.HTTPSEnable, values.HTTPSProxy, values.HTTPSPort);
|
|
147
|
+
}
|
|
148
|
+
if (url.protocol === "http:") {
|
|
149
|
+
return formatMacProxy(values.HTTPEnable, values.HTTPProxy, values.HTTPPort);
|
|
150
|
+
}
|
|
97
151
|
return undefined;
|
|
98
152
|
}
|
|
99
|
-
function
|
|
100
|
-
const
|
|
101
|
-
|
|
153
|
+
function parseScutilProxyValues(output) {
|
|
154
|
+
const values = {};
|
|
155
|
+
for (const line of output.split(/\r?\n/)) {
|
|
156
|
+
const match = line.match(/^\s*([A-Za-z]+(?:Enable|Proxy|Port))\s*:\s*(.+?)\s*$/);
|
|
157
|
+
if (match)
|
|
158
|
+
values[match[1]] = match[2];
|
|
159
|
+
}
|
|
160
|
+
return values;
|
|
161
|
+
}
|
|
162
|
+
function formatMacProxy(enabled, host, port) {
|
|
163
|
+
if (enabled !== "1" || !host || !port)
|
|
102
164
|
return undefined;
|
|
103
|
-
if (
|
|
104
|
-
return
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return defaultNodeProxy(env);
|
|
165
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(host))
|
|
166
|
+
return host;
|
|
167
|
+
const normalizedHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
168
|
+
return `http://${normalizedHost}:${port}`;
|
|
108
169
|
}
|
|
109
|
-
function
|
|
110
|
-
|
|
170
|
+
function macProxyExceptionMatches(output, hostname, port) {
|
|
171
|
+
const exceptions = parseMacProxyExceptions(output);
|
|
172
|
+
return exceptions.some((entry) => noProxyEntryMatches(entry.toLowerCase(), hostname.toLowerCase(), port));
|
|
173
|
+
}
|
|
174
|
+
function parseMacProxyExceptions(output) {
|
|
175
|
+
const exceptions = [];
|
|
176
|
+
let inExceptions = false;
|
|
177
|
+
for (const line of output.split(/\r?\n/)) {
|
|
178
|
+
if (line.includes("ExceptionsList")) {
|
|
179
|
+
inExceptions = true;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (!inExceptions)
|
|
183
|
+
continue;
|
|
184
|
+
if (/^\s*}\s*$/.test(line))
|
|
185
|
+
break;
|
|
186
|
+
const match = line.match(/^\s*\d+\s*:\s*(.+?)\s*$/);
|
|
187
|
+
if (match)
|
|
188
|
+
exceptions.push(match[1].trim());
|
|
189
|
+
}
|
|
190
|
+
return exceptions;
|
|
191
|
+
}
|
|
192
|
+
let cachedMacSystemProxy;
|
|
193
|
+
function readMacSystemProxy() {
|
|
194
|
+
const now = Date.now();
|
|
195
|
+
if (cachedMacSystemProxy && now - cachedMacSystemProxy.checkedAtMs < 5_000) {
|
|
196
|
+
return cachedMacSystemProxy.output;
|
|
197
|
+
}
|
|
198
|
+
let output;
|
|
199
|
+
try {
|
|
200
|
+
output = execFileSync("scutil", ["--proxy"], {
|
|
201
|
+
encoding: "utf-8",
|
|
202
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
203
|
+
timeout: 1_000,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
output = undefined;
|
|
208
|
+
}
|
|
209
|
+
cachedMacSystemProxy = { checkedAtMs: now, output };
|
|
210
|
+
return output;
|
|
211
|
+
}
|
|
212
|
+
function truthyEnv(value) {
|
|
213
|
+
return /^(1|true|yes|on)$/i.test(value?.trim() ?? "");
|
|
111
214
|
}
|
|
112
215
|
function bunExtraCaFiles(env) {
|
|
113
216
|
const bun = globalThis.Bun;
|
|
@@ -177,6 +280,9 @@ function networkEnvSignature(env) {
|
|
|
177
280
|
env.https_proxy,
|
|
178
281
|
env.ALL_PROXY,
|
|
179
282
|
env.all_proxy,
|
|
283
|
+
env.BUBBLE_CHATGPT_PROXY,
|
|
284
|
+
env.bubble_chatgpt_proxy,
|
|
285
|
+
env.BUBBLE_DISABLE_SYSTEM_PROXY,
|
|
180
286
|
env.NO_PROXY,
|
|
181
287
|
env.no_proxy,
|
|
182
288
|
env.NODE_EXTRA_CA_CERTS,
|