dsh-mobile 0.4.4 → 0.4.5
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/CHANGELOG.md +9 -0
- package/CONTRIBUTORS.md +4 -2
- package/README.en.md +10 -9
- package/README.md +10 -10
- package/SECURITY.md +2 -2
- package/lib/client.js +104 -7
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +9 -0
- package/lib/index.mjs +32 -14
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-compat.js +1 -1
- package/lib/mobile-compat.js.map +1 -1
- package/package.json +20 -20
package/lib/index.d.mts
CHANGED
|
@@ -1422,6 +1422,15 @@ declare class TaskEventHub {
|
|
|
1422
1422
|
}
|
|
1423
1423
|
//#endregion
|
|
1424
1424
|
//#region src/plugin.d.ts
|
|
1425
|
+
declare module '@deepseek-ai/dsh-llm' {
|
|
1426
|
+
interface MessageSourceMap {
|
|
1427
|
+
'plugin:dsh-mobile': {
|
|
1428
|
+
kind: 'plugin:dsh-mobile';
|
|
1429
|
+
form: 'notice';
|
|
1430
|
+
summary: string;
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1425
1434
|
/** Stable Cordis plugin name. */
|
|
1426
1435
|
declare const name = "dsh-mobile";
|
|
1427
1436
|
/** The stock WebServer serves the control card; Connection authenticates the loopback DSH origin. */
|
package/lib/index.mjs
CHANGED
|
@@ -2451,6 +2451,16 @@ const MOBILE_BOOT_SIZE_PROBE_HEADER = "x-dsh-mobile-size-probe";
|
|
|
2451
2451
|
const MAX_MOBILE_BOOT_BATCHES = 8;
|
|
2452
2452
|
const MOBILE_BOOT_UPSTREAM_ATTEMPTS = 4;
|
|
2453
2453
|
const MOBILE_BOOT_RETRY_DELAY_MS = 150;
|
|
2454
|
+
function upstreamPluginBundleUrl(source, upstreamOrigin) {
|
|
2455
|
+
if (!source.startsWith("/plugins/") && !source.startsWith("plugins/") || source.includes("#")) return void 0;
|
|
2456
|
+
try {
|
|
2457
|
+
const target = new URL(source, new URL("/", upstreamOrigin));
|
|
2458
|
+
if (target.origin !== upstreamOrigin.origin || !target.pathname.startsWith("/plugins/")) return void 0;
|
|
2459
|
+
return target;
|
|
2460
|
+
} catch {
|
|
2461
|
+
return;
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2454
2464
|
const TRANSIENT_UPSTREAM_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
2455
2465
|
"EAI_AGAIN",
|
|
2456
2466
|
"ECONNABORTED",
|
|
@@ -2870,8 +2880,9 @@ function shouldCompressResponse(request, response) {
|
|
|
2870
2880
|
const pathname = request.url?.split("?", 1)[0] ?? "";
|
|
2871
2881
|
return (request.method === "GET" && (pathname.startsWith("/plugins/") || pathname.startsWith("/assets/")) || request.method === "POST" && pathname === SESSION_HISTORY_PATH) && response.statusCode === 200 && request.headers.range === void 0 && response.headers["content-range"] === void 0 && response.headers["content-encoding"] === void 0 && acceptsGzip(request.headers["accept-encoding"]) && isCompressibleContentType(response.headers["content-type"]);
|
|
2872
2882
|
}
|
|
2873
|
-
function revisionedStaticCacheControl(request) {
|
|
2883
|
+
function revisionedStaticCacheControl(request, statusCode) {
|
|
2874
2884
|
if (request.method !== "GET" && request.method !== "HEAD") return void 0;
|
|
2885
|
+
if (statusCode !== 200) return void 0;
|
|
2875
2886
|
let target;
|
|
2876
2887
|
try {
|
|
2877
2888
|
target = new URL(request.url ?? "/", "https://dsh-mobile.invalid");
|
|
@@ -2991,6 +3002,12 @@ function discoveryBroadcastTargets(cidrs) {
|
|
|
2991
3002
|
}
|
|
2992
3003
|
return [...targets];
|
|
2993
3004
|
}
|
|
3005
|
+
/** Keep Node's upgrade-owned socket safe after the HTTP parser removes its listener. */
|
|
3006
|
+
function guardUpgradeSocket(socket) {
|
|
3007
|
+
socket.on("error", () => {
|
|
3008
|
+
if (!socket.destroyed) socket.destroy();
|
|
3009
|
+
});
|
|
3010
|
+
}
|
|
2994
3011
|
function discoveryFailure(error) {
|
|
2995
3012
|
const code = error instanceof Error && typeof error.code === "string" ? error.code : "unknown";
|
|
2996
3013
|
return Object.freeze({
|
|
@@ -3225,9 +3242,11 @@ var MobileAccessGateway = class {
|
|
|
3225
3242
|
socket.destroy();
|
|
3226
3243
|
});
|
|
3227
3244
|
server.on("upgrade", (request, socket, head) => {
|
|
3228
|
-
|
|
3245
|
+
const client = socket;
|
|
3246
|
+
guardUpgradeSocket(client);
|
|
3247
|
+
this.handleUpgrade(request, client, head).catch((error) => {
|
|
3229
3248
|
const mapped = mapError(error);
|
|
3230
|
-
rejectUpgrade(
|
|
3249
|
+
rejectUpgrade(client, mapped.status, mapped.code);
|
|
3231
3250
|
});
|
|
3232
3251
|
});
|
|
3233
3252
|
server.on("clientError", (_error, socket) => {
|
|
@@ -4118,7 +4137,7 @@ var MobileAccessGateway = class {
|
|
|
4118
4137
|
async resolveMobileBootSizes(planEntries) {
|
|
4119
4138
|
const sizes = /* @__PURE__ */ new Map();
|
|
4120
4139
|
const passThrough = /* @__PURE__ */ new Set();
|
|
4121
|
-
const candidates = planEntries.filter((entry) => entry.id !== MOBILE_LAYOUT_MODULE && entry.url.
|
|
4140
|
+
const candidates = planEntries.filter((entry) => entry.id !== MOBILE_LAYOUT_MODULE && upstreamPluginBundleUrl(entry.url, this.config.upstreamOrigin) !== void 0);
|
|
4122
4141
|
let cursor = 0;
|
|
4123
4142
|
const worker = async () => {
|
|
4124
4143
|
while (cursor < candidates.length) {
|
|
@@ -4142,11 +4161,10 @@ var MobileAccessGateway = class {
|
|
|
4142
4161
|
* cached for a short window.
|
|
4143
4162
|
*/
|
|
4144
4163
|
async upstreamBundleSize(source) {
|
|
4145
|
-
|
|
4164
|
+
const target = upstreamPluginBundleUrl(source, this.config.upstreamOrigin);
|
|
4165
|
+
if (target === void 0) return void 0;
|
|
4146
4166
|
const cached = this.bootEntrySizeCache.get(source);
|
|
4147
4167
|
if (cached !== void 0 && Date.now() - cached.at < MOBILE_BOOT_SIZE_CACHE_TTL_MS) return cached.size;
|
|
4148
|
-
const target = new URL(source, this.config.upstreamOrigin);
|
|
4149
|
-
if (target.origin !== this.config.upstreamOrigin.origin) return void 0;
|
|
4150
4168
|
const upstreamCookie = await this.upstreamCookieHeader();
|
|
4151
4169
|
let upstreamRequest;
|
|
4152
4170
|
try {
|
|
@@ -4312,9 +4330,8 @@ var MobileAccessGateway = class {
|
|
|
4312
4330
|
}
|
|
4313
4331
|
async readUpstreamClientBundle(source, signal) {
|
|
4314
4332
|
signal.throwIfAborted();
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
if (target.origin !== this.config.upstreamOrigin.origin) throw new HttpError(502, "upstream_unavailable");
|
|
4333
|
+
const target = upstreamPluginBundleUrl(source, this.config.upstreamOrigin);
|
|
4334
|
+
if (target === void 0) throw new HttpError(502, "upstream_unavailable");
|
|
4318
4335
|
let upstreamRequest;
|
|
4319
4336
|
const aborted = () => {
|
|
4320
4337
|
upstreamRequest?.destroy(/* @__PURE__ */ new Error("request aborted"));
|
|
@@ -4460,7 +4477,8 @@ var MobileAccessGateway = class {
|
|
|
4460
4477
|
});
|
|
4461
4478
|
setSecurityHeaders(response, this.tlsEnabled, "proxied");
|
|
4462
4479
|
const headers = sanitizeResponseHeaders(proxied.headers, this.config.upstreamOrigin);
|
|
4463
|
-
const
|
|
4480
|
+
const statusCode = proxied.statusCode ?? 502;
|
|
4481
|
+
const cacheControl = revisionedStaticCacheControl(request$2, statusCode);
|
|
4464
4482
|
if (cacheControl !== void 0) headers["cache-control"] = cacheControl;
|
|
4465
4483
|
const compressed = shouldCompressResponse(request$2, proxied);
|
|
4466
4484
|
if (compressed) {
|
|
@@ -4470,7 +4488,7 @@ var MobileAccessGateway = class {
|
|
|
4470
4488
|
headers["content-encoding"] = "gzip";
|
|
4471
4489
|
addVaryAcceptEncoding(headers);
|
|
4472
4490
|
}
|
|
4473
|
-
response.writeHead(
|
|
4491
|
+
response.writeHead(statusCode, headers);
|
|
4474
4492
|
await Promise.all([bodyDone, compressed ? pipeline(proxied, createGzip(), response) : pipeline(proxied, response)]);
|
|
4475
4493
|
} catch (error) {
|
|
4476
4494
|
holder.request?.destroy();
|
|
@@ -4680,6 +4698,7 @@ var MobileAccessGateway = class {
|
|
|
4680
4698
|
const authorization = this.authorize(request);
|
|
4681
4699
|
if (this.activeWebSockets.size >= this.config.maxWebSockets) throw new HttpError(429, "busy");
|
|
4682
4700
|
const upstreamCookie = await this.upstreamCookieHeader();
|
|
4701
|
+
if (client.destroyed) return;
|
|
4683
4702
|
const upstream = connect({
|
|
4684
4703
|
host: stripIpv6Brackets(this.config.upstreamOrigin.hostname),
|
|
4685
4704
|
port: Number(this.config.upstreamOrigin.port)
|
|
@@ -11926,8 +11945,7 @@ async function apply(ctx, config) {
|
|
|
11926
11945
|
text: `${guide}\n\n用户需求:${task}`
|
|
11927
11946
|
}],
|
|
11928
11947
|
source: {
|
|
11929
|
-
kind: "plugin",
|
|
11930
|
-
plugin: "dsh-mobile",
|
|
11948
|
+
kind: "plugin:dsh-mobile",
|
|
11931
11949
|
form: "notice",
|
|
11932
11950
|
summary: boundContextSummary(`/mobile ${task}`)
|
|
11933
11951
|
}
|