@xiaoso/dsh-tool-plus 0.1.0-beta.2 → 0.1.0-beta.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.
- package/README.en.md +122 -0
- package/README.md +3 -2
- package/THIRD_PARTY_NOTICES.md +4 -35
- package/lib/abort-B7izMMna.mjs +51 -0
- package/lib/abort-D1_Z5Ut5.mjs +51 -0
- package/lib/abort-dIkZBV7Q.mjs +51 -0
- package/lib/amazon-bedrock-B0nxT06F.mjs +917 -0
- package/lib/amazon-bedrock-D-shAr1X.mjs +917 -0
- package/lib/amazon-bedrock-WQhvjHYu.mjs +917 -0
- package/lib/anthropic-BWsZFzU8.mjs +262 -0
- package/lib/anthropic-CJCauvYz.mjs +3115 -0
- package/lib/anthropic-CMT1Qj1t.mjs +3115 -0
- package/lib/anthropic-CZghe_9U.mjs +3115 -0
- package/lib/anthropic-sCMH7JID.mjs +262 -0
- package/lib/anthropic-tTntD8BD.mjs +262 -0
- package/lib/api-key-login-BzqNdM2y.mjs +157 -0
- package/lib/api-key-login-CWVwvHT1.mjs +157 -0
- package/lib/api-key-login-CpzSezsi.mjs +157 -0
- package/lib/aws-credentials-8Mgn5MAy.mjs +867 -0
- package/lib/aws-credentials-CXryMobh.mjs +867 -0
- package/lib/aws-credentials-ChG79ey-.mjs +867 -0
- package/lib/azure-openai-responses-CNkHiLUh.mjs +274 -0
- package/lib/azure-openai-responses-Cgmc-rbL.mjs +274 -0
- package/lib/azure-openai-responses-DuukbD0F.mjs +274 -0
- package/lib/bun-shim.mjs +252 -12
- package/lib/bundled-references-CSuBco5p.mjs +55 -0
- package/lib/bundled-references-Cavaffah.mjs +55 -0
- package/lib/bundled-references-sLN0YdXU.mjs +55 -0
- package/lib/callback-server-BEFNf-jf.mjs +398 -0
- package/lib/callback-server-CQ5Jo8F-.mjs +398 -0
- package/lib/callback-server-DDSrZ6l_.mjs +398 -0
- package/lib/client.js +111 -41
- package/lib/cursor-B3zg7EHj.mjs +273 -0
- package/lib/cursor-B6TUEmgP.mjs +135 -0
- package/lib/cursor-BIQfBC6V.mjs +135 -0
- package/lib/cursor-D8gvtTL3.mjs +135 -0
- package/lib/cursor-DNk6eXDd.mjs +5470 -0
- package/lib/cursor-DkSX2h3H.mjs +5441 -0
- package/lib/cursor-MjSm74IJ.mjs +273 -0
- package/lib/cursor-aJrXeYCb.mjs +273 -0
- package/lib/cursor-mJQwUKXK.mjs +5441 -0
- package/lib/device-code-DBo3B5nh.mjs +51 -0
- package/lib/device-code-DIb0FU-H.mjs +51 -0
- package/lib/device-code-OI_tQcmc.mjs +51 -0
- package/lib/devin-BHZZyMhO.mjs +95 -0
- package/lib/devin-BK7DHeca.mjs +114 -0
- package/lib/devin-CfMp3cXS.mjs +95 -0
- package/lib/devin-CldyPD8K.mjs +95 -0
- package/lib/devin-CzRpTUOA.mjs +114 -0
- package/lib/devin-D9zR5B_R.mjs +542 -0
- package/lib/devin-DXOeNyU7.mjs +542 -0
- package/lib/devin-Q1yem3lp.mjs +114 -0
- package/lib/devin-vNZTNUON.mjs +542 -0
- package/lib/error-B3bevhrs.mjs +1449 -0
- package/lib/error-Dd8DIqs-.mjs +1449 -0
- package/lib/error-yCSG0mGC.mjs +1449 -0
- package/lib/event-stream-BDPi64mK.mjs +151 -0
- package/lib/event-stream-BbFZJy-z.mjs +151 -0
- package/lib/event-stream-loUa0s1T.mjs +151 -0
- package/lib/fs-promises-shim-Bx0fpxuk.mjs +57 -0
- package/lib/github-copilot-4GaMtQmg.mjs +208 -0
- package/lib/github-copilot-B1yiyVRh.mjs +104 -0
- package/lib/github-copilot-B2HGSIJK.mjs +104 -0
- package/lib/github-copilot-BWT3eAOF.mjs +208 -0
- package/lib/github-copilot-CYkjeTi3.mjs +208 -0
- package/lib/github-copilot-jscUZ8Hf.mjs +104 -0
- package/lib/gitlab-duo-CM7Ref1H.mjs +155 -0
- package/lib/gitlab-duo-akICyfeD.mjs +155 -0
- package/lib/gitlab-duo-gubO4FNw.mjs +155 -0
- package/lib/gitlab-duo-workflow-BGAF6JDw.mjs +91 -0
- package/lib/gitlab-duo-workflow-CSEgYk3N.mjs +91 -0
- package/lib/gitlab-duo-workflow-D78tdn8E.mjs +91 -0
- package/lib/google-BS4T25Pn.mjs +30 -0
- package/lib/google-TFe-7-of.mjs +30 -0
- package/lib/google-antigravity-DGmwC2KV.mjs +198 -0
- package/lib/google-antigravity-DNYhgTpA.mjs +198 -0
- package/lib/google-antigravity-Dlkdhahq.mjs +198 -0
- package/lib/google-auth-B6gjsZvg.mjs +232 -0
- package/lib/google-auth-De7_s3eo.mjs +232 -0
- package/lib/google-auth-DhqpOYrX.mjs +232 -0
- package/lib/google-gemini-cli-BG_LRdIV.mjs +850 -0
- package/lib/google-gemini-cli-CWu8HvXW.mjs +211 -0
- package/lib/google-gemini-cli-Cb97bzQc.mjs +850 -0
- package/lib/google-gemini-cli-Cqds_Q5T.mjs +850 -0
- package/lib/google-gemini-cli-Dsztq0N5.mjs +211 -0
- package/lib/google-gemini-cli-zJhiOcND.mjs +211 -0
- package/lib/google-oauth-shared-CuEHaDDj.mjs +147 -0
- package/lib/google-oauth-shared-CwNWYdmR.mjs +147 -0
- package/lib/google-oauth-shared-yqlyqPPC.mjs +147 -0
- package/lib/google-shared-CgKmmdO3.mjs +760 -0
- package/lib/google-shared-Dr-VG-ap.mjs +760 -0
- package/lib/google-shared-DzhCb9b0.mjs +760 -0
- package/lib/google-vertex-BDN2tLaD.mjs +99 -0
- package/lib/google-vertex-DopVOQOG.mjs +99 -0
- package/lib/google-vertex-kscbkyRb.mjs +99 -0
- package/lib/google-zyDOG9x7.mjs +30 -0
- package/lib/identity-Da1GPU9a.mjs +219 -0
- package/lib/identity-DnB63pS7.mjs +219 -0
- package/lib/identity-xRP7_eSR.mjs +219 -0
- package/lib/idle-iterator-Baoyn3Hp.mjs +366 -0
- package/lib/idle-iterator-C6XbWcuB.mjs +366 -0
- package/lib/idle-iterator-DUjKQEGT.mjs +366 -0
- package/lib/index.mjs +4608 -3935
- package/lib/kimi--wUOWER5.mjs +236 -0
- package/lib/kimi-DMVI-zrZ.mjs +236 -0
- package/lib/kimi-Dh0pv3WZ.mjs +236 -0
- package/lib/markit-BL3cH47U.mjs +1417 -0
- package/lib/markit-BNiNY6SE.mjs +1417 -0
- package/lib/markit-M0jhziqU.mjs +1417 -0
- package/lib/minimax-code-BK-3eBbz.mjs +53 -0
- package/lib/minimax-code-DCbuA6Yk.mjs +53 -0
- package/lib/minimax-code-Uq8Qu9LW.mjs +53 -0
- package/lib/models-CfT4coYT.mjs +104660 -0
- package/lib/models-D_XnT6ay.mjs +104660 -0
- package/lib/models-SGASygJ3.mjs +104660 -0
- package/lib/ollama-BQMGPxtV.mjs +589 -0
- package/lib/ollama-DPzBmq89.mjs +589 -0
- package/lib/ollama-DoBz5SFm.mjs +589 -0
- package/lib/openai-codex-Bi-4S4PX.mjs +255 -0
- package/lib/openai-codex-CQL_kvYL.mjs +255 -0
- package/lib/openai-codex-Ch8CkT3p.mjs +255 -0
- package/lib/openai-codex-responses-CvxL_FMF.mjs +3144 -0
- package/lib/openai-codex-responses-Cz-2qpC7.mjs +3144 -0
- package/lib/openai-codex-responses-pejZgdZo.mjs +3144 -0
- package/lib/openai-completions-CF8IyNtP.mjs +1360 -0
- package/lib/openai-completions-DIf361kF.mjs +1360 -0
- package/lib/openai-completions-i8doczAk.mjs +1360 -0
- package/lib/openai-reasoning-fallback-DGVVLgd1.mjs +286 -0
- package/lib/openai-reasoning-fallback-Nj6yB2rY.mjs +286 -0
- package/lib/openai-reasoning-fallback-OdJREP1o.mjs +286 -0
- package/lib/openai-responses-BqXJInHX.mjs +867 -0
- package/lib/openai-responses-DTkBiiy-.mjs +867 -0
- package/lib/openai-responses-Db_a-U4-.mjs +867 -0
- package/lib/opencode-DaJjcUbf.mjs +38 -0
- package/lib/opencode-DhMolSoB.mjs +38 -0
- package/lib/opencode-Rq_Q2vAO.mjs +38 -0
- package/lib/perplexity-9b-RQ0mV.mjs +194 -0
- package/lib/perplexity-C4iLVMUj.mjs +194 -0
- package/lib/perplexity-Uu7Jzg5b.mjs +194 -0
- package/lib/proxy-DDqrmjjC.mjs +190 -0
- package/lib/proxy-ZtUcl6r5.mjs +190 -0
- package/lib/proxy-pM_NIv-H.mjs +190 -0
- package/lib/request-debug-HJMoJncL.mjs +183 -0
- package/lib/sanitize-text-CfcXzw5X.mjs +33 -0
- package/lib/scrapers-BkFfcPHj.mjs +9198 -0
- package/lib/scrapers-CL-WgdU1.mjs +9198 -0
- package/lib/scrapers-CVptboMs.mjs +9198 -0
- package/lib/scrapers-CipyKyCH.mjs +9198 -0
- package/lib/scrapers-DGfrYhtw.mjs +9198 -0
- package/lib/scrapers-DSUPYoUF.mjs +9198 -0
- package/lib/scrapers-DZ2GXmp7.mjs +9198 -0
- package/lib/scrapers-nhQAvc8d.mjs +9198 -0
- package/lib/shell-snapshot-fn-env.sh +67 -43
- package/lib/src-BgpusnsB.mjs +5076 -0
- package/lib/src-DTid0g4K.mjs +5001 -0
- package/lib/src-lDmkSRbm.mjs +5006 -0
- package/lib/stream-BlldLkVq.mjs +10236 -0
- package/lib/stream-CRtexYLe.mjs +10236 -0
- package/lib/stream-C_RAoxJC.mjs +10236 -0
- package/lib/stream-markup-healing-2toHXWXM.mjs +491 -0
- package/lib/stream-markup-healing-DMsxBx1L.mjs +491 -0
- package/lib/stream-markup-healing-KMkiAA00.mjs +491 -0
- package/lib/turndown-DN7ByzEI.mjs +667 -0
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/config/fields.d.ts +1 -1
- package/lib/types/config/settings.d.ts +25 -3
- package/lib/types/config/truncate.d.ts +9 -5
- package/lib/types/tools/bash/background.d.ts +1 -1
- package/lib/types/tools/bash/bash-executor.d.ts +4 -0
- package/lib/types/tools/bash/bash-interceptor.d.ts +10 -8
- package/lib/types/tools/bash/cd-workdir.d.ts +35 -0
- package/lib/types/tools/bash/non-interactive-env.d.ts +2 -0
- package/lib/types/tools/bash/render.d.ts +4 -3
- package/lib/types/tools/bash/sanitize-text.d.ts +5 -0
- package/lib/types/tools/bash/shell-snapshot.d.ts +4 -1
- package/lib/types/tools/bash/shell-tokenize.d.ts +60 -0
- package/lib/types/tools/bash/streaming-output.d.ts +12 -212
- package/lib/types/tools/bash/which.d.ts +20 -0
- package/lib/types/tools/bash/windows-shell.d.ts +31 -0
- package/lib/types/tools/edit/adapter/index.d.ts +25 -3
- package/lib/types/tools/edit/adapter/tools/auto-generated-guard.d.ts +7 -17
- package/lib/types/tools/glob/adapter/index.d.ts +27 -1
- package/lib/types/tools/grep/adapter/index.d.ts +29 -1
- package/lib/types/tools/omp/edit/dispatch.d.ts +43 -0
- package/lib/types/tools/omp/edit/hashline/noop-loop-guard.d.ts +3 -1
- package/lib/types/tools/omp/sdk.d.ts +6 -0
- package/lib/types/tools/omp/tools/auto-generated-guard.d.ts +20 -0
- package/lib/types/tools/omp/tools/fetch.d.ts +2 -0
- package/lib/types/tools/omp/tools/read.d.ts +2 -8
- package/lib/types/tools/omp/tools/write.d.ts +4 -4
- package/lib/types/tools/omp/utils/image-loading.d.ts +10 -36
- package/lib/types/tools/read/adapter/index.d.ts +4 -1
- package/lib/types/tools/read-image/index.d.ts +4 -0
- package/lib/types/tools/shared/auto-generated-guard.dsh.d.ts +16 -4
- package/lib/types/tools/shared/bun-shim.d.ts +39 -3
- package/lib/types/tools/shared/fs-promises-shim.d.ts +3 -1
- package/lib/types/tools/shared/image-bridge.d.ts +98 -0
- package/lib/types/tools/shared/omp-prompt.d.ts +17 -9
- package/lib/types/tools/shared/session-state.d.ts +9 -3
- package/lib/utils-2Wq2eATG.mjs +8119 -0
- package/lib/utils-BnX8O3vi.mjs +12303 -0
- package/lib/utils-Bw139LTz.mjs +8119 -0
- package/lib/utils-CE8N8P4Q.mjs +12315 -0
- package/lib/utils-CZw4tVZQ.mjs +12315 -0
- package/lib/utils-C_MrQ_0z.mjs +12301 -0
- package/lib/utils-CiGN7OHR.mjs +12315 -0
- package/lib/utils-Cp76gCyk.mjs +8119 -0
- package/lib/utils-D0GRDgAr.mjs +12315 -0
- package/lib/utils-D0Z1fyoO.mjs +12299 -0
- package/lib/utils-D2YknRk4.mjs +51 -0
- package/lib/utils-DppGHXQB.mjs +51 -0
- package/lib/utils-DzXZDfYc.mjs +51 -0
- package/lib/utils-W8WdSBpX.mjs +12315 -0
- package/lib/vision-guard-01qSwFpf.mjs +92 -0
- package/lib/vision-guard-BDD4OrKo.mjs +92 -0
- package/lib/vision-guard-DzZGaOga.mjs +92 -0
- package/lib/wafer-CgtPHerf.mjs +17 -0
- package/lib/wafer-DRU0EjZX.mjs +17 -0
- package/lib/wafer-cBtrn6q9.mjs +17 -0
- package/lib/xai-oauth-Buk8XVvM.mjs +446 -0
- package/lib/xai-oauth-CMal8l1O.mjs +446 -0
- package/lib/xai-oauth-DPHDXKD7.mjs +446 -0
- package/lib/xiaomi-CZgwvX2G.mjs +157 -0
- package/lib/xiaomi-D2qT5cAC.mjs +157 -0
- package/lib/xiaomi-Dd87KSg5.mjs +157 -0
- package/lib/zai-7DWMkEqM.mjs +203 -0
- package/lib/zai-C59YnomG.mjs +203 -0
- package/lib/zai-CFeOt-53.mjs +203 -0
- package/lib/zip-Cg9ANN_U.mjs +1316 -0
- package/lib/zip-D36ZKsP8.mjs +1316 -0
- package/lib/zip-D6QXpvaJ.mjs +1316 -0
- package/package.json +14 -10
- package/presets/package.json +1 -1
- package/presets/tool-plus-ptc/preset.yml +1 -1
- package/presets/tool-plus-standard/preset.yml +1 -1
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { installBunShim as __installBunShim } from "./bun-shim.mjs";
|
|
2
|
+
__installBunShim();
|
|
3
|
+
import { D as Cookie, E as $, O as CookieMap, P as $env } from "./src-lDmkSRbm.mjs";
|
|
4
|
+
import { V as ProviderHttpError, _ as OnPromptRequiredError, c as OAuthError, h as LoginCancelledError } from "./error-yCSG0mGC.mjs";
|
|
5
|
+
import * as os from "node:os";
|
|
6
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-ai@17.3.5/node_modules/@oh-my-pi/pi-ai/src/registry/oauth/perplexity.ts
|
|
7
|
+
/**
|
|
8
|
+
* Perplexity login and token refresh.
|
|
9
|
+
*
|
|
10
|
+
* Login paths (in priority order):
|
|
11
|
+
* 1. macOS native app: reads JWT from NSUserDefaults (`defaults read ai.perplexity.mac authToken`)
|
|
12
|
+
* 2. HTTP email OTP: `GET /api/auth/csrf` → `POST /api/auth/signin-email` → `POST /api/auth/signin-otp`
|
|
13
|
+
*
|
|
14
|
+
* No browser or manual cookie paste required.
|
|
15
|
+
* Refresh: Socket.IO `refreshJWT` RPC over authenticated WebSocket connection.
|
|
16
|
+
*
|
|
17
|
+
* Protocol: Engine.IO v4 + Socket.IO v4 over WebSocket (bypasses Cloudflare managed challenge).
|
|
18
|
+
* Architecture reverse-engineered from Perplexity macOS app (ai.perplexity.mac).
|
|
19
|
+
*/
|
|
20
|
+
const API_VERSION = "2.18";
|
|
21
|
+
const NATIVE_APP_BUNDLE = "ai.perplexity.mac";
|
|
22
|
+
const APP_USER_AGENT = "Perplexity/641 CFNetwork/1568 Darwin/25.2.0";
|
|
23
|
+
function serializeCookies(cookies) {
|
|
24
|
+
let header = "";
|
|
25
|
+
for (const [name, value] of cookies) header += `${header ? "; " : ""}${name}=${value}`;
|
|
26
|
+
return header;
|
|
27
|
+
}
|
|
28
|
+
function rememberCookies(cookies, response) {
|
|
29
|
+
for (const setCookie of response.headers.getSetCookie()) {
|
|
30
|
+
const cookie = Cookie.parse(setCookie);
|
|
31
|
+
if (cookie.isExpired()) cookies.delete(cookie.name);
|
|
32
|
+
else cookies.set(cookie.name, cookie.value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Extract expiry from a JWT. Perplexity tokens generally lack an `exp` claim
|
|
37
|
+
* (their sessions are server-side and effectively non-expiring from the client's
|
|
38
|
+
* point of view), so we return a far-future sentinel when no `exp` is present.
|
|
39
|
+
* When `exp` IS present, subtract a 5-minute safety margin.
|
|
40
|
+
*/
|
|
41
|
+
const NEVER_EXPIRES = 864e13;
|
|
42
|
+
function getJwtExpiry(token) {
|
|
43
|
+
try {
|
|
44
|
+
const parts = token.split(".");
|
|
45
|
+
if (parts.length !== 3) return NEVER_EXPIRES;
|
|
46
|
+
const payload = parts[1] ?? "";
|
|
47
|
+
const decoded = JSON.parse(atob(payload.replace(/-/g, "+").replace(/_/g, "/")));
|
|
48
|
+
if (typeof decoded?.exp === "number" && Number.isFinite(decoded.exp)) return decoded.exp * 1e3 - 3e5;
|
|
49
|
+
} catch {}
|
|
50
|
+
return NEVER_EXPIRES;
|
|
51
|
+
}
|
|
52
|
+
/** Build OAuthCredentials from a Perplexity JWT string. */
|
|
53
|
+
function jwtToCredentials(jwt, email) {
|
|
54
|
+
return {
|
|
55
|
+
access: jwt,
|
|
56
|
+
refresh: jwt,
|
|
57
|
+
expires: getJwtExpiry(jwt),
|
|
58
|
+
email
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Read the Perplexity JWT from the native macOS Catalyst app's UserDefaults.
|
|
63
|
+
* Tokens are stored in NSUserDefaults (not Keychain), readable by any same-UID process.
|
|
64
|
+
*/
|
|
65
|
+
async function extractFromNativeApp() {
|
|
66
|
+
if (os.platform() !== "darwin") return null;
|
|
67
|
+
try {
|
|
68
|
+
const result = await $`defaults read ${NATIVE_APP_BUNDLE} authToken`.quiet().nothrow();
|
|
69
|
+
if (result.exitCode !== 0) return null;
|
|
70
|
+
const token = result.text().trim();
|
|
71
|
+
if (!token || token === "(null)") return null;
|
|
72
|
+
return token;
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Send email OTP and exchange it for a Perplexity JWT via HTTP endpoints.
|
|
79
|
+
*/
|
|
80
|
+
async function httpEmailLogin(ctrl) {
|
|
81
|
+
if (!ctrl.onPrompt) throw new OnPromptRequiredError("Perplexity");
|
|
82
|
+
const trimmedEmail = (await ctrl.onPrompt({
|
|
83
|
+
message: "Enter your Perplexity email address",
|
|
84
|
+
placeholder: "user@example.com"
|
|
85
|
+
})).trim();
|
|
86
|
+
if (!trimmedEmail) throw new OAuthError("Email is required for Perplexity login", {
|
|
87
|
+
kind: "validation",
|
|
88
|
+
provider: "perplexity"
|
|
89
|
+
});
|
|
90
|
+
if (ctrl.signal?.aborted) throw new LoginCancelledError();
|
|
91
|
+
const fetchImpl = ctrl.fetch ?? fetch;
|
|
92
|
+
const cookies = new CookieMap();
|
|
93
|
+
const request = async (url, init = {}) => {
|
|
94
|
+
const headers = new Headers(init.headers);
|
|
95
|
+
if (cookies.size > 0) headers.set("Cookie", serializeCookies(cookies));
|
|
96
|
+
const response = await fetchImpl(url, {
|
|
97
|
+
...init,
|
|
98
|
+
headers
|
|
99
|
+
});
|
|
100
|
+
rememberCookies(cookies, response);
|
|
101
|
+
return response;
|
|
102
|
+
};
|
|
103
|
+
ctrl.onProgress?.("Fetching Perplexity CSRF token...");
|
|
104
|
+
const csrfResponse = await request("https://www.perplexity.ai/api/auth/csrf", {
|
|
105
|
+
headers: {
|
|
106
|
+
"User-Agent": APP_USER_AGENT,
|
|
107
|
+
"X-App-ApiVersion": API_VERSION
|
|
108
|
+
},
|
|
109
|
+
signal: ctrl.signal
|
|
110
|
+
});
|
|
111
|
+
if (!csrfResponse.ok) throw new ProviderHttpError(`Perplexity CSRF request failed: ${csrfResponse.status}`, csrfResponse.status);
|
|
112
|
+
const csrfData = await csrfResponse.json();
|
|
113
|
+
if (!csrfData.csrfToken) throw new OAuthError("Perplexity CSRF response missing csrfToken", {
|
|
114
|
+
kind: "validation",
|
|
115
|
+
provider: "perplexity"
|
|
116
|
+
});
|
|
117
|
+
ctrl.onProgress?.("Sending login code to your email...");
|
|
118
|
+
const sendResponse = await request("https://www.perplexity.ai/api/auth/signin-email", {
|
|
119
|
+
method: "POST",
|
|
120
|
+
headers: {
|
|
121
|
+
"Content-Type": "application/json",
|
|
122
|
+
"User-Agent": APP_USER_AGENT,
|
|
123
|
+
"X-App-ApiVersion": API_VERSION
|
|
124
|
+
},
|
|
125
|
+
body: JSON.stringify({
|
|
126
|
+
email: trimmedEmail,
|
|
127
|
+
csrfToken: csrfData.csrfToken
|
|
128
|
+
}),
|
|
129
|
+
signal: ctrl.signal
|
|
130
|
+
});
|
|
131
|
+
if (!sendResponse.ok) {
|
|
132
|
+
const body = await sendResponse.text();
|
|
133
|
+
throw new ProviderHttpError(`Perplexity send login code failed (${sendResponse.status}): ${body}`, sendResponse.status);
|
|
134
|
+
}
|
|
135
|
+
const trimmedOtp = (await ctrl.onPrompt({
|
|
136
|
+
message: "Enter the code sent to your email",
|
|
137
|
+
placeholder: "123456"
|
|
138
|
+
})).trim();
|
|
139
|
+
if (!trimmedOtp) throw new OAuthError("OTP code is required", {
|
|
140
|
+
kind: "validation",
|
|
141
|
+
provider: "perplexity"
|
|
142
|
+
});
|
|
143
|
+
if (ctrl.signal?.aborted) throw new LoginCancelledError();
|
|
144
|
+
ctrl.onProgress?.("Verifying login code...");
|
|
145
|
+
const verifyResponse = await request("https://www.perplexity.ai/api/auth/signin-otp", {
|
|
146
|
+
method: "POST",
|
|
147
|
+
headers: {
|
|
148
|
+
"Content-Type": "application/json",
|
|
149
|
+
"User-Agent": APP_USER_AGENT,
|
|
150
|
+
"X-App-ApiVersion": API_VERSION
|
|
151
|
+
},
|
|
152
|
+
body: JSON.stringify({
|
|
153
|
+
email: trimmedEmail,
|
|
154
|
+
otp: trimmedOtp,
|
|
155
|
+
csrfToken: csrfData.csrfToken
|
|
156
|
+
}),
|
|
157
|
+
signal: ctrl.signal
|
|
158
|
+
});
|
|
159
|
+
const verifyData = await verifyResponse.json();
|
|
160
|
+
if (!verifyResponse.ok) {
|
|
161
|
+
const reason = verifyData.text ?? verifyData.error_code ?? verifyData.status ?? "OTP verification failed";
|
|
162
|
+
throw new OAuthError(`Perplexity OTP verification failed: ${reason}`, {
|
|
163
|
+
kind: "validation",
|
|
164
|
+
provider: "perplexity",
|
|
165
|
+
status: verifyResponse.status
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (!verifyData.token) throw new OAuthError("Perplexity OTP verification response missing token", {
|
|
169
|
+
kind: "validation",
|
|
170
|
+
provider: "perplexity"
|
|
171
|
+
});
|
|
172
|
+
return jwtToCredentials(verifyData.token, trimmedEmail);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Login to Perplexity.
|
|
176
|
+
*
|
|
177
|
+
* Tries auto-extraction from the desktop app, then runs HTTP email OTP login.
|
|
178
|
+
*
|
|
179
|
+
* No browser/manual token paste fallback is used.
|
|
180
|
+
*/
|
|
181
|
+
async function loginPerplexity(ctrl) {
|
|
182
|
+
if (!ctrl.onPrompt) throw new OnPromptRequiredError("Perplexity");
|
|
183
|
+
if (!$env.PI_AUTH_NO_BORROW) {
|
|
184
|
+
ctrl.onProgress?.("Checking for Perplexity desktop app...");
|
|
185
|
+
const nativeJwt = await extractFromNativeApp();
|
|
186
|
+
if (nativeJwt) {
|
|
187
|
+
ctrl.onProgress?.("Found Perplexity JWT from native app");
|
|
188
|
+
return jwtToCredentials(nativeJwt);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return httpEmailLogin(ctrl);
|
|
192
|
+
}
|
|
193
|
+
//#endregion
|
|
194
|
+
export { loginPerplexity };
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { installBunShim as __installBunShim } from "./bun-shim.mjs";
|
|
2
|
+
__installBunShim();
|
|
3
|
+
import { n as StreamTimeoutError, r as ValidationError, v as AbortError } from "./error-yCSG0mGC.mjs";
|
|
4
|
+
import * as tls from "node:tls";
|
|
5
|
+
import * as net from "node:net";
|
|
6
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-ai@17.3.5/node_modules/@oh-my-pi/pi-ai/src/utils/proxy.ts
|
|
7
|
+
/**
|
|
8
|
+
* Checks if a host is local or cloud metadata, which should always bypass the proxy
|
|
9
|
+
* (e.g. localhost, 127/8, ::1, 169.254.169.254, metadata.google.internal).
|
|
10
|
+
*/
|
|
11
|
+
function isLocalOrMetadataHost(host) {
|
|
12
|
+
const lowerHost = host.toLowerCase();
|
|
13
|
+
if (lowerHost === "localhost" || lowerHost.endsWith(".localhost") || lowerHost === "metadata.google.internal") return true;
|
|
14
|
+
const ip = lowerHost.replace(/^\[|\]$/g, "");
|
|
15
|
+
const v4 = ip.match(/^(\d{1,3})\.(\d{1,3})\.\d{1,3}\.\d{1,3}$/);
|
|
16
|
+
if (v4) {
|
|
17
|
+
const a = Number(v4[1]);
|
|
18
|
+
const b = Number(v4[2]);
|
|
19
|
+
if (a === 127 || a === 10 || a === 0) return true;
|
|
20
|
+
if (a === 169 && b === 254) return true;
|
|
21
|
+
if (a === 192 && b === 168) return true;
|
|
22
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
if (ip === "::1" || ip === "::") return true;
|
|
26
|
+
if (/^fe[89ab][0-9a-f]:/.test(ip)) return true;
|
|
27
|
+
if (/^f[cd][0-9a-f]{2}:/.test(ip)) return true;
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check if the url should bypass the proxy due to hard-coded localhost/metadata checks
|
|
32
|
+
* or custom NO_PROXY/no_proxy environment variables rules.
|
|
33
|
+
*/
|
|
34
|
+
function shouldBypassProxy(urlObj) {
|
|
35
|
+
if (isLocalOrMetadataHost(urlObj.hostname)) return true;
|
|
36
|
+
const noProxyVal = Bun.env.NO_PROXY || Bun.env.no_proxy;
|
|
37
|
+
if (!noProxyVal) return false;
|
|
38
|
+
const rules = noProxyVal.split(/[,\s]+/).map((r) => r.trim()).filter(Boolean);
|
|
39
|
+
const targetHost = urlObj.hostname.toLowerCase();
|
|
40
|
+
const targetPort = urlObj.port || (urlObj.protocol === "https:" || urlObj.protocol === "wss:" ? "443" : "80");
|
|
41
|
+
for (const rule of rules) {
|
|
42
|
+
if (rule === "*") return true;
|
|
43
|
+
let ruleHost = rule.toLowerCase();
|
|
44
|
+
let rulePort;
|
|
45
|
+
if (ruleHost.includes("]:")) {
|
|
46
|
+
const lastColon = ruleHost.lastIndexOf(":");
|
|
47
|
+
rulePort = ruleHost.slice(lastColon + 1);
|
|
48
|
+
ruleHost = ruleHost.slice(0, lastColon);
|
|
49
|
+
} else if (!ruleHost.includes("]") && ruleHost.includes(":")) {
|
|
50
|
+
const lastColon = ruleHost.lastIndexOf(":");
|
|
51
|
+
rulePort = ruleHost.slice(lastColon + 1);
|
|
52
|
+
ruleHost = ruleHost.slice(0, lastColon);
|
|
53
|
+
}
|
|
54
|
+
ruleHost = ruleHost.replace(/^\[|\]$/g, "");
|
|
55
|
+
if (rulePort && rulePort !== targetPort) continue;
|
|
56
|
+
if (ruleHost.startsWith(".")) {
|
|
57
|
+
const suffix = ruleHost;
|
|
58
|
+
if (targetHost === ruleHost.slice(1) || targetHost.endsWith(suffix)) return true;
|
|
59
|
+
} else if (targetHost === ruleHost || targetHost.endsWith(`.${ruleHost}`)) return true;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const proxyCache = /* @__PURE__ */ new Map();
|
|
64
|
+
/**
|
|
65
|
+
* Normalizes provider id (e.g. github-copilot -> PI_PROXY_GITHUB_COPILOT) and looks it up.
|
|
66
|
+
* If not found, falls back to PI_PROXY. Results are memoized because env values are static
|
|
67
|
+
* for the lifetime of the process and this function is called for every outgoing request.
|
|
68
|
+
*/
|
|
69
|
+
function getProxyForProvider(provider) {
|
|
70
|
+
if (proxyCache.has(provider)) return proxyCache.get(provider);
|
|
71
|
+
const envKey = `PI_PROXY_${provider.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
72
|
+
const value = Bun.env[envKey] || Bun.env.PI_PROXY;
|
|
73
|
+
proxyCache.set(provider, value);
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
/** Resolves provider-specific and standard proxy variables for a target URL, honoring NO_PROXY. */
|
|
77
|
+
function getProxyForUrl(provider, url) {
|
|
78
|
+
if (shouldBypassProxy(url)) return void 0;
|
|
79
|
+
const protocolProxy = url.protocol === "https:" || url.protocol === "wss:" ? Bun.env.HTTPS_PROXY || Bun.env.https_proxy : Bun.env.HTTP_PROXY || Bun.env.http_proxy;
|
|
80
|
+
return getProxyForProvider(provider) || protocolProxy || Bun.env.ALL_PROXY || Bun.env.all_proxy || void 0;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Tunnel a socket connection through an HTTP CONNECT proxy.
|
|
84
|
+
* This is used specifically to wrap Node's `http2.connect(baseUrl, { createConnection })` for Cursor.
|
|
85
|
+
*/
|
|
86
|
+
async function connectProxiedSocket(proxyUrlStr, targetUrlStr, options) {
|
|
87
|
+
if (options?.signal?.aborted) throw new AbortError("Proxy tunnel aborted");
|
|
88
|
+
const proxyUrl = new URL(proxyUrlStr);
|
|
89
|
+
const targetUrl = new URL(targetUrlStr);
|
|
90
|
+
const useProxySsl = proxyUrl.protocol === "https:";
|
|
91
|
+
const proxyPort = proxyUrl.port ? parseInt(proxyUrl.port, 10) : useProxySsl ? 443 : 80;
|
|
92
|
+
const proxyHost = proxyUrl.hostname;
|
|
93
|
+
const targetPort = targetUrl.port ? parseInt(targetUrl.port, 10) : 443;
|
|
94
|
+
const targetHost = targetUrl.hostname;
|
|
95
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
96
|
+
const readyEvent = useProxySsl ? "secureConnect" : "connect";
|
|
97
|
+
let rawSocket;
|
|
98
|
+
let tunnelSocket;
|
|
99
|
+
let timeout;
|
|
100
|
+
let responseData = "";
|
|
101
|
+
let settled = false;
|
|
102
|
+
const cleanup = () => {
|
|
103
|
+
if (timeout) {
|
|
104
|
+
clearTimeout(timeout);
|
|
105
|
+
timeout = void 0;
|
|
106
|
+
}
|
|
107
|
+
options?.signal?.removeEventListener("abort", onAbort);
|
|
108
|
+
rawSocket?.off("error", onRawError);
|
|
109
|
+
rawSocket?.off(readyEvent, onProxyReady);
|
|
110
|
+
rawSocket?.off("data", onProxyData);
|
|
111
|
+
tunnelSocket?.off("secureConnect", onTunnelReady);
|
|
112
|
+
tunnelSocket?.off("error", onTunnelError);
|
|
113
|
+
};
|
|
114
|
+
const destroyInProgress = () => {
|
|
115
|
+
tunnelSocket?.destroy();
|
|
116
|
+
rawSocket?.destroy();
|
|
117
|
+
};
|
|
118
|
+
const rejectOnce = (error) => {
|
|
119
|
+
if (settled) return;
|
|
120
|
+
settled = true;
|
|
121
|
+
cleanup();
|
|
122
|
+
destroyInProgress();
|
|
123
|
+
reject(error);
|
|
124
|
+
};
|
|
125
|
+
const resolveOnce = (socket) => {
|
|
126
|
+
if (settled) return;
|
|
127
|
+
settled = true;
|
|
128
|
+
cleanup();
|
|
129
|
+
resolve(socket);
|
|
130
|
+
};
|
|
131
|
+
const onAbort = () => rejectOnce(new AbortError("Proxy tunnel aborted"));
|
|
132
|
+
const onRawError = (error) => rejectOnce(error);
|
|
133
|
+
const onTunnelError = (error) => rejectOnce(error);
|
|
134
|
+
const onTunnelReady = () => {
|
|
135
|
+
if (!tunnelSocket) return;
|
|
136
|
+
resolveOnce(tunnelSocket);
|
|
137
|
+
};
|
|
138
|
+
const onProxyData = (chunk) => {
|
|
139
|
+
if (!rawSocket) return;
|
|
140
|
+
responseData += chunk.toString("binary");
|
|
141
|
+
if (!responseData.includes("\r\n\r\n")) return;
|
|
142
|
+
rawSocket.off("data", onProxyData);
|
|
143
|
+
rawSocket.off("error", onRawError);
|
|
144
|
+
const firstLine = responseData.split("\r\n")[0];
|
|
145
|
+
if (!firstLine.includes(" 200 ")) {
|
|
146
|
+
rejectOnce(new ValidationError(`Proxy tunnel failed: ${firstLine}`));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const tlsOptions = options?.tls;
|
|
150
|
+
tunnelSocket = tls.connect({
|
|
151
|
+
...tlsOptions,
|
|
152
|
+
socket: rawSocket,
|
|
153
|
+
servername: tlsOptions?.servername ?? targetHost,
|
|
154
|
+
ALPNProtocols: tlsOptions?.ALPNProtocols ?? ["h2"]
|
|
155
|
+
});
|
|
156
|
+
tunnelSocket.once("secureConnect", onTunnelReady);
|
|
157
|
+
tunnelSocket.once("error", onTunnelError);
|
|
158
|
+
};
|
|
159
|
+
const onProxyReady = () => {
|
|
160
|
+
if (!rawSocket) return;
|
|
161
|
+
let connectReq = `CONNECT ${targetHost}:${targetPort} HTTP/1.1\r\nHost: ${targetHost}:${targetPort}\r\n`;
|
|
162
|
+
if (proxyUrl.username || proxyUrl.password) {
|
|
163
|
+
const creds = Buffer.from(`${decodeURIComponent(proxyUrl.username)}:${decodeURIComponent(proxyUrl.password)}`).toString("base64");
|
|
164
|
+
connectReq += `Proxy-Authorization: Basic ${creds}\r\n`;
|
|
165
|
+
}
|
|
166
|
+
connectReq += "\r\n";
|
|
167
|
+
rawSocket.write(connectReq);
|
|
168
|
+
rawSocket.on("data", onProxyData);
|
|
169
|
+
};
|
|
170
|
+
options?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
171
|
+
if (options?.timeoutMs !== void 0 && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0) {
|
|
172
|
+
const timeoutMs = Math.trunc(options.timeoutMs);
|
|
173
|
+
timeout = setTimeout(() => {
|
|
174
|
+
rejectOnce(new StreamTimeoutError(`Proxy tunnel timed out after ${timeoutMs}ms`));
|
|
175
|
+
}, timeoutMs);
|
|
176
|
+
timeout.unref?.();
|
|
177
|
+
}
|
|
178
|
+
rawSocket = useProxySsl ? tls.connect({
|
|
179
|
+
host: proxyHost,
|
|
180
|
+
port: proxyPort
|
|
181
|
+
}) : net.connect({
|
|
182
|
+
host: proxyHost,
|
|
183
|
+
port: proxyPort
|
|
184
|
+
});
|
|
185
|
+
rawSocket.once("error", onRawError);
|
|
186
|
+
rawSocket.once(readyEvent, onProxyReady);
|
|
187
|
+
return promise;
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
export { shouldBypassProxy as a, isLocalOrMetadataHost as i, getProxyForProvider as n, getProxyForUrl as r, connectProxiedSocket as t };
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { installBunShim as __installBunShim } from "./bun-shim.mjs";
|
|
2
|
+
__installBunShim();
|
|
3
|
+
import { n as StreamTimeoutError, r as ValidationError, v as AbortError } from "./error-B3bevhrs.mjs";
|
|
4
|
+
import * as tls from "node:tls";
|
|
5
|
+
import * as net from "node:net";
|
|
6
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-ai@17.3.5/node_modules/@oh-my-pi/pi-ai/src/utils/proxy.ts
|
|
7
|
+
/**
|
|
8
|
+
* Checks if a host is local or cloud metadata, which should always bypass the proxy
|
|
9
|
+
* (e.g. localhost, 127/8, ::1, 169.254.169.254, metadata.google.internal).
|
|
10
|
+
*/
|
|
11
|
+
function isLocalOrMetadataHost(host) {
|
|
12
|
+
const lowerHost = host.toLowerCase();
|
|
13
|
+
if (lowerHost === "localhost" || lowerHost.endsWith(".localhost") || lowerHost === "metadata.google.internal") return true;
|
|
14
|
+
const ip = lowerHost.replace(/^\[|\]$/g, "");
|
|
15
|
+
const v4 = ip.match(/^(\d{1,3})\.(\d{1,3})\.\d{1,3}\.\d{1,3}$/);
|
|
16
|
+
if (v4) {
|
|
17
|
+
const a = Number(v4[1]);
|
|
18
|
+
const b = Number(v4[2]);
|
|
19
|
+
if (a === 127 || a === 10 || a === 0) return true;
|
|
20
|
+
if (a === 169 && b === 254) return true;
|
|
21
|
+
if (a === 192 && b === 168) return true;
|
|
22
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
if (ip === "::1" || ip === "::") return true;
|
|
26
|
+
if (/^fe[89ab][0-9a-f]:/.test(ip)) return true;
|
|
27
|
+
if (/^f[cd][0-9a-f]{2}:/.test(ip)) return true;
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check if the url should bypass the proxy due to hard-coded localhost/metadata checks
|
|
32
|
+
* or custom NO_PROXY/no_proxy environment variables rules.
|
|
33
|
+
*/
|
|
34
|
+
function shouldBypassProxy(urlObj) {
|
|
35
|
+
if (isLocalOrMetadataHost(urlObj.hostname)) return true;
|
|
36
|
+
const noProxyVal = Bun.env.NO_PROXY || Bun.env.no_proxy;
|
|
37
|
+
if (!noProxyVal) return false;
|
|
38
|
+
const rules = noProxyVal.split(/[,\s]+/).map((r) => r.trim()).filter(Boolean);
|
|
39
|
+
const targetHost = urlObj.hostname.toLowerCase();
|
|
40
|
+
const targetPort = urlObj.port || (urlObj.protocol === "https:" || urlObj.protocol === "wss:" ? "443" : "80");
|
|
41
|
+
for (const rule of rules) {
|
|
42
|
+
if (rule === "*") return true;
|
|
43
|
+
let ruleHost = rule.toLowerCase();
|
|
44
|
+
let rulePort;
|
|
45
|
+
if (ruleHost.includes("]:")) {
|
|
46
|
+
const lastColon = ruleHost.lastIndexOf(":");
|
|
47
|
+
rulePort = ruleHost.slice(lastColon + 1);
|
|
48
|
+
ruleHost = ruleHost.slice(0, lastColon);
|
|
49
|
+
} else if (!ruleHost.includes("]") && ruleHost.includes(":")) {
|
|
50
|
+
const lastColon = ruleHost.lastIndexOf(":");
|
|
51
|
+
rulePort = ruleHost.slice(lastColon + 1);
|
|
52
|
+
ruleHost = ruleHost.slice(0, lastColon);
|
|
53
|
+
}
|
|
54
|
+
ruleHost = ruleHost.replace(/^\[|\]$/g, "");
|
|
55
|
+
if (rulePort && rulePort !== targetPort) continue;
|
|
56
|
+
if (ruleHost.startsWith(".")) {
|
|
57
|
+
const suffix = ruleHost;
|
|
58
|
+
if (targetHost === ruleHost.slice(1) || targetHost.endsWith(suffix)) return true;
|
|
59
|
+
} else if (targetHost === ruleHost || targetHost.endsWith(`.${ruleHost}`)) return true;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const proxyCache = /* @__PURE__ */ new Map();
|
|
64
|
+
/**
|
|
65
|
+
* Normalizes provider id (e.g. github-copilot -> PI_PROXY_GITHUB_COPILOT) and looks it up.
|
|
66
|
+
* If not found, falls back to PI_PROXY. Results are memoized because env values are static
|
|
67
|
+
* for the lifetime of the process and this function is called for every outgoing request.
|
|
68
|
+
*/
|
|
69
|
+
function getProxyForProvider(provider) {
|
|
70
|
+
if (proxyCache.has(provider)) return proxyCache.get(provider);
|
|
71
|
+
const envKey = `PI_PROXY_${provider.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
72
|
+
const value = Bun.env[envKey] || Bun.env.PI_PROXY;
|
|
73
|
+
proxyCache.set(provider, value);
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
/** Resolves provider-specific and standard proxy variables for a target URL, honoring NO_PROXY. */
|
|
77
|
+
function getProxyForUrl(provider, url) {
|
|
78
|
+
if (shouldBypassProxy(url)) return void 0;
|
|
79
|
+
const protocolProxy = url.protocol === "https:" || url.protocol === "wss:" ? Bun.env.HTTPS_PROXY || Bun.env.https_proxy : Bun.env.HTTP_PROXY || Bun.env.http_proxy;
|
|
80
|
+
return getProxyForProvider(provider) || protocolProxy || Bun.env.ALL_PROXY || Bun.env.all_proxy || void 0;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Tunnel a socket connection through an HTTP CONNECT proxy.
|
|
84
|
+
* This is used specifically to wrap Node's `http2.connect(baseUrl, { createConnection })` for Cursor.
|
|
85
|
+
*/
|
|
86
|
+
async function connectProxiedSocket(proxyUrlStr, targetUrlStr, options) {
|
|
87
|
+
if (options?.signal?.aborted) throw new AbortError("Proxy tunnel aborted");
|
|
88
|
+
const proxyUrl = new URL(proxyUrlStr);
|
|
89
|
+
const targetUrl = new URL(targetUrlStr);
|
|
90
|
+
const useProxySsl = proxyUrl.protocol === "https:";
|
|
91
|
+
const proxyPort = proxyUrl.port ? parseInt(proxyUrl.port, 10) : useProxySsl ? 443 : 80;
|
|
92
|
+
const proxyHost = proxyUrl.hostname;
|
|
93
|
+
const targetPort = targetUrl.port ? parseInt(targetUrl.port, 10) : 443;
|
|
94
|
+
const targetHost = targetUrl.hostname;
|
|
95
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
96
|
+
const readyEvent = useProxySsl ? "secureConnect" : "connect";
|
|
97
|
+
let rawSocket;
|
|
98
|
+
let tunnelSocket;
|
|
99
|
+
let timeout;
|
|
100
|
+
let responseData = "";
|
|
101
|
+
let settled = false;
|
|
102
|
+
const cleanup = () => {
|
|
103
|
+
if (timeout) {
|
|
104
|
+
clearTimeout(timeout);
|
|
105
|
+
timeout = void 0;
|
|
106
|
+
}
|
|
107
|
+
options?.signal?.removeEventListener("abort", onAbort);
|
|
108
|
+
rawSocket?.off("error", onRawError);
|
|
109
|
+
rawSocket?.off(readyEvent, onProxyReady);
|
|
110
|
+
rawSocket?.off("data", onProxyData);
|
|
111
|
+
tunnelSocket?.off("secureConnect", onTunnelReady);
|
|
112
|
+
tunnelSocket?.off("error", onTunnelError);
|
|
113
|
+
};
|
|
114
|
+
const destroyInProgress = () => {
|
|
115
|
+
tunnelSocket?.destroy();
|
|
116
|
+
rawSocket?.destroy();
|
|
117
|
+
};
|
|
118
|
+
const rejectOnce = (error) => {
|
|
119
|
+
if (settled) return;
|
|
120
|
+
settled = true;
|
|
121
|
+
cleanup();
|
|
122
|
+
destroyInProgress();
|
|
123
|
+
reject(error);
|
|
124
|
+
};
|
|
125
|
+
const resolveOnce = (socket) => {
|
|
126
|
+
if (settled) return;
|
|
127
|
+
settled = true;
|
|
128
|
+
cleanup();
|
|
129
|
+
resolve(socket);
|
|
130
|
+
};
|
|
131
|
+
const onAbort = () => rejectOnce(new AbortError("Proxy tunnel aborted"));
|
|
132
|
+
const onRawError = (error) => rejectOnce(error);
|
|
133
|
+
const onTunnelError = (error) => rejectOnce(error);
|
|
134
|
+
const onTunnelReady = () => {
|
|
135
|
+
if (!tunnelSocket) return;
|
|
136
|
+
resolveOnce(tunnelSocket);
|
|
137
|
+
};
|
|
138
|
+
const onProxyData = (chunk) => {
|
|
139
|
+
if (!rawSocket) return;
|
|
140
|
+
responseData += chunk.toString("binary");
|
|
141
|
+
if (!responseData.includes("\r\n\r\n")) return;
|
|
142
|
+
rawSocket.off("data", onProxyData);
|
|
143
|
+
rawSocket.off("error", onRawError);
|
|
144
|
+
const firstLine = responseData.split("\r\n")[0];
|
|
145
|
+
if (!firstLine.includes(" 200 ")) {
|
|
146
|
+
rejectOnce(new ValidationError(`Proxy tunnel failed: ${firstLine}`));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const tlsOptions = options?.tls;
|
|
150
|
+
tunnelSocket = tls.connect({
|
|
151
|
+
...tlsOptions,
|
|
152
|
+
socket: rawSocket,
|
|
153
|
+
servername: tlsOptions?.servername ?? targetHost,
|
|
154
|
+
ALPNProtocols: tlsOptions?.ALPNProtocols ?? ["h2"]
|
|
155
|
+
});
|
|
156
|
+
tunnelSocket.once("secureConnect", onTunnelReady);
|
|
157
|
+
tunnelSocket.once("error", onTunnelError);
|
|
158
|
+
};
|
|
159
|
+
const onProxyReady = () => {
|
|
160
|
+
if (!rawSocket) return;
|
|
161
|
+
let connectReq = `CONNECT ${targetHost}:${targetPort} HTTP/1.1\r\nHost: ${targetHost}:${targetPort}\r\n`;
|
|
162
|
+
if (proxyUrl.username || proxyUrl.password) {
|
|
163
|
+
const creds = Buffer.from(`${decodeURIComponent(proxyUrl.username)}:${decodeURIComponent(proxyUrl.password)}`).toString("base64");
|
|
164
|
+
connectReq += `Proxy-Authorization: Basic ${creds}\r\n`;
|
|
165
|
+
}
|
|
166
|
+
connectReq += "\r\n";
|
|
167
|
+
rawSocket.write(connectReq);
|
|
168
|
+
rawSocket.on("data", onProxyData);
|
|
169
|
+
};
|
|
170
|
+
options?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
171
|
+
if (options?.timeoutMs !== void 0 && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0) {
|
|
172
|
+
const timeoutMs = Math.trunc(options.timeoutMs);
|
|
173
|
+
timeout = setTimeout(() => {
|
|
174
|
+
rejectOnce(new StreamTimeoutError(`Proxy tunnel timed out after ${timeoutMs}ms`));
|
|
175
|
+
}, timeoutMs);
|
|
176
|
+
timeout.unref?.();
|
|
177
|
+
}
|
|
178
|
+
rawSocket = useProxySsl ? tls.connect({
|
|
179
|
+
host: proxyHost,
|
|
180
|
+
port: proxyPort
|
|
181
|
+
}) : net.connect({
|
|
182
|
+
host: proxyHost,
|
|
183
|
+
port: proxyPort
|
|
184
|
+
});
|
|
185
|
+
rawSocket.once("error", onRawError);
|
|
186
|
+
rawSocket.once(readyEvent, onProxyReady);
|
|
187
|
+
return promise;
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
export { shouldBypassProxy as a, isLocalOrMetadataHost as i, getProxyForProvider as n, getProxyForUrl as r, connectProxiedSocket as t };
|