dsh-provider-proxy 0.3.0 → 0.4.1
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 +21 -5
- package/index.js +5 -5
- package/lib/client.js +240 -21
- package/lib/client.js.map +3 -3
- package/lib/index.js +291 -14
- package/lib/index.js.map +4 -4
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -4,10 +4,24 @@ var __export = (target, all) => {
|
|
|
4
4
|
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
+
// src/index.ts
|
|
8
|
+
import { fetch as undiciFetch2 } from "undici";
|
|
9
|
+
|
|
7
10
|
// src/fetch-router.ts
|
|
8
11
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
9
12
|
import { fetch as undiciFetch } from "undici";
|
|
10
13
|
var STATE_KEY = /* @__PURE__ */ Symbol.for("dsh-provider-proxy.fetch-router.v1");
|
|
14
|
+
function requestHost(input) {
|
|
15
|
+
try {
|
|
16
|
+
if (typeof input === "string") return new URL(input).hostname;
|
|
17
|
+
if (input instanceof URL) return input.hostname;
|
|
18
|
+
const url = input?.url;
|
|
19
|
+
if (typeof url === "string") return new URL(url).hostname;
|
|
20
|
+
} catch {
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
return void 0;
|
|
24
|
+
}
|
|
11
25
|
function installSharedRouter() {
|
|
12
26
|
const current = globalThis[STATE_KEY];
|
|
13
27
|
if (current !== void 0) {
|
|
@@ -26,6 +40,10 @@ function installSharedRouter() {
|
|
|
26
40
|
if (init !== void 0 && init !== null && init.dispatcher !== void 0) {
|
|
27
41
|
return originalFetch.call(globalThis, input, init);
|
|
28
42
|
}
|
|
43
|
+
if (active.decide !== void 0 && active.decide(requestHost(input)) === "direct") {
|
|
44
|
+
return originalFetch.call(globalThis, input, init);
|
|
45
|
+
}
|
|
46
|
+
active.onHit?.();
|
|
29
47
|
return undiciFetch(input, {
|
|
30
48
|
...init,
|
|
31
49
|
dispatcher: active.dispatcher
|
|
@@ -81,11 +99,11 @@ function acquireFetchRouter() {
|
|
|
81
99
|
const state = installSharedRouter();
|
|
82
100
|
let released = false;
|
|
83
101
|
return {
|
|
84
|
-
run(
|
|
85
|
-
return state.storage.run(
|
|
102
|
+
run(store, callback) {
|
|
103
|
+
return state.storage.run(store, callback);
|
|
86
104
|
},
|
|
87
|
-
wrap(
|
|
88
|
-
return contextualIterable(state.storage,
|
|
105
|
+
wrap(store, factory) {
|
|
106
|
+
return contextualIterable(state.storage, store, factory);
|
|
89
107
|
},
|
|
90
108
|
release() {
|
|
91
109
|
if (released) return;
|
|
@@ -1011,16 +1029,31 @@ var ProviderProxyRoute = z.object({
|
|
|
1011
1029
|
/** Optional proxy username. */
|
|
1012
1030
|
username: z.string().default(""),
|
|
1013
1031
|
/** SOCKS5 proxy password. Only used when `protocol` is `socks5`. */
|
|
1014
|
-
password: z.string().default("")
|
|
1032
|
+
password: z.string().default(""),
|
|
1033
|
+
/**
|
|
1034
|
+
* Comma/space-separated TARGET hosts that must go through the proxy. Empty
|
|
1035
|
+
* means "no restriction" (every target is proxied). Suffix match.
|
|
1036
|
+
*/
|
|
1037
|
+
onlyHosts: z.string().default(""),
|
|
1038
|
+
/** Comma/space-separated TARGET hosts that bypass the proxy; takes priority over `onlyHosts`. */
|
|
1039
|
+
bypassHosts: z.string().default("")
|
|
1015
1040
|
});
|
|
1016
1041
|
var Config = z.object({
|
|
1017
1042
|
/** Per-provider proxy routes, keyed by provider route id (e.g. `sensenova`). */
|
|
1018
|
-
providers: vol(z.dict(ProviderProxyRoute).default({}))
|
|
1043
|
+
providers: vol(z.dict(ProviderProxyRoute).default({})),
|
|
1044
|
+
/**
|
|
1045
|
+
* URL the per-provider connection self-check requests THROUGH the proxy.
|
|
1046
|
+
* Volatile, so a settings write does not remount the plugin; the default is an
|
|
1047
|
+
* IP echo service, which lets the check report the exit address.
|
|
1048
|
+
*/
|
|
1049
|
+
testUrl: vol(z.string().default("https://api.ipify.org?format=json"))
|
|
1019
1050
|
});
|
|
1020
1051
|
function normalizeRoute(provider, raw) {
|
|
1021
1052
|
const route = ProviderProxyRoute(raw ?? {});
|
|
1022
1053
|
const value = typeof route.get === "function" ? route.get() : route;
|
|
1023
|
-
|
|
1054
|
+
const onlyHosts = value.onlyHosts.trim().toLowerCase();
|
|
1055
|
+
const bypassHosts = value.bypassHosts.trim().toLowerCase();
|
|
1056
|
+
if (!value.enabled) return { ...value, port: value.port || 0, onlyHosts, bypassHosts };
|
|
1024
1057
|
const host = value.host.trim();
|
|
1025
1058
|
if (host.length === 0) {
|
|
1026
1059
|
throw new TypeError(`dsh-provider-proxy: provider "${provider}" \u542F\u7528\u4E86\u4EE3\u7406\uFF0C\u4F46\u672A\u586B\u5199\u4EE3\u7406\u5730\u5740`);
|
|
@@ -1033,14 +1066,14 @@ function normalizeRoute(provider, raw) {
|
|
|
1033
1066
|
`dsh-provider-proxy: provider "${provider}" \u7684\u4EE3\u7406\u7AEF\u53E3\u65E0\u6548\uFF08\u9700\u4E3A 1-65535 \u7684\u6574\u6570\uFF09`
|
|
1034
1067
|
);
|
|
1035
1068
|
}
|
|
1036
|
-
return { ...value, host, username: value.username.trim() };
|
|
1069
|
+
return { ...value, host, username: value.username.trim(), onlyHosts, bypassHosts };
|
|
1037
1070
|
}
|
|
1038
1071
|
|
|
1039
1072
|
// src/proxy-table.ts
|
|
1040
1073
|
function proxyUrl(route) {
|
|
1041
|
-
const
|
|
1074
|
+
const credentials = route.username.length === 0 ? "" : route.password.length > 0 && route.protocol !== "socks5" ? `${encodeURIComponent(route.username)}:${encodeURIComponent(route.password)}@` : `${encodeURIComponent(route.username)}@`;
|
|
1042
1075
|
const host = route.host.includes(":") && !route.host.startsWith("[") ? `[${route.host}]` : route.host;
|
|
1043
|
-
return `${route.protocol}://${
|
|
1076
|
+
return `${route.protocol}://${credentials}${host}:${route.port}`;
|
|
1044
1077
|
}
|
|
1045
1078
|
function displayOrigin(route, url) {
|
|
1046
1079
|
if (route.protocol !== "socks5") return new URL(url).origin;
|
|
@@ -1056,6 +1089,26 @@ function createDispatcher(route, url, password) {
|
|
|
1056
1089
|
}
|
|
1057
1090
|
return new ProxyAgent({ uri: url });
|
|
1058
1091
|
}
|
|
1092
|
+
function parseHostList(raw) {
|
|
1093
|
+
return raw.split(/[,\s]+/).map((entry) => entry.trim().replace(/^\*?\./, "")).filter((entry) => entry.length > 0);
|
|
1094
|
+
}
|
|
1095
|
+
function hostMatches(host, rules) {
|
|
1096
|
+
for (const rule of rules) {
|
|
1097
|
+
if (rule === "*") return true;
|
|
1098
|
+
if (host === rule || host.endsWith(`.${rule}`)) return true;
|
|
1099
|
+
}
|
|
1100
|
+
return false;
|
|
1101
|
+
}
|
|
1102
|
+
function buildDecide(route) {
|
|
1103
|
+
const only = parseHostList(route.onlyHosts);
|
|
1104
|
+
const bypass = parseHostList(route.bypassHosts);
|
|
1105
|
+
return (host) => {
|
|
1106
|
+
if (host === void 0) return "proxy";
|
|
1107
|
+
if (hostMatches(host, bypass)) return "direct";
|
|
1108
|
+
if (only.length > 0 && !hostMatches(host, only)) return "direct";
|
|
1109
|
+
return "proxy";
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1059
1112
|
function createProxyTable(config) {
|
|
1060
1113
|
const entries = /* @__PURE__ */ new Map();
|
|
1061
1114
|
const problems = /* @__PURE__ */ new Map();
|
|
@@ -1075,6 +1128,27 @@ function createProxyTable(config) {
|
|
|
1075
1128
|
);
|
|
1076
1129
|
continue;
|
|
1077
1130
|
}
|
|
1131
|
+
if (route.protocol === "socks5" && route.password.length > 0 && route.username.length === 0) {
|
|
1132
|
+
problems.set(
|
|
1133
|
+
provider,
|
|
1134
|
+
"dsh-provider-proxy: SOCKS5 \u914D\u7F6E\u4E86\u5BC6\u7801\u4F46\u6CA1\u6709\u7528\u6237\u540D\uFF1B\u8BF7\u8865\u5168\u7528\u6237\u540D\u6216\u6E05\u7A7A\u5BC6\u7801"
|
|
1135
|
+
);
|
|
1136
|
+
continue;
|
|
1137
|
+
}
|
|
1138
|
+
if (route.protocol !== "socks5" && route.password.length > 0 && route.username.length === 0) {
|
|
1139
|
+
problems.set(
|
|
1140
|
+
provider,
|
|
1141
|
+
"dsh-provider-proxy: \u914D\u7F6E\u4E86\u4EE3\u7406\u5BC6\u7801\u4F46\u6CA1\u6709\u7528\u6237\u540D\uFF1B\u8BF7\u8865\u5168\u7528\u6237\u540D\u6216\u6E05\u7A7A\u5BC6\u7801"
|
|
1142
|
+
);
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
1145
|
+
if (route.protocol !== "socks5" && route.username.length > 0 && route.password.length === 0) {
|
|
1146
|
+
problems.set(
|
|
1147
|
+
provider,
|
|
1148
|
+
"dsh-provider-proxy: \u914D\u7F6E\u4E86\u4EE3\u7406\u7528\u6237\u540D\u4F46\u6CA1\u6709\u5BC6\u7801\uFF1B\u8BF7\u8865\u5168\u5BC6\u7801\u6216\u6E05\u7A7A\u7528\u6237\u540D"
|
|
1149
|
+
);
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1078
1152
|
const url = proxyUrl(route);
|
|
1079
1153
|
let dispatcher;
|
|
1080
1154
|
try {
|
|
@@ -1083,7 +1157,12 @@ function createProxyTable(config) {
|
|
|
1083
1157
|
problems.set(provider, error instanceof Error ? error.message : String(error));
|
|
1084
1158
|
continue;
|
|
1085
1159
|
}
|
|
1086
|
-
entries.set(provider, {
|
|
1160
|
+
entries.set(provider, {
|
|
1161
|
+
provider,
|
|
1162
|
+
proxyOrigin: displayOrigin(route, url),
|
|
1163
|
+
dispatcher,
|
|
1164
|
+
decide: buildDecide(route)
|
|
1165
|
+
});
|
|
1087
1166
|
}
|
|
1088
1167
|
return { entries, problems };
|
|
1089
1168
|
}
|
|
@@ -1096,6 +1175,7 @@ async function closeTable(entries) {
|
|
|
1096
1175
|
// src/index.ts
|
|
1097
1176
|
var name = "dsh-provider-proxy";
|
|
1098
1177
|
var inject = ["llm"];
|
|
1178
|
+
var WEB_SEARCH_KEY = "web-search";
|
|
1099
1179
|
function plainProviders(config) {
|
|
1100
1180
|
const raw = config?.providers;
|
|
1101
1181
|
if (raw === void 0) return {};
|
|
@@ -1108,6 +1188,92 @@ function apply(ctx, config) {
|
|
|
1108
1188
|
let current = /* @__PURE__ */ new Map();
|
|
1109
1189
|
let problems = /* @__PURE__ */ new Map();
|
|
1110
1190
|
let closed = false;
|
|
1191
|
+
let webAvailable = false;
|
|
1192
|
+
const hits = /* @__PURE__ */ new Map();
|
|
1193
|
+
const bump = (provider) => {
|
|
1194
|
+
hits.set(provider, (hits.get(provider) ?? 0) + 1);
|
|
1195
|
+
};
|
|
1196
|
+
const DEFAULT_TEST_URL = "https://api.ipify.org?format=json";
|
|
1197
|
+
const TEST_TIMEOUT_MS = 1e4;
|
|
1198
|
+
const MAX_REQUEST_BYTES = 8 * 1024;
|
|
1199
|
+
const selfChecks = /* @__PURE__ */ new Map();
|
|
1200
|
+
const plainTestUrl = () => {
|
|
1201
|
+
const raw = config?.testUrl;
|
|
1202
|
+
const snapshot2 = raw?.get;
|
|
1203
|
+
const value = typeof snapshot2 === "function" ? snapshot2.call(raw) : raw;
|
|
1204
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : DEFAULT_TEST_URL;
|
|
1205
|
+
};
|
|
1206
|
+
const bareMessage = (message) => message.replace(/^dsh-provider-proxy:\s*/, "");
|
|
1207
|
+
const causesOf = (error) => {
|
|
1208
|
+
const out = [];
|
|
1209
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1210
|
+
const queue = [error];
|
|
1211
|
+
while (queue.length > 0) {
|
|
1212
|
+
const node = queue.shift();
|
|
1213
|
+
if (node === null || typeof node !== "object" || seen.has(node)) continue;
|
|
1214
|
+
seen.add(node);
|
|
1215
|
+
out.push(node);
|
|
1216
|
+
const cause = node.cause;
|
|
1217
|
+
if (cause !== void 0 && cause !== null) queue.push(cause);
|
|
1218
|
+
const errors = node.errors;
|
|
1219
|
+
if (Array.isArray(errors)) queue.push(...errors);
|
|
1220
|
+
}
|
|
1221
|
+
return out;
|
|
1222
|
+
};
|
|
1223
|
+
const classifyTestError = (error) => {
|
|
1224
|
+
const chain = causesOf(error);
|
|
1225
|
+
const names = chain.map((node) => node.name).filter((name2) => typeof name2 === "string");
|
|
1226
|
+
const codes = chain.map((node) => node.code).filter((code2) => typeof code2 === "string");
|
|
1227
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1228
|
+
if (names.includes("TimeoutError") || names.includes("AbortError")) {
|
|
1229
|
+
return { kind: "timeout", error: `\u8D85\u65F6\uFF08${TEST_TIMEOUT_MS / 1e3}s\uFF09\uFF1A\u4EE3\u7406\u6216\u76EE\u6807\u65E0\u54CD\u5E94` };
|
|
1230
|
+
}
|
|
1231
|
+
const connectivity = ["ECONNREFUSED", "ENOTFOUND", "EHOSTUNREACH", "ENETUNREACH", "ECONNRESET", "EAI_AGAIN"];
|
|
1232
|
+
const code = connectivity.find((candidate) => codes.includes(candidate));
|
|
1233
|
+
if (code !== void 0) {
|
|
1234
|
+
return { kind: "proxy-unreachable", error: `\u4EE3\u7406\u4E0D\u53EF\u8FBE\uFF08${code}\uFF09\uFF1A${message}` };
|
|
1235
|
+
}
|
|
1236
|
+
if (names.includes("ProxyAuthentication") || message.includes("407")) {
|
|
1237
|
+
return { kind: "proxy-auth", error: `\u4EE3\u7406\u8981\u6C42\u8BA4\u8BC1\uFF08HTTP 407\uFF09\uFF1A${message}` };
|
|
1238
|
+
}
|
|
1239
|
+
return { kind: "target", error: `\u8BF7\u6C42\u5931\u8D25\uFF1A${message}` };
|
|
1240
|
+
};
|
|
1241
|
+
const runSelfCheck = async (provider) => {
|
|
1242
|
+
const route = current.get(provider);
|
|
1243
|
+
if (route === void 0) {
|
|
1244
|
+
const problem = problems.get(provider);
|
|
1245
|
+
if (problem !== void 0) {
|
|
1246
|
+
return { ok: false, kind: "route-rejected", error: bareMessage(problem) };
|
|
1247
|
+
}
|
|
1248
|
+
return { ok: false, kind: "no-route", error: `provider "${provider}" \u672A\u542F\u7528\u4EE3\u7406` };
|
|
1249
|
+
}
|
|
1250
|
+
const started = performance.now();
|
|
1251
|
+
try {
|
|
1252
|
+
const response = await undiciFetch2(plainTestUrl(), {
|
|
1253
|
+
dispatcher: route.dispatcher,
|
|
1254
|
+
signal: AbortSignal.timeout(TEST_TIMEOUT_MS)
|
|
1255
|
+
});
|
|
1256
|
+
const ms = Math.round(performance.now() - started);
|
|
1257
|
+
if (response.status === 407) {
|
|
1258
|
+
return { ok: false, ms, status: 407, kind: "proxy-auth", error: "\u4EE3\u7406\u8981\u6C42\u8BA4\u8BC1\uFF08HTTP 407\uFF09" };
|
|
1259
|
+
}
|
|
1260
|
+
let exitIp;
|
|
1261
|
+
try {
|
|
1262
|
+
const body = await response.json();
|
|
1263
|
+
if (typeof body?.ip === "string" && body.ip.length > 0) exitIp = body.ip;
|
|
1264
|
+
} catch {
|
|
1265
|
+
}
|
|
1266
|
+
return {
|
|
1267
|
+
ok: response.ok === true,
|
|
1268
|
+
ms,
|
|
1269
|
+
status: response.status,
|
|
1270
|
+
...exitIp === void 0 ? {} : { exitIp },
|
|
1271
|
+
kind: response.ok === true ? "ok" : "target"
|
|
1272
|
+
};
|
|
1273
|
+
} catch (error) {
|
|
1274
|
+
return { ok: false, ms: Math.round(performance.now() - started), ...classifyTestError(error) };
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1111
1277
|
try {
|
|
1112
1278
|
ctx.logger.info("dsh-provider-proxy mounted (pid %d)", process.pid);
|
|
1113
1279
|
} catch {
|
|
@@ -1147,6 +1313,9 @@ function apply(ctx, config) {
|
|
|
1147
1313
|
}
|
|
1148
1314
|
} catch {
|
|
1149
1315
|
}
|
|
1316
|
+
if (webAvailable && out.every((row) => row.id !== WEB_SEARCH_KEY)) {
|
|
1317
|
+
out.push({ id: WEB_SEARCH_KEY, name: "Web \u641C\u7D22", live: true });
|
|
1318
|
+
}
|
|
1150
1319
|
out.sort((a, b) => a.name.localeCompare(b.name));
|
|
1151
1320
|
return { providers: out };
|
|
1152
1321
|
};
|
|
@@ -1157,7 +1326,13 @@ function apply(ctx, config) {
|
|
|
1157
1326
|
path: "/dsh-provider-proxy/providers",
|
|
1158
1327
|
handler: (_req, res) => {
|
|
1159
1328
|
try {
|
|
1160
|
-
const text = JSON.stringify({
|
|
1329
|
+
const text = JSON.stringify({
|
|
1330
|
+
...catalog(),
|
|
1331
|
+
problems: Object.fromEntries(
|
|
1332
|
+
[...problems].map(([id, message]) => [id, bareMessage(message)])
|
|
1333
|
+
),
|
|
1334
|
+
hits: Object.fromEntries(hits)
|
|
1335
|
+
});
|
|
1161
1336
|
res.writeHead(200, {
|
|
1162
1337
|
"content-type": "application/json; charset=utf-8",
|
|
1163
1338
|
"cache-control": "no-store",
|
|
@@ -1170,10 +1345,103 @@ function apply(ctx, config) {
|
|
|
1170
1345
|
}
|
|
1171
1346
|
}
|
|
1172
1347
|
});
|
|
1348
|
+
wctx?.webServer?.register?.({
|
|
1349
|
+
kind: "exact",
|
|
1350
|
+
path: "/dsh-provider-proxy/test",
|
|
1351
|
+
handler: (req, res) => {
|
|
1352
|
+
let replied = false;
|
|
1353
|
+
const send = (status, payload) => {
|
|
1354
|
+
if (replied) return;
|
|
1355
|
+
replied = true;
|
|
1356
|
+
const text = JSON.stringify(payload);
|
|
1357
|
+
res.writeHead(status, {
|
|
1358
|
+
"content-type": "application/json; charset=utf-8",
|
|
1359
|
+
"cache-control": "no-store",
|
|
1360
|
+
"content-length": Buffer.byteLength(text)
|
|
1361
|
+
});
|
|
1362
|
+
res.end(text);
|
|
1363
|
+
};
|
|
1364
|
+
if (req?.method !== "POST") {
|
|
1365
|
+
send(405, { ok: false, kind: "method", error: "\u81EA\u68C0\u7AEF\u70B9\u53EA\u63A5\u53D7 POST" });
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
if (typeof req.on !== "function") {
|
|
1369
|
+
send(400, { ok: false, kind: "body", error: "\u65E0\u6CD5\u8BFB\u53D6\u8BF7\u6C42\u4F53" });
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
let bytes = 0;
|
|
1373
|
+
const chunks = [];
|
|
1374
|
+
req.on("data", (chunk) => {
|
|
1375
|
+
if (replied) return;
|
|
1376
|
+
bytes += chunk.length;
|
|
1377
|
+
if (bytes > MAX_REQUEST_BYTES) {
|
|
1378
|
+
send(413, {
|
|
1379
|
+
ok: false,
|
|
1380
|
+
kind: "body",
|
|
1381
|
+
error: `\u8BF7\u6C42\u4F53\u8D85\u8FC7 ${MAX_REQUEST_BYTES} \u5B57\u8282\u4E0A\u9650`
|
|
1382
|
+
});
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
chunks.push(chunk);
|
|
1386
|
+
});
|
|
1387
|
+
req.on("error", () => send(400, { ok: false, kind: "body", error: "\u8BFB\u53D6\u8BF7\u6C42\u4F53\u5931\u8D25" }));
|
|
1388
|
+
req.on("end", () => {
|
|
1389
|
+
if (replied) return;
|
|
1390
|
+
void (async () => {
|
|
1391
|
+
let provider;
|
|
1392
|
+
try {
|
|
1393
|
+
const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
|
|
1394
|
+
provider = parsed?.provider;
|
|
1395
|
+
} catch {
|
|
1396
|
+
send(400, { ok: false, kind: "body", error: "\u8BF7\u6C42\u4F53\u4E0D\u662F\u5408\u6CD5 JSON" });
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
if (typeof provider !== "string" || provider.length === 0) {
|
|
1400
|
+
send(400, { ok: false, kind: "body", error: "\u7F3A\u5C11 provider" });
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
let pending = selfChecks.get(provider);
|
|
1404
|
+
if (pending === void 0) {
|
|
1405
|
+
pending = runSelfCheck(provider);
|
|
1406
|
+
selfChecks.set(provider, pending);
|
|
1407
|
+
void pending.finally(() => {
|
|
1408
|
+
if (selfChecks.get(provider) === pending) selfChecks.delete(provider);
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
send(200, await pending);
|
|
1412
|
+
})().catch((error) => {
|
|
1413
|
+
send(500, { ok: false, kind: "internal", error: String(error) });
|
|
1414
|
+
});
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
});
|
|
1173
1418
|
} catch (error) {
|
|
1174
1419
|
void error;
|
|
1175
1420
|
}
|
|
1176
1421
|
});
|
|
1422
|
+
ctx.inject?.(["web"], (wctx) => {
|
|
1423
|
+
const web = wctx?.web;
|
|
1424
|
+
if (web === void 0 || typeof web.search !== "function") return;
|
|
1425
|
+
webAvailable = true;
|
|
1426
|
+
const original = web.search;
|
|
1427
|
+
const patched = function(request, signal) {
|
|
1428
|
+
const route = current.get(WEB_SEARCH_KEY);
|
|
1429
|
+
if (route === void 0) return original.call(this, request, signal);
|
|
1430
|
+
return router.run(
|
|
1431
|
+
{
|
|
1432
|
+
dispatcher: route.dispatcher,
|
|
1433
|
+
onHit: () => bump(WEB_SEARCH_KEY),
|
|
1434
|
+
decide: route.decide
|
|
1435
|
+
},
|
|
1436
|
+
() => original.call(this, request, signal)
|
|
1437
|
+
);
|
|
1438
|
+
};
|
|
1439
|
+
web.search = patched;
|
|
1440
|
+
const registerEffect = typeof wctx?.effect === "function" ? wctx.effect.bind(wctx) : ctx.effect?.bind(ctx);
|
|
1441
|
+
registerEffect?.(() => () => {
|
|
1442
|
+
if (Object.getOwnPropertyDescriptor(web, "search")?.value === patched) delete web.search;
|
|
1443
|
+
});
|
|
1444
|
+
});
|
|
1177
1445
|
ctx.effect(() => {
|
|
1178
1446
|
const stop = ctx.on(
|
|
1179
1447
|
"llm/stream",
|
|
@@ -1182,7 +1450,14 @@ function apply(ctx, config) {
|
|
|
1182
1450
|
if (typeof provider !== "string") return next();
|
|
1183
1451
|
const route = current.get(provider);
|
|
1184
1452
|
if (route === void 0) return next();
|
|
1185
|
-
return router.wrap(
|
|
1453
|
+
return router.wrap(
|
|
1454
|
+
{
|
|
1455
|
+
dispatcher: route.dispatcher,
|
|
1456
|
+
onHit: () => bump(provider),
|
|
1457
|
+
decide: route.decide
|
|
1458
|
+
},
|
|
1459
|
+
next
|
|
1460
|
+
);
|
|
1186
1461
|
},
|
|
1187
1462
|
{ global: true }
|
|
1188
1463
|
);
|
|
@@ -1207,7 +1482,8 @@ function apply(ctx, config) {
|
|
|
1207
1482
|
provider: entry.provider,
|
|
1208
1483
|
proxyOrigin: entry.proxyOrigin
|
|
1209
1484
|
})),
|
|
1210
|
-
problems: Object.fromEntries(problems)
|
|
1485
|
+
problems: Object.fromEntries(problems),
|
|
1486
|
+
hits: Object.fromEntries(hits)
|
|
1211
1487
|
})
|
|
1212
1488
|
});
|
|
1213
1489
|
}
|
|
@@ -1215,6 +1491,7 @@ export {
|
|
|
1215
1491
|
Config,
|
|
1216
1492
|
PROTOCOLS,
|
|
1217
1493
|
ProviderProxyRoute,
|
|
1494
|
+
WEB_SEARCH_KEY,
|
|
1218
1495
|
acquireFetchRouter,
|
|
1219
1496
|
apply,
|
|
1220
1497
|
closeTable,
|