dsh-provider-proxy 0.7.0 → 0.9.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 +85 -5
- package/lib/client.js +1922 -236
- package/lib/client.js.map +4 -4
- package/lib/index.js +443 -32
- package/lib/index.js.map +4 -4
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1073,6 +1073,15 @@ var DefaultProxy = z.object({
|
|
|
1073
1073
|
var ProviderProxyRoute = z.object({
|
|
1074
1074
|
/** Whether this provider's calls are routed through the proxy below. */
|
|
1075
1075
|
enabled: z.boolean().default(false),
|
|
1076
|
+
/**
|
|
1077
|
+
* A named preset from the top-level `proxyProfiles` dictionary.
|
|
1078
|
+
*
|
|
1079
|
+
* Non-empty wins over `useDefault`: the route's TRANSPORT fields come from
|
|
1080
|
+
* that preset. Empty (the default) keeps the 0.8.0 behaviour, where
|
|
1081
|
+
* `useDefault` may borrow the top-level `defaultProxy`. Target-host rules and
|
|
1082
|
+
* the self-check target are always the route's own.
|
|
1083
|
+
*/
|
|
1084
|
+
profileId: z.string().default(""),
|
|
1076
1085
|
/**
|
|
1077
1086
|
* When true, this route's TRANSPORT fields (protocol/host/port/username/
|
|
1078
1087
|
* password) come from the top-level `defaultProxy` instead of its own, so the
|
|
@@ -1109,6 +1118,12 @@ var ProviderProxyRoute = z.object({
|
|
|
1109
1118
|
var Config = z.object({
|
|
1110
1119
|
/** Per-provider proxy routes, keyed by provider route id (e.g. `sensenova`). */
|
|
1111
1120
|
providers: vol(z.dict(ProviderProxyRoute).default({})),
|
|
1121
|
+
/**
|
|
1122
|
+
* Named proxy endpoints, keyed by a user-chosen name. A route opts into one
|
|
1123
|
+
* with `profileId`; the top-level `defaultProxy` remains the implicit
|
|
1124
|
+
* "default preset" so an old configuration keeps working unchanged.
|
|
1125
|
+
*/
|
|
1126
|
+
proxyProfiles: vol(z.dict(DefaultProxy).default({})),
|
|
1112
1127
|
/**
|
|
1113
1128
|
* Shared endpoint for every route that sets `useDefault`. Volatile for the
|
|
1114
1129
|
* same reason as `providers`: a settings write is committed into the running
|
|
@@ -1149,8 +1164,9 @@ function normalizeRoute(provider, raw, options) {
|
|
|
1149
1164
|
const onlyHosts = value.onlyHosts.trim().toLowerCase();
|
|
1150
1165
|
const bypassHosts = value.bypassHosts.trim().toLowerCase();
|
|
1151
1166
|
const testTarget = value.testTarget.trim();
|
|
1167
|
+
const profileId = value.profileId.trim();
|
|
1152
1168
|
if (!value.enabled && options?.requireEnabled !== false) {
|
|
1153
|
-
return { ...value, port: value.port || 0, onlyHosts, bypassHosts, testTarget };
|
|
1169
|
+
return { ...value, profileId, port: value.port || 0, onlyHosts, bypassHosts, testTarget };
|
|
1154
1170
|
}
|
|
1155
1171
|
const host = value.host.trim();
|
|
1156
1172
|
const hostProblem = proxyHostProblem(host);
|
|
@@ -1164,7 +1180,15 @@ function normalizeRoute(provider, raw, options) {
|
|
|
1164
1180
|
`dsh-provider-proxy: provider "${provider}" \u7684\u4EE3\u7406\u7AEF\u53E3\u65E0\u6548\uFF08\u9700\u4E3A 1-65535 \u7684\u6574\u6570\uFF09`
|
|
1165
1181
|
);
|
|
1166
1182
|
}
|
|
1167
|
-
return {
|
|
1183
|
+
return {
|
|
1184
|
+
...value,
|
|
1185
|
+
profileId,
|
|
1186
|
+
host,
|
|
1187
|
+
username: value.username.trim(),
|
|
1188
|
+
onlyHosts,
|
|
1189
|
+
bypassHosts,
|
|
1190
|
+
testTarget
|
|
1191
|
+
};
|
|
1168
1192
|
}
|
|
1169
1193
|
|
|
1170
1194
|
// src/host-rules.ts
|
|
@@ -1201,6 +1225,107 @@ function hostRuleProblem(raw) {
|
|
|
1201
1225
|
function deadRuleEntries(raw) {
|
|
1202
1226
|
return splitHostRules(raw).filter((entry) => hostRuleProblem(entry) !== void 0);
|
|
1203
1227
|
}
|
|
1228
|
+
var PROVIDER_HOST_TEMPLATES = {
|
|
1229
|
+
openai: { label: "OpenAI", hosts: "api.openai.com, *.openai.com" },
|
|
1230
|
+
anthropic: { label: "Anthropic", hosts: "api.anthropic.com" },
|
|
1231
|
+
google: { label: "Google", hosts: "generativelanguage.googleapis.com" },
|
|
1232
|
+
deepseek: { label: "DeepSeek", hosts: "api.deepseek.com" }
|
|
1233
|
+
};
|
|
1234
|
+
function providerHostTemplate(providerId) {
|
|
1235
|
+
const key = providerId.trim().toLowerCase().split(/[-_/:.]/)[0] ?? "";
|
|
1236
|
+
const template = PROVIDER_HOST_TEMPLATES[key];
|
|
1237
|
+
return template === void 0 ? void 0 : { id: key, ...template };
|
|
1238
|
+
}
|
|
1239
|
+
function appendHostRules(raw, hosts) {
|
|
1240
|
+
const existing = new Set(parseHostList(raw));
|
|
1241
|
+
const additions = [];
|
|
1242
|
+
for (const token of splitHostRules(hosts)) {
|
|
1243
|
+
const normalized = normalizeHostRule(token);
|
|
1244
|
+
if (normalized.length === 0 || existing.has(normalized)) continue;
|
|
1245
|
+
existing.add(normalized);
|
|
1246
|
+
additions.push(token);
|
|
1247
|
+
}
|
|
1248
|
+
if (additions.length === 0) return raw.trim();
|
|
1249
|
+
const prefix = raw.trim();
|
|
1250
|
+
return prefix.length === 0 ? additions.join(", ") : `${prefix}, ${additions.join(", ")}`;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/presets.ts
|
|
1254
|
+
var DEFAULT_PROFILE_LABEL = "\u9ED8\u8BA4\u9884\u8BBE (Default)";
|
|
1255
|
+
var DEFAULT_PROFILE_BINDING = "__default__";
|
|
1256
|
+
function routeBindingOf(route) {
|
|
1257
|
+
const profileId = (route?.profileId ?? "").trim();
|
|
1258
|
+
if (profileId.length > 0) return { kind: "profile", profileId };
|
|
1259
|
+
if (route?.useDefault === true) return { kind: "default" };
|
|
1260
|
+
return { kind: "own" };
|
|
1261
|
+
}
|
|
1262
|
+
function bindingFields(binding) {
|
|
1263
|
+
switch (binding.kind) {
|
|
1264
|
+
case "profile":
|
|
1265
|
+
return { profileId: binding.profileId, useDefault: false };
|
|
1266
|
+
case "default":
|
|
1267
|
+
return { profileId: "", useDefault: true };
|
|
1268
|
+
case "own":
|
|
1269
|
+
return { profileId: "", useDefault: false };
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
function resolveEndpoint(provider, route, defaultProxy, profiles) {
|
|
1273
|
+
const binding = routeBindingOf(route);
|
|
1274
|
+
if (binding.kind === "own") return { ok: true, binding };
|
|
1275
|
+
if (binding.kind === "default") return { ok: true, binding, endpoint: defaultProxy ?? {} };
|
|
1276
|
+
const preset = profiles?.[binding.profileId];
|
|
1277
|
+
if (preset === void 0) {
|
|
1278
|
+
return {
|
|
1279
|
+
ok: false,
|
|
1280
|
+
binding,
|
|
1281
|
+
problem: `dsh-provider-proxy: provider "${provider}" \u7ED1\u5B9A\u7684\u9884\u8BBE\u300C${binding.profileId}\u300D\u4E0D\u5B58\u5728\uFF1B\u8BF7\u9009\u62E9\u4E00\u4E2A\u73B0\u6709\u9884\u8BBE\u6216\u6539\u7528\u81EA\u5B9A\u4E49\u914D\u7F6E`
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
return { ok: true, binding, endpoint: preset };
|
|
1285
|
+
}
|
|
1286
|
+
function profileNameProblem(raw) {
|
|
1287
|
+
const name2 = raw.trim();
|
|
1288
|
+
if (name2.length === 0) return "\u9884\u8BBE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
|
|
1289
|
+
if (name2.length > 40) return "\u9884\u8BBE\u540D\u79F0\u4E0D\u80FD\u8D85\u8FC7 40 \u4E2A\u5B57\u7B26";
|
|
1290
|
+
if (/[\u0000-\u001f\u007f]/.test(name2)) return "\u9884\u8BBE\u540D\u79F0\u4E0D\u80FD\u5305\u542B\u63A7\u5236\u5B57\u7B26";
|
|
1291
|
+
if (name2 === DEFAULT_PROFILE_LABEL) return "\u8BE5\u540D\u79F0\u7531\u5185\u7F6E\u9ED8\u8BA4\u9884\u8BBE\u4FDD\u7559\uFF0C\u8BF7\u6362\u4E00\u4E2A\u540D\u79F0";
|
|
1292
|
+
if (name2 === DEFAULT_PROFILE_BINDING) return "\u8BE5\u540D\u79F0\u7531\u63D2\u4EF6\u4FDD\u7559\uFF0C\u8BF7\u6362\u4E00\u4E2A\u540D\u79F0";
|
|
1293
|
+
return void 0;
|
|
1294
|
+
}
|
|
1295
|
+
function withEndpoint(route, endpoint) {
|
|
1296
|
+
return {
|
|
1297
|
+
...route,
|
|
1298
|
+
protocol: endpoint.protocol ?? "http",
|
|
1299
|
+
host: endpoint.host ?? "",
|
|
1300
|
+
port: endpoint.port ?? 0,
|
|
1301
|
+
username: endpoint.username ?? "",
|
|
1302
|
+
password: endpoint.password ?? ""
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
function effectiveRoute(route, defaultProxy, profiles) {
|
|
1306
|
+
const base = route ?? {};
|
|
1307
|
+
const binding = routeBindingOf(base);
|
|
1308
|
+
if (binding.kind === "own") return base;
|
|
1309
|
+
const resolved = resolveEndpoint("", base, defaultProxy, profiles);
|
|
1310
|
+
if (!resolved.ok || resolved.endpoint === void 0) return base;
|
|
1311
|
+
return withEndpoint(base, resolved.endpoint);
|
|
1312
|
+
}
|
|
1313
|
+
function profileNames(profiles) {
|
|
1314
|
+
return Object.keys(profiles ?? {}).sort((a, b) => a.localeCompare(b));
|
|
1315
|
+
}
|
|
1316
|
+
function routesBoundTo(routes, profileId) {
|
|
1317
|
+
const out = [];
|
|
1318
|
+
for (const [id, route] of Object.entries(routes ?? {})) {
|
|
1319
|
+
if ((route?.profileId ?? "").trim() === profileId) out.push(id);
|
|
1320
|
+
}
|
|
1321
|
+
return out.sort();
|
|
1322
|
+
}
|
|
1323
|
+
function endpointSummary(endpoint) {
|
|
1324
|
+
const protocol = endpoint?.protocol ?? "http";
|
|
1325
|
+
const host = (endpoint?.host ?? "").trim() || "\u2014";
|
|
1326
|
+
const port = endpoint?.port ?? 0;
|
|
1327
|
+
return `${protocol}://${host}:${port === 0 ? "\u2014" : port}`;
|
|
1328
|
+
}
|
|
1204
1329
|
|
|
1205
1330
|
// src/proxy-table.ts
|
|
1206
1331
|
function normalizedRuleSet(raw) {
|
|
@@ -1270,19 +1395,21 @@ function buildDecide(route, localDirect) {
|
|
|
1270
1395
|
return "proxy";
|
|
1271
1396
|
};
|
|
1272
1397
|
}
|
|
1273
|
-
function withDefaultProxy(route, defaultProxy) {
|
|
1274
|
-
return {
|
|
1275
|
-
...route,
|
|
1276
|
-
protocol: defaultProxy.protocol,
|
|
1277
|
-
host: defaultProxy.host,
|
|
1278
|
-
port: defaultProxy.port,
|
|
1279
|
-
username: defaultProxy.username,
|
|
1280
|
-
password: defaultProxy.password
|
|
1281
|
-
};
|
|
1282
|
-
}
|
|
1283
1398
|
function buildRouteEntry(provider, raw, options) {
|
|
1284
1399
|
let effective = raw;
|
|
1285
|
-
|
|
1400
|
+
const resolved = resolveEndpoint(provider, raw, options.defaultProxy, options.profiles);
|
|
1401
|
+
if (!resolved.ok) return { ok: false, problem: resolved.problem };
|
|
1402
|
+
if (resolved.binding.kind === "profile") {
|
|
1403
|
+
const preset = resolved.endpoint ?? {};
|
|
1404
|
+
const why = defaultProxyProblem(preset);
|
|
1405
|
+
if (why !== void 0) {
|
|
1406
|
+
return {
|
|
1407
|
+
ok: false,
|
|
1408
|
+
problem: `dsh-provider-proxy: provider "${provider}" \u7ED1\u5B9A\u7684\u9884\u8BBE\u300C${resolved.binding.profileId}\u300D\u4E0D\u53EF\u7528\uFF1A${why}`
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
effective = withEndpoint(raw, normalizeDefaultProxy(preset));
|
|
1412
|
+
} else if (resolved.binding.kind === "default") {
|
|
1286
1413
|
const why = defaultProxyProblem(options.defaultProxy);
|
|
1287
1414
|
if (why !== void 0) {
|
|
1288
1415
|
return {
|
|
@@ -1290,7 +1417,7 @@ function buildRouteEntry(provider, raw, options) {
|
|
|
1290
1417
|
problem: `dsh-provider-proxy: provider "${provider}" \u542F\u7528\u4E86\u300C\u4F7F\u7528\u9ED8\u8BA4\u4EE3\u7406\u300D\uFF0C\u4F46\u9876\u5C42\u9ED8\u8BA4\u4EE3\u7406\u4E0D\u53EF\u7528\uFF1A${why}`
|
|
1291
1418
|
};
|
|
1292
1419
|
}
|
|
1293
|
-
effective =
|
|
1420
|
+
effective = withEndpoint(raw, options.defaultProxy);
|
|
1294
1421
|
}
|
|
1295
1422
|
let route;
|
|
1296
1423
|
try {
|
|
@@ -1345,6 +1472,7 @@ function createProxyTable(config, options = {}) {
|
|
|
1345
1472
|
const problems = /* @__PURE__ */ new Map();
|
|
1346
1473
|
const buildOptions = {
|
|
1347
1474
|
defaultProxy: normalizeDefaultProxy(options.defaultProxy),
|
|
1475
|
+
profiles: options.profiles,
|
|
1348
1476
|
requireEnabled: true,
|
|
1349
1477
|
localDirect: options.localDirect !== false
|
|
1350
1478
|
};
|
|
@@ -1362,16 +1490,260 @@ async function closeTable(entries) {
|
|
|
1362
1490
|
entries.clear();
|
|
1363
1491
|
}
|
|
1364
1492
|
|
|
1493
|
+
// src/proxy-env.ts
|
|
1494
|
+
var SOURCES = ["HTTPS_PROXY", "HTTP_PROXY", "ALL_PROXY"];
|
|
1495
|
+
function explicitPortOf(text) {
|
|
1496
|
+
const authority = text.slice(text.indexOf("://") + 3).split(/[/?#]/, 1)[0] ?? "";
|
|
1497
|
+
const hostPort = authority.slice(authority.lastIndexOf("@") + 1);
|
|
1498
|
+
const match = /^\[[^\]]+\]:(\d+)$/.exec(hostPort) ?? /^[^:]+:(\d+)$/.exec(hostPort);
|
|
1499
|
+
if (match === null) return 0;
|
|
1500
|
+
const parsed = Number.parseInt(match[1], 10);
|
|
1501
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
1502
|
+
}
|
|
1503
|
+
function parseProxyEnvValue(raw) {
|
|
1504
|
+
const text = (raw ?? "").trim();
|
|
1505
|
+
if (text.length === 0) return void 0;
|
|
1506
|
+
const first = text.split(",")[0]?.trim() ?? "";
|
|
1507
|
+
if (first.length === 0) return void 0;
|
|
1508
|
+
const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(first) ? first : `http://${first}`;
|
|
1509
|
+
let url;
|
|
1510
|
+
try {
|
|
1511
|
+
url = new URL(withScheme);
|
|
1512
|
+
} catch {
|
|
1513
|
+
return void 0;
|
|
1514
|
+
}
|
|
1515
|
+
const scheme = url.protocol.slice(0, -1).toLowerCase();
|
|
1516
|
+
const protocol = scheme === "socks" ? "socks5" : scheme === "http" || scheme === "https" || scheme === "socks5" ? scheme : void 0;
|
|
1517
|
+
if (protocol === void 0) return void 0;
|
|
1518
|
+
const host = url.hostname.startsWith("[") && url.hostname.endsWith("]") ? url.hostname.slice(1, -1) : url.hostname;
|
|
1519
|
+
if (host.length === 0) return void 0;
|
|
1520
|
+
const port = url.port.length > 0 ? Number.parseInt(url.port, 10) : explicitPortOf(withScheme);
|
|
1521
|
+
return {
|
|
1522
|
+
protocol,
|
|
1523
|
+
host,
|
|
1524
|
+
port: Number.isNaN(port) ? 0 : port,
|
|
1525
|
+
hadCredentials: url.username.length > 0 || url.password.length > 0
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
function detectProxyEnv(env = process.env) {
|
|
1529
|
+
for (const source of SOURCES) {
|
|
1530
|
+
const parsed = parseProxyEnvValue(env[source]);
|
|
1531
|
+
if (parsed === void 0) continue;
|
|
1532
|
+
if (parsed.port < 1 || parsed.port > 65535) continue;
|
|
1533
|
+
return { source, ...parsed };
|
|
1534
|
+
}
|
|
1535
|
+
return void 0;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
// src/proxy-clients.ts
|
|
1539
|
+
var PROXY_CLIENT_PRESETS = [
|
|
1540
|
+
{ id: "clash", label: "Clash / Mihomo (127.0.0.1:7890)", protocol: "http", host: "127.0.0.1", port: 7890 },
|
|
1541
|
+
{ id: "clash-verge", label: "Clash Verge / Meta (127.0.0.1:7897)", protocol: "http", host: "127.0.0.1", port: 7897 },
|
|
1542
|
+
{ id: "v2ray-http", label: "V2Ray / Xray HTTP (127.0.0.1:10809)", protocol: "http", host: "127.0.0.1", port: 10809 },
|
|
1543
|
+
{ id: "v2ray-socks", label: "V2Ray / Xray SOCKS5 (127.0.0.1:10808)", protocol: "socks5", host: "127.0.0.1", port: 10808 }
|
|
1544
|
+
];
|
|
1545
|
+
function proxyClientPreset(id) {
|
|
1546
|
+
return PROXY_CLIENT_PRESETS.find((preset) => preset.id === id);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
// src/backup.ts
|
|
1550
|
+
var PROTOCOLS2 = ["http", "https", "socks5"];
|
|
1551
|
+
function normalizeEndpoint(raw) {
|
|
1552
|
+
const value = raw ?? {};
|
|
1553
|
+
return {
|
|
1554
|
+
protocol: PROTOCOLS2.includes(value.protocol) ? value.protocol : "http",
|
|
1555
|
+
host: typeof value.host === "string" ? value.host.trim() : "",
|
|
1556
|
+
port: typeof value.port === "number" && Number.isFinite(value.port) ? value.port : 0,
|
|
1557
|
+
username: typeof value.username === "string" ? value.username.trim() : "",
|
|
1558
|
+
password: typeof value.password === "string" ? value.password : ""
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
var FALLBACK_TEST_URL = "https://api.ipify.org?format=json";
|
|
1562
|
+
function fillRoute(raw) {
|
|
1563
|
+
const value = raw ?? {};
|
|
1564
|
+
return {
|
|
1565
|
+
enabled: value.enabled === true,
|
|
1566
|
+
profileId: typeof value.profileId === "string" ? value.profileId.trim() : "",
|
|
1567
|
+
useDefault: value.useDefault === true,
|
|
1568
|
+
protocol: PROTOCOLS2.includes(value.protocol) ? value.protocol : "http",
|
|
1569
|
+
host: typeof value.host === "string" ? value.host.trim() : "",
|
|
1570
|
+
port: typeof value.port === "number" && Number.isFinite(value.port) ? value.port : 0,
|
|
1571
|
+
username: typeof value.username === "string" ? value.username.trim() : "",
|
|
1572
|
+
password: typeof value.password === "string" ? value.password : "",
|
|
1573
|
+
onlyHosts: typeof value.onlyHosts === "string" ? value.onlyHosts : "",
|
|
1574
|
+
bypassHosts: typeof value.bypassHosts === "string" ? value.bypassHosts : "",
|
|
1575
|
+
testTarget: typeof value.testTarget === "string" ? value.testTarget : ""
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
function buildBackup(source, options = { includePasswords: false }) {
|
|
1579
|
+
const include = options.includePasswords === true;
|
|
1580
|
+
const hide = (password) => include ? password : "";
|
|
1581
|
+
const providers = {};
|
|
1582
|
+
for (const [id, raw] of Object.entries(source.providers ?? {})) {
|
|
1583
|
+
const filled = fillRoute(raw);
|
|
1584
|
+
providers[id] = { ...filled, password: hide(filled.password) };
|
|
1585
|
+
}
|
|
1586
|
+
const proxyProfiles = {};
|
|
1587
|
+
for (const [name2, raw] of Object.entries(source.proxyProfiles ?? {})) {
|
|
1588
|
+
const filled = normalizeEndpoint(raw);
|
|
1589
|
+
proxyProfiles[name2] = { ...filled, password: hide(filled.password) };
|
|
1590
|
+
}
|
|
1591
|
+
const defaultProxy = normalizeEndpoint(source.defaultProxy);
|
|
1592
|
+
return {
|
|
1593
|
+
version: 1,
|
|
1594
|
+
exportedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
1595
|
+
providers,
|
|
1596
|
+
proxyProfiles,
|
|
1597
|
+
defaultProxy: { ...defaultProxy, password: hide(defaultProxy.password) },
|
|
1598
|
+
localDirect: source.localDirect !== false,
|
|
1599
|
+
testUrl: typeof source.testUrl === "string" && source.testUrl.trim().length > 0 ? source.testUrl : FALLBACK_TEST_URL
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
function backupFileName(now = /* @__PURE__ */ new Date()) {
|
|
1603
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1604
|
+
return `dsh-proxy-backup-${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}.json`;
|
|
1605
|
+
}
|
|
1606
|
+
function isPlainObject2(value) {
|
|
1607
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1608
|
+
}
|
|
1609
|
+
function parseBackup(text) {
|
|
1610
|
+
let parsed;
|
|
1611
|
+
try {
|
|
1612
|
+
parsed = JSON.parse(text);
|
|
1613
|
+
} catch (error) {
|
|
1614
|
+
return {
|
|
1615
|
+
ok: false,
|
|
1616
|
+
error: `\u4E0D\u662F\u5408\u6CD5\u7684 JSON\uFF1A${error instanceof Error ? error.message : String(error)}`
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
if (!isPlainObject2(parsed)) return { ok: false, error: "\u5907\u4EFD\u6587\u4EF6\u7684\u9876\u5C42\u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61" };
|
|
1620
|
+
const root = parsed;
|
|
1621
|
+
if (root.providers === void 0 && root.defaultProxy === void 0 && root.proxyProfiles === void 0) {
|
|
1622
|
+
return {
|
|
1623
|
+
ok: false,
|
|
1624
|
+
error: "\u5907\u4EFD\u6587\u4EF6\u7F3A\u5C11 providers / proxyProfiles / defaultProxy\uFF0C\u65E0\u6CD5\u8BC6\u522B\u4E3A\u4EE3\u7406\u914D\u7F6E"
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
if (root.providers !== void 0 && !isPlainObject2(root.providers)) {
|
|
1628
|
+
return { ok: false, error: "providers \u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61" };
|
|
1629
|
+
}
|
|
1630
|
+
if (root.proxyProfiles !== void 0 && !isPlainObject2(root.proxyProfiles)) {
|
|
1631
|
+
return { ok: false, error: "proxyProfiles \u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61" };
|
|
1632
|
+
}
|
|
1633
|
+
if (root.defaultProxy !== void 0 && !isPlainObject2(root.defaultProxy)) {
|
|
1634
|
+
return { ok: false, error: "defaultProxy \u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61" };
|
|
1635
|
+
}
|
|
1636
|
+
if (root.localDirect !== void 0 && typeof root.localDirect !== "boolean") {
|
|
1637
|
+
return { ok: false, error: "localDirect \u5FC5\u987B\u662F\u5E03\u5C14\u503C" };
|
|
1638
|
+
}
|
|
1639
|
+
if (root.testUrl !== void 0 && typeof root.testUrl !== "string") {
|
|
1640
|
+
return { ok: false, error: "testUrl \u5FC5\u987B\u662F\u5B57\u7B26\u4E32" };
|
|
1641
|
+
}
|
|
1642
|
+
const endpoint = (value, label) => {
|
|
1643
|
+
if (value.protocol !== void 0 && !PROTOCOLS2.includes(value.protocol)) {
|
|
1644
|
+
return `${label}.protocol \u5FC5\u987B\u662F ${PROTOCOLS2.join(" / ")}`;
|
|
1645
|
+
}
|
|
1646
|
+
for (const key of ["host", "username", "password"]) {
|
|
1647
|
+
if (value[key] !== void 0 && typeof value[key] !== "string") {
|
|
1648
|
+
return `${label}.${key} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
if (value.port !== void 0 && (typeof value.port !== "number" || !Number.isFinite(value.port))) {
|
|
1652
|
+
return `${label}.port \u5FC5\u987B\u662F\u6570\u5B57`;
|
|
1653
|
+
}
|
|
1654
|
+
return void 0;
|
|
1655
|
+
};
|
|
1656
|
+
const providers = {};
|
|
1657
|
+
for (const [id, value] of Object.entries(root.providers ?? {})) {
|
|
1658
|
+
if (!isPlainObject2(value)) return { ok: false, error: `providers.${id} \u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61` };
|
|
1659
|
+
const why = endpoint(value, `providers.${id}`);
|
|
1660
|
+
if (why !== void 0) return { ok: false, error: why };
|
|
1661
|
+
for (const key of ["enabled", "useDefault"]) {
|
|
1662
|
+
if (value[key] !== void 0 && typeof value[key] !== "boolean") {
|
|
1663
|
+
return { ok: false, error: `providers.${id}.${key} \u5FC5\u987B\u662F\u5E03\u5C14\u503C` };
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
for (const key of ["profileId", "onlyHosts", "bypassHosts", "testTarget"]) {
|
|
1667
|
+
if (value[key] !== void 0 && typeof value[key] !== "string") {
|
|
1668
|
+
return { ok: false, error: `providers.${id}.${key} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32` };
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
providers[id] = fillRoute(value);
|
|
1672
|
+
}
|
|
1673
|
+
const proxyProfiles = {};
|
|
1674
|
+
for (const [name2, value] of Object.entries(
|
|
1675
|
+
root.proxyProfiles ?? {}
|
|
1676
|
+
)) {
|
|
1677
|
+
if (!isPlainObject2(value)) return { ok: false, error: `proxyProfiles.${name2} \u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61` };
|
|
1678
|
+
const why = endpoint(value, `proxyProfiles.${name2}`);
|
|
1679
|
+
if (why !== void 0) return { ok: false, error: why };
|
|
1680
|
+
proxyProfiles[name2] = normalizeEndpoint(value);
|
|
1681
|
+
}
|
|
1682
|
+
if (root.defaultProxy !== void 0) {
|
|
1683
|
+
const why = endpoint(root.defaultProxy, "defaultProxy");
|
|
1684
|
+
if (why !== void 0) return { ok: false, error: why };
|
|
1685
|
+
}
|
|
1686
|
+
const document = {
|
|
1687
|
+
version: 1,
|
|
1688
|
+
exportedAt: typeof root.exportedAt === "string" ? root.exportedAt : "",
|
|
1689
|
+
providers,
|
|
1690
|
+
proxyProfiles,
|
|
1691
|
+
defaultProxy: normalizeEndpoint(root.defaultProxy ?? {}),
|
|
1692
|
+
localDirect: root.localDirect !== false,
|
|
1693
|
+
testUrl: typeof root.testUrl === "string" && root.testUrl.trim().length > 0 ? root.testUrl : FALLBACK_TEST_URL
|
|
1694
|
+
};
|
|
1695
|
+
return { ok: true, document };
|
|
1696
|
+
}
|
|
1697
|
+
function backupOps(document) {
|
|
1698
|
+
return [
|
|
1699
|
+
{ op: "set", path: ["providers"], value: document.providers },
|
|
1700
|
+
{ op: "set", path: ["proxyProfiles"], value: document.proxyProfiles },
|
|
1701
|
+
{ op: "set", path: ["defaultProxy"], value: document.defaultProxy },
|
|
1702
|
+
{ op: "set", path: ["localDirect"], value: document.localDirect },
|
|
1703
|
+
{ op: "set", path: ["testUrl"], value: document.testUrl }
|
|
1704
|
+
];
|
|
1705
|
+
}
|
|
1706
|
+
function backupCounts(document) {
|
|
1707
|
+
return {
|
|
1708
|
+
providers: Object.keys(document.providers).length,
|
|
1709
|
+
profiles: Object.keys(document.proxyProfiles).length,
|
|
1710
|
+
enabled: Object.values(document.providers).filter((route) => route.enabled).length
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
function validateEnabledRoutes(document) {
|
|
1714
|
+
for (const [id, route] of Object.entries(document.providers)) {
|
|
1715
|
+
if (!route.enabled) continue;
|
|
1716
|
+
if (route.profileId.length > 0 || route.useDefault) continue;
|
|
1717
|
+
const hostProblem = proxyHostProblem(route.host);
|
|
1718
|
+
if (hostProblem !== void 0) {
|
|
1719
|
+
return {
|
|
1720
|
+
provider: id,
|
|
1721
|
+
problem: hostProblem === "\u672A\u586B\u5199\u4EE3\u7406\u5730\u5740" ? `provider "${id}" \u542F\u7528\u4E86\u4EE3\u7406\uFF0C\u4F46\u672A\u586B\u5199\u4EE3\u7406\u5730\u5740` : `provider "${id}" \u7684${hostProblem}`
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
if (!Number.isInteger(route.port) || route.port < 1 || route.port > 65535) {
|
|
1725
|
+
return { provider: id, problem: `provider "${id}" \u7684\u4EE3\u7406\u7AEF\u53E3\u65E0\u6548\uFF08\u9700\u4E3A 1-65535 \u7684\u6574\u6570\uFF09` };
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
return void 0;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1365
1731
|
// src/index.ts
|
|
1366
1732
|
var name = "dsh-provider-proxy";
|
|
1367
1733
|
var inject = ["llm"];
|
|
1368
1734
|
var WEB_SEARCH_KEY = "web-search";
|
|
1369
1735
|
function plainProviders(config) {
|
|
1370
|
-
|
|
1371
|
-
|
|
1736
|
+
return plainDict(config?.providers, {});
|
|
1737
|
+
}
|
|
1738
|
+
function plainDict(field, fallback) {
|
|
1739
|
+
const raw = field;
|
|
1740
|
+
if (raw === void 0) return fallback;
|
|
1372
1741
|
const snapshot2 = raw.get;
|
|
1373
1742
|
const value = typeof snapshot2 === "function" ? snapshot2.call(raw) : raw;
|
|
1374
|
-
return value ??
|
|
1743
|
+
return value ?? fallback;
|
|
1744
|
+
}
|
|
1745
|
+
function plainProfiles(config) {
|
|
1746
|
+
return plainDict(config?.proxyProfiles, {});
|
|
1375
1747
|
}
|
|
1376
1748
|
function plainDefaultProxy(config) {
|
|
1377
1749
|
const raw = config?.defaultProxy;
|
|
@@ -1674,7 +2046,7 @@ function apply(ctx, config) {
|
|
|
1674
2046
|
};
|
|
1675
2047
|
const selfCheckKey = (provider) => {
|
|
1676
2048
|
const raw = plainProviders(config)[provider] ?? {};
|
|
1677
|
-
const endpoint = raw
|
|
2049
|
+
const endpoint = effectiveRoute(raw, plainDefaultProxy(config), plainProfiles(config));
|
|
1678
2050
|
return JSON.stringify([
|
|
1679
2051
|
provider,
|
|
1680
2052
|
endpoint.protocol ?? "http",
|
|
@@ -1692,20 +2064,25 @@ function apply(ctx, config) {
|
|
|
1692
2064
|
const runSelfCheck = async (provider) => {
|
|
1693
2065
|
let route = current.get(provider);
|
|
1694
2066
|
let temporary = false;
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
2067
|
+
const saved = plainProviders(config)[provider];
|
|
2068
|
+
const buildOptions = {
|
|
2069
|
+
defaultProxy: normalizeDefaultProxy(plainDefaultProxy(config)),
|
|
2070
|
+
profiles: plainProfiles(config),
|
|
2071
|
+
requireEnabled: false,
|
|
2072
|
+
localDirect: plainLocalDirect(config)
|
|
2073
|
+
};
|
|
2074
|
+
if (route !== void 0) {
|
|
2075
|
+
const expected = saved === void 0 || saved.enabled !== true ? void 0 : buildRouteEntry(provider, saved, buildOptions);
|
|
2076
|
+
if (expected === void 0 || !expected.ok || expected.entry.proxyOrigin !== route.proxyOrigin) {
|
|
2077
|
+
route = void 0;
|
|
1699
2078
|
}
|
|
1700
|
-
|
|
1701
|
-
|
|
2079
|
+
}
|
|
2080
|
+
if (route === void 0) {
|
|
2081
|
+
const configured = saved;
|
|
2082
|
+
if (configured === void 0) {
|
|
1702
2083
|
return { ok: false, kind: "no-route", error: `provider "${provider}" \u672A\u542F\u7528\u4EE3\u7406` };
|
|
1703
2084
|
}
|
|
1704
|
-
const built = buildRouteEntry(provider, configured,
|
|
1705
|
-
defaultProxy: normalizeDefaultProxy(plainDefaultProxy(config)),
|
|
1706
|
-
requireEnabled: false,
|
|
1707
|
-
localDirect: plainLocalDirect(config)
|
|
1708
|
-
});
|
|
2085
|
+
const built = buildRouteEntry(provider, configured, buildOptions);
|
|
1709
2086
|
if (!built.ok) {
|
|
1710
2087
|
return { ok: false, kind: "route-rejected", error: bareMessage(built.problem) };
|
|
1711
2088
|
}
|
|
@@ -1733,8 +2110,9 @@ function apply(ctx, config) {
|
|
|
1733
2110
|
const rebuild = () => {
|
|
1734
2111
|
const providers = plainProviders(config);
|
|
1735
2112
|
const defaultProxy = plainDefaultProxy(config);
|
|
2113
|
+
const profiles = plainProfiles(config);
|
|
1736
2114
|
const localDirect = plainLocalDirect(config);
|
|
1737
|
-
const next = createProxyTable(providers, { defaultProxy, localDirect });
|
|
2115
|
+
const next = createProxyTable(providers, { defaultProxy, profiles, localDirect });
|
|
1738
2116
|
const previous = current;
|
|
1739
2117
|
current = next.entries;
|
|
1740
2118
|
problems = next.problems;
|
|
@@ -1746,7 +2124,7 @@ function apply(ctx, config) {
|
|
|
1746
2124
|
forgetChecks(provider);
|
|
1747
2125
|
}
|
|
1748
2126
|
for (const [provider, raw] of Object.entries(providers)) {
|
|
1749
|
-
const effective = raw
|
|
2127
|
+
const effective = effectiveRoute(raw, defaultProxy, profiles);
|
|
1750
2128
|
const fingerprint = routeFingerprint({
|
|
1751
2129
|
...effective,
|
|
1752
2130
|
localDirect
|
|
@@ -1813,6 +2191,15 @@ function apply(ctx, config) {
|
|
|
1813
2191
|
out.sort((a, b) => a.name.localeCompare(b.name));
|
|
1814
2192
|
return { providers: out };
|
|
1815
2193
|
};
|
|
2194
|
+
let environmentDetected = false;
|
|
2195
|
+
let environmentProxy;
|
|
2196
|
+
const environmentProxyOf = () => {
|
|
2197
|
+
if (!environmentDetected) {
|
|
2198
|
+
environmentProxy = detectProxyEnv();
|
|
2199
|
+
environmentDetected = true;
|
|
2200
|
+
}
|
|
2201
|
+
return environmentProxy;
|
|
2202
|
+
};
|
|
1816
2203
|
ctx.inject?.(["webServer"], (wctx) => {
|
|
1817
2204
|
try {
|
|
1818
2205
|
wctx?.webServer?.register?.({
|
|
@@ -1822,6 +2209,7 @@ function apply(ctx, config) {
|
|
|
1822
2209
|
try {
|
|
1823
2210
|
const text = JSON.stringify({
|
|
1824
2211
|
...catalog(),
|
|
2212
|
+
environment: { proxy: environmentProxyOf() ?? null },
|
|
1825
2213
|
problems: Object.fromEntries(
|
|
1826
2214
|
[...problems].map(([id, message]) => [id, bareMessage(message)])
|
|
1827
2215
|
),
|
|
@@ -2023,27 +2411,50 @@ function apply(ctx, config) {
|
|
|
2023
2411
|
}
|
|
2024
2412
|
export {
|
|
2025
2413
|
Config,
|
|
2414
|
+
DEFAULT_PROFILE_LABEL,
|
|
2026
2415
|
PROTOCOLS,
|
|
2416
|
+
PROVIDER_HOST_TEMPLATES,
|
|
2417
|
+
PROXY_CLIENT_PRESETS,
|
|
2027
2418
|
ProviderProxyRoute,
|
|
2028
2419
|
WEB_SEARCH_KEY,
|
|
2029
2420
|
acquireFetchRouter,
|
|
2421
|
+
appendHostRules,
|
|
2030
2422
|
apply,
|
|
2423
|
+
backupCounts,
|
|
2424
|
+
backupFileName,
|
|
2425
|
+
backupOps,
|
|
2426
|
+
bindingFields,
|
|
2427
|
+
buildBackup,
|
|
2031
2428
|
buildRouteEntry,
|
|
2032
2429
|
closeTable,
|
|
2033
2430
|
createProxyTable,
|
|
2034
2431
|
deadRuleEntries,
|
|
2035
2432
|
defaultProxyProblem,
|
|
2433
|
+
detectProxyEnv,
|
|
2434
|
+
effectiveRoute,
|
|
2435
|
+
endpointSummary,
|
|
2036
2436
|
hostMatches,
|
|
2037
2437
|
hostRuleProblem,
|
|
2038
2438
|
inject,
|
|
2039
2439
|
name,
|
|
2040
2440
|
normalizeDefaultProxy,
|
|
2041
2441
|
normalizeRoute,
|
|
2442
|
+
parseBackup,
|
|
2042
2443
|
parseHostList,
|
|
2444
|
+
parseProxyEnvValue,
|
|
2445
|
+
profileNameProblem,
|
|
2446
|
+
profileNames,
|
|
2447
|
+
providerHostTemplate,
|
|
2448
|
+
proxyClientPreset,
|
|
2043
2449
|
proxyHostProblem,
|
|
2044
2450
|
proxyUrl,
|
|
2451
|
+
resolveEndpoint,
|
|
2452
|
+
routeBindingOf,
|
|
2045
2453
|
routeFingerprint,
|
|
2046
2454
|
routeStateDecision,
|
|
2047
|
-
|
|
2455
|
+
routesBoundTo,
|
|
2456
|
+
splitHostRules,
|
|
2457
|
+
validateEnabledRoutes,
|
|
2458
|
+
withEndpoint
|
|
2048
2459
|
};
|
|
2049
2460
|
//# sourceMappingURL=index.js.map
|