@yansigit/opencodex 2.32.0 → 2.33.0
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/README.md +2 -2
- package/gui/dist/assets/index-DKLr4LTE.js +102 -0
- package/gui/dist/assets/index-DrSQdTRd.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +6 -5
- package/src/adapters/anthropic.ts +20 -6
- package/src/adapters/azure.ts +20 -4
- package/src/adapters/base.ts +3 -1
- package/src/adapters/command-code.ts +40 -7
- package/src/adapters/cursor/live-transport.ts +1 -1
- package/src/adapters/cursor/protobuf-events.ts +158 -7
- package/src/adapters/cursor/protobuf-request.ts +33 -15
- package/src/adapters/cursor/request-builder.ts +4 -3
- package/src/adapters/cursor/tool-definitions.ts +27 -1
- package/src/adapters/cursor/types.ts +4 -3
- package/src/adapters/cursor.ts +9 -0
- package/src/adapters/google-antigravity-replay.ts +2 -2
- package/src/adapters/google-antigravity-wire.ts +7 -0
- package/src/adapters/google-errors.ts +6 -2
- package/src/adapters/google-http.ts +30 -7
- package/src/adapters/google-truncation.ts +5 -0
- package/src/adapters/google-wire-compiler.ts +38 -6
- package/src/adapters/google.ts +148 -26
- package/src/adapters/kiro-tools.ts +20 -9
- package/src/adapters/openai-chat.ts +9 -0
- package/src/adapters/openai-responses.ts +1 -1
- package/src/bridge.ts +112 -9
- package/src/claude/context-windows.ts +16 -9
- package/src/cli/doctor.ts +2 -2
- package/src/cli/index.ts +9 -2
- package/src/cli/provider.ts +6 -0
- package/src/cli/status.ts +23 -0
- package/src/codex/auth-api.ts +4 -2
- package/src/codex/autostart-health.ts +16 -0
- package/src/codex/catalog/aggregation.ts +12 -12
- package/src/codex/catalog/effort.ts +18 -3
- package/src/codex/catalog/metadata.ts +27 -1
- package/src/codex/catalog/model-metadata.ts +39 -12
- package/src/codex/catalog/parsing.ts +38 -27
- package/src/codex/catalog/provider-fetch.ts +198 -133
- package/src/codex/catalog/sync.ts +1 -1
- package/src/codex/convergence.ts +5 -0
- package/src/codex/shim.ts +56 -3
- package/src/config/provider-validation.ts +37 -0
- package/src/config.ts +78 -2
- package/src/generated/compatibility-version.json +120 -96
- package/src/images/loop.ts +37 -6
- package/src/lib/azure-identity.ts +154 -0
- package/src/lib/debug.ts +42 -0
- package/src/lib/errors.ts +14 -0
- package/src/lib/provider-outbound.ts +45 -33
- package/src/lib/provider-tls-profile.ts +309 -0
- package/src/lib/proxy-env.ts +49 -0
- package/src/lib/redact.ts +10 -1
- package/src/oauth/antigravity-routing.ts +282 -236
- package/src/oauth/callback-server.ts +22 -2
- package/src/oauth/command-code.ts +5 -16
- package/src/oauth/google-antigravity.ts +42 -5
- package/src/oauth/index.ts +15 -3
- package/src/oauth/kimi.ts +9 -1
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +6 -0
- package/src/providers/antigravity-quota.ts +3 -1
- package/src/providers/api-keys.ts +2 -1
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/derive.ts +4 -0
- package/src/providers/key-failover.ts +5 -1
- package/src/providers/openai-tiers.ts +5 -0
- package/src/providers/provider-id-rewrite.ts +1 -0
- package/src/providers/quota.ts +59 -13
- package/src/providers/registry.ts +3 -1
- package/src/providers/request-pacing.ts +33 -6
- package/src/providers/xai-transport.ts +21 -0
- package/src/responses/google-provider-options.ts +36 -0
- package/src/responses/namespace-tool-compat.ts +84 -4
- package/src/responses/parser.ts +11 -0
- package/src/responses/provider-opaque-metadata.ts +3 -3
- package/src/responses/schema.ts +37 -0
- package/src/responses/state.ts +94 -4
- package/src/router.ts +8 -2
- package/src/server/auth-cors.ts +28 -0
- package/src/server/images.ts +19 -35
- package/src/server/management/agent-settings-routes.ts +205 -15
- package/src/server/management/combo-routes.ts +6 -0
- package/src/server/management/config-routes.ts +31 -5
- package/src/server/management/model-rows.ts +4 -0
- package/src/server/management/oauth-account-routes.ts +25 -4
- package/src/server/management/provider-routes.ts +113 -15
- package/src/server/management/routing-profile-routes.ts +3 -0
- package/src/server/request-log.ts +21 -0
- package/src/server/responses/agent-task-recovery.ts +1 -1
- package/src/server/responses/compact.ts +30 -1
- package/src/server/responses/core.ts +359 -153
- package/src/server/responses/empty-completion-guard.ts +35 -6
- package/src/server/responses/fetch-helpers.ts +18 -5
- package/src/server/responses/v2-native-parent-override.ts +59 -0
- package/src/server/responses/ws-upstream.ts +75 -2
- package/src/server/responses-undeclared-tool-guard.ts +90 -8
- package/src/service.ts +1 -1
- package/src/types/config.ts +16 -1
- package/src/types/provider.ts +16 -0
- package/src/types/request.ts +28 -0
- package/src/types/tools.ts +27 -0
- package/src/types.ts +6 -0
- package/src/web-search/gemini-executor.ts +6 -4
- package/src/web-search/loop.ts +42 -6
- package/gui/dist/assets/index-BG43zwVe.js +0 -102
- package/gui/dist/assets/index-CiSI-jrP.css +0 -1
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import type { OcxProviderConfig, ProviderTlsProfile } from "../types";
|
|
3
|
+
import { resolvePublicAddresses } from "./destination-policy";
|
|
4
|
+
import { registerOptionalShutdownHook } from "./optional-shutdown-hooks";
|
|
5
|
+
import { proxyForUrl } from "./proxy-env";
|
|
6
|
+
import { redactErrorMessage } from "./redact";
|
|
7
|
+
|
|
8
|
+
export const ANTIGRAVITY_TLS_HOSTS = new Set([
|
|
9
|
+
"daily-cloudcode-pa.googleapis.com",
|
|
10
|
+
"cloudcode-pa.googleapis.com",
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
export type ProviderTlsProfileStatus = "disabled" | "active" | "fallback";
|
|
14
|
+
|
|
15
|
+
interface WreqTransport {
|
|
16
|
+
close(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface WreqModule {
|
|
20
|
+
createTransport(options: {
|
|
21
|
+
browser: "chrome_142";
|
|
22
|
+
os: "windows" | "macos" | "linux";
|
|
23
|
+
proxy?: string;
|
|
24
|
+
}): Promise<WreqTransport>;
|
|
25
|
+
fetch(input: string | URL | Request, init?: Record<string, unknown>): Promise<unknown>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface InitializedTransport {
|
|
29
|
+
module: WreqModule;
|
|
30
|
+
transport: WreqTransport;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type ProviderTlsContract = Pick<OcxProviderConfig, "adapter" | "authMode" | "googleMode" | "baseUrl" | "tlsProfile">;
|
|
34
|
+
|
|
35
|
+
type AntigravityOAuthDestinationContract = Pick<OcxProviderConfig, "baseUrl"> & Partial<Pick<OcxProviderConfig, "adapter" | "authMode" | "googleMode">>;
|
|
36
|
+
|
|
37
|
+
export interface ProviderTlsRuntimeForTest {
|
|
38
|
+
importWreq: () => Promise<WreqModule>;
|
|
39
|
+
resolveDestination?: typeof resolvePublicAddresses;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const defaultRuntime: ProviderTlsRuntimeForTest = {
|
|
43
|
+
importWreq: async () => await import("wreq-js") as unknown as WreqModule,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
let runtime = defaultRuntime;
|
|
47
|
+
const statusByProvider = new Map<string, { status: ProviderTlsProfileStatus; fingerprint: string }>();
|
|
48
|
+
const transports = new Map<string, Promise<InitializedTransport | undefined>>();
|
|
49
|
+
let shutdownDetach: (() => void) | undefined;
|
|
50
|
+
let fallbackWarned = false;
|
|
51
|
+
|
|
52
|
+
function providerTlsContractFingerprint(providerName: string, provider: ProviderTlsContract): string {
|
|
53
|
+
return createHash("sha256").update(JSON.stringify([
|
|
54
|
+
providerName,
|
|
55
|
+
provider.adapter,
|
|
56
|
+
provider.authMode,
|
|
57
|
+
provider.googleMode,
|
|
58
|
+
provider.baseUrl,
|
|
59
|
+
provider.tlsProfile,
|
|
60
|
+
])).digest("hex");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function setStatus(providerName: string, provider: ProviderTlsContract, status: ProviderTlsProfileStatus): void {
|
|
64
|
+
statusByProvider.set(providerName, { status, fingerprint: providerTlsContractFingerprint(providerName, provider) });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const SAFE_NATIVE_ERROR_NAMES = new Set(["AbortError", "TimeoutError"]);
|
|
68
|
+
const SAFE_NATIVE_ERROR_CODES = new Set([
|
|
69
|
+
"ABORT_ERR", "ECONNREFUSED", "ECONNRESET", "EAI_AGAIN", "ENETUNREACH", "ENOTFOUND",
|
|
70
|
+
"ETIMEDOUT", "UND_ERR_CONNECT_TIMEOUT", "UND_ERR_SOCKET",
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
function safeProviderTlsError(error: unknown): Error {
|
|
74
|
+
const raw = error instanceof Error ? error.message : String(error);
|
|
75
|
+
const safe = new Error(redactErrorMessage(raw));
|
|
76
|
+
if (!error || typeof error !== "object") return safe;
|
|
77
|
+
const native = error as { name?: unknown; code?: unknown };
|
|
78
|
+
if (typeof native.name === "string" && SAFE_NATIVE_ERROR_NAMES.has(native.name)) {
|
|
79
|
+
safe.name = native.name;
|
|
80
|
+
}
|
|
81
|
+
if (typeof native.code === "string" && SAFE_NATIVE_ERROR_CODES.has(native.code)) {
|
|
82
|
+
Object.defineProperty(safe, "code", {
|
|
83
|
+
configurable: true,
|
|
84
|
+
enumerable: false,
|
|
85
|
+
value: native.code,
|
|
86
|
+
writable: false,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return safe;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function warnFallbackOnce(): void {
|
|
93
|
+
if (fallbackWarned) return;
|
|
94
|
+
fallbackWarned = true;
|
|
95
|
+
console.warn("[opencodex] Antigravity TLS profile requested → fallback to Bun fetch; native transport initialization failed");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function emulationOs(): "windows" | "macos" | "linux" {
|
|
99
|
+
if (process.platform === "win32") return "windows";
|
|
100
|
+
if (process.platform === "darwin") return "macos";
|
|
101
|
+
if (process.platform === "linux") return "linux";
|
|
102
|
+
throw new Error("unsupported host operating system for Antigravity TLS profile");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function profileIsSupported(providerName: string, provider: Pick<OcxProviderConfig, "adapter" | "authMode" | "googleMode" | "baseUrl" | "tlsProfile">): boolean {
|
|
106
|
+
return providerName === "google-antigravity"
|
|
107
|
+
&& provider.adapter === "google"
|
|
108
|
+
&& provider.authMode === "oauth"
|
|
109
|
+
&& provider.googleMode === "cloud-code-assist"
|
|
110
|
+
&& isCanonicalAntigravityUrl(provider.baseUrl)
|
|
111
|
+
&& provider.tlsProfile === "antigravity-browser";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function providerTlsProfileConfigError(
|
|
115
|
+
providerName: string,
|
|
116
|
+
provider: Pick<OcxProviderConfig, "adapter" | "authMode" | "googleMode" | "baseUrl" | "tlsProfile">,
|
|
117
|
+
): string | null {
|
|
118
|
+
if (provider.tlsProfile === undefined) return null;
|
|
119
|
+
if (provider.tlsProfile !== "antigravity-browser") {
|
|
120
|
+
return "tlsProfile must be antigravity-browser";
|
|
121
|
+
}
|
|
122
|
+
if (providerName !== "google-antigravity") return "tlsProfile antigravity-browser is valid only for google-antigravity";
|
|
123
|
+
if (provider.adapter !== "google" || provider.authMode !== "oauth") {
|
|
124
|
+
return "tlsProfile antigravity-browser requires Google OAuth authentication";
|
|
125
|
+
}
|
|
126
|
+
if (provider.googleMode !== "cloud-code-assist") {
|
|
127
|
+
return "tlsProfile antigravity-browser requires Google Cloud Code Assist mode";
|
|
128
|
+
}
|
|
129
|
+
if (!isCanonicalAntigravityUrl(provider.baseUrl)) {
|
|
130
|
+
return "tlsProfile antigravity-browser requires a canonical Antigravity HTTPS destination";
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function isCanonicalAntigravityUrl(input: string | URL): boolean {
|
|
136
|
+
try {
|
|
137
|
+
const url = new URL(input);
|
|
138
|
+
return url.protocol === "https:"
|
|
139
|
+
&& (url.port === "" || url.port === "443")
|
|
140
|
+
&& url.username === ""
|
|
141
|
+
&& url.password === ""
|
|
142
|
+
&& ANTIGRAVITY_TLS_HOSTS.has(url.hostname.toLowerCase());
|
|
143
|
+
} catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function isAntigravityOAuthProvider(
|
|
149
|
+
providerName: string,
|
|
150
|
+
provider: Pick<OcxProviderConfig, "authMode">,
|
|
151
|
+
): boolean {
|
|
152
|
+
return providerName === "google-antigravity"
|
|
153
|
+
&& (provider.authMode === undefined || provider.authMode === "oauth");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Same-name Antigravity rows are OAuth destinations even when legacy config omitted authMode. */
|
|
157
|
+
export function antigravityOAuthDestinationConfigError(
|
|
158
|
+
providerName: string,
|
|
159
|
+
provider: AntigravityOAuthDestinationContract,
|
|
160
|
+
): string | null {
|
|
161
|
+
if (providerName !== "google-antigravity") return null;
|
|
162
|
+
if (provider.googleMode !== undefined && provider.googleMode !== "cloud-code-assist") {
|
|
163
|
+
return "requires Google Cloud Code Assist mode for OAuth";
|
|
164
|
+
}
|
|
165
|
+
if (!isCanonicalAntigravityUrl(provider.baseUrl)) {
|
|
166
|
+
return "requires a canonical Antigravity HTTPS destination for OAuth";
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function getProviderTlsProfileStatus(providerName: string, provider: ProviderTlsContract): ProviderTlsProfileStatus {
|
|
172
|
+
const current = statusByProvider.get(providerName);
|
|
173
|
+
return current?.fingerprint === providerTlsContractFingerprint(providerName, provider)
|
|
174
|
+
? current.status
|
|
175
|
+
: "disabled";
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function clearProviderTlsProfileStatus(providerName: string): void {
|
|
179
|
+
statusByProvider.delete(providerName);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function registerShutdownHook(): void {
|
|
183
|
+
if (shutdownDetach) return;
|
|
184
|
+
shutdownDetach = registerOptionalShutdownHook("provider-tls-profile", () => {
|
|
185
|
+
const pending = [...transports.values()];
|
|
186
|
+
transports.clear();
|
|
187
|
+
shutdownDetach = undefined;
|
|
188
|
+
for (const transport of pending) {
|
|
189
|
+
void transport.then(value => value?.transport.close()).catch(() => undefined);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function transportKey(proxy: string | undefined): string {
|
|
195
|
+
return `${process.platform}:${proxy ?? "direct"}`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function getTransport(proxy: string | undefined): Promise<InitializedTransport | undefined> {
|
|
199
|
+
const key = transportKey(proxy);
|
|
200
|
+
const existing = transports.get(key);
|
|
201
|
+
if (existing) return existing;
|
|
202
|
+
const pending = (async () => {
|
|
203
|
+
let transport: WreqTransport | undefined;
|
|
204
|
+
try {
|
|
205
|
+
const module = await runtime.importWreq();
|
|
206
|
+
transport = await module.createTransport({
|
|
207
|
+
browser: "chrome_142",
|
|
208
|
+
os: emulationOs(),
|
|
209
|
+
...(proxy ? { proxy } : {}),
|
|
210
|
+
});
|
|
211
|
+
registerShutdownHook();
|
|
212
|
+
return { module, transport };
|
|
213
|
+
} catch {
|
|
214
|
+
if (transport) await transport.close().catch(() => undefined);
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
})();
|
|
218
|
+
transports.set(key, pending);
|
|
219
|
+
return pending;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function providerTlsFetch(
|
|
223
|
+
providerName: string,
|
|
224
|
+
provider: Pick<OcxProviderConfig, "adapter" | "authMode" | "googleMode" | "baseUrl" | "tlsProfile">,
|
|
225
|
+
bunFetch: typeof globalThis.fetch,
|
|
226
|
+
): typeof globalThis.fetch {
|
|
227
|
+
const antigravityError = antigravityOAuthDestinationConfigError(providerName, provider);
|
|
228
|
+
if (antigravityError) {
|
|
229
|
+
return (async () => {
|
|
230
|
+
throw new Error(`provider ${providerName} ${antigravityError}`);
|
|
231
|
+
}) as unknown as typeof globalThis.fetch;
|
|
232
|
+
}
|
|
233
|
+
if (provider.tlsProfile === undefined) {
|
|
234
|
+
setStatus(providerName, provider, "disabled");
|
|
235
|
+
return bunFetch;
|
|
236
|
+
}
|
|
237
|
+
if (!profileIsSupported(providerName, provider)) {
|
|
238
|
+
setStatus(providerName, provider, "fallback");
|
|
239
|
+
warnFallbackOnce();
|
|
240
|
+
return bunFetch;
|
|
241
|
+
}
|
|
242
|
+
return (async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
|
|
243
|
+
const url = typeof input === "string" || input instanceof URL ? input : input.url;
|
|
244
|
+
if (!isCanonicalAntigravityUrl(url)) return bunFetch(input, init);
|
|
245
|
+
const proxy = proxyForUrl(url);
|
|
246
|
+
// A direct profiled request must pass the same public-destination gate as the
|
|
247
|
+
// ordinary provider executor before wreq can send its bearer-bearing request.
|
|
248
|
+
// Proxied requests deliberately leave peer selection to the configured proxy,
|
|
249
|
+
// matching providerOutbound's proxy boundary (and its inability to pin a peer).
|
|
250
|
+
if (!proxy) {
|
|
251
|
+
await (runtime.resolveDestination ?? resolvePublicAddresses)(String(url), {
|
|
252
|
+
context: "Antigravity profile URL",
|
|
253
|
+
allowPrivateNetwork: false,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
const initialized = await getTransport(proxy);
|
|
257
|
+
if (!initialized) {
|
|
258
|
+
setStatus(providerName, provider, "fallback");
|
|
259
|
+
warnFallbackOnce();
|
|
260
|
+
return bunFetch(input, { ...init, redirect: "manual" });
|
|
261
|
+
}
|
|
262
|
+
setStatus(providerName, provider, "active");
|
|
263
|
+
const wreqInit = {
|
|
264
|
+
...init,
|
|
265
|
+
transport: initialized.transport,
|
|
266
|
+
disableDefaultHeaders: true,
|
|
267
|
+
cookieMode: "ephemeral" as const,
|
|
268
|
+
redirect: "manual" as const,
|
|
269
|
+
};
|
|
270
|
+
try {
|
|
271
|
+
return await initialized.module.fetch(input, wreqInit) as Response;
|
|
272
|
+
} catch (error) {
|
|
273
|
+
// Native errors are post-dispatch failures. Redact at this boundary and
|
|
274
|
+
// never replay through Bun, since generation may already have started.
|
|
275
|
+
throw safeProviderTlsError(error);
|
|
276
|
+
}
|
|
277
|
+
}) as typeof globalThis.fetch;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function setProviderTlsRuntimeForTest(next: ProviderTlsRuntimeForTest | undefined): void {
|
|
281
|
+
if (next === undefined) {
|
|
282
|
+
runtime = defaultRuntime;
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
// Test callers normally replace only the native module. Keep those tests
|
|
286
|
+
// deterministic in offline sandboxes while allowing explicit DNS policy
|
|
287
|
+
// regressions to inject a rejecting resolver.
|
|
288
|
+
runtime = {
|
|
289
|
+
...defaultRuntime,
|
|
290
|
+
resolveDestination: async (url: string) => ({
|
|
291
|
+
hostname: new URL(url).hostname,
|
|
292
|
+
addresses: [{ address: "142.250.1.1", family: 4 }],
|
|
293
|
+
privateNetwork: false,
|
|
294
|
+
}),
|
|
295
|
+
...next,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function resetProviderTlsProfileForTests(): void {
|
|
300
|
+
for (const pending of transports.values()) void pending.then(value => value?.transport.close()).catch(() => undefined);
|
|
301
|
+
transports.clear();
|
|
302
|
+
shutdownDetach?.();
|
|
303
|
+
shutdownDetach = undefined;
|
|
304
|
+
statusByProvider.clear();
|
|
305
|
+
fallbackWarned = false;
|
|
306
|
+
runtime = defaultRuntime;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export type { ProviderTlsProfile };
|
package/src/lib/proxy-env.ts
CHANGED
|
@@ -16,3 +16,52 @@ export function outboundProxyConfigured(
|
|
|
16
16
|
): boolean {
|
|
17
17
|
return OUTBOUND_PROXY_ENV_KEYS.some(key => proxyEnvPresent(key, env));
|
|
18
18
|
}
|
|
19
|
+
|
|
20
|
+
function proxyValue(key: ProxyEnvKey, env: ProxyEnvMap): string | undefined {
|
|
21
|
+
const value = env[key]?.trim() || env[key.toLowerCase()]?.trim();
|
|
22
|
+
return value || undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function noProxyMatches(url: URL, env: ProxyEnvMap = process.env): boolean {
|
|
26
|
+
const raw = (env.NO_PROXY ?? env.no_proxy ?? "").trim();
|
|
27
|
+
const hostname = url.hostname.trim().toLowerCase().replace(/^\[|\]$/g, "").replace(/\.+$/, "");
|
|
28
|
+
const port = url.port || (url.protocol === "https:" ? "443" : "80");
|
|
29
|
+
for (const value of raw.split(",")) {
|
|
30
|
+
let entry = value.trim().toLowerCase();
|
|
31
|
+
if (!entry) continue;
|
|
32
|
+
if (entry === "*") return true;
|
|
33
|
+
entry = entry.replace(/^https?:\/\//, "").split("/", 1)[0]!;
|
|
34
|
+
let entryHost = entry;
|
|
35
|
+
let entryPort = "";
|
|
36
|
+
const bracketed = /^\[([^\]]+)](?::(\d+))?$/.exec(entry);
|
|
37
|
+
if (bracketed) {
|
|
38
|
+
entryHost = bracketed[1]!;
|
|
39
|
+
entryPort = bracketed[2] ?? "";
|
|
40
|
+
} else if ((entry.match(/:/g)?.length ?? 0) === 1) {
|
|
41
|
+
const separator = entry.lastIndexOf(":");
|
|
42
|
+
const possiblePort = entry.slice(separator + 1);
|
|
43
|
+
if (/^\d+$/.test(possiblePort)) {
|
|
44
|
+
entryHost = entry.slice(0, separator);
|
|
45
|
+
entryPort = possiblePort;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (entryPort && entryPort !== port) continue;
|
|
49
|
+
entryHost = entryHost.replace(/^\*?\./, "").replace(/^\[|\]$/g, "").replace(/\.+$/, "");
|
|
50
|
+
if (entryHost && (hostname === entryHost || hostname.endsWith(`.${entryHost}`))) return true;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Select the standard proxy for a URL without exposing its credentials. */
|
|
56
|
+
export function proxyForUrl(url: string | URL, env: ProxyEnvMap = process.env): string | undefined {
|
|
57
|
+
let parsed: URL;
|
|
58
|
+
try {
|
|
59
|
+
parsed = new URL(url);
|
|
60
|
+
} catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
if (noProxyMatches(parsed, env)) return undefined;
|
|
64
|
+
if (parsed.protocol === "https:") return proxyValue("HTTPS_PROXY", env) ?? proxyValue("ALL_PROXY", env);
|
|
65
|
+
if (parsed.protocol === "http:") return proxyValue("HTTP_PROXY", env) ?? proxyValue("ALL_PROXY", env);
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
package/src/lib/redact.ts
CHANGED
|
@@ -490,10 +490,19 @@ export function redactUrlForLog(url: string): string {
|
|
|
490
490
|
parsed.hash = "";
|
|
491
491
|
return parsed.toString();
|
|
492
492
|
} catch {
|
|
493
|
-
|
|
493
|
+
const stripped = (url.split(/[?#]/)[0] ?? url).replace(/:\/\/[^@\s]+@/g, "://");
|
|
494
|
+
return redactSecretString(stripped);
|
|
494
495
|
}
|
|
495
496
|
}
|
|
496
497
|
|
|
498
|
+
/** Redact credentials and userinfo from arbitrary diagnostic text. */
|
|
499
|
+
export function redactErrorMessage(value: string): string {
|
|
500
|
+
return redactSecretString(value).replace(
|
|
501
|
+
/[a-z][a-z0-9+.-]*:\/\/[^\s"'<>]+/gi,
|
|
502
|
+
match => redactUrlForLog(match),
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
497
506
|
const USER_HOME_PATH_PATTERNS: Array<[RegExp, string]> = [
|
|
498
507
|
// Windows: C:\Users\<name>\... -> C:\Users\[USER]\...
|
|
499
508
|
[/([A-Za-z]:\\Users\\)[^\\/]+/gi, "$1[USER]"],
|