@lightharu/krouter 1.8.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.
Files changed (61) hide show
  1. package/LICENSE +679 -0
  2. package/README.md +238 -0
  3. package/dist-web/assets/index-CM4-0adf.css +1 -0
  4. package/dist-web/assets/index-DCslvfUR.js +139 -0
  5. package/dist-web/favicon.svg +9 -0
  6. package/dist-web/icon.svg +9 -0
  7. package/dist-web/index.html +19 -0
  8. package/out-server/main/kiroAuthSync.js +249 -0
  9. package/out-server/main/kproxy/certManager.js +262 -0
  10. package/out-server/main/kproxy/index.js +254 -0
  11. package/out-server/main/kproxy/mitmProxy.js +475 -0
  12. package/out-server/main/kproxy/types.js +23 -0
  13. package/out-server/main/proxy/accountPool.js +543 -0
  14. package/out-server/main/proxy/clientConfig.js +596 -0
  15. package/out-server/main/proxy/index.js +25 -0
  16. package/out-server/main/proxy/kiroApi.js +1996 -0
  17. package/out-server/main/proxy/logger.js +407 -0
  18. package/out-server/main/proxy/modelCatalog.js +75 -0
  19. package/out-server/main/proxy/promptCacheTracker.js +301 -0
  20. package/out-server/main/proxy/proxyServer.js +3543 -0
  21. package/out-server/main/proxy/selfSignedCert.js +179 -0
  22. package/out-server/main/proxy/systemProxy.js +250 -0
  23. package/out-server/main/proxy/tokenCounter.js +164 -0
  24. package/out-server/main/proxy/toolNameRegistry.js +57 -0
  25. package/out-server/main/proxy/translator.js +1084 -0
  26. package/out-server/main/proxy/types.js +3 -0
  27. package/out-server/main/registration/browser-identity.js +184 -0
  28. package/out-server/main/registration/chainProxy.js +349 -0
  29. package/out-server/main/registration/config.js +58 -0
  30. package/out-server/main/registration/email-service.js +801 -0
  31. package/out-server/main/registration/fingerprint.js +352 -0
  32. package/out-server/main/registration/http-utils.js +148 -0
  33. package/out-server/main/registration/jwe.js +74 -0
  34. package/out-server/main/registration/names.js +142 -0
  35. package/out-server/main/registration/proton-mail-window.js +339 -0
  36. package/out-server/main/registration/registrar.js +1715 -0
  37. package/out-server/main/registration/tlsClientPool.js +70 -0
  38. package/out-server/main/registration/xxtea.js +161 -0
  39. package/out-server/main/runtimePaths.js +19 -0
  40. package/out-server/main/utils/redact.js +95 -0
  41. package/out-server/server/index.js +1272 -0
  42. package/out-server/server/services/accountExtras.js +105 -0
  43. package/out-server/server/services/accountProfileHydration.js +95 -0
  44. package/out-server/server/services/authFlows.js +509 -0
  45. package/out-server/server/services/dashboardTunnel.js +315 -0
  46. package/out-server/server/services/diagnostics.js +326 -0
  47. package/out-server/server/services/kiroAccounts.js +431 -0
  48. package/out-server/server/services/kiroSettings.js +260 -0
  49. package/out-server/server/services/kproxyRuntime.js +264 -0
  50. package/out-server/server/services/localKiroCredentials.js +320 -0
  51. package/out-server/server/services/machineIdRuntime.js +327 -0
  52. package/out-server/server/services/protonBrowserRuntime.js +724 -0
  53. package/out-server/server/services/proxyRuntime.js +523 -0
  54. package/out-server/server/services/registrationRuntime.js +106 -0
  55. package/out-server/server/store.js +266 -0
  56. package/package.json +113 -0
  57. package/resources/tls-client-xgo-1.14.0-windows-amd64.dll +0 -0
  58. package/scripts/kiro-manager-cli.cjs +3 -0
  59. package/scripts/krouter-cli.cjs +509 -0
  60. package/src/renderer/src/assets/krouter-logo.svg +11 -0
  61. package/src/renderer/src/assets/krouter-mark.svg +9 -0
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ // Kiro Proxy 类型定义
3
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.randomFullName = void 0;
7
+ exports.randomIdentity = randomIdentity;
8
+ const crypto_1 = __importDefault(require("crypto"));
9
+ const LSUBID_PREFIXES = ['X10', 'X19', 'X42', 'X55', 'X73', 'X81', 'X96'];
10
+ const GPU_CONFIGS = [
11
+ { vendor: 'Google Inc. (Intel)', model: 'ANGLE (Intel, Intel(R) Iris(R) Xe Graphics (0x000046A6) Direct3D11 vs_5_0 ps_5_0, D3D11)' },
12
+ { vendor: 'Google Inc. (Intel)', model: 'ANGLE (Intel, Intel(R) UHD Graphics 630 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
13
+ { vendor: 'Google Inc. (Intel)', model: 'ANGLE (Intel, Intel(R) UHD Graphics 770 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
14
+ { vendor: 'Google Inc. (Intel)', model: 'ANGLE (Intel, Intel(R) UHD Graphics 730 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
15
+ { vendor: 'Google Inc. (Intel)', model: 'ANGLE (Intel, Intel(R) HD Graphics 620 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
16
+ { vendor: 'Google Inc. (Intel)', model: 'ANGLE (Intel, Intel(R) HD Graphics 530 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
17
+ { vendor: 'Google Inc. (Intel)', model: 'ANGLE (Intel, Intel(R) Iris(R) Plus Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)' },
18
+ { vendor: 'Google Inc. (NVIDIA)', model: 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1060 6GB Direct3D11 vs_5_0 ps_5_0, D3D11)' },
19
+ { vendor: 'Google Inc. (NVIDIA)', model: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
20
+ { vendor: 'Google Inc. (NVIDIA)', model: 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1650 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
21
+ { vendor: 'Google Inc. (NVIDIA)', model: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 2060 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
22
+ { vendor: 'Google Inc. (NVIDIA)', model: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3070 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
23
+ { vendor: 'Google Inc. (NVIDIA)', model: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
24
+ { vendor: 'Google Inc. (NVIDIA)', model: 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1080 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)' },
25
+ { vendor: 'Google Inc. (NVIDIA)', model: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3080 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
26
+ { vendor: 'Google Inc. (NVIDIA)', model: 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1070 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
27
+ { vendor: 'Google Inc. (NVIDIA)', model: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
28
+ { vendor: 'Google Inc. (AMD)', model: 'ANGLE (AMD, AMD Radeon RX 580 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
29
+ { vendor: 'Google Inc. (AMD)', model: 'ANGLE (AMD, AMD Radeon RX 6600 XT Direct3D11 vs_5_0 ps_5_0, D3D11)' },
30
+ { vendor: 'Google Inc. (AMD)', model: 'ANGLE (AMD, AMD Radeon RX 5700 XT Direct3D11 vs_5_0 ps_5_0, D3D11)' },
31
+ { vendor: 'Google Inc. (AMD)', model: 'ANGLE (AMD, AMD Radeon RX 6700 XT Direct3D11 vs_5_0 ps_5_0, D3D11)' },
32
+ { vendor: 'Google Inc. (AMD)', model: 'ANGLE (AMD, AMD Radeon RX 570 Direct3D11 vs_5_0 ps_5_0, D3D11)' }
33
+ ];
34
+ const SCREEN_CONFIGS = [
35
+ [1920, 1080, 1920, 1040, 24], [2560, 1440, 2560, 1400, 24],
36
+ [1920, 1200, 1920, 1160, 24], [1366, 768, 1366, 728, 24],
37
+ [1536, 864, 1536, 824, 24], [1680, 1050, 1680, 1010, 24],
38
+ [1440, 900, 1440, 860, 24], [1600, 900, 1600, 860, 24],
39
+ [2560, 1080, 2560, 1040, 24], [3440, 1440, 3440, 1400, 24],
40
+ [3840, 2160, 3840, 2120, 24], [1280, 1024, 1280, 984, 24]
41
+ ];
42
+ const MATH_POOL = [
43
+ { tan: '-1.4214488238747245', sin: '0.8178819121159085', cos: '-0.5753861119575491' },
44
+ { tan: '-1.4214488238747245', sin: '0.8178819121159085', cos: '-0.5765775004286854' },
45
+ { tan: '-1.4214488238747243', sin: '0.8178819121159083', cos: '-0.5753861119575489' },
46
+ { tan: '-1.4214488238747247', sin: '0.8178819121159087', cos: '-0.5753861119575493' },
47
+ { tan: '-1.4214488238747244', sin: '0.8178819121159084', cos: '-0.5765775004286855' },
48
+ { tan: '-1.4214488238747246', sin: '0.8178819121159086', cos: '-0.5753861119575490' },
49
+ { tan: '-1.4214488238747242', sin: '0.8178819121159082', cos: '-0.5765775004286853' },
50
+ { tan: '-1.4214488238747248', sin: '0.8178819121159088', cos: '-0.5753861119575492' },
51
+ { tan: '-1.4214488238747241', sin: '0.8178819121159081', cos: '-0.5765775004286852' },
52
+ { tan: '-1.4214488238747249', sin: '0.8178819121159089', cos: '-0.5753861119575494' }
53
+ ];
54
+ const WEBGL_EXT_CORE = [
55
+ 'ANGLE_instanced_arrays', 'EXT_blend_minmax', 'EXT_color_buffer_half_float',
56
+ 'EXT_float_blend', 'EXT_frag_depth', 'EXT_shader_texture_lod',
57
+ 'EXT_texture_filter_anisotropic', 'EXT_sRGB', 'KHR_parallel_shader_compile',
58
+ 'OES_element_index_uint', 'OES_fbo_render_mipmap', 'OES_standard_derivatives',
59
+ 'OES_texture_float', 'OES_texture_float_linear', 'OES_texture_half_float',
60
+ 'OES_texture_half_float_linear', 'OES_vertex_array_object',
61
+ 'WEBGL_color_buffer_float', 'WEBGL_compressed_texture_s3tc',
62
+ 'WEBGL_compressed_texture_s3tc_srgb', 'WEBGL_debug_renderer_info',
63
+ 'WEBGL_debug_shaders', 'WEBGL_depth_texture', 'WEBGL_draw_buffers',
64
+ 'WEBGL_lose_context', 'WEBGL_multi_draw'
65
+ ];
66
+ const WEBGL_EXT_OPTIONAL = [
67
+ 'EXT_disjoint_timer_query', 'EXT_texture_compression_bptc',
68
+ 'EXT_texture_compression_rgtc', 'WEBGL_compressed_texture_astc',
69
+ 'WEBGL_compressed_texture_etc', 'OES_draw_buffers_indexed',
70
+ 'EXT_color_buffer_float'
71
+ ];
72
+ const PLUGINS_POOL = [
73
+ { name: 'PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
74
+ { name: 'Chrome PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
75
+ { name: 'Chromium PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
76
+ { name: 'Microsoft Edge PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
77
+ { name: 'WebKit built-in PDF', filename: 'internal-pdf-viewer', description: 'Portable Document Format' }
78
+ ];
79
+ function randInt(max) {
80
+ return Math.floor(Math.random() * max);
81
+ }
82
+ function pick(arr) {
83
+ return arr[randInt(arr.length)];
84
+ }
85
+ function shuffle(arr) {
86
+ for (let i = arr.length - 1; i > 0; i--) {
87
+ const j = randInt(i + 1);
88
+ [arr[i], arr[j]] = [arr[j], arr[i]];
89
+ }
90
+ return arr;
91
+ }
92
+ function generateCanvasData() {
93
+ const bins = new Array(256).fill(0);
94
+ const totalSamples = 36000;
95
+ bins[0] = 10000 + randInt(5001);
96
+ bins[255] = 12000 + randInt(4001);
97
+ const colorPeaks = [
98
+ [255, 400 + randInt(301)], [165, 200 + randInt(201)],
99
+ [0, 300 + randInt(301)], [128, 100 + randInt(201)],
100
+ [64, 50 + randInt(101)], [192, 80 + randInt(121)],
101
+ [32, 30 + randInt(71)], [224, 60 + randInt(121)]
102
+ ];
103
+ for (const [idx, val] of colorPeaks)
104
+ bins[idx] = val;
105
+ let remaining = totalSamples - bins.reduce((a, b) => a + b, 0);
106
+ for (let i = 1; i < 255; i++) {
107
+ if (bins[i] === 0 && remaining > 0) {
108
+ const v = Math.min(4 + randInt(97), remaining);
109
+ bins[i] = v;
110
+ remaining -= v;
111
+ }
112
+ }
113
+ bins[0] += remaining;
114
+ const raw = Buffer.alloc(256 * 4);
115
+ for (let i = 0; i < 256; i++)
116
+ raw.writeUInt32LE(bins[i], i * 4);
117
+ const digest = crypto_1.default.createHash('sha256').update(raw).digest();
118
+ const hash = digest.readInt32LE(0);
119
+ return { hash, histogram: bins };
120
+ }
121
+ /**
122
+ * 生成真实范围的 Chrome 详细版本号(major.minor.build.patch)
123
+ * Chrome 稳定版格式:主版本.0.buildNumber.patchNumber
124
+ * 随机从近几个主版本中选取,build/patch 在真实范围内随机
125
+ */
126
+ function randomChromeVersion() {
127
+ // 近期稳定版主版本及其对应的 build 号范围 (从 Chromium release history)
128
+ const versions = [
129
+ { major: 137, buildMin: 7151, buildMax: 7160 },
130
+ { major: 138, buildMin: 7204, buildMax: 7213 },
131
+ { major: 139, buildMin: 7259, buildMax: 7268 },
132
+ { major: 140, buildMin: 7316, buildMax: 7325 },
133
+ { major: 141, buildMin: 7371, buildMax: 7380 },
134
+ { major: 142, buildMin: 7430, buildMax: 7439 },
135
+ { major: 143, buildMin: 7485, buildMax: 7494 },
136
+ { major: 144, buildMin: 7544, buildMax: 7553 },
137
+ { major: 145, buildMin: 7601, buildMax: 7610 },
138
+ { major: 146, buildMin: 7660, buildMax: 7669 },
139
+ ];
140
+ const v = versions[Math.floor(Math.random() * versions.length)];
141
+ const build = v.buildMin + Math.floor(Math.random() * (v.buildMax - v.buildMin + 1));
142
+ const patch = Math.floor(Math.random() * 150); // patch 通常 0-150
143
+ return `${v.major}.0.${build}.${patch}`;
144
+ }
145
+ function randomIdentity() {
146
+ const chromeVer = randomChromeVersion();
147
+ const gpu = pick(GPU_CONFIGS);
148
+ const scr = pick(SCREEN_CONFIGS);
149
+ const math = pick(MATH_POOL);
150
+ const { hash: canvasHash, histogram } = generateCanvasData();
151
+ const exts = [...WEBGL_EXT_CORE];
152
+ const nOpt = randInt(5);
153
+ if (nOpt > 0) {
154
+ const perm = shuffle([...Array(WEBGL_EXT_OPTIONAL.length).keys()]);
155
+ for (let i = 0; i < Math.min(nOpt, WEBGL_EXT_OPTIONAL.length); i++) {
156
+ exts.push(WEBGL_EXT_OPTIONAL[perm[i]]);
157
+ }
158
+ }
159
+ exts.sort();
160
+ const plugins = shuffle([...PLUGINS_POOL]);
161
+ return {
162
+ chromeVer: chromeVer,
163
+ ua: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVer} Safari/537.36`,
164
+ gpuVendor: gpu.vendor,
165
+ gpuModel: gpu.model,
166
+ webGLExts: exts,
167
+ canvasHash,
168
+ histogramBase: histogram,
169
+ mathTan: math.tan,
170
+ mathSin: math.sin,
171
+ mathCos: math.cos,
172
+ plugins,
173
+ screen: {
174
+ width: scr[0], height: scr[1],
175
+ availWidth: scr[2], availHeight: scr[3],
176
+ colorDepth: scr[4]
177
+ },
178
+ lsubidPrefixSignin: pick(LSUBID_PREFIXES),
179
+ lsubidPrefixProfile: pick(LSUBID_PREFIXES),
180
+ webpackHash: randInt(0x7fffffff).toString(16).padStart(10, '0').slice(0, 10)
181
+ };
182
+ }
183
+ var names_1 = require("./names");
184
+ Object.defineProperty(exports, "randomFullName", { enumerable: true, get: function () { return names_1.randomFullName; } });
@@ -0,0 +1,349 @@
1
+ "use strict";
2
+ // 本地中继代理链(Proxy Chaining)
3
+ //
4
+ // 背景:部分目标代理(如 bestproxy)要求「来源 IP 必须为非大陆」,大陆 IP 既不能加白名单也会被拒(610)。
5
+ // 底层 TLS 引擎只支持单层代理,因此这里在本机起一个本地中继,把链路串成:
6
+ // 本机 → 本地中继 → 上游中转(非大陆, upstream) → 目标代理(target, bestproxy) → 目标站点
7
+ // 这样目标代理看到的来源 IP 是上游中转的出口(非大陆),即可通过。
8
+ //
9
+ // 仅实现 HTTP CONNECT 入站(注册全程为 https,足够);上游中转支持 http / socks5(4)。
10
+ var __importDefault = (this && this.__importDefault) || function (mod) {
11
+ return (mod && mod.__esModule) ? mod : { "default": mod };
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.ChainProxyRelay = void 0;
15
+ const net_1 = __importDefault(require("net"));
16
+ const socks_1 = require("socks");
17
+ function parseChainProxy(url) {
18
+ try {
19
+ const u = new URL(url);
20
+ const proto = u.protocol.replace(':', '').toLowerCase();
21
+ let protocol;
22
+ if (proto === 'http')
23
+ protocol = 'http';
24
+ else if (proto === 'https')
25
+ protocol = 'https';
26
+ else if (proto === 'socks5' || proto === 'socks5h' || proto === 'socks')
27
+ protocol = 'socks5';
28
+ else if (proto === 'socks4' || proto === 'socks4a')
29
+ protocol = 'socks4';
30
+ else
31
+ return null;
32
+ const port = Number(u.port) || (protocol.startsWith('socks') ? 1080 : 8080);
33
+ if (!u.hostname)
34
+ return null;
35
+ return {
36
+ protocol,
37
+ host: u.hostname,
38
+ port,
39
+ username: u.username ? decodeURIComponent(u.username) : undefined,
40
+ password: u.password ? decodeURIComponent(u.password) : undefined
41
+ };
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ class ChainProxyRelay {
48
+ server = null;
49
+ /** 跟踪所有活跃的入站连接,stop() 时强制销毁,避免 server.close() 等 Keep-Alive 超时(~60s)*/
50
+ sockets = new Set();
51
+ upstream;
52
+ target;
53
+ log;
54
+ port = 0;
55
+ constructor(upstreamUrl, targetUrl, log) {
56
+ const up = parseChainProxy(upstreamUrl);
57
+ const tg = parseChainProxy(targetUrl);
58
+ if (!up)
59
+ throw new Error(`上游中转代理无效: ${upstreamUrl}`);
60
+ if (!tg)
61
+ throw new Error(`目标代理无效: ${targetUrl}`);
62
+ this.upstream = up;
63
+ this.target = tg;
64
+ this.log = log || (() => { });
65
+ }
66
+ /** 启动本地中继,返回可直接作为代理使用的 http://127.0.0.1:port */
67
+ start() {
68
+ return new Promise((resolve, reject) => {
69
+ const server = net_1.default.createServer((client) => this.handleClient(client));
70
+ server.once('error', reject);
71
+ server.listen(0, '127.0.0.1', () => {
72
+ const addr = server.address();
73
+ if (addr && typeof addr === 'object') {
74
+ this.port = addr.port;
75
+ this.server = server;
76
+ server.removeListener('error', reject);
77
+ resolve(`http://127.0.0.1:${this.port}`);
78
+ }
79
+ else {
80
+ reject(new Error('本地中继启动失败:无法获取端口'));
81
+ }
82
+ });
83
+ });
84
+ }
85
+ stop() {
86
+ return new Promise((resolve) => {
87
+ const srv = this.server;
88
+ this.server = null;
89
+ // 强制销毁所有活跃隧道连接:否则 server.close() 会等 DLL Go http.Transport
90
+ // 的 Keep-Alive 连接自然超时(~60s),导致注册结束后 cleanup 卡住一分钟
91
+ for (const sock of this.sockets) {
92
+ try {
93
+ sock.destroy();
94
+ }
95
+ catch { /* ignore */ }
96
+ }
97
+ this.sockets.clear();
98
+ if (!srv) {
99
+ resolve();
100
+ return;
101
+ }
102
+ srv.close(() => resolve());
103
+ // 双保险:500ms 后无论 close 回调是否触发都 resolve
104
+ setTimeout(resolve, 500);
105
+ });
106
+ }
107
+ handleClient(client) {
108
+ this.sockets.add(client);
109
+ client.on('close', () => this.sockets.delete(client));
110
+ client.on('error', () => client.destroy());
111
+ client.once('data', (chunk) => {
112
+ const head = chunk.toString('latin1');
113
+ const m = head.match(/^CONNECT\s+([^\s:]+):(\d+)\s+HTTP\/1\.[01]/i);
114
+ if (!m) {
115
+ client.end('HTTP/1.1 405 Method Not Allowed\r\n\r\n');
116
+ return;
117
+ }
118
+ const host = m[1];
119
+ const port = Number(m[2]);
120
+ this.dialChain(host, port)
121
+ .then((tunnel) => {
122
+ client.write('HTTP/1.1 200 Connection Established\r\n\r\n');
123
+ client.pipe(tunnel);
124
+ tunnel.pipe(client);
125
+ client.on('close', () => tunnel.destroy());
126
+ tunnel.on('close', () => client.destroy());
127
+ tunnel.on('error', () => { client.destroy(); tunnel.destroy(); });
128
+ })
129
+ .catch((err) => {
130
+ this.log(`[ProxyChain] 隧道建立失败: ${err instanceof Error ? err.message : String(err)}`);
131
+ if (!client.destroyed)
132
+ client.end('HTTP/1.1 502 Bad Gateway\r\n\r\n');
133
+ });
134
+ });
135
+ }
136
+ /** 经上游中转连到目标代理入口,再在该连接上对目标代理做 CONNECT 抵达最终目标 */
137
+ async dialChain(host, port) {
138
+ const sock = await this.connectViaUpstream(this.target.host, this.target.port);
139
+ try {
140
+ const resp = await this.sendConnectRequest(sock, host, port, this.target);
141
+ if (resp.status !== 200) {
142
+ throw new Error(this.formatConnectError('目标代理', resp));
143
+ }
144
+ }
145
+ catch (err) {
146
+ sock.destroy();
147
+ throw err;
148
+ }
149
+ return sock;
150
+ }
151
+ connectViaUpstream(host, port) {
152
+ if (this.upstream.protocol === 'socks5' || this.upstream.protocol === 'socks4') {
153
+ return this.connectViaSocks(host, port);
154
+ }
155
+ return this.connectViaHttpUpstream(host, port);
156
+ }
157
+ connectViaHttpUpstream(host, port) {
158
+ return new Promise((resolve, reject) => {
159
+ const sock = net_1.default.connect(this.upstream.port, this.upstream.host);
160
+ sock.setTimeout(20000);
161
+ sock.once('timeout', () => { sock.destroy(); reject(new Error('上游中转连接超时')); });
162
+ sock.once('error', reject);
163
+ sock.once('connect', () => {
164
+ sock.setNoDelay(true);
165
+ this.sendConnectRequest(sock, host, port, this.upstream)
166
+ .then((resp) => {
167
+ sock.setTimeout(0);
168
+ if (resp.status === 200)
169
+ resolve(sock);
170
+ else {
171
+ sock.destroy();
172
+ reject(new Error(this.formatConnectError('上游中转', resp)));
173
+ }
174
+ })
175
+ .catch((err) => { sock.destroy(); reject(err); });
176
+ });
177
+ });
178
+ }
179
+ connectViaSocks(host, port) {
180
+ return new Promise((resolve, reject) => {
181
+ void socks_1.SocksClient.createConnection({
182
+ proxy: {
183
+ host: this.upstream.host,
184
+ port: this.upstream.port,
185
+ type: this.upstream.protocol === 'socks4' ? 4 : 5,
186
+ userId: this.upstream.username,
187
+ password: this.upstream.password
188
+ },
189
+ command: 'connect',
190
+ destination: { host, port },
191
+ timeout: 20000
192
+ })
193
+ .then(({ socket }) => {
194
+ // socks 包返回的 socket 默认开启了 30s timeout,会在空闲后触发 'end',导致我们误判为"被对端关闭"
195
+ socket.setTimeout(0);
196
+ socket.setNoDelay(true);
197
+ socket.setKeepAlive(true, 30000);
198
+ resolve(socket);
199
+ })
200
+ .catch((err) => reject(err));
201
+ });
202
+ }
203
+ /**
204
+ * 通用 CONNECT:发送请求 + 解析响应。
205
+ *
206
+ * 关键容错:
207
+ * - 部分代理返回错误时只发状态行就 close,**不补 \r\n\r\n**(如 bestproxy 的 610),
208
+ * 旧实现会等空行等到 FIN 触发 'end' 然后误报「代理连接被对端关闭」,错误状态码被丢。
209
+ * 新实现:'end' 事件触发时若 buf 已含状态行,尽力解析;只有空 buf 才报「关闭」。
210
+ * - 附带常见兼容头(Proxy-Connection / User-Agent),减少代理服务端的策略性拒绝。
211
+ */
212
+ sendConnectRequest(sock, host, port, auth) {
213
+ return new Promise((resolve, reject) => {
214
+ const lines = [
215
+ `CONNECT ${host}:${port} HTTP/1.1`,
216
+ `Host: ${host}:${port}`,
217
+ 'Proxy-Connection: keep-alive',
218
+ 'User-Agent: Mozilla/5.0'
219
+ ];
220
+ if (auth.username) {
221
+ const b64 = Buffer.from(`${auth.username}:${auth.password || ''}`).toString('base64');
222
+ lines.push(`Proxy-Authorization: Basic ${b64}`);
223
+ }
224
+ const req = lines.join('\r\n') + '\r\n\r\n';
225
+ this.readHttpResponse(sock).then(resolve, reject);
226
+ sock.write(req);
227
+ });
228
+ }
229
+ /** 读取 HTTP 响应:直到 \r\n\r\n 完整、或对端关闭/出错时尽力解析。返回结构化结果。 */
230
+ readHttpResponse(sock) {
231
+ return new Promise((resolve, reject) => {
232
+ let buf = '';
233
+ const cleanup = () => {
234
+ sock.removeListener('data', onData);
235
+ sock.removeListener('error', onErr);
236
+ sock.removeListener('end', onEnd);
237
+ sock.removeListener('close', onEnd);
238
+ };
239
+ const parse = (raw) => {
240
+ const nlIdx = raw.indexOf('\r\n');
241
+ if (nlIdx < 0)
242
+ return null;
243
+ const statusLine = raw.slice(0, nlIdx);
244
+ const m = statusLine.match(/^HTTP\/1\.[01]\s+(\d{3})\s*(.*)$/);
245
+ if (!m)
246
+ return null;
247
+ const status = Number(m[1]);
248
+ const statusText = m[2] || '';
249
+ const sep = raw.indexOf('\r\n\r\n');
250
+ const headersEnd = sep >= 0 ? sep : raw.length;
251
+ const headersRaw = raw.slice(nlIdx + 2, headersEnd);
252
+ const bodySnippet = sep >= 0 ? raw.slice(sep + 4, sep + 4 + 200) : '';
253
+ return { status, statusText, headersRaw, bodySnippet };
254
+ };
255
+ const finish = (raw, viaClose) => {
256
+ cleanup();
257
+ const parsed = parse(raw);
258
+ if (parsed) {
259
+ if (parsed.status === 200 && raw.indexOf('\r\n\r\n') >= 0) {
260
+ const sep = raw.indexOf('\r\n\r\n');
261
+ const rest = raw.slice(sep + 4);
262
+ if (rest.length > 0)
263
+ sock.unshift(Buffer.from(rest, 'latin1'));
264
+ }
265
+ resolve(parsed);
266
+ }
267
+ else if (viaClose) {
268
+ reject(new Error(raw ? `代理返回不可解析: ${raw.slice(0, 120)}` : '代理连接被对端关闭(无任何响应)'));
269
+ }
270
+ };
271
+ const onData = (d) => {
272
+ buf += d.toString('latin1');
273
+ const sep = buf.indexOf('\r\n\r\n');
274
+ if (sep >= 0)
275
+ finish(buf, false);
276
+ };
277
+ const onErr = (err) => { cleanup(); reject(err); };
278
+ const onEnd = () => finish(buf, true);
279
+ sock.on('data', onData);
280
+ sock.once('error', onErr);
281
+ sock.once('end', onEnd);
282
+ sock.once('close', onEnd);
283
+ });
284
+ }
285
+ formatConnectError(stage, resp) {
286
+ const suffix = resp.bodySnippet ? ` body=${resp.bodySnippet.replace(/[\r\n]/g, ' ').slice(0, 120)}` : '';
287
+ return `${stage} CONNECT 失败: HTTP ${resp.status} ${resp.statusText}${suffix}`;
288
+ }
289
+ /**
290
+ * 分阶段诊断:
291
+ * A) 上游中转 TCP 连通
292
+ * B) 经上游 CONNECT 到目标代理入口
293
+ * C) 经完整链路 CONNECT 到 testHost:testPort
294
+ * 不依赖本地 server,独立可用;定位问题精确到哪一层。
295
+ */
296
+ async diagnose(testHost = 'www.gstatic.com', testPort = 443) {
297
+ const result = { upstreamReachable: false, targetReachable: false };
298
+ const t0 = Date.now();
299
+ try {
300
+ await this.tcpProbe(this.upstream.host, this.upstream.port, 8000);
301
+ result.upstreamReachable = true;
302
+ result.upstreamRtMs = Date.now() - t0;
303
+ }
304
+ catch (err) {
305
+ result.upstreamError = err instanceof Error ? err.message : String(err);
306
+ return result;
307
+ }
308
+ const t1 = Date.now();
309
+ let chainSock = null;
310
+ try {
311
+ chainSock = await this.connectViaUpstream(this.target.host, this.target.port);
312
+ result.targetReachable = true;
313
+ result.targetRtMs = Date.now() - t1;
314
+ }
315
+ catch (err) {
316
+ result.targetError = err instanceof Error ? err.message : String(err);
317
+ return result;
318
+ }
319
+ const t2 = Date.now();
320
+ try {
321
+ const resp = await this.sendConnectRequest(chainSock, testHost, testPort, this.target);
322
+ result.targetStatus = resp.status;
323
+ result.targetStatusText = resp.statusText;
324
+ result.targetBodySnippet = resp.bodySnippet;
325
+ result.endToEndOk = resp.status === 200;
326
+ result.endToEndRtMs = Date.now() - t2;
327
+ if (resp.status !== 200) {
328
+ result.endToEndError = `目标代理拒绝: HTTP ${resp.status} ${resp.statusText}`;
329
+ }
330
+ }
331
+ catch (err) {
332
+ result.endToEndOk = false;
333
+ result.endToEndError = err instanceof Error ? err.message : String(err);
334
+ }
335
+ finally {
336
+ chainSock.destroy();
337
+ }
338
+ return result;
339
+ }
340
+ tcpProbe(host, port, timeoutMs) {
341
+ return new Promise((resolve, reject) => {
342
+ const sock = net_1.default.connect(port, host);
343
+ const timer = setTimeout(() => { sock.destroy(); reject(new Error(`TCP 连接超时 ${host}:${port}`)); }, timeoutMs);
344
+ sock.once('connect', () => { clearTimeout(timer); sock.destroy(); resolve(); });
345
+ sock.once('error', (err) => { clearTimeout(timer); reject(err); });
346
+ });
347
+ }
348
+ }
349
+ exports.ChainProxyRelay = ChainProxyRelay;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.genPassword = genPassword;
4
+ exports.newConfig = newConfig;
5
+ const browser_identity_1 = require("./browser-identity");
6
+ function genPassword() {
7
+ const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
8
+ const lower = 'abcdefghijklmnopqrstuvwxyz';
9
+ const digits = '0123456789';
10
+ const special = '!@#$%^&*';
11
+ let pw = '';
12
+ for (let i = 0; i < 3; i++)
13
+ pw += upper[Math.floor(Math.random() * upper.length)];
14
+ for (let i = 0; i < 6; i++)
15
+ pw += lower[Math.floor(Math.random() * lower.length)];
16
+ for (let i = 0; i < 3; i++)
17
+ pw += digits[Math.floor(Math.random() * digits.length)];
18
+ for (let i = 0; i < 2; i++)
19
+ pw += special[Math.floor(Math.random() * special.length)];
20
+ const arr = pw.split('');
21
+ for (let i = arr.length - 1; i > 0; i--) {
22
+ const j = Math.floor(Math.random() * (i + 1));
23
+ [arr[i], arr[j]] = [arr[j], arr[i]];
24
+ }
25
+ return arr.join('');
26
+ }
27
+ function newConfig(overrides) {
28
+ return {
29
+ oidcBase: 'https://oidc.us-east-1.amazonaws.com',
30
+ signinBase: 'https://us-east-1.signin.aws',
31
+ profileBase: 'https://profile.aws.amazon.com',
32
+ viewBase: 'https://view.awsapps.com',
33
+ portalBase: 'https://portal.sso.us-east-1.amazonaws.com',
34
+ directoryId: 'd-9067642ac7',
35
+ startURL: 'https://view.awsapps.com/start',
36
+ password: genPassword(),
37
+ fullName: (0, browser_identity_1.randomFullName)(),
38
+ proxy: '',
39
+ upstreamProxy: '',
40
+ strictProxy: false,
41
+ moEmailBaseURL: '',
42
+ moEmailAPIKey: '',
43
+ useOutlook: false,
44
+ outlookData: '',
45
+ useTempMailPlus: false,
46
+ tempMailPlusEmail: '',
47
+ tempMailPlusEpin: '',
48
+ tempMailPlusDomain: '',
49
+ useTingamefiMail: false,
50
+ tingamefiMailApiUrl: 'https://temp-email-worker.thienp1301.workers.dev',
51
+ tingamefiMailAdminPassword: '',
52
+ tingamefiMailDomain: 'mail.tingamefi.com',
53
+ useProton: false,
54
+ protonEmail: '',
55
+ manualMode: false,
56
+ ...overrides
57
+ };
58
+ }