dsh-mobile 0.8.1 → 0.8.3
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 +33 -0
- package/CONTRIBUTORS.md +30 -2
- package/README.en.md +7 -5
- package/README.md +7 -5
- package/SECURITY.md +1 -1
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/apps/mobile/README.md +8 -4
- package/apps/mobile/README.zh-CN.md +8 -4
- package/apps/mobile/release.json +3 -3
- package/docs/ATTACH_EXISTING_FRPS.en.md +2 -0
- package/docs/ATTACH_EXISTING_FRPS.md +2 -0
- package/docs/CLIENT_MODULES.en.md +6 -2
- package/docs/CLIENT_MODULES.md +6 -2
- package/docs/CONNECTION_DIAGNOSTICS.en.md +56 -0
- package/docs/CONNECTION_DIAGNOSTICS.md +56 -0
- package/docs/IROH.en.md +7 -1
- package/docs/IROH.md +7 -1
- package/docs/MOBILE_PLUGIN_ADAPTATION.en.md +1 -1
- package/docs/MOBILE_PLUGIN_ADAPTATION.md +1 -1
- package/docs/README.md +1 -0
- package/docs/SKIN_COMPATIBILITY.en.md +1 -1
- package/docs/SKIN_COMPATIBILITY.md +1 -1
- package/docs/SLOW_CONNECTIONS.en.md +6 -2
- package/docs/SLOW_CONNECTIONS.md +6 -2
- package/lib/cli.js +6 -12
- package/lib/cli.js.map +1 -1
- package/lib/client.js +772 -248
- package/lib/client.js.map +1 -1
- package/lib/extension-worker-runtime.mjs +4 -8
- package/lib/extension-worker-runtime.mjs.map +1 -1
- package/lib/index.d.mts +19 -89
- package/lib/index.mjs +397 -273
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-compat.js +2 -2
- package/lib/mobile-compat.js.map +1 -1
- package/lib/mobile-layout.js +116 -33
- package/lib/mobile-layout.js.map +1 -1
- package/package.json +9 -7
- package/packages/question-fixes/lib/client.js +1 -2
- package/packages/question-fixes/lib/index.mjs +1 -2
- package/packages/question-fixes/package.json +1 -1
package/lib/index.mjs
CHANGED
|
@@ -28,7 +28,7 @@ import { lookup } from "node:dns/promises";
|
|
|
28
28
|
import { constants } from "node:fs";
|
|
29
29
|
import { z as z$1 } from "zod";
|
|
30
30
|
import webPush from "web-push";
|
|
31
|
-
|
|
31
|
+
// src/access.ts
|
|
32
32
|
/** Stable error categories converted to deliberately terse HTTP responses. */
|
|
33
33
|
var AccessError = class extends Error {
|
|
34
34
|
status;
|
|
@@ -466,8 +466,7 @@ var AccessController = class {
|
|
|
466
466
|
});
|
|
467
467
|
}
|
|
468
468
|
};
|
|
469
|
-
|
|
470
|
-
//#region src/ip.ts
|
|
469
|
+
// src/ip.ts
|
|
471
470
|
/**
|
|
472
471
|
* Browser-safe IP literal classification.
|
|
473
472
|
*
|
|
@@ -548,8 +547,7 @@ function isIP$1(value) {
|
|
|
548
547
|
if (leftGroups < 0 || rightGroups < 0) return 0;
|
|
549
548
|
return leftGroups + rightGroups >= 8 ? 0 : 6;
|
|
550
549
|
}
|
|
551
|
-
|
|
552
|
-
//#region src/network.ts
|
|
550
|
+
// src/network.ts
|
|
553
551
|
function parseIpv4$1(address) {
|
|
554
552
|
const parts = address.split(".");
|
|
555
553
|
if (parts.length !== 4) throw new Error(`invalid IPv4 address ${JSON.stringify(address)}`);
|
|
@@ -826,8 +824,7 @@ async function probeTcpReachable(host, port, timeoutMs = 1500) {
|
|
|
826
824
|
});
|
|
827
825
|
});
|
|
828
826
|
}
|
|
829
|
-
|
|
830
|
-
//#region src/websocket-paths.ts
|
|
827
|
+
// src/websocket-paths.ts
|
|
831
828
|
/** Cap the admin-approved extra WebSocket upgrade paths (exact pathnames). */
|
|
832
829
|
const MAX_EXTRA_WEBSOCKET_PATHS = 16;
|
|
833
830
|
const MAX_WEBSOCKET_PATH_LENGTH = 256;
|
|
@@ -932,8 +929,7 @@ var WebSocketPathStore = class {
|
|
|
932
929
|
return this.list();
|
|
933
930
|
}
|
|
934
931
|
};
|
|
935
|
-
|
|
936
|
-
//#region src/extension-worker-config.ts
|
|
932
|
+
// src/extension-worker-config.ts
|
|
937
933
|
function integer$1(value, name, fallback, minimum, maximum) {
|
|
938
934
|
const resolved = value ?? fallback;
|
|
939
935
|
if (typeof resolved !== "number" || !Number.isSafeInteger(resolved) || resolved < minimum || resolved > maximum) throw new Error(`hostExecution.${name} must be an integer from ${minimum} through ${maximum}`);
|
|
@@ -971,8 +967,7 @@ function resolveHostExecution(raw) {
|
|
|
971
967
|
logMessageBytes: integer$1(value.logMessageBytes, "logMessageBytes", 4096, 128, 65536)
|
|
972
968
|
});
|
|
973
969
|
}
|
|
974
|
-
|
|
975
|
-
//#region src/config.ts
|
|
970
|
+
// src/config.ts
|
|
976
971
|
/** Loader-facing defaults; {@link parseGatewayConfig} enforces cross-field security rules. */
|
|
977
972
|
const Config = z.object({
|
|
978
973
|
hostExecution: z.object({
|
|
@@ -1206,8 +1201,7 @@ function parseGatewayConfig(raw) {
|
|
|
1206
1201
|
maxRateLimitKeys: integer(value.maxRateLimitKeys, "maxRateLimitKeys", 256, 1, 4096)
|
|
1207
1202
|
});
|
|
1208
1203
|
}
|
|
1209
|
-
|
|
1210
|
-
//#region src/exec-file.ts
|
|
1204
|
+
// src/exec-file.ts
|
|
1211
1205
|
/** Capture both output streams even when a desktop host wraps execFile without Node's promisify metadata. */
|
|
1212
1206
|
function execFileText(file, args, options = {}) {
|
|
1213
1207
|
return new Promise((resolve, reject) => {
|
|
@@ -1231,8 +1225,7 @@ function execFileText(file, args, options = {}) {
|
|
|
1231
1225
|
});
|
|
1232
1226
|
});
|
|
1233
1227
|
}
|
|
1234
|
-
|
|
1235
|
-
//#region src/private-file.ts
|
|
1228
|
+
// src/private-file.ts
|
|
1236
1229
|
let userSidTask;
|
|
1237
1230
|
function commandFailureDetails(error) {
|
|
1238
1231
|
if (error === null || typeof error !== "object") return {};
|
|
@@ -1371,8 +1364,7 @@ async function restrictPrivateDirectory(directory) {
|
|
|
1371
1364
|
else if (info.isFile()) await restrictPrivateFile(child);
|
|
1372
1365
|
}
|
|
1373
1366
|
}
|
|
1374
|
-
|
|
1375
|
-
//#region src/client-module-preferences.ts
|
|
1367
|
+
// src/client-module-preferences.ts
|
|
1376
1368
|
const MAX_RECORD_BYTES = 1048576;
|
|
1377
1369
|
/** Validate untrusted JSON selections; graph membership is checked by the Gateway. */
|
|
1378
1370
|
function parseExcludedClientModules(value) {
|
|
@@ -1545,8 +1537,7 @@ var ClientModulePreferenceStore = class {
|
|
|
1545
1537
|
}
|
|
1546
1538
|
}
|
|
1547
1539
|
};
|
|
1548
|
-
|
|
1549
|
-
//#region src/control.ts
|
|
1540
|
+
// src/control.ts
|
|
1550
1541
|
/** A configuration change failed; recovered distinguishes a restored runtime from an unknown configuration. */
|
|
1551
1542
|
var MobileAccessReconfigurationError = class extends Error {
|
|
1552
1543
|
recovered;
|
|
@@ -1868,8 +1859,7 @@ function isLocalAdminHostname(hostname) {
|
|
|
1868
1859
|
if (octets === void 0) return false;
|
|
1869
1860
|
return isLoopbackIpv4(octets) || isPrivateIpv4(octets) || isLinkLocalIpv4(octets);
|
|
1870
1861
|
}
|
|
1871
|
-
|
|
1872
|
-
//#region src/http-security.ts
|
|
1862
|
+
// src/http-security.ts
|
|
1873
1863
|
const DEVICE_COOKIE = "dsh_ma_device";
|
|
1874
1864
|
const SESSION_COOKIE = "dsh_ma_session";
|
|
1875
1865
|
const CSRF_COOKIE = "dsh_ma_csrf";
|
|
@@ -2053,8 +2043,7 @@ function assertLocalAdminTrust(request, requireBrowserOrigin, authenticateDeskto
|
|
|
2053
2043
|
if (requireBrowserOrigin && site !== void 0 && site !== "same-origin") throw new HttpError(403, "forbidden");
|
|
2054
2044
|
if (!loopback && !authenticateDesktop()) throw new HttpError(401, "admin_authentication_required");
|
|
2055
2045
|
}
|
|
2056
|
-
|
|
2057
|
-
//#region src/browser-auth-cookies.ts
|
|
2046
|
+
// src/browser-auth-cookies.ts
|
|
2058
2047
|
const HOST_SESSION_COOKIE = "__Host-dsh_ma_session";
|
|
2059
2048
|
const HOST_CSRF_COOKIE = "__Host-dsh_ma_csrf";
|
|
2060
2049
|
/** Non-secret browser identity; the persistent device credential keeps its narrow renewal path. */
|
|
@@ -2106,8 +2095,7 @@ function authenticatedCookie(values, authenticate, accepts = () => true) {
|
|
|
2106
2095
|
if (selected === void 0) throw new HttpError(401, "authentication_failed");
|
|
2107
2096
|
return selected;
|
|
2108
2097
|
}
|
|
2109
|
-
|
|
2110
|
-
//#region src/auth-failure-log.ts
|
|
2098
|
+
// src/auth-failure-log.ts
|
|
2111
2099
|
const LOG_INTERVAL_MS$3 = 3e4;
|
|
2112
2100
|
const MAX_LOGGED_ORIGIN_LENGTH = 512;
|
|
2113
2101
|
function requestPathCategory(path) {
|
|
@@ -2222,12 +2210,17 @@ var AuthFailureRecorder = class {
|
|
|
2222
2210
|
}
|
|
2223
2211
|
}
|
|
2224
2212
|
};
|
|
2225
|
-
|
|
2226
|
-
//#region src/http-failure-log.ts
|
|
2213
|
+
// src/http-failure-log.ts
|
|
2227
2214
|
function safeParserCode(code) {
|
|
2228
2215
|
switch (code) {
|
|
2229
2216
|
case "ERR_HTTP_REQUEST_TIMEOUT":
|
|
2230
2217
|
case "HPE_HEADER_OVERFLOW":
|
|
2218
|
+
case "HPE_INVALID_METHOD":
|
|
2219
|
+
case "HPE_INVALID_CONSTANT":
|
|
2220
|
+
case "HPE_INVALID_URL":
|
|
2221
|
+
case "HPE_INVALID_VERSION":
|
|
2222
|
+
case "HPE_LF_EXPECTED":
|
|
2223
|
+
case "HPE_PAUSED_H2_UPGRADE":
|
|
2231
2224
|
case "HPE_INVALID_CHUNK_SIZE":
|
|
2232
2225
|
case "HPE_INVALID_CONTENT_LENGTH":
|
|
2233
2226
|
case "HPE_INVALID_EOF_STATE":
|
|
@@ -2262,7 +2255,7 @@ function classifyHttpParserFailure(nodeCode) {
|
|
|
2262
2255
|
}
|
|
2263
2256
|
const LOG_INTERVAL_MS$2 = 3e4;
|
|
2264
2257
|
/**
|
|
2265
|
-
* Per-Gateway diagnostics with
|
|
2258
|
+
* Per-Gateway diagnostics with four fixed sampling slots and no timers or request-selected keys.
|
|
2266
2259
|
* Each kind records once immediately, then at most once per 30 seconds with suppressed counts.
|
|
2267
2260
|
* A sink failure cannot change a response, retry a write or expose the diagnostic sink's error.
|
|
2268
2261
|
*/
|
|
@@ -2275,6 +2268,10 @@ var HttpFailureRecorder = class {
|
|
|
2275
2268
|
nextLogAt: 0,
|
|
2276
2269
|
suppressed: 0
|
|
2277
2270
|
},
|
|
2271
|
+
transport: {
|
|
2272
|
+
nextLogAt: 0,
|
|
2273
|
+
suppressed: 0
|
|
2274
|
+
},
|
|
2278
2275
|
target: {
|
|
2279
2276
|
nextLogAt: 0,
|
|
2280
2277
|
suppressed: 0
|
|
@@ -2302,6 +2299,26 @@ var HttpFailureRecorder = class {
|
|
|
2302
2299
|
});
|
|
2303
2300
|
return failure;
|
|
2304
2301
|
}
|
|
2302
|
+
/** Classify a client socket error without assigning an HTTP status to a transport failure. */
|
|
2303
|
+
recordClientError(nodeCode) {
|
|
2304
|
+
switch (nodeCode) {
|
|
2305
|
+
case "ECONNRESET":
|
|
2306
|
+
case "EPIPE":
|
|
2307
|
+
case "ETIMEDOUT":
|
|
2308
|
+
case "ERR_STREAM_PREMATURE_CLOSE": {
|
|
2309
|
+
const failure = {
|
|
2310
|
+
kind: "transport",
|
|
2311
|
+
code: nodeCode
|
|
2312
|
+
};
|
|
2313
|
+
this.record(failure);
|
|
2314
|
+
return failure;
|
|
2315
|
+
}
|
|
2316
|
+
default: return {
|
|
2317
|
+
kind: "parser",
|
|
2318
|
+
...this.recordParser(nodeCode)
|
|
2319
|
+
};
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2305
2322
|
/** Sample an invalid request target without retaining its raw or decoded URL. */
|
|
2306
2323
|
recordTarget() {
|
|
2307
2324
|
this.record({
|
|
@@ -2345,8 +2362,7 @@ var HttpFailureRecorder = class {
|
|
|
2345
2362
|
}
|
|
2346
2363
|
}
|
|
2347
2364
|
};
|
|
2348
|
-
|
|
2349
|
-
//#region src/api-latency-log.ts
|
|
2365
|
+
// src/api-latency-log.ts
|
|
2350
2366
|
/**
|
|
2351
2367
|
* Bounded API latency samples for diagnosing slow mobile requests.
|
|
2352
2368
|
*
|
|
@@ -2453,8 +2469,7 @@ var ApiLatencyRecorder = class {
|
|
|
2453
2469
|
}
|
|
2454
2470
|
}
|
|
2455
2471
|
};
|
|
2456
|
-
|
|
2457
|
-
//#region src/upstream-auth-log.ts
|
|
2472
|
+
// src/upstream-auth-log.ts
|
|
2458
2473
|
const LOG_INTERVAL_MS = 3e4;
|
|
2459
2474
|
/** Two fixed sampling slots keep a first failure visible even after a successful exchange. */
|
|
2460
2475
|
var UpstreamAuthRecorder = class {
|
|
@@ -2519,8 +2534,7 @@ var UpstreamAuthRecorder = class {
|
|
|
2519
2534
|
}
|
|
2520
2535
|
}
|
|
2521
2536
|
};
|
|
2522
|
-
|
|
2523
|
-
//#region src/client-module-recovery.ts
|
|
2537
|
+
// src/client-module-recovery.ts
|
|
2524
2538
|
const CLIENT_MODULE_RECOVERY_COPY = {
|
|
2525
2539
|
zh: {
|
|
2526
2540
|
title: "移动页面模块需要调整",
|
|
@@ -2601,8 +2615,7 @@ function renderClientModuleRecoveryScript(locale) {
|
|
|
2601
2615
|
})
|
|
2602
2616
|
})()`;
|
|
2603
2617
|
}
|
|
2604
|
-
|
|
2605
|
-
//#region src/mobile-compat-bootstrap.ts
|
|
2618
|
+
// src/mobile-compat-bootstrap.ts
|
|
2606
2619
|
const MOBILE_COMPAT_PATH = `${AUTH_PREFIX}/compat.js`;
|
|
2607
2620
|
/**
|
|
2608
2621
|
* Name the compatibility bundle by content.
|
|
@@ -2678,8 +2691,7 @@ const EXTENSION_LIMITS = Object.freeze({
|
|
|
2678
2691
|
assetBytes: 33554432,
|
|
2679
2692
|
assetDepth: 8
|
|
2680
2693
|
});
|
|
2681
|
-
|
|
2682
|
-
//#region src/http-cache.ts
|
|
2694
|
+
// src/http-cache.ts
|
|
2683
2695
|
/** Entity-tag handling for authenticated GET and HEAD cache validation. */
|
|
2684
2696
|
const ENTITY_TAG = /^[\t ]*(?:W\/)?("[\x21\x23-\x7e\x80-\xff]*")[\t ]*$/u;
|
|
2685
2697
|
const ENTITY_TAG_LIST_ITEM = /(?:W\/)?"[\x21\x23-\x7e\x80-\xff]*"/gu;
|
|
@@ -2716,8 +2728,7 @@ function weakenEntityTag(etag) {
|
|
|
2716
2728
|
const opaque = etag === void 0 ? void 0 : ENTITY_TAG.exec(etag)?.[1];
|
|
2717
2729
|
return opaque === void 0 ? void 0 : `W/${opaque}`;
|
|
2718
2730
|
}
|
|
2719
|
-
|
|
2720
|
-
//#region src/mobile-graph-events.ts
|
|
2731
|
+
// src/mobile-graph-events.ts
|
|
2721
2732
|
/** Bounded SSE framing for the dedicated Mobile client graph stream. */
|
|
2722
2733
|
/** Maximum retained UTF-8 frame/output bytes, with CRLF normalized to LF. */
|
|
2723
2734
|
const MOBILE_GRAPH_EVENT_MAX_BYTES = 4194304;
|
|
@@ -2906,8 +2917,7 @@ var MobileGraphEventTransform = class extends Transform {
|
|
|
2906
2917
|
function createMobileGraphEventTransform(project) {
|
|
2907
2918
|
return new MobileGraphEventTransform(project);
|
|
2908
2919
|
}
|
|
2909
|
-
|
|
2910
|
-
//#region src/websocket-compression.ts
|
|
2920
|
+
// src/websocket-compression.ts
|
|
2911
2921
|
function payloadBytes(data) {
|
|
2912
2922
|
if (Array.isArray(data)) return data.reduce((bytes, part) => bytes + part.byteLength, 0);
|
|
2913
2923
|
return data.byteLength;
|
|
@@ -3127,15 +3137,13 @@ function bridgeCompressedWebSocket(request, client, upstream, head, target, upst
|
|
|
3127
3137
|
}
|
|
3128
3138
|
});
|
|
3129
3139
|
}
|
|
3130
|
-
|
|
3131
|
-
//#region src/version.ts
|
|
3140
|
+
// src/version.ts
|
|
3132
3141
|
const manifest = createRequire(import.meta.url)("../package.json");
|
|
3133
3142
|
/** Version of the installed DSH Mobile plugin package. */
|
|
3134
3143
|
const DSH_MOBILE_VERSION = typeof manifest.version === "string" ? manifest.version : "unknown";
|
|
3135
3144
|
/** Oldest Android App release supported by this plugin generation. */
|
|
3136
3145
|
const MINIMUM_ANDROID_APP_VERSION = "0.2.2";
|
|
3137
|
-
|
|
3138
|
-
//#region src/computer-images.ts
|
|
3146
|
+
// src/computer-images.ts
|
|
3139
3147
|
const MAX_ENTRIES = 500;
|
|
3140
3148
|
const MAX_IMAGE_BYTES = 20971520;
|
|
3141
3149
|
const IMAGE_TYPES = Object.freeze({
|
|
@@ -3336,8 +3344,7 @@ async function readComputerImage(path, signal) {
|
|
|
3336
3344
|
throw new HttpError(404, "file_unavailable");
|
|
3337
3345
|
}
|
|
3338
3346
|
}
|
|
3339
|
-
|
|
3340
|
-
//#region src/extension-worker.ts
|
|
3347
|
+
// src/extension-worker.ts
|
|
3341
3348
|
/** Supervise opt-in local hosts without executing extension code on the DSH thread. */
|
|
3342
3349
|
/** JSON bytes serialized and size-checked inside the worker. */
|
|
3343
3350
|
var PreparedJsonResult = class {
|
|
@@ -3990,8 +3997,7 @@ var WorkerStreamBridge = class extends Readable {
|
|
|
3990
3997
|
return super.emit(event, ...args);
|
|
3991
3998
|
}
|
|
3992
3999
|
};
|
|
3993
|
-
|
|
3994
|
-
//#region src/extensions.ts
|
|
4000
|
+
// src/extensions.ts
|
|
3995
4001
|
/** A misbehaving host activation must not wedge the local watcher forever. */
|
|
3996
4002
|
const HOST_ACTIVATION_TIMEOUT_MS = 5e3;
|
|
3997
4003
|
/** The previous Host outlives the hidden-page refresh interval and one timed refresh. */
|
|
@@ -5064,8 +5070,7 @@ function workerLoggerAdapter(context) {
|
|
|
5064
5070
|
function createMobileAccessService(ctx) {
|
|
5065
5071
|
return new MobileAccessService(ctx);
|
|
5066
5072
|
}
|
|
5067
|
-
|
|
5068
|
-
//#region src/auth-pages.ts
|
|
5073
|
+
// src/auth-pages.ts
|
|
5069
5074
|
const COPY = {
|
|
5070
5075
|
en: {
|
|
5071
5076
|
lang: "en",
|
|
@@ -5293,8 +5298,7 @@ function renderLoginScript(locale) {
|
|
|
5293
5298
|
})()
|
|
5294
5299
|
`;
|
|
5295
5300
|
}
|
|
5296
|
-
|
|
5297
|
-
//#region src/browser-ui-locale.ts
|
|
5301
|
+
// src/browser-ui-locale.ts
|
|
5298
5302
|
const UI_LOCALE_COOKIE = "dsh_ma_ui_locale";
|
|
5299
5303
|
/** Read presentation data only; malformed or ambiguous cookies keep browser negotiation. */
|
|
5300
5304
|
function readBrowserUiLocale(cookieHeader) {
|
|
@@ -5313,8 +5317,7 @@ function readBrowserUiLocale(cookieHeader) {
|
|
|
5313
5317
|
function resolveBrowserUiLocale(cookieHeader, acceptLanguage) {
|
|
5314
5318
|
return readBrowserUiLocale(cookieHeader) ?? resolveAuthPageLocale(acceptLanguage);
|
|
5315
5319
|
}
|
|
5316
|
-
|
|
5317
|
-
//#region src/mobile-push/assets.ts
|
|
5320
|
+
// src/mobile-push/assets.ts
|
|
5318
5321
|
const ICON_FILES = Object.freeze({
|
|
5319
5322
|
"/mobile-access/push/icon-180.png": "icon-180.png",
|
|
5320
5323
|
"/mobile-access/push/icon-192.png": "icon-192.png",
|
|
@@ -5366,16 +5369,14 @@ const loadFromPackage = createMobilePushAssetLoader(packageRoot);
|
|
|
5366
5369
|
function mobilePushAsset(path, root = packageRoot) {
|
|
5367
5370
|
return root === packageRoot ? loadFromPackage(path) : createMobilePushAssetLoader(root)(path);
|
|
5368
5371
|
}
|
|
5369
|
-
|
|
5370
|
-
//#region src/mobile-push/installation-id.ts
|
|
5372
|
+
// src/mobile-push/installation-id.ts
|
|
5371
5373
|
/** Browser-safe installation-id validation shared by the browser controller and the Gateway. */
|
|
5372
5374
|
const INSTALLATION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
5373
5375
|
/** True only for a UUID-shaped installation id with an allowed version and variant nibble. */
|
|
5374
5376
|
function isPushInstallationId(value) {
|
|
5375
5377
|
return typeof value === "string" && INSTALLATION_ID.test(value);
|
|
5376
5378
|
}
|
|
5377
|
-
|
|
5378
|
-
//#region src/gateway.ts
|
|
5379
|
+
// src/gateway.ts
|
|
5379
5380
|
const MAX_CONTROL_BODY_BYTES = 16384;
|
|
5380
5381
|
const MAX_HEADER_BYTES = 16384;
|
|
5381
5382
|
const DISCOVERY_QUERY = Buffer.from("DSH_MOBILE_DISCOVER_V1", "ascii");
|
|
@@ -5548,6 +5549,7 @@ const RUNTIME_MODULE = "@deepseek-ai/dsh-client-runtime";
|
|
|
5548
5549
|
const RENDERER_MODULE = "@deepseek-ai/dsh-client-ui-renderer";
|
|
5549
5550
|
const SIDEBAR_MODULE = "@deepseek-ai/dsh-client-ui-sidebar";
|
|
5550
5551
|
const SETTINGS_MODULE = "@deepseek-ai/dsh-client-ui-settings";
|
|
5552
|
+
const SETTINGS_GENERAL_MODULE = "@deepseek-ai/dsh-client-ui-settings-general";
|
|
5551
5553
|
const API_GATEWAY_MODULE = "@deepseek-ai/dsh-api-gateway";
|
|
5552
5554
|
const API_REMOTES_MODULE = "@deepseek-ai/dsh-api-remotes";
|
|
5553
5555
|
const MOBILE_BOOT_CORE_MODULES = /* @__PURE__ */ new Set([
|
|
@@ -5558,6 +5560,7 @@ const MOBILE_BOOT_CORE_MODULES = /* @__PURE__ */ new Set([
|
|
|
5558
5560
|
RENDERER_MODULE,
|
|
5559
5561
|
SIDEBAR_MODULE,
|
|
5560
5562
|
SETTINGS_MODULE,
|
|
5563
|
+
SETTINGS_GENERAL_MODULE,
|
|
5561
5564
|
API_GATEWAY_MODULE,
|
|
5562
5565
|
API_REMOTES_MODULE
|
|
5563
5566
|
]);
|
|
@@ -5678,8 +5681,8 @@ var ExcludedClientModulesError = class extends Error {
|
|
|
5678
5681
|
/** A graph-selection conflict with a bounded diagnostic safe for authenticated callers. */
|
|
5679
5682
|
var ClientModuleConflictError = class extends HttpError {
|
|
5680
5683
|
detail;
|
|
5681
|
-
constructor(detail) {
|
|
5682
|
-
super(409,
|
|
5684
|
+
constructor(detail, code = "excluded_client_modules_invalid") {
|
|
5685
|
+
super(409, code);
|
|
5683
5686
|
this.detail = detail.replace(/[\u0000-\u001f\u007f]/gu, " ").replace(/(?:https?:\/\/|[a-z]:[\\/])\S+/giu, "[redacted]").slice(0, 512);
|
|
5684
5687
|
}
|
|
5685
5688
|
};
|
|
@@ -5714,6 +5717,25 @@ function describeMobileClientModules(html) {
|
|
|
5714
5717
|
dependencies: Object.freeze(clientModuleDependencies(entry).filter((id) => ids.has(id)))
|
|
5715
5718
|
})));
|
|
5716
5719
|
}
|
|
5720
|
+
/**
|
|
5721
|
+
* Canonical fingerprint of an installed catalog.
|
|
5722
|
+
*
|
|
5723
|
+
* A settings page computes "required only" client-side. If a plugin was added,
|
|
5724
|
+
* removed or became required while that page stayed open, the computed set is
|
|
5725
|
+
* based on a catalog that no longer exists. The fingerprint travels with the
|
|
5726
|
+
* read and back with the write, so a stale page is told to reread instead of
|
|
5727
|
+
* saving a selection made against an older graph.
|
|
5728
|
+
* @param entries - Catalog entries in any order.
|
|
5729
|
+
* @returns Stable 16-character hexadecimal revision.
|
|
5730
|
+
*/
|
|
5731
|
+
function clientModuleCatalogRevision(entries) {
|
|
5732
|
+
const canonical = entries.map((entry) => [
|
|
5733
|
+
entry.id,
|
|
5734
|
+
entry.required,
|
|
5735
|
+
[...new Set(entry.dependencies)].sort()
|
|
5736
|
+
]).sort((left, right) => left[0] < right[0] ? -1 : left[0] > right[0] ? 1 : 0);
|
|
5737
|
+
return createHash("sha256").update(JSON.stringify(canonical)).digest("hex").slice(0, 16);
|
|
5738
|
+
}
|
|
5717
5739
|
/** Validate graph edges before removing application rows from both manifest views. */
|
|
5718
5740
|
function excludeMobileBootEntries(entries, batches, excludedIds) {
|
|
5719
5741
|
if (excludedIds.length === 0) return;
|
|
@@ -6609,7 +6631,11 @@ var MobileAccessGateway = class {
|
|
|
6609
6631
|
});
|
|
6610
6632
|
server.on("clientError", (error, socket) => {
|
|
6611
6633
|
const nodeCode = "code" in error && typeof error.code === "string" ? error.code : void 0;
|
|
6612
|
-
const failure = this.httpFailures.
|
|
6634
|
+
const failure = this.httpFailures.recordClientError(nodeCode);
|
|
6635
|
+
if (failure.kind === "transport" || !socket.writable) {
|
|
6636
|
+
socket.destroy();
|
|
6637
|
+
return;
|
|
6638
|
+
}
|
|
6613
6639
|
rejectUpgrade(socket, failure.status, failure.code);
|
|
6614
6640
|
});
|
|
6615
6641
|
await new Promise((resolve, reject) => {
|
|
@@ -7339,18 +7365,19 @@ var MobileAccessGateway = class {
|
|
|
7339
7365
|
try {
|
|
7340
7366
|
let kind = "describe";
|
|
7341
7367
|
let excluded;
|
|
7368
|
+
let revision;
|
|
7342
7369
|
if (request.method === "POST") {
|
|
7343
7370
|
const body = await readJsonObject(request, this.config.maxBodyBytes);
|
|
7344
7371
|
const keys = Object.keys(body);
|
|
7345
|
-
if (keys.length
|
|
7346
|
-
if (keys[0] === "
|
|
7347
|
-
else if (keys
|
|
7348
|
-
else if (keys[0] === "excludedClientModules") {
|
|
7372
|
+
if (keys.length === 1 && keys[0] === "reset" && body.reset === true) kind = "reset";
|
|
7373
|
+
else if (keys.length === 1 && keys[0] === "measure" && body.measure === true) kind = "measure";
|
|
7374
|
+
else if (keys.every((key) => key === "excludedClientModules" || key === "revision") && Object.prototype.hasOwnProperty.call(body, "excludedClientModules") && (body.revision === void 0 || typeof body.revision === "string")) {
|
|
7349
7375
|
kind = "configure";
|
|
7350
7376
|
excluded = body.excludedClientModules;
|
|
7377
|
+
revision = body.revision;
|
|
7351
7378
|
} else throw new HttpError(400, "bad_request");
|
|
7352
7379
|
}
|
|
7353
|
-
const view = await this.clientModulePreferenceOperation(kind, excluded, authorization.deviceId, operation.signal);
|
|
7380
|
+
const view = await this.clientModulePreferenceOperation(kind, excluded, authorization.deviceId, operation.signal, revision);
|
|
7354
7381
|
operation.signal.throwIfAborted();
|
|
7355
7382
|
sendJson(response, 200, view, this.tlsEnabled);
|
|
7356
7383
|
} catch (error) {
|
|
@@ -7835,10 +7862,11 @@ var MobileAccessGateway = class {
|
|
|
7835
7862
|
* @param excluded - Untrusted module IDs to validate against the current boot graph.
|
|
7836
7863
|
* @param deviceId - Authenticated device identity; omitted for computer defaults.
|
|
7837
7864
|
* @param callerSignal - Cancels fetching and validation, not an atomic write already started.
|
|
7865
|
+
* @param revision - Catalog fingerprint the selection was computed against; a mismatch is rejected.
|
|
7838
7866
|
* @returns The catalog and saved selection without reloading clients.
|
|
7839
7867
|
*/
|
|
7840
|
-
configureClientModules(excluded, deviceId, callerSignal) {
|
|
7841
|
-
return this.clientModulePreferenceOperation("configure", excluded, deviceId, callerSignal);
|
|
7868
|
+
configureClientModules(excluded, deviceId, callerSignal, revision) {
|
|
7869
|
+
return this.clientModulePreferenceOperation("configure", excluded, deviceId, callerSignal, revision);
|
|
7842
7870
|
}
|
|
7843
7871
|
/** Reset one override without reloading connected clients.
|
|
7844
7872
|
* @param deviceId - Authenticated device identity; omitted to reset computer defaults.
|
|
@@ -8024,7 +8052,7 @@ var MobileAccessGateway = class {
|
|
|
8024
8052
|
await upstreamClosed;
|
|
8025
8053
|
}
|
|
8026
8054
|
}
|
|
8027
|
-
async clientModulePreferenceOperation(kind, excluded, deviceId, callerSignal) {
|
|
8055
|
+
async clientModulePreferenceOperation(kind, excluded, deviceId, callerSignal, revision) {
|
|
8028
8056
|
if (this.closing || !this.started) throw new HttpError(503, "unavailable");
|
|
8029
8057
|
if (kind === "measure" && this.clientModuleMeasurementActive) throw new HttpError(429, "busy");
|
|
8030
8058
|
if (kind === "measure") this.clientModuleMeasurementActive = true;
|
|
@@ -8043,8 +8071,10 @@ var MobileAccessGateway = class {
|
|
|
8043
8071
|
const validate = async (selection) => {
|
|
8044
8072
|
html = (await this.fetchAuthenticatedUpstreamIndex(controller.signal)).html;
|
|
8045
8073
|
try {
|
|
8074
|
+
if (revision !== void 0 && revision !== clientModuleCatalogRevision(describeMobileClientModules(html))) throw new ClientModuleConflictError("the module catalog changed since it was read; reread it and confirm the selection again", "client_module_catalog_changed");
|
|
8046
8075
|
parseMobileBootPlan(html, selection);
|
|
8047
8076
|
} catch (error) {
|
|
8077
|
+
if (error instanceof ClientModuleConflictError) throw error;
|
|
8048
8078
|
if (error instanceof ExcludedClientModulesError) throw new ClientModuleConflictError(error.message);
|
|
8049
8079
|
throw new HttpError(502, "upstream_unavailable");
|
|
8050
8080
|
}
|
|
@@ -8072,8 +8102,10 @@ var MobileAccessGateway = class {
|
|
|
8072
8102
|
} catch (error) {
|
|
8073
8103
|
if (!measurementExpired || callerSignal?.aborted === true || this.closing) throw error;
|
|
8074
8104
|
}
|
|
8105
|
+
const entries = this.describeMobileModulesWithSizes(html);
|
|
8075
8106
|
return Object.freeze({
|
|
8076
|
-
entries
|
|
8107
|
+
entries,
|
|
8108
|
+
revision: clientModuleCatalogRevision(entries),
|
|
8077
8109
|
...selection,
|
|
8078
8110
|
...plan.batches === void 0 ? {
|
|
8079
8111
|
excludedClientModules: Object.freeze([]),
|
|
@@ -9232,8 +9264,7 @@ var MobileAccessGateway = class {
|
|
|
9232
9264
|
};
|
|
9233
9265
|
}
|
|
9234
9266
|
};
|
|
9235
|
-
|
|
9236
|
-
//#region src/storage.ts
|
|
9267
|
+
// src/storage.ts
|
|
9237
9268
|
function assertInteger(value, name) {
|
|
9238
9269
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new Error(`device state ${name} must be a non-negative integer`);
|
|
9239
9270
|
}
|
|
@@ -9360,8 +9391,7 @@ var MemoryDeviceStore = class {
|
|
|
9360
9391
|
return structuredClone(this.snapshot);
|
|
9361
9392
|
}
|
|
9362
9393
|
};
|
|
9363
|
-
|
|
9364
|
-
//#region src/managed-files.ts
|
|
9394
|
+
// src/managed-files.ts
|
|
9365
9395
|
/** Refuse link-shaped ancestors inside an owned state root before accessing a child. */
|
|
9366
9396
|
async function assertManagedParents(root, target, prefix) {
|
|
9367
9397
|
const parent = resolve(root);
|
|
@@ -9528,8 +9558,7 @@ async function replaceManagedDirectory(root, target, candidate, prefix, operatio
|
|
|
9528
9558
|
}
|
|
9529
9559
|
}
|
|
9530
9560
|
}
|
|
9531
|
-
|
|
9532
|
-
//#region src/component-download.ts
|
|
9561
|
+
// src/component-download.ts
|
|
9533
9562
|
/**
|
|
9534
9563
|
* Hosts a pinned GitHub release download may redirect to.
|
|
9535
9564
|
*
|
|
@@ -9640,8 +9669,7 @@ async function downloadPinnedArtifact(request) {
|
|
|
9640
9669
|
}
|
|
9641
9670
|
throw lastError;
|
|
9642
9671
|
}
|
|
9643
|
-
|
|
9644
|
-
//#region src/frp-component.ts
|
|
9672
|
+
// src/frp-component.ts
|
|
9645
9673
|
const FRP_VERSION$1 = "0.70.1";
|
|
9646
9674
|
const MAX_ARCHIVE_ENTRIES$1 = 128;
|
|
9647
9675
|
const MAX_ARCHIVE_LIST_BYTES$1 = 262144;
|
|
@@ -9925,8 +9953,7 @@ var FrpComponentManager = class {
|
|
|
9925
9953
|
return task.then(() => this.status());
|
|
9926
9954
|
}
|
|
9927
9955
|
};
|
|
9928
|
-
|
|
9929
|
-
//#region src/windows-msi-extract.ts
|
|
9956
|
+
// src/windows-msi-extract.ts
|
|
9930
9957
|
/** A failed read-only MSI extraction; causes deliberately exclude raw command arguments. */
|
|
9931
9958
|
var WindowsMsiExtractionError = class extends Error {
|
|
9932
9959
|
stage;
|
|
@@ -10410,8 +10437,7 @@ async function extractWindowsMsiBinary(msi, destination, executableName, expecte
|
|
|
10410
10437
|
await copyFile(binary, join(destination, executableName));
|
|
10411
10438
|
});
|
|
10412
10439
|
}
|
|
10413
|
-
|
|
10414
|
-
//#region src/cpolar-component.ts
|
|
10440
|
+
// src/cpolar-component.ts
|
|
10415
10441
|
const CPOLAR_VERSION = "3.3.18";
|
|
10416
10442
|
/** Pinned official cpolar release metadata for supported desktop targets. */
|
|
10417
10443
|
const CPOLAR_COMPONENT_RELEASES = Object.freeze(Object.fromEntries([
|
|
@@ -10779,8 +10805,7 @@ var CpolarComponentManager = class {
|
|
|
10779
10805
|
return task.then(() => this.status());
|
|
10780
10806
|
}
|
|
10781
10807
|
};
|
|
10782
|
-
|
|
10783
|
-
//#region src/frp-template.ts
|
|
10808
|
+
// src/frp-template.ts
|
|
10784
10809
|
/** Loopback-only HTTP vhost port used between Caddy and frps. */
|
|
10785
10810
|
const FRP_VHOST_HTTP_PORT = 7080;
|
|
10786
10811
|
/** Caddy snippet owned entirely by DSH Mobile; the main Caddyfile only imports it. */
|
|
@@ -10909,8 +10934,7 @@ function createRestrictedFrpServerTemplate(serverPort, token, publicOrigin, opti
|
|
|
10909
10934
|
if (publicIpv4Address(publicHost)) lines.push(manualIpCertificateGuide(publicHost), "");
|
|
10910
10935
|
return lines.join("\n");
|
|
10911
10936
|
}
|
|
10912
|
-
|
|
10913
|
-
//#region src/frp-config.ts
|
|
10937
|
+
// src/frp-config.ts
|
|
10914
10938
|
const MAX_SETTINGS_BYTES$1 = 8192;
|
|
10915
10939
|
/** Public entry port used by the self-signed TCP passthrough (never 443 by requirement). */
|
|
10916
10940
|
const FRP_DEFAULT_PUBLIC_PORT = 33080;
|
|
@@ -10930,7 +10954,8 @@ const FRP_SETTINGS_KEYS = /* @__PURE__ */ new Set([
|
|
|
10930
10954
|
"mode",
|
|
10931
10955
|
"entryTls",
|
|
10932
10956
|
"vhostHttpPort",
|
|
10933
|
-
"publicPort"
|
|
10957
|
+
"publicPort",
|
|
10958
|
+
"user"
|
|
10934
10959
|
]);
|
|
10935
10960
|
function hostname$2(value) {
|
|
10936
10961
|
if (value.length > 253 || !value.includes(".")) return false;
|
|
@@ -10953,6 +10978,11 @@ function validateFrpToken(value) {
|
|
|
10953
10978
|
if (typeof value !== "string" || value.length < 16 || value.length > 512 || /[\s\u0000-\u001f\u007f]/u.test(value)) throw new Error("frp_token_invalid");
|
|
10954
10979
|
return value;
|
|
10955
10980
|
}
|
|
10981
|
+
/** Validate the optional frpc `user` field; it must stay one safe TOML token. */
|
|
10982
|
+
function validateFrpUser(value) {
|
|
10983
|
+
if (typeof value !== "string" || value.length < 1 || value.length > 128 || !/^[A-Za-z0-9._-]+$/u.test(value)) throw new Error("frp_user_invalid");
|
|
10984
|
+
return value;
|
|
10985
|
+
}
|
|
10956
10986
|
/** Validate the public HTTPS origin used by Caddy and Android pairing. */
|
|
10957
10987
|
function validateFrpPublicOrigin(value) {
|
|
10958
10988
|
if (typeof value !== "string" || value.length > 512) throw new Error("frp_public_origin_invalid");
|
|
@@ -11043,6 +11073,7 @@ function parseFrpSettings(value) {
|
|
|
11043
11073
|
const entryTls = record.entryTls === void 0 ? void 0 : validateFrpEntryTls(record.entryTls);
|
|
11044
11074
|
const vhostHttpPort = record.vhostHttpPort === void 0 ? void 0 : validateFrpVhostHttpPort(record.vhostHttpPort);
|
|
11045
11075
|
const publicPort = record.publicPort === void 0 ? void 0 : validateFrpPublicPort(record.publicPort);
|
|
11076
|
+
const user = record.user === void 0 || record.user === "" ? void 0 : validateFrpUser(record.user);
|
|
11046
11077
|
const resolvedMode = mode ?? "deploy";
|
|
11047
11078
|
const resolvedEntryTls = entryTls ?? "public-ip-cert";
|
|
11048
11079
|
if (resolvedEntryTls === "self-signed" && resolvedMode !== "attach") throw new Error("frp_entry_tls_invalid");
|
|
@@ -11058,7 +11089,8 @@ function parseFrpSettings(value) {
|
|
|
11058
11089
|
...mode === void 0 ? {} : { mode },
|
|
11059
11090
|
...entryTls === void 0 ? {} : { entryTls },
|
|
11060
11091
|
...vhostHttpPort === void 0 ? {} : { vhostHttpPort },
|
|
11061
|
-
...publicPort === void 0 ? {} : { publicPort }
|
|
11092
|
+
...publicPort === void 0 ? {} : { publicPort },
|
|
11093
|
+
...user === void 0 ? {} : { user }
|
|
11062
11094
|
});
|
|
11063
11095
|
}
|
|
11064
11096
|
/**
|
|
@@ -11077,6 +11109,7 @@ function mergeSavedFrpSettings(partial, saved) {
|
|
|
11077
11109
|
const entryTls = pick("entryTls");
|
|
11078
11110
|
const vhostHttpPort = pick("vhostHttpPort");
|
|
11079
11111
|
const publicPort = pick("publicPort");
|
|
11112
|
+
const user = partial.user === void 0 ? saved?.user : partial.user;
|
|
11080
11113
|
const merged = {
|
|
11081
11114
|
serverAddress: partial.serverAddress === "" || partial.serverAddress === void 0 ? saved?.serverAddress : partial.serverAddress,
|
|
11082
11115
|
serverPort: typeof partial.serverPort === "number" && Number.isSafeInteger(partial.serverPort) && partial.serverPort >= 1 ? partial.serverPort : saved?.serverPort,
|
|
@@ -11085,7 +11118,8 @@ function mergeSavedFrpSettings(partial, saved) {
|
|
|
11085
11118
|
...mode === "" || mode === void 0 ? {} : { mode },
|
|
11086
11119
|
...entryTls === "" || entryTls === void 0 ? {} : { entryTls },
|
|
11087
11120
|
...vhostHttpPort === "" || vhostHttpPort === void 0 ? {} : { vhostHttpPort },
|
|
11088
|
-
...publicPort === "" || publicPort === void 0 ? {} : { publicPort }
|
|
11121
|
+
...publicPort === "" || publicPort === void 0 ? {} : { publicPort },
|
|
11122
|
+
...user === "" || user === void 0 ? {} : { user }
|
|
11089
11123
|
};
|
|
11090
11124
|
if (merged.serverAddress === void 0 && merged.serverPort === void 0 && merged.token === void 0 && merged.publicOrigin === void 0) throw new Error("frp_config_missing");
|
|
11091
11125
|
return parseFrpSettings(merged);
|
|
@@ -11155,6 +11189,7 @@ function createFrpcToml(settings, localPort, proxyName = FRP_DEFAULT_PROXY_NAME)
|
|
|
11155
11189
|
`serverPort = ${String(settings.serverPort)}`,
|
|
11156
11190
|
"auth.method = \"token\"",
|
|
11157
11191
|
`auth.token = ${tomlString(settings.token)}`,
|
|
11192
|
+
...settings.user === void 0 ? [] : [`user = ${tomlString(settings.user)}`],
|
|
11158
11193
|
"transport.tls.enable = true",
|
|
11159
11194
|
"",
|
|
11160
11195
|
"[[proxies]]",
|
|
@@ -11267,7 +11302,8 @@ var FrpConfigStore = class {
|
|
|
11267
11302
|
...this.errorCode === void 0 ? {} : { errorCode: this.errorCode },
|
|
11268
11303
|
...mode === void 0 || mode === "deploy" ? {} : { mode },
|
|
11269
11304
|
...entryTls === void 0 || entryTls === "public-ip-cert" ? {} : { entryTls },
|
|
11270
|
-
...settings?.publicPort === void 0 ? {} : { publicPort: settings.publicPort }
|
|
11305
|
+
...settings?.publicPort === void 0 ? {} : { publicPort: settings.publicPort },
|
|
11306
|
+
...settings?.user === void 0 ? {} : { user: settings.user }
|
|
11271
11307
|
});
|
|
11272
11308
|
}
|
|
11273
11309
|
/** Return private settings only to the provider lifecycle. */
|
|
@@ -11298,8 +11334,7 @@ var FrpConfigStore = class {
|
|
|
11298
11334
|
return this.status();
|
|
11299
11335
|
}
|
|
11300
11336
|
};
|
|
11301
|
-
|
|
11302
|
-
//#region src/frp-attach.ts
|
|
11337
|
+
// src/frp-attach.ts
|
|
11303
11338
|
/** Default proof path used to confirm the public entry answered from this computer. */
|
|
11304
11339
|
const FRP_ATTACH_DISCOVERY_PATH = "/mobile-access/discovery";
|
|
11305
11340
|
/** Declaration printed above the VPS half of an attach template. */
|
|
@@ -11496,8 +11531,7 @@ function createFrpAttachTemplateParts(settings, options = {}) {
|
|
|
11496
11531
|
function createFrpAttachTemplate(settings, options = {}) {
|
|
11497
11532
|
return createFrpAttachTemplateParts(settings, options).text;
|
|
11498
11533
|
}
|
|
11499
|
-
|
|
11500
|
-
//#region src/frp-attach-plan.ts
|
|
11534
|
+
// src/frp-attach-plan.ts
|
|
11501
11535
|
const DEFAULT_PLAN_CONFIG_FILE = "<DSH Mobile 私有目录>/remote/frp/config/frpc.toml";
|
|
11502
11536
|
function planConfigFile(options) {
|
|
11503
11537
|
const file = options.configFile;
|
|
@@ -11635,8 +11669,7 @@ function createFrpAttachPlan(settings, options = {}) {
|
|
|
11635
11669
|
])
|
|
11636
11670
|
});
|
|
11637
11671
|
}
|
|
11638
|
-
|
|
11639
|
-
//#region src/cert-renewal.ts
|
|
11672
|
+
// src/cert-renewal.ts
|
|
11640
11673
|
/** Days left below which the entry certificate is reported as expiring. */
|
|
11641
11674
|
const CERT_EXPIRING_DAYS = 2;
|
|
11642
11675
|
const DAY_MS = 864e5;
|
|
@@ -11742,8 +11775,7 @@ async function probeOriginCertificate(host, port, timeoutMs = 5e3) {
|
|
|
11742
11775
|
});
|
|
11743
11776
|
});
|
|
11744
11777
|
}
|
|
11745
|
-
|
|
11746
|
-
//#region src/managed-setup.ts
|
|
11778
|
+
// src/managed-setup.ts
|
|
11747
11779
|
const VIRTUAL_INTERFACE_MARKERS = [
|
|
11748
11780
|
"bridge",
|
|
11749
11781
|
"docker",
|
|
@@ -12241,8 +12273,7 @@ async function materializeManagedSetup(setup, table) {
|
|
|
12241
12273
|
}
|
|
12242
12274
|
};
|
|
12243
12275
|
}
|
|
12244
|
-
|
|
12245
|
-
//#region src/frp-ingress.ts
|
|
12276
|
+
// src/frp-ingress.ts
|
|
12246
12277
|
/** Private ingress directory, always a sibling of the remote device file. */
|
|
12247
12278
|
function frpIngressPaths(stateFile) {
|
|
12248
12279
|
const directory = join(dirname(stateFile), "ingress");
|
|
@@ -12462,8 +12493,7 @@ async function purgeFrpIngressCertificates(stateFile, ownedRoot = dirname(stateF
|
|
|
12462
12493
|
throw error;
|
|
12463
12494
|
}
|
|
12464
12495
|
}
|
|
12465
|
-
|
|
12466
|
-
//#region src/remote.ts
|
|
12496
|
+
// src/remote.ts
|
|
12467
12497
|
const REMOTE_PROVIDERS = Object.freeze([
|
|
12468
12498
|
"tailscale",
|
|
12469
12499
|
"cpolar",
|
|
@@ -12699,8 +12729,7 @@ function configuredRemoteProvider(environment) {
|
|
|
12699
12729
|
if (value !== "tailscale" && value !== "cpolar" && value !== "cloudflared" && value !== "frp" && value !== "origin" && value !== "iroh") throw new Error("DSH_MOBILE_REMOTE_PROVIDER must be tailscale, cpolar, cloudflared, frp, origin, or iroh");
|
|
12700
12730
|
return value;
|
|
12701
12731
|
}
|
|
12702
|
-
|
|
12703
|
-
//#region src/frp.ts
|
|
12732
|
+
// src/frp.ts
|
|
12704
12733
|
const START_TIMEOUT_MS$2 = 45e3;
|
|
12705
12734
|
const DISCOVERY_REQUEST_TIMEOUT_MS = 5e3;
|
|
12706
12735
|
const DISCOVERY_RETRY_MS = 1e3;
|
|
@@ -13310,8 +13339,7 @@ var FrpController = class {
|
|
|
13310
13339
|
], "FRP resource cleanup failed");
|
|
13311
13340
|
}
|
|
13312
13341
|
};
|
|
13313
|
-
|
|
13314
|
-
//#region src/origin-proxy-config.ts
|
|
13342
|
+
// src/origin-proxy-config.ts
|
|
13315
13343
|
const DEFAULT_ORIGIN_LISTEN_PORT = 3444;
|
|
13316
13344
|
const MAX_SETTINGS_BYTES = 8192;
|
|
13317
13345
|
/**
|
|
@@ -13481,8 +13509,7 @@ var OriginConfigStore = class {
|
|
|
13481
13509
|
return this.status();
|
|
13482
13510
|
}
|
|
13483
13511
|
};
|
|
13484
|
-
|
|
13485
|
-
//#region src/origin-proxy.ts
|
|
13512
|
+
// src/origin-proxy.ts
|
|
13486
13513
|
/** Owns an authenticated HTTP gateway, with no tunnel process or public network probes. */
|
|
13487
13514
|
var OriginController = class {
|
|
13488
13515
|
options;
|
|
@@ -13632,13 +13659,12 @@ var OriginController = class {
|
|
|
13632
13659
|
});
|
|
13633
13660
|
}
|
|
13634
13661
|
};
|
|
13635
|
-
|
|
13636
|
-
//#region src/cloudflared-component.ts
|
|
13662
|
+
// src/cloudflared-component.ts
|
|
13637
13663
|
/** Upper bound for `tar -t` output; a pinned release listing is a few lines. */
|
|
13638
13664
|
const MAX_ARCHIVE_LIST_BYTES = 1048576;
|
|
13639
13665
|
const MAX_ARCHIVE_ENTRIES = 256;
|
|
13640
13666
|
const DOWNLOAD_TIMEOUT_MS = 3e5;
|
|
13641
|
-
const CLOUDFLARED_VERSION = "2026.
|
|
13667
|
+
const CLOUDFLARED_VERSION = "2026.10.7";
|
|
13642
13668
|
/** Pinned official cloudflared release metadata for supported desktop targets. */
|
|
13643
13669
|
const CLOUDFLARED_COMPONENT_RELEASES = Object.freeze(Object.fromEntries([
|
|
13644
13670
|
{
|
|
@@ -13646,44 +13672,44 @@ const CLOUDFLARED_COMPONENT_RELEASES = Object.freeze(Object.fromEntries([
|
|
|
13646
13672
|
platform: "win32",
|
|
13647
13673
|
arch: "x64",
|
|
13648
13674
|
downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-windows-amd64.exe`,
|
|
13649
|
-
downloadBytes:
|
|
13650
|
-
downloadSha256: "
|
|
13675
|
+
downloadBytes: 55390512,
|
|
13676
|
+
downloadSha256: "2493dbc168923bb3e59e5e7716f71deced92992eea9acd6e9d74f84ac7751343",
|
|
13651
13677
|
executableName: "cloudflared.exe",
|
|
13652
|
-
executableBytes:
|
|
13653
|
-
executableSha256: "
|
|
13678
|
+
executableBytes: 55390512,
|
|
13679
|
+
executableSha256: "2493dbc168923bb3e59e5e7716f71deced92992eea9acd6e9d74f84ac7751343"
|
|
13654
13680
|
},
|
|
13655
13681
|
{
|
|
13656
13682
|
version: CLOUDFLARED_VERSION,
|
|
13657
13683
|
platform: "linux",
|
|
13658
13684
|
arch: "x64",
|
|
13659
13685
|
downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64`,
|
|
13660
|
-
downloadBytes:
|
|
13661
|
-
downloadSha256: "
|
|
13686
|
+
downloadBytes: 40274445,
|
|
13687
|
+
downloadSha256: "1f99fb518b83dec2ba8d870a7cc991d82a77e6196295a3d2754ba17ab9f91872",
|
|
13662
13688
|
executableName: "cloudflared",
|
|
13663
|
-
executableBytes:
|
|
13664
|
-
executableSha256: "
|
|
13689
|
+
executableBytes: 40274445,
|
|
13690
|
+
executableSha256: "1f99fb518b83dec2ba8d870a7cc991d82a77e6196295a3d2754ba17ab9f91872"
|
|
13665
13691
|
},
|
|
13666
13692
|
{
|
|
13667
13693
|
version: CLOUDFLARED_VERSION,
|
|
13668
13694
|
platform: "linux",
|
|
13669
13695
|
arch: "arm64",
|
|
13670
13696
|
downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64`,
|
|
13671
|
-
downloadBytes:
|
|
13672
|
-
downloadSha256: "
|
|
13697
|
+
downloadBytes: 37667293,
|
|
13698
|
+
downloadSha256: "3df43aedc9ff291d1735b5cb576cb97e0b157488796db2e32b28aecabb445775",
|
|
13673
13699
|
executableName: "cloudflared",
|
|
13674
|
-
executableBytes:
|
|
13675
|
-
executableSha256: "
|
|
13700
|
+
executableBytes: 37667293,
|
|
13701
|
+
executableSha256: "3df43aedc9ff291d1735b5cb576cb97e0b157488796db2e32b28aecabb445775"
|
|
13676
13702
|
},
|
|
13677
13703
|
{
|
|
13678
13704
|
version: CLOUDFLARED_VERSION,
|
|
13679
13705
|
platform: "darwin",
|
|
13680
13706
|
arch: "x64",
|
|
13681
13707
|
downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-darwin-amd64.tgz`,
|
|
13682
|
-
downloadBytes:
|
|
13683
|
-
downloadSha256: "
|
|
13708
|
+
downloadBytes: 21834416,
|
|
13709
|
+
downloadSha256: "02f3edfd87684dd5b705607688e4eb4b323f6e777b81e7b3a85af556aa356848",
|
|
13684
13710
|
executableName: "cloudflared",
|
|
13685
|
-
executableBytes:
|
|
13686
|
-
executableSha256: "
|
|
13711
|
+
executableBytes: 42430512,
|
|
13712
|
+
executableSha256: "5c55e77da7012cebca19170357e196176c48c9647cc2ead404702fa125ddbd86",
|
|
13687
13713
|
archive: {
|
|
13688
13714
|
format: "tgz",
|
|
13689
13715
|
member: "cloudflared"
|
|
@@ -13694,11 +13720,11 @@ const CLOUDFLARED_COMPONENT_RELEASES = Object.freeze(Object.fromEntries([
|
|
|
13694
13720
|
platform: "darwin",
|
|
13695
13721
|
arch: "arm64",
|
|
13696
13722
|
downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-darwin-arm64.tgz`,
|
|
13697
|
-
downloadBytes:
|
|
13698
|
-
downloadSha256: "
|
|
13723
|
+
downloadBytes: 19847161,
|
|
13724
|
+
downloadSha256: "3586ccf397e98e5f437e60e7c307ca6ac463e110580503af8bb8e6f9ebf831bd",
|
|
13699
13725
|
executableName: "cloudflared",
|
|
13700
|
-
executableBytes:
|
|
13701
|
-
executableSha256: "
|
|
13726
|
+
executableBytes: 39487168,
|
|
13727
|
+
executableSha256: "2136431d5eb467bf447d2ee514b713e192719b143aec1d9e09bf7dc290d5bb12",
|
|
13702
13728
|
archive: {
|
|
13703
13729
|
format: "tgz",
|
|
13704
13730
|
member: "cloudflared"
|
|
@@ -13958,8 +13984,7 @@ var CloudflaredComponentManager = class {
|
|
|
13958
13984
|
return task.then(() => this.status());
|
|
13959
13985
|
}
|
|
13960
13986
|
};
|
|
13961
|
-
|
|
13962
|
-
//#region src/cloudflared.ts
|
|
13987
|
+
// src/cloudflared.ts
|
|
13963
13988
|
const MAX_LOG_BUFFER_BYTES$1 = 65536;
|
|
13964
13989
|
/**
|
|
13965
13990
|
* A quick tunnel has to be allocated and registered with the Cloudflare edge
|
|
@@ -14477,8 +14502,7 @@ var CloudflaredController = class {
|
|
|
14477
14502
|
], "cloudflared resource cleanup failed");
|
|
14478
14503
|
}
|
|
14479
14504
|
};
|
|
14480
|
-
|
|
14481
|
-
//#region src/cloudflared-tunnel.ts
|
|
14505
|
+
// src/cloudflared-tunnel.ts
|
|
14482
14506
|
const MAX_CONFIG_BYTES$1 = 8192;
|
|
14483
14507
|
const QUICK_TUNNEL_SUFFIX = ".trycloudflare.com";
|
|
14484
14508
|
/** Cloudflare's own tunnel-routing domain; it can never be a public hostname. */
|
|
@@ -14684,8 +14708,7 @@ var CloudflaredTunnelStore = class {
|
|
|
14684
14708
|
return this.status();
|
|
14685
14709
|
}
|
|
14686
14710
|
};
|
|
14687
|
-
|
|
14688
|
-
//#region src/iroh-files.ts
|
|
14711
|
+
// src/iroh-files.ts
|
|
14689
14712
|
async function renameIrohPath(root, source, destination) {
|
|
14690
14713
|
await renameManagedPath(root, source, destination, "iroh");
|
|
14691
14714
|
}
|
|
@@ -14695,8 +14718,7 @@ async function ensureIrohDirectory(root, directory) {
|
|
|
14695
14718
|
async function removeIrohTree(root, target) {
|
|
14696
14719
|
await removeManagedTree(root, target, "iroh");
|
|
14697
14720
|
}
|
|
14698
|
-
|
|
14699
|
-
//#region src/iroh-manifest.ts
|
|
14721
|
+
// src/iroh-manifest.ts
|
|
14700
14722
|
/** Checksum-gated resolution of the iroh transport host binary.
|
|
14701
14723
|
*
|
|
14702
14724
|
* The sidecar is built and published by the independent `dsh-mobile-iroh`
|
|
@@ -14800,8 +14822,7 @@ async function resolveIrohSidecarAt(companionRoot, platform, arch) {
|
|
|
14800
14822
|
if (process.platform !== "win32" && platform !== "win32" && (before.mode & 73) === 0) throw new Error("iroh_native_not_executable");
|
|
14801
14823
|
return path;
|
|
14802
14824
|
}
|
|
14803
|
-
|
|
14804
|
-
//#region src/iroh-component.ts
|
|
14825
|
+
// src/iroh-component.ts
|
|
14805
14826
|
/** Managed view of the iroh transport host sidecar.
|
|
14806
14827
|
*
|
|
14807
14828
|
* Explicit installation verifies and promotes a pinned sidecar release into
|
|
@@ -15115,8 +15136,7 @@ var IrohComponentManager = class {
|
|
|
15115
15136
|
return this.cached;
|
|
15116
15137
|
}
|
|
15117
15138
|
};
|
|
15118
|
-
|
|
15119
|
-
//#region src/iroh-sidecar.ts
|
|
15139
|
+
// src/iroh-sidecar.ts
|
|
15120
15140
|
/** Versioned parent-to-sidecar protocol for the iroh transport host.
|
|
15121
15141
|
*
|
|
15122
15142
|
* Ported from the independent `dsh-mobile-iroh` companion's sidecar
|
|
@@ -15246,8 +15266,7 @@ function decodeIrohEvent(line) {
|
|
|
15246
15266
|
}
|
|
15247
15267
|
return Object.freeze(event);
|
|
15248
15268
|
}
|
|
15249
|
-
|
|
15250
|
-
//#region src/iroh-origin.ts
|
|
15269
|
+
// src/iroh-origin.ts
|
|
15251
15270
|
/** Identity-derived browser origins for the raw TLS-over-iroh carrier.
|
|
15252
15271
|
*
|
|
15253
15272
|
* Ported from the independent `dsh-mobile-iroh` companion's origin contract.
|
|
@@ -15278,8 +15297,7 @@ function validateIrohOrigin(value, endpointId, localPort = DEFAULT_IROH_LOCAL_PO
|
|
|
15278
15297
|
if (value !== expected) throw new Error("iroh origin does not match the selected endpoint and local port");
|
|
15279
15298
|
return expected;
|
|
15280
15299
|
}
|
|
15281
|
-
|
|
15282
|
-
//#region src/iroh-pairing.ts
|
|
15300
|
+
// src/iroh-pairing.ts
|
|
15283
15301
|
/** Short-lived iroh pairing descriptors for QR transport.
|
|
15284
15302
|
*
|
|
15285
15303
|
* Byte-compatible with the independent `dsh-mobile-iroh` companion's
|
|
@@ -15372,6 +15390,14 @@ function validatePairingDescriptor(value, options = {}) {
|
|
|
15372
15390
|
function encodePairingDescriptor(value, options = {}) {
|
|
15373
15391
|
return JSON.stringify(validatePairingDescriptor(value, options));
|
|
15374
15392
|
}
|
|
15393
|
+
/** Draw the canonical pairing payload with its four-module quiet zone inside the SVG. */
|
|
15394
|
+
function renderIrohPairingQr(encoded) {
|
|
15395
|
+
return QRCode.toString(encoded, {
|
|
15396
|
+
type: "svg",
|
|
15397
|
+
margin: 4,
|
|
15398
|
+
errorCorrectionLevel: "M"
|
|
15399
|
+
});
|
|
15400
|
+
}
|
|
15375
15401
|
/** Decode untrusted QR text, validating its expiry and public CA before use. */
|
|
15376
15402
|
function decodePairingDescriptor(encoded, options = {}) {
|
|
15377
15403
|
if (typeof encoded !== "string" || Buffer.byteLength(encoded, "utf8") > 2200) reject("pairing_descriptor_too_large");
|
|
@@ -15387,8 +15413,7 @@ function decodePairingDescriptor(encoded, options = {}) {
|
|
|
15387
15413
|
}
|
|
15388
15414
|
/** Phone-side listener port advertised in QR codes minted by this computer. */
|
|
15389
15415
|
const PAIRING_DESCRIPTOR_LOCAL_PORT = DEFAULT_IROH_LOCAL_PORT;
|
|
15390
|
-
|
|
15391
|
-
//#region src/iroh.ts
|
|
15416
|
+
// src/iroh.ts
|
|
15392
15417
|
/** Experimental iroh remote transport as a first-class remote provider.
|
|
15393
15418
|
*
|
|
15394
15419
|
* The sidecar is distributed by this plugin itself: an explicit install
|
|
@@ -15939,8 +15964,7 @@ var IrohController = class {
|
|
|
15939
15964
|
});
|
|
15940
15965
|
}
|
|
15941
15966
|
};
|
|
15942
|
-
|
|
15943
|
-
//#region src/iroh-tls.ts
|
|
15967
|
+
// src/iroh-tls.ts
|
|
15944
15968
|
/** Self-signed TLS identity for the experimental iroh gateway.
|
|
15945
15969
|
*
|
|
15946
15970
|
* Reuses this repository's managed CA path (`managed-setup.ts`) instead of
|
|
@@ -16107,8 +16131,7 @@ function irohGatewayConfig(template, endpointId, stateFile, tls, listenPort = 0)
|
|
|
16107
16131
|
pairingTtlMs: 3e5
|
|
16108
16132
|
});
|
|
16109
16133
|
}
|
|
16110
|
-
|
|
16111
|
-
//#region src/task-events.ts
|
|
16134
|
+
// src/task-events.ts
|
|
16112
16135
|
const TASK_EVENT_DEBOUNCE_MS = 1e3;
|
|
16113
16136
|
function turnNumber(value) {
|
|
16114
16137
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : 0;
|
|
@@ -16185,8 +16208,99 @@ var TaskEventHub = class {
|
|
|
16185
16208
|
return this.sinks.size;
|
|
16186
16209
|
}
|
|
16187
16210
|
};
|
|
16188
|
-
|
|
16189
|
-
|
|
16211
|
+
// src/diagnostic-network-context.ts
|
|
16212
|
+
function addressKind(address) {
|
|
16213
|
+
if (isIP(address) === 4) {
|
|
16214
|
+
const [first, second] = address.split(".").map(Number);
|
|
16215
|
+
if (first === 100 && second >= 64 && second <= 127) return "tailnet";
|
|
16216
|
+
if (first === 198 && (second === 18 || second === 19)) return "fake-ip";
|
|
16217
|
+
if (first === 0 || first === 10 || first === 127 || first >= 224 || first === 169 && second === 254 || first === 172 && second >= 16 && second <= 31 || first === 192 && second === 168) return "private";
|
|
16218
|
+
return "public";
|
|
16219
|
+
}
|
|
16220
|
+
if (isIP(address) === 6) {
|
|
16221
|
+
const lower = address.toLowerCase();
|
|
16222
|
+
if (lower.startsWith("::ffff:") && isIP(lower.slice(7)) === 4) return addressKind(lower.slice(7));
|
|
16223
|
+
if (lower.startsWith("fd7a:115c:a1e0:")) return "tailnet";
|
|
16224
|
+
if (lower === "::" || lower === "::1" || /^(?:f[cd]|fe[89ab]|ff)/u.test(lower)) return "private";
|
|
16225
|
+
return "public";
|
|
16226
|
+
}
|
|
16227
|
+
return "unknown";
|
|
16228
|
+
}
|
|
16229
|
+
/**
|
|
16230
|
+
* Collapse system resolver results into non-identifying categories, without retaining addresses.
|
|
16231
|
+
* @param addresses - Addresses supplied by the system resolver.
|
|
16232
|
+
* @returns Address category and observed IP families; mixed answers are not treated as tailnet-only.
|
|
16233
|
+
*/
|
|
16234
|
+
function classifyDiagnosticDns(addresses) {
|
|
16235
|
+
const kinds = new Set(addresses.map((entry) => addressKind(entry.address)));
|
|
16236
|
+
const families = new Set(addresses.map((entry) => isIP(entry.address)).filter((value) => value !== 0));
|
|
16237
|
+
return {
|
|
16238
|
+
kind: kinds.size === 0 ? "unknown" : kinds.size === 1 ? [...kinds][0] : "mixed",
|
|
16239
|
+
family: families.size === 0 ? "unknown" : families.size > 1 ? "dual" : families.has(4) ? "ipv4" : "ipv6"
|
|
16240
|
+
};
|
|
16241
|
+
}
|
|
16242
|
+
/**
|
|
16243
|
+
* Read bounded system-DNS context; never consult an external DNS service or modify settings.
|
|
16244
|
+
* @param hostname - The configured endpoint's hostname.
|
|
16245
|
+
* @param budgetMs - Maximum wait; OS lookup completion itself cannot be cancelled.
|
|
16246
|
+
* @param resolve - System lookup or an instance-local controlled fixture.
|
|
16247
|
+
* @returns Unknown on failure/deadline, not a conclusion that DNS or the public service is down.
|
|
16248
|
+
*/
|
|
16249
|
+
async function probeDiagnosticDns(hostname, budgetMs, resolve = (host) => lookup(host, { all: true })) {
|
|
16250
|
+
let timer;
|
|
16251
|
+
try {
|
|
16252
|
+
return classifyDiagnosticDns(await Promise.race([resolve(hostname), new Promise((_resolve, reject) => {
|
|
16253
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error("dns_context_timeout")), Math.max(1, budgetMs));
|
|
16254
|
+
timer.unref();
|
|
16255
|
+
})]));
|
|
16256
|
+
} catch {
|
|
16257
|
+
return {
|
|
16258
|
+
kind: "unknown",
|
|
16259
|
+
family: "unknown"
|
|
16260
|
+
};
|
|
16261
|
+
} finally {
|
|
16262
|
+
clearTimeout(timer);
|
|
16263
|
+
}
|
|
16264
|
+
}
|
|
16265
|
+
/**
|
|
16266
|
+
* Classify fixed transport machine codes without reflecting messages, URLs or nested errors.
|
|
16267
|
+
* @param error - A failed endpoint request.
|
|
16268
|
+
* @returns The last observed transport stage, or unknown rather than a guessed cause.
|
|
16269
|
+
*/
|
|
16270
|
+
function diagnosticFailureStage(error) {
|
|
16271
|
+
let current = error;
|
|
16272
|
+
for (let depth = 0; depth < 4 && current !== null && typeof current === "object"; depth++) {
|
|
16273
|
+
const code = "code" in current && typeof current.code === "string" ? current.code : "";
|
|
16274
|
+
if ("name" in current && current.name === "TimeoutError") return "timeout";
|
|
16275
|
+
if (["ENOTFOUND", "EAI_AGAIN"].includes(code)) return "dns";
|
|
16276
|
+
if ([
|
|
16277
|
+
"ECONNREFUSED",
|
|
16278
|
+
"ECONNRESET",
|
|
16279
|
+
"EPIPE",
|
|
16280
|
+
"ENETUNREACH",
|
|
16281
|
+
"EHOSTUNREACH",
|
|
16282
|
+
"UND_ERR_SOCKET"
|
|
16283
|
+
].includes(code)) return "connect";
|
|
16284
|
+
if ([
|
|
16285
|
+
"ETIMEDOUT",
|
|
16286
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
16287
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
16288
|
+
"UND_ERR_BODY_TIMEOUT"
|
|
16289
|
+
].includes(code)) return "timeout";
|
|
16290
|
+
if ([
|
|
16291
|
+
"ERR_TLS_CERT_ALTNAME_INVALID",
|
|
16292
|
+
"CERT_HAS_EXPIRED",
|
|
16293
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
16294
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
16295
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
16296
|
+
"UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
|
16297
|
+
"ERR_SSL_WRONG_VERSION_NUMBER"
|
|
16298
|
+
].includes(code)) return "tls";
|
|
16299
|
+
current = "cause" in current ? current.cause : void 0;
|
|
16300
|
+
}
|
|
16301
|
+
return "unknown";
|
|
16302
|
+
}
|
|
16303
|
+
// src/diagnostics.ts
|
|
16190
16304
|
/** Recognize the active remote-web-ui boot hook without evaluating or modifying it. */
|
|
16191
16305
|
function hasCompetingRemoteChannelBoot(rows) {
|
|
16192
16306
|
return rows.some((row) => row.kind === "script" && row.text?.includes("__DSH_REMOTE_CHANNEL_BOOT__") === true);
|
|
@@ -16245,6 +16359,7 @@ function check(id, status, reason, label, detail, action, facts) {
|
|
|
16245
16359
|
id,
|
|
16246
16360
|
status,
|
|
16247
16361
|
reason,
|
|
16362
|
+
scope: id === "versions" ? "configuration" : id === "phone-network" ? "phone" : "computer",
|
|
16248
16363
|
...facts === void 0 ? {} : { facts: Object.freeze(facts) },
|
|
16249
16364
|
label,
|
|
16250
16365
|
detail,
|
|
@@ -16489,28 +16604,6 @@ async function relayProbeThroughProxy(target, proxy, budgetMs = remoteDiagnostic
|
|
|
16489
16604
|
hopRequest.end();
|
|
16490
16605
|
});
|
|
16491
16606
|
}
|
|
16492
|
-
/** Bounded supplementary DNS context for a failed direct attempt. */
|
|
16493
|
-
async function unreachableWithDnsContext(hostname, budgetMs) {
|
|
16494
|
-
let fakeIp = false;
|
|
16495
|
-
let timer;
|
|
16496
|
-
try {
|
|
16497
|
-
fakeIp = (await Promise.race([lookup(hostname, { all: true }), new Promise((_resolve, reject) => {
|
|
16498
|
-
timer = setTimeout(() => {
|
|
16499
|
-
reject(/* @__PURE__ */ new Error("dns_context_timeout"));
|
|
16500
|
-
}, Math.max(1, budgetMs));
|
|
16501
|
-
timer.unref();
|
|
16502
|
-
})])).some(({ address }) => {
|
|
16503
|
-
const [first, second] = address.split(".").map(Number);
|
|
16504
|
-
return first === 198 && (second === 18 || second === 19);
|
|
16505
|
-
});
|
|
16506
|
-
} catch {} finally {
|
|
16507
|
-
clearTimeout(timer);
|
|
16508
|
-
}
|
|
16509
|
-
return {
|
|
16510
|
-
state: "unreachable",
|
|
16511
|
-
...fakeIp ? { fakeIp: true } : {}
|
|
16512
|
-
};
|
|
16513
|
-
}
|
|
16514
16607
|
async function fetchRemoteHealth(target, budgetMs) {
|
|
16515
16608
|
const started = performance.now();
|
|
16516
16609
|
try {
|
|
@@ -16520,19 +16613,28 @@ async function fetchRemoteHealth(target, budgetMs) {
|
|
|
16520
16613
|
signal: AbortSignal.timeout(Math.max(1, budgetMs - 500))
|
|
16521
16614
|
});
|
|
16522
16615
|
const latencyMs = Math.max(0, Math.round(performance.now() - started));
|
|
16523
|
-
if (response.status === 429)
|
|
16524
|
-
|
|
16525
|
-
|
|
16526
|
-
|
|
16527
|
-
|
|
16616
|
+
if (response.status === 429) {
|
|
16617
|
+
await response.body?.cancel();
|
|
16618
|
+
return {
|
|
16619
|
+
state: "rate-limited",
|
|
16620
|
+
latencyMs
|
|
16621
|
+
};
|
|
16622
|
+
}
|
|
16623
|
+
const observation = response.ok ? {
|
|
16528
16624
|
state: "ready",
|
|
16529
16625
|
latencyMs
|
|
16530
16626
|
} : {
|
|
16531
16627
|
state: "unreachable",
|
|
16532
|
-
latencyMs
|
|
16628
|
+
latencyMs,
|
|
16629
|
+
failureStage: "http"
|
|
16630
|
+
};
|
|
16631
|
+
await response.body?.cancel();
|
|
16632
|
+
return observation;
|
|
16633
|
+
} catch (error) {
|
|
16634
|
+
return {
|
|
16635
|
+
state: "unreachable",
|
|
16636
|
+
failureStage: diagnosticFailureStage(error)
|
|
16533
16637
|
};
|
|
16534
|
-
} catch {
|
|
16535
|
-
return unreachableWithDnsContext(target.hostname, Math.max(1, budgetMs - (performance.now() - started)));
|
|
16536
16638
|
}
|
|
16537
16639
|
}
|
|
16538
16640
|
/**
|
|
@@ -16555,7 +16657,14 @@ async function probeRemoteHealth(origin, options = {}) {
|
|
|
16555
16657
|
return { state: "not-applicable" };
|
|
16556
16658
|
}
|
|
16557
16659
|
const budgetMs = remoteDiagnosticTimeoutMs(origin);
|
|
16558
|
-
const
|
|
16660
|
+
const direct = options.direct ?? fetchRemoteHealth;
|
|
16661
|
+
const [observed, dns] = await Promise.all([direct(target, budgetMs), options.direct !== void 0 && options.lookup === void 0 ? void 0 : probeDiagnosticDns(target.hostname, Math.min(750, budgetMs), options.lookup)]);
|
|
16662
|
+
const context = dns === void 0 ? {} : { dns };
|
|
16663
|
+
const observation = {
|
|
16664
|
+
...observed,
|
|
16665
|
+
...context,
|
|
16666
|
+
...dns?.kind === "fake-ip" ? { fakeIp: true } : {}
|
|
16667
|
+
};
|
|
16559
16668
|
if (observation.state !== "unreachable") return observation;
|
|
16560
16669
|
const relay = configuredProxyFor(origin, options.env ?? process.env);
|
|
16561
16670
|
if (relay === void 0) return observation;
|
|
@@ -16565,12 +16674,14 @@ async function probeRemoteHealth(origin, options = {}) {
|
|
|
16565
16674
|
if (status === 429) return {
|
|
16566
16675
|
state: "rate-limited",
|
|
16567
16676
|
latencyMs,
|
|
16568
|
-
viaProxy: true
|
|
16677
|
+
viaProxy: true,
|
|
16678
|
+
...context
|
|
16569
16679
|
};
|
|
16570
16680
|
return status >= 200 && status < 300 ? {
|
|
16571
16681
|
state: "ready",
|
|
16572
16682
|
latencyMs,
|
|
16573
|
-
viaProxy: true
|
|
16683
|
+
viaProxy: true,
|
|
16684
|
+
...context
|
|
16574
16685
|
} : observation;
|
|
16575
16686
|
} catch {
|
|
16576
16687
|
return observation;
|
|
@@ -16580,28 +16691,38 @@ async function defaultRemoteProbe(origin) {
|
|
|
16580
16691
|
return probeRemoteHealth(origin);
|
|
16581
16692
|
}
|
|
16582
16693
|
function reportLine(entry) {
|
|
16583
|
-
return `[${entry.status.toUpperCase()}] ${entry.label}: ${entry.detail}${entry.action === void 0 ? "" : ` ${entry.action}`}`;
|
|
16694
|
+
return `[${entry.status.toUpperCase()}][${entry.scope}] ${entry.label}: ${entry.detail}${entry.action === void 0 ? "" : ` ${entry.action}`}`;
|
|
16584
16695
|
}
|
|
16585
16696
|
/** Run bounded read-only checks and return a report safe to paste into an issue. */
|
|
16586
16697
|
async function collectConnectionDiagnostics(snapshot, probes = {}) {
|
|
16587
16698
|
const checks = [];
|
|
16588
16699
|
const remoteProbe = snapshot.remote.provider !== "origin" && snapshot.remote.provider !== "iroh" && snapshot.remote.running && snapshot.remote.state === "ready" && snapshot.remote.origin !== void 0 ? (probes.remote ?? defaultRemoteProbe)(snapshot.remote.origin) : Promise.resolve({ state: "not-applicable" });
|
|
16589
|
-
const [firewall, remoteObservation] = await Promise.all([(probes.firewall ?? defaultFirewallProbe())(snapshot.lan.port), remoteProbe]);
|
|
16590
|
-
checks.push(check("versions", "ok", "versions-current", "
|
|
16700
|
+
const [firewall, remoteObservation] = await Promise.all([snapshot.lan.running ? (probes.firewall ?? defaultFirewallProbe())(snapshot.lan.port) : Promise.resolve({ state: "not-applicable" }), remoteProbe]);
|
|
16701
|
+
checks.push(check("versions", "ok", "versions-current", "版本信息", `插件 ${DSH_MOBILE_VERSION},DSH ${snapshot.dshVersion},Android App 最低 ${MINIMUM_ANDROID_APP_VERSION}。`));
|
|
16591
16702
|
if (snapshot.competingRemoteChannelBoot === true) checks.push(check("remote-plugin-conflict", "warning", "competing-remote-channel", "第三方远程插件", "检测到另一套远程请求通道脚本;手机页面可能进入该插件自己的配对流程。", "在电脑端关闭 dsh-remote-web-ui 的远程访问后刷新,再使用 DSH Mobile 当前二维码配对;不要混用两种配对码。"));
|
|
16592
|
-
if (snapshot.
|
|
16593
|
-
|
|
16703
|
+
if (snapshot.indexInspectionUnavailable === true) checks.push(check("plugin-inspection", "warning", "plugin-inspection-unavailable", "页面脚本检查", "本次未能检查活动页面脚本;不能据此认定不存在其他远程通道。", "其余网络项目独立检查;保留复现环境并核对相关插件。"));
|
|
16704
|
+
if (snapshot.lan.configured === false) checks.push(check("network", snapshot.lan.running ? "error" : "info", "lan-setup-required", "局域网配置", "尚未完成局域网初始化。", "需要局域网连接时,返回局域网页选择网卡并完成配置。"));
|
|
16705
|
+
else if (snapshot.lan.networkError !== void 0) checks.push(check("network", snapshot.lan.running ? "error" : "info", "network-unavailable", "局域网网卡", "已保存的网卡当前不可用。", snapshot.lan.running ? "在局域网页重新确认网卡配置。" : "局域网未启用;此配置不用于判断远程入口。"));
|
|
16594
16706
|
else if (snapshot.lan.configuredInterface !== void 0) {
|
|
16595
16707
|
const interfaceName = snapshot.lan.interfaceName ?? snapshot.lan.configuredInterface;
|
|
16596
16708
|
checks.push(check("network", "ok", "network-interface", "局域网网卡", `正在跟随 ${interfaceName}。`, void 0, { interfaceName }));
|
|
16597
16709
|
} else checks.push(check("network", "info", "network-fixed", "局域网网卡", "当前使用固定网络配置。"));
|
|
16598
16710
|
if (snapshot.lan.running && snapshot.lan.origin !== void 0) {
|
|
16599
16711
|
const endpointSuffix = maskLanOrigin(snapshot.lan.origin);
|
|
16600
|
-
checks.push(check("lan", "ok", "lan-ready", "局域网网关",
|
|
16712
|
+
checks.push(check("lan", "ok", "lan-ready", "局域网网关", `电脑网关已监听 ${endpointSuffix};尚未验证手机能否到达。`, void 0, { endpointSuffix }));
|
|
16601
16713
|
} else checks.push(check("lan", "info", "lan-off", "局域网网关", "当前未开启。", "需要手机直连时开启局域网访问。"));
|
|
16602
|
-
if (
|
|
16603
|
-
else if (firewall.state === "
|
|
16714
|
+
if (!snapshot.lan.running) checks.push(check("firewall", "info", "firewall-inactive", "Windows 防火墙", "局域网未启用,本次未检查局域网防火墙规则。", "不据此判断远程入口;需要局域网时再启用并检查。"));
|
|
16715
|
+
else if (firewall.state === "ready") checks.push(check("firewall", "ok", "firewall-ready", "Windows 防火墙", "局域网 TCP 与发现规则已启用。"));
|
|
16716
|
+
else if (firewall.state === "missing") checks.push(check("firewall", snapshot.lan.running ? "warning" : "info", "firewall-missing", "Windows 防火墙", "未找到完整的局域网放行规则。", snapshot.lan.running ? "确认需要局域网连接,再检查相应规则;不要关闭整个防火墙。" : "局域网未启用,不据此判断远程入口失败。"));
|
|
16604
16717
|
else if (firewall.state === "unknown") checks.push(check("firewall", "info", "firewall-unknown", "Windows 防火墙", "系统未允许插件读取防火墙状态。", "若手机找不到电脑,以管理员身份重新运行 setup。"));
|
|
16718
|
+
if (snapshot.remote.running && snapshot.remote.state === "ready") checks.push(check("remote-service", "ok", "remote-service-ready", "本机通道状态", "提供方报告本机组件已就绪;不代表公网或手机路径已验证。", void 0, { provider: snapshot.remote.provider }));
|
|
16719
|
+
if (remoteObservation.dns !== void 0) {
|
|
16720
|
+
const kind = remoteObservation.dns.kind;
|
|
16721
|
+
checks.push(check("remote-dns", kind === "fake-ip" ? "warning" : "info", `dns-${kind}`, "系统 DNS", `电脑系统解析分类:${kind};IP:${remoteObservation.dns.family}。未查询公共 DNS,地址不写入报告。`, void 0, {
|
|
16722
|
+
dnsKind: kind,
|
|
16723
|
+
dnsFamily: remoteObservation.dns.family
|
|
16724
|
+
}));
|
|
16725
|
+
}
|
|
16605
16726
|
if (!snapshot.remote.running || snapshot.remote.state === "off") checks.push(check("remote", "info", "remote-off", "远程通道", "当前未启用。", void 0, { provider: snapshot.remote.provider }));
|
|
16606
16727
|
else if (snapshot.remote.provider === "origin" && snapshot.remote.state === "ready") checks.push(check("remote", "info", "remote-origin-ready", "远程通道", "私网 HTTP 后端已就绪;未检测公网 HTTPS、证书或 WebSocket 连通性。", "在手机上经反向代理验证连接;代理须保留原始 Host(含端口)并支持 WebSocket。", { provider: "origin" }));
|
|
16607
16728
|
else if (snapshot.remote.provider === "iroh" && snapshot.remote.state === "ready") checks.push(check("remote", "info", "remote-iroh-ready", "远程通道", "iroh 通道已就绪;端点身份为回环 TLS,不从电脑端探测手机可达性。", "在手机上用配对二维码验证连接;二维码过期或通道报错时重新生成并重连。", { provider: "iroh" }));
|
|
@@ -16613,10 +16734,14 @@ async function collectConnectionDiagnostics(snapshot, probes = {}) {
|
|
|
16613
16734
|
...remoteObservation.latencyMs === void 0 ? {} : { latencyMs: remoteObservation.latencyMs },
|
|
16614
16735
|
...remoteObservation.viaProxy === true ? { viaProxy: true } : {}
|
|
16615
16736
|
};
|
|
16616
|
-
if (remoteObservation.state === "ready") checks.push(check("remote", remoteObservation.viaProxy === true ? "warning" : "ok", "remote-ready", "远程通道", remoteObservation.viaProxy === true ? `电脑经代理可访问 ${endpointSuffix},但尚未验证手机能否连接。` :
|
|
16737
|
+
if (remoteObservation.state === "ready") checks.push(check("remote", remoteObservation.viaProxy === true ? "warning" : "ok", "remote-ready", "远程通道", remoteObservation.viaProxy === true ? `电脑经代理可访问 ${endpointSuffix},但尚未验证手机能否连接。` : `电脑当前出口可访问 ${snapshot.remote.provider} 入口 ${endpointSuffix},往返约 ${String(remoteObservation.latencyMs ?? 0)} ms;公网及手机路径未独立验证。`, remoteObservation.viaProxy === true ? "请在手机上测试远程连接。" : void 0, facts));
|
|
16617
16738
|
else if (remoteObservation.state === "rate-limited") checks.push(check("remote", "warning", "remote-rate-limited", "远程通道", remoteObservation.viaProxy === true ? "电脑经代理访问时观察到限流,尚未验证手机能否连接。" : "公共地址可达,但本次检查观察到服务限流。", "稍后重试;旧会话会按需加载以减少流量。", facts));
|
|
16618
|
-
else if (snapshot.remote.provider === "tailscale" && remoteObservation.
|
|
16619
|
-
else checks.push(check("remote", "error", "remote-
|
|
16739
|
+
else if (snapshot.remote.provider === "tailscale" && remoteObservation.dns?.kind === "tailnet" && (remoteObservation.failureStage === "connect" || remoteObservation.failureStage === "timeout")) checks.push(check("remote", "warning", "remote-tailnet-shadowed", "电脑出口探测", "本机系统 DNS 返回 tailnet 地址,当前 Funnel-only 监听不接受此私网路径;本次失败不能证明公网 Funnel 故障。", "用 tailnet 外的网络单独验证;如自愿临时调整设备 DNS,注意其对其他私网服务的影响并在对照后恢复。", facts));
|
|
16740
|
+
else if (snapshot.remote.provider === "tailscale" && (remoteObservation.fakeIp === true || remoteObservation.dns?.kind === "fake-ip") && remoteObservation.failureStage !== "tls" && remoteObservation.failureStage !== "http") checks.push(check("remote", "error", "remote-fake-ip", "远程通道", "本机系统 DNS 返回 Fake-IP 网段,本次出口探测未成功;这不是公网故障的证明。", "核对 DNS 与代理路由,用其他网络对照,不要关闭证书验证。", facts));
|
|
16741
|
+
else {
|
|
16742
|
+
const reason = remoteObservation.failureStage === "dns" ? "remote-dns-failed" : remoteObservation.failureStage === "connect" ? "remote-connect-failed" : remoteObservation.failureStage === "tls" ? "remote-tls-failed" : remoteObservation.failureStage === "timeout" ? "remote-probe-timeout" : remoteObservation.failureStage === "http" ? "remote-http-failed" : "remote-unreachable";
|
|
16743
|
+
checks.push(check("remote", "error", reason, "电脑出口探测", "电脑当前出口的探测未通过;不代表其他网络或手机一定失败。", "按失败阶段核对 DNS、路由、证书或代理,并用手机当前网络对照。", facts));
|
|
16744
|
+
}
|
|
16620
16745
|
} else if (snapshot.remote.state === "starting" || snapshot.remote.state === "connecting" || snapshot.remote.state === "needs-login") {
|
|
16621
16746
|
const needsLogin = snapshot.remote.state === "needs-login";
|
|
16622
16747
|
checks.push(check("remote", "warning", needsLogin ? "remote-needs-login" : "remote-connecting", "远程通道", needsLogin ? "等待完成 Tailscale 登录。" : "仍在建立连接。", needsLogin ? "返回远程页继续登录。" : "等待片刻后重新检查。", { provider: snapshot.remote.provider }));
|
|
@@ -16627,15 +16752,16 @@ async function collectConnectionDiagnostics(snapshot, probes = {}) {
|
|
|
16627
16752
|
controllerCode
|
|
16628
16753
|
}));
|
|
16629
16754
|
}
|
|
16630
|
-
checks.push(check("phone-network", "info", "phone-network-unknown", "手机网络", "
|
|
16755
|
+
checks.push(check("phone-network", "info", "phone-network-unknown", "手机网络", "电脑没有验证手机网络、认证页面、资源加载或 WebSocket。", "请用手机当前网络实际连接,记录失败阶段;不要提供配对凭据或直接关闭安全隔离措施。"));
|
|
16631
16756
|
const overall = checks.some((entry) => entry.status === "error") ? "error" : checks.some((entry) => entry.status === "warning") ? "attention" : "ok";
|
|
16632
|
-
const summary = overall === "ok" ? "
|
|
16757
|
+
const summary = overall === "ok" ? "电脑端基础检查完成;手机、认证页面、资源与 WebSocket 尚未验证。" : overall === "attention" ? "电脑端检查有待确认项目;请区分测量路径与实际手机连接。" : "电脑端检查有失败项目;不据此推断所有网络均不可达。";
|
|
16633
16758
|
const report = [
|
|
16634
16759
|
"DSH Mobile 诊断报告",
|
|
16635
16760
|
`生成时间: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
16636
16761
|
`版本: plugin=${DSH_MOBILE_VERSION}; dsh=${snapshot.dshVersion}; min-app=${MINIMUM_ANDROID_APP_VERSION}`,
|
|
16637
16762
|
`LAN: ${snapshot.lan.running ? "on" : "off"}; endpoint=${maskLanOrigin(snapshot.lan.origin)}`,
|
|
16638
16763
|
`Remote: provider=${snapshot.remote.provider}; state=${snapshot.remote.state}; endpoint=${remoteSuffix(snapshot.remote.origin)}`,
|
|
16764
|
+
"Coverage: computer-side only; phone, authenticated page, resources and WebSocket remain unverified.",
|
|
16639
16765
|
...checks.map(reportLine)
|
|
16640
16766
|
].join("\n");
|
|
16641
16767
|
return Object.freeze({
|
|
@@ -16649,11 +16775,19 @@ async function collectConnectionDiagnostics(snapshot, probes = {}) {
|
|
|
16649
16775
|
}),
|
|
16650
16776
|
summary,
|
|
16651
16777
|
checks: Object.freeze(checks),
|
|
16652
|
-
report
|
|
16778
|
+
report,
|
|
16779
|
+
coverage: Object.freeze({
|
|
16780
|
+
measuredFrom: "computer",
|
|
16781
|
+
unverified: Object.freeze([
|
|
16782
|
+
"phone",
|
|
16783
|
+
"authenticated-page",
|
|
16784
|
+
"resources",
|
|
16785
|
+
"websocket"
|
|
16786
|
+
])
|
|
16787
|
+
})
|
|
16653
16788
|
});
|
|
16654
16789
|
}
|
|
16655
|
-
|
|
16656
|
-
//#region src/mobile-guide.ts
|
|
16790
|
+
// src/mobile-guide.ts
|
|
16657
16791
|
/** Compose the guide with the current customization state injected. */
|
|
16658
16792
|
function buildMobileGuide(state) {
|
|
16659
16793
|
const styleLine = state.hasCustomCss ? "存在(已检测到文件,需在已配对页面验证加载)" : "不存在(使用内置默认样式)";
|
|
@@ -16718,8 +16852,7 @@ const MOBILE_CUSTOMIZATION_GUIDE_BODY = `你在为用户定制 DSH Mobile 的手
|
|
|
16718
16852
|
- 创建或修改扩展后,确认 extension.json 的 schemaVersion 为 1、id 与目录名一致、且 id 只含小写字母数字和连字符
|
|
16719
16853
|
- 扩展的 host.mjs 若在完成前无法激活,先修正而不是留下损坏的扩展
|
|
16720
16854
|
- 完成后检查自己实际写入了哪些文件,向用户简要说明改了什么、手机端会有什么变化`;
|
|
16721
|
-
|
|
16722
|
-
//#region src/vps-deploy.ts
|
|
16855
|
+
// src/vps-deploy.ts
|
|
16723
16856
|
const FRP_VERSION = "0.70.1";
|
|
16724
16857
|
const SSH_TIMEOUT_MS = 3e5;
|
|
16725
16858
|
const LINUX_ARTIFACTS = Object.freeze({
|
|
@@ -17690,8 +17823,7 @@ async function uninstallVps(serverAddress, uninstall, input, options = {}) {
|
|
|
17690
17823
|
checks
|
|
17691
17824
|
});
|
|
17692
17825
|
}
|
|
17693
|
-
|
|
17694
|
-
//#region src/lan-rebind.ts
|
|
17826
|
+
// src/lan-rebind.ts
|
|
17695
17827
|
/** An unreadable setup is rejected before stopping the listener or replacing any file. */
|
|
17696
17828
|
var ManagedLanConfigurationError = class extends Error {
|
|
17697
17829
|
constructor(cause) {
|
|
@@ -17728,8 +17860,7 @@ async function rebindManagedLan(options) {
|
|
|
17728
17860
|
running: result.running
|
|
17729
17861
|
};
|
|
17730
17862
|
}
|
|
17731
|
-
|
|
17732
|
-
//#region src/funnel.ts
|
|
17863
|
+
// src/funnel.ts
|
|
17733
17864
|
const MAX_PROTOCOL_LINE_BYTES = 16384;
|
|
17734
17865
|
const FUNNEL_START_TIMEOUT_MS = 45e3;
|
|
17735
17866
|
function publicStatus$1(status) {
|
|
@@ -18243,8 +18374,7 @@ function funnelExecutable(importMetaUrl, environment = process.env) {
|
|
|
18243
18374
|
const file = `dsh-mobile-funnel-${process.platform}-${process.arch}${suffix}`;
|
|
18244
18375
|
return resolve(fileURLToPath(new URL(`../bin/${file}`, importMetaUrl)));
|
|
18245
18376
|
}
|
|
18246
|
-
|
|
18247
|
-
//#region src/cpolar.ts
|
|
18377
|
+
// src/cpolar.ts
|
|
18248
18378
|
const MAX_LOG_BUFFER_BYTES = 65536;
|
|
18249
18379
|
const START_TIMEOUT_MS = 45e3;
|
|
18250
18380
|
const CPOLAR_HOST_SUFFIXES = Object.freeze([
|
|
@@ -18660,8 +18790,7 @@ var CpolarController = class {
|
|
|
18660
18790
|
], "cpolar resource cleanup failed");
|
|
18661
18791
|
}
|
|
18662
18792
|
};
|
|
18663
|
-
|
|
18664
|
-
//#region src/caddy-files.ts
|
|
18793
|
+
// src/caddy-files.ts
|
|
18665
18794
|
/** Refuse link-shaped ancestors within Caddy's owned state root. */
|
|
18666
18795
|
async function assertCaddyParents(root, target) {
|
|
18667
18796
|
await assertManagedParents(root, target, "caddy");
|
|
@@ -18682,8 +18811,7 @@ async function writeCaddyPrivateFile(root, file, body) {
|
|
|
18682
18811
|
async function removeCaddyTree(root, target) {
|
|
18683
18812
|
await removeManagedTree(root, target, "caddy");
|
|
18684
18813
|
}
|
|
18685
|
-
|
|
18686
|
-
//#region src/caddy-component.ts
|
|
18814
|
+
// src/caddy-component.ts
|
|
18687
18815
|
const CADDY_VERSION = "2.11.7";
|
|
18688
18816
|
const CADDY_DNS_PLUGIN_VERSION = "v0.4.3";
|
|
18689
18817
|
const CADDY_DOWNLOAD_PAGE = "https://github.com/saya-ch/dsh-mobile/releases/tag/caddy-component-2.11.7-tencentcloud-0.4.3-r1";
|
|
@@ -19202,8 +19330,7 @@ var CaddyConfigStore = class {
|
|
|
19202
19330
|
return task;
|
|
19203
19331
|
}
|
|
19204
19332
|
};
|
|
19205
|
-
|
|
19206
|
-
//#region src/caddy.ts
|
|
19333
|
+
// src/caddy.ts
|
|
19207
19334
|
/** Private directories override Caddy's platform defaults; ambient API keys and admin/proxy overrides are omitted. */
|
|
19208
19335
|
function caddyProcessEnvironment(config, settings, environment = process.env) {
|
|
19209
19336
|
const credentials = config.credentials();
|
|
@@ -19533,8 +19660,7 @@ var CaddyController = class {
|
|
|
19533
19660
|
}
|
|
19534
19661
|
}
|
|
19535
19662
|
};
|
|
19536
|
-
|
|
19537
|
-
//#region src/managed-origin.ts
|
|
19663
|
+
// src/managed-origin.ts
|
|
19538
19664
|
/** Children never independently restore a durable enabled switch. */
|
|
19539
19665
|
function createVolatileRemoteControlStore() {
|
|
19540
19666
|
let value = {
|
|
@@ -19715,8 +19841,7 @@ var ManagedOriginController = class {
|
|
|
19715
19841
|
return task;
|
|
19716
19842
|
}
|
|
19717
19843
|
};
|
|
19718
|
-
|
|
19719
|
-
//#region src/release-update.ts
|
|
19844
|
+
// src/release-update.ts
|
|
19720
19845
|
const PACKAGE_NAME = "dsh-mobile";
|
|
19721
19846
|
const NPM_LATEST_URL = "https://registry.npmjs.org/dsh-mobile/latest";
|
|
19722
19847
|
const ANDROID_RELEASE_DESCRIPTOR_URL = "https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/apps/mobile/release.json";
|
|
@@ -20179,8 +20304,7 @@ var PluginReleaseManager = class {
|
|
|
20179
20304
|
});
|
|
20180
20305
|
}
|
|
20181
20306
|
};
|
|
20182
|
-
|
|
20183
|
-
//#region src/file-logger.ts
|
|
20307
|
+
// src/file-logger.ts
|
|
20184
20308
|
const MAX_LOG_BYTES = 5242880;
|
|
20185
20309
|
const MAX_QUEUED_BYTES = 262144;
|
|
20186
20310
|
const PATH_PREFIX = "mobile_log";
|
|
@@ -20290,14 +20414,12 @@ async function installMobileFileLogger(ctx, stateDirectory, options = {}) {
|
|
|
20290
20414
|
}
|
|
20291
20415
|
return file;
|
|
20292
20416
|
}
|
|
20293
|
-
|
|
20294
|
-
//#region src/mobile-push/contracts.ts
|
|
20417
|
+
// src/mobile-push/contracts.ts
|
|
20295
20418
|
const OUTCOME_COALESCE_MS = 1e3;
|
|
20296
20419
|
const ATTENTION_TTL_MS = 3e5;
|
|
20297
20420
|
const OUTCOME_TTL_MS = 18e5;
|
|
20298
20421
|
const ATTEMPT_TIMEOUT_MS = 1e4;
|
|
20299
|
-
|
|
20300
|
-
//#region src/mobile-push/requests.ts
|
|
20422
|
+
// src/mobile-push/requests.ts
|
|
20301
20423
|
var RequestLifetimes = class {
|
|
20302
20424
|
#records = /* @__PURE__ */ new Map();
|
|
20303
20425
|
#byCall = /* @__PURE__ */ new Map();
|
|
@@ -20477,8 +20599,7 @@ var RequestLifetimes = class {
|
|
|
20477
20599
|
}, 250);
|
|
20478
20600
|
}
|
|
20479
20601
|
};
|
|
20480
|
-
|
|
20481
|
-
//#region src/mobile-push/outcomes.ts
|
|
20602
|
+
// src/mobile-push/outcomes.ts
|
|
20482
20603
|
const isCompletedGoalPhase = (phase) => phase === "complete";
|
|
20483
20604
|
var OutcomeLifetimes = class {
|
|
20484
20605
|
#ctx;
|
|
@@ -20658,8 +20779,7 @@ var OutcomeLifetimes = class {
|
|
|
20658
20779
|
return current;
|
|
20659
20780
|
}
|
|
20660
20781
|
};
|
|
20661
|
-
|
|
20662
|
-
//#region src/mobile-push/reconcile.ts
|
|
20782
|
+
// src/mobile-push/reconcile.ts
|
|
20663
20783
|
const TURN_REASONS = [
|
|
20664
20784
|
"completed",
|
|
20665
20785
|
"error",
|
|
@@ -20931,8 +21051,7 @@ var PushReconciler = class {
|
|
|
20931
21051
|
return verifyOutcome(source, values, agent);
|
|
20932
21052
|
}
|
|
20933
21053
|
};
|
|
20934
|
-
|
|
20935
|
-
//#region src/mobile-push/host.ts
|
|
21054
|
+
// src/mobile-push/host.ts
|
|
20936
21055
|
function hasContract(ctx) {
|
|
20937
21056
|
return typeof ctx.on === "function" && typeof ctx.sessionProjections?.stateOf === "function" && typeof ctx.sessionProjections.snapshot === "function" && typeof ctx.sessionProjections.register === "function" && typeof ctx.sessionQuery?.observeSession === "function" && typeof ctx.agents?.get === "function" && typeof ctx.agents.list === "function" && typeof ctx.agents.roots === "function" && typeof ctx.agents.isOwnedBy === "function" && ctx.userQuestions !== void 0 && ctx.approval !== void 0;
|
|
20938
21057
|
}
|
|
@@ -21143,8 +21262,7 @@ function createPushHost(ctx) {
|
|
|
21143
21262
|
}
|
|
21144
21263
|
};
|
|
21145
21264
|
}
|
|
21146
|
-
|
|
21147
|
-
//#region src/mobile-push/endpoint.ts
|
|
21265
|
+
// src/mobile-push/endpoint.ts
|
|
21148
21266
|
const MAX_ENDPOINT_BYTES = 2048;
|
|
21149
21267
|
const APPLE_SUFFIX = ".push.apple.com";
|
|
21150
21268
|
const BASE64URL = /^[A-Za-z0-9_-]+$/u;
|
|
@@ -21261,8 +21379,7 @@ async function resolveAppleEndpoint(endpoint, resolver = systemResolver) {
|
|
|
21261
21379
|
addresses: Object.freeze([...addresses])
|
|
21262
21380
|
});
|
|
21263
21381
|
}
|
|
21264
|
-
|
|
21265
|
-
//#region src/mobile-push/store.ts
|
|
21382
|
+
// src/mobile-push/store.ts
|
|
21266
21383
|
const MAX_STATE_BYTES = 4194304;
|
|
21267
21384
|
const emptySnapshot = () => ({
|
|
21268
21385
|
version: 1,
|
|
@@ -21525,8 +21642,7 @@ var JsonPushStore = class {
|
|
|
21525
21642
|
}
|
|
21526
21643
|
}
|
|
21527
21644
|
};
|
|
21528
|
-
|
|
21529
|
-
//#region src/mobile-push/registry.ts
|
|
21645
|
+
// src/mobile-push/registry.ts
|
|
21530
21646
|
const empty = () => ({
|
|
21531
21647
|
version: 1,
|
|
21532
21648
|
ownerGenerations: [],
|
|
@@ -21914,8 +22030,7 @@ var PushRegistry = class {
|
|
|
21914
22030
|
};
|
|
21915
22031
|
}
|
|
21916
22032
|
};
|
|
21917
|
-
|
|
21918
|
-
//#region src/mobile-push/payload.ts
|
|
22033
|
+
// src/mobile-push/payload.ts
|
|
21919
22034
|
const messages = {
|
|
21920
22035
|
en: {
|
|
21921
22036
|
question: ["Input requested", "A response needs your attention."],
|
|
@@ -21967,8 +22082,7 @@ function buildDeclarativePayload(category, locale, navigate, tag) {
|
|
|
21967
22082
|
if (Buffer.byteLength(payload, "utf8") > 3072) throw new TypeError("push_payload_too_large");
|
|
21968
22083
|
return payload;
|
|
21969
22084
|
}
|
|
21970
|
-
|
|
21971
|
-
//#region src/mobile-push/retention.ts
|
|
22085
|
+
// src/mobile-push/retention.ts
|
|
21972
22086
|
/** Pure defer-membership retention helpers shared by delivery maintenance. */
|
|
21973
22087
|
/** Removes deferred-recheck entries whose owning pending intent no longer exists; returns how many were removed. */
|
|
21974
22088
|
function pruneOrphanedDeferrals(deferred, pending) {
|
|
@@ -21981,8 +22095,7 @@ function pruneOrphanedDeferrals(deferred, pending) {
|
|
|
21981
22095
|
}
|
|
21982
22096
|
return removed;
|
|
21983
22097
|
}
|
|
21984
|
-
|
|
21985
|
-
//#region src/mobile-push/sender.ts
|
|
22098
|
+
// src/mobile-push/sender.ts
|
|
21986
22099
|
const MAX_RESPONSE_BYTES = 8192;
|
|
21987
22100
|
const MAX_TTL_SECONDS = 1800;
|
|
21988
22101
|
const CONTENT_ENCODING = "aes128gcm";
|
|
@@ -22268,8 +22381,7 @@ var PushSender = class {
|
|
|
22268
22381
|
}
|
|
22269
22382
|
}
|
|
22270
22383
|
};
|
|
22271
|
-
|
|
22272
|
-
//#region src/mobile-push/delivery.ts
|
|
22384
|
+
// src/mobile-push/delivery.ts
|
|
22273
22385
|
const RECONCILE_INTERVAL_MS = 3e4;
|
|
22274
22386
|
const RETRY_BACKOFF_MS = [1e3, 5e3];
|
|
22275
22387
|
const MAX_TEST_QUEUE = 64;
|
|
@@ -23055,8 +23167,7 @@ var PushDelivery = class {
|
|
|
23055
23167
|
}, delay);
|
|
23056
23168
|
}
|
|
23057
23169
|
};
|
|
23058
|
-
|
|
23059
|
-
//#region src/mobile-push/gateway.ts
|
|
23170
|
+
// src/mobile-push/gateway.ts
|
|
23060
23171
|
/** One service shared by every Gateway while each Gateway owns a separate auth/origin attachment. */
|
|
23061
23172
|
var PushGatewayCoordinator = class {
|
|
23062
23173
|
host;
|
|
@@ -23179,8 +23290,7 @@ var PushGatewayCoordinator = class {
|
|
|
23179
23290
|
return true;
|
|
23180
23291
|
}
|
|
23181
23292
|
};
|
|
23182
|
-
|
|
23183
|
-
//#region src/installation-id.ts
|
|
23293
|
+
// src/installation-id.ts
|
|
23184
23294
|
const INSTALLATION_ID_PATTERN = /^[a-f0-9]{64}\n$/u;
|
|
23185
23295
|
const INSTALLATION_ID_BYTES = 65;
|
|
23186
23296
|
async function readInstallationId(file) {
|
|
@@ -23244,8 +23354,7 @@ async function loadOrCreateInstallationId(file) {
|
|
|
23244
23354
|
}
|
|
23245
23355
|
}
|
|
23246
23356
|
}
|
|
23247
|
-
|
|
23248
|
-
//#region src/gateway-identity.ts
|
|
23357
|
+
// src/gateway-identity.ts
|
|
23249
23358
|
/**
|
|
23250
23359
|
* Attach the authenticated installation identity while retaining existing entry identity rules.
|
|
23251
23360
|
* @param config - Validated gateway configuration containing the legacy identity.
|
|
@@ -23265,8 +23374,7 @@ function withInstallationIdentity(config, installationId, explicitIdentity) {
|
|
|
23265
23374
|
legacyInstanceId: config.instanceId
|
|
23266
23375
|
});
|
|
23267
23376
|
}
|
|
23268
|
-
|
|
23269
|
-
//#region src/lan-setup.ts
|
|
23377
|
+
// src/lan-setup.ts
|
|
23270
23378
|
const FIREWALL_TCP_RULE = "DSH Mobile HTTPS";
|
|
23271
23379
|
const FIREWALL_UDP_RULE = "DSH Mobile Discovery";
|
|
23272
23380
|
async function runElevatedPowerShell(script) {
|
|
@@ -23338,8 +23446,7 @@ async function prepareManagedLanSetup(options) {
|
|
|
23338
23446
|
androidCertificate
|
|
23339
23447
|
});
|
|
23340
23448
|
}
|
|
23341
|
-
|
|
23342
|
-
//#region src/plugin.ts
|
|
23449
|
+
// src/plugin.ts
|
|
23343
23450
|
/** Stable Cordis plugin name. */
|
|
23344
23451
|
const name = "dsh-mobile";
|
|
23345
23452
|
/** The stock WebServer serves the control card; Connection authenticates the loopback DSH origin. */
|
|
@@ -24281,7 +24388,8 @@ async function apply(ctx, config) {
|
|
|
24281
24388
|
}, cpolarComponent.status(), cloudflaredComponent.status(), cloudflaredTunnel.status(), frpComponent.status(), frpConfig.status(), originConfig.status(), caddyComponent.status(), irohComponent.status()),
|
|
24282
24389
|
originMode: originController.mode(),
|
|
24283
24390
|
caddyConfiguration: caddyConfig.status(),
|
|
24284
|
-
caddyState: caddyController.status()
|
|
24391
|
+
caddyState: caddyController.status(),
|
|
24392
|
+
...process.platform === "darwin" ? { macosHost: true } : {}
|
|
24285
24393
|
});
|
|
24286
24394
|
const lanPayload = () => ({
|
|
24287
24395
|
...loaded.kind === "unconfigured" ? {
|
|
@@ -24324,7 +24432,18 @@ async function apply(ctx, config) {
|
|
|
24324
24432
|
windowsFirewall: process.platform === "win32"
|
|
24325
24433
|
};
|
|
24326
24434
|
};
|
|
24327
|
-
const diagnosticsPayload = async () => {
|
|
24435
|
+
const diagnosticsPayload = async (request) => {
|
|
24436
|
+
const admission = ctx.connection?.admit?.(request);
|
|
24437
|
+
if (admission !== void 0 && "rejection" in admission) throw new HttpError(admission.rejection, "authentication_failed");
|
|
24438
|
+
const indexCollector = ctx.webServer;
|
|
24439
|
+
let indexInjections = [];
|
|
24440
|
+
let indexInspectionUnavailable = false;
|
|
24441
|
+
if (typeof indexCollector.collectIndexInjections === "function") try {
|
|
24442
|
+
indexInjections = indexCollector.collectIndexInjections(admission?.peer);
|
|
24443
|
+
} catch (inspectionError) {
|
|
24444
|
+
indexInspectionUnavailable = true;
|
|
24445
|
+
}
|
|
24446
|
+
else indexInspectionUnavailable = true;
|
|
24328
24447
|
let interfaceName;
|
|
24329
24448
|
let networkError;
|
|
24330
24449
|
if (loaded.kind === "managed") try {
|
|
@@ -24354,7 +24473,8 @@ async function apply(ctx, config) {
|
|
|
24354
24473
|
} : void 0;
|
|
24355
24474
|
return collectConnectionDiagnostics({
|
|
24356
24475
|
dshVersion,
|
|
24357
|
-
competingRemoteChannelBoot: hasCompetingRemoteChannelBoot(
|
|
24476
|
+
competingRemoteChannelBoot: hasCompetingRemoteChannelBoot(indexInjections),
|
|
24477
|
+
indexInspectionUnavailable,
|
|
24358
24478
|
lan: {
|
|
24359
24479
|
configured: loaded.kind !== "unconfigured" || preparedLanSetup !== void 0,
|
|
24360
24480
|
running: lanController.isRunning(),
|
|
@@ -24425,12 +24545,15 @@ async function apply(ctx, config) {
|
|
|
24425
24545
|
}
|
|
24426
24546
|
if (request.method !== "POST") throw new HttpError(405, "method_not_allowed");
|
|
24427
24547
|
const body = await readJsonObject(request, template.maxBodyBytes);
|
|
24428
|
-
|
|
24429
|
-
|
|
24548
|
+
const keys = Object.keys(body);
|
|
24549
|
+
const configure = keys.every((key) => key === "excludedClientModules" || key === "revision") && Object.prototype.hasOwnProperty.call(body, "excludedClientModules") && (body.revision === void 0 || typeof body.revision === "string");
|
|
24550
|
+
const single = keys.length === 1 && (body.reset === true || body.measure === true);
|
|
24551
|
+
if (!configure && !single) throw new HttpError(400, "bad_request");
|
|
24552
|
+
sendJson(response, 200, body.measure === true ? await gateway.measureClientModules(void 0, operation.signal) : body.reset === true ? await gateway.resetClientModules(void 0, operation.signal) : await gateway.configureClientModules(body.excludedClientModules, void 0, operation.signal, typeof body.revision === "string" ? body.revision : void 0), false);
|
|
24430
24553
|
} catch (error) {
|
|
24431
24554
|
if (!(error instanceof ClientModuleConflictError)) throw error;
|
|
24432
24555
|
sendJson(response, 409, {
|
|
24433
|
-
error:
|
|
24556
|
+
error: error.code,
|
|
24434
24557
|
detail: error.detail
|
|
24435
24558
|
}, false);
|
|
24436
24559
|
} finally {
|
|
@@ -24461,7 +24584,7 @@ async function apply(ctx, config) {
|
|
|
24461
24584
|
return;
|
|
24462
24585
|
}
|
|
24463
24586
|
if (request.method === "GET" && target.decodedPathname === `/api/mobile-access/diagnostics`) {
|
|
24464
|
-
sendJson(response, 200, await diagnosticsPayload(), false);
|
|
24587
|
+
sendJson(response, 200, await diagnosticsPayload(request), false);
|
|
24465
24588
|
return;
|
|
24466
24589
|
}
|
|
24467
24590
|
if (request.method === "GET" && target.decodedPathname === `/api/mobile-access/release`) {
|
|
@@ -24764,7 +24887,12 @@ async function apply(ctx, config) {
|
|
|
24764
24887
|
if (request.method === "POST" && target.decodedPathname === `/api/mobile-access/remote/frp/configure`) {
|
|
24765
24888
|
const body = await readJsonObject(request, 4096);
|
|
24766
24889
|
await remoteProviders.mutate(async () => {
|
|
24767
|
-
|
|
24890
|
+
const saved = frpConfig.settings();
|
|
24891
|
+
const reuseToken = body.token === "" && saved !== void 0 && body.serverAddress === saved.serverAddress && body.serverPort === saved.serverPort;
|
|
24892
|
+
await frpConfig.configure(reuseToken ? {
|
|
24893
|
+
...body,
|
|
24894
|
+
token: saved.token
|
|
24895
|
+
} : body);
|
|
24768
24896
|
if (remoteControllers.frp.status().enabled) await remoteControllers.frp.reconnect();
|
|
24769
24897
|
});
|
|
24770
24898
|
sendJson(response, 200, remotePayload(), false);
|
|
@@ -24811,10 +24939,7 @@ async function apply(ctx, config) {
|
|
|
24811
24939
|
});
|
|
24812
24940
|
let qrSvg = "";
|
|
24813
24941
|
try {
|
|
24814
|
-
qrSvg = await
|
|
24815
|
-
type: "svg",
|
|
24816
|
-
margin: 1
|
|
24817
|
-
});
|
|
24942
|
+
qrSvg = await renderIrohPairingQr(pairing.encoded);
|
|
24818
24943
|
} catch {}
|
|
24819
24944
|
logger.info("iroh pairing window opened");
|
|
24820
24945
|
sendJson(response, 201, {
|
|
@@ -25146,7 +25271,6 @@ async function apply(ctx, config) {
|
|
|
25146
25271
|
};
|
|
25147
25272
|
}, "dsh-mobile: independent LAN and selectable remote providers with /mobile command");
|
|
25148
25273
|
}
|
|
25149
|
-
//#endregion
|
|
25150
25274
|
export { AUTH_PREFIX, AccessController, AccessError, BlockedUpgradePathLog, BoundedRateLimiter, CERT_EXPIRING_DAYS, CLOUDFLARED_COMPONENT_RELEASE, CLOUDFLARED_COMPONENT_RELEASES, CPOLAR_COMPONENT_RELEASE, CPOLAR_COMPONENT_RELEASES, CSRF_COOKIE, CSRF_HEADER, ClientModuleConflictError, ClientModulePreferenceStore, CloudflaredComponentManager, CloudflaredController, CloudflaredTunnelStore, Config, CpolarComponentManager, DEFAULT_IROH_LOCAL_PORT, DEFAULT_ORIGIN_LISTEN_PORT, FRP_VHOST_HTTP_PORT as DEFAULT_VHOST_HTTP_PORT, FRP_VHOST_HTTP_PORT, DEVICE_COOKIE, EXTENSION_LIMITS, FRP_ATTACH_DISCOVERY_PATH, FRP_ATTACH_LOCAL_DECLARATION, FRP_ATTACH_SELF_SIGNED_DECLARATION, FRP_ATTACH_VPS_DECLARATION, FRP_CADDY_IMPORT_LINE, FRP_CADDY_SNIPPET_MARKER, FRP_CADDY_SNIPPET_PATH, FRP_COMPONENT_RELEASES, FRP_DEFAULT_PROXY_NAME, FRP_DEFAULT_PUBLIC_PORT, FRP_RESERVED_PORTS, FrpComponentManager, FrpConfigStore, FrpController, IROH_COMPONENT_RELEASES, IROH_LEAF_RENEW_BEFORE_DAYS, IROH_LEAF_VALIDITY_DAYS, IROH_VERSION, IrohComponentManager, IrohController, JsonDeviceStore, JsonMobileAccessControlStore, JsonRemoteProviderStore, LOCAL_ADMIN_PREFIX, MAX_BLOCKED_UPGRADE_PATHS, MAX_EXTRA_WEBSOCKET_PATHS, MAX_PAIRING_DESCRIPTOR_BYTES, MAX_PAIRING_LIFETIME_MS, MAX_WEBSOCKET_PATH_LENGTH, MemoryDeviceStore, MobileAccessGateway, MobileAccessGatewayController, MobileAccessService, MobileExtensionError, OriginConfigStore, OriginController, PAIRING_DESCRIPTOR_LOCAL_PORT, PAIRING_DESCRIPTOR_TYPE, PAIRING_DESCRIPTOR_VERSION, REMOTE_PROVIDERS, RequestTrustPolicy, SESSION_COOKIE, TASK_EVENT_DEBOUNCE_MS, TaskEventHub, WS_PATHS, WebSocketPathStore, addressAllowed, apply, assertExtensionId, configuredRemoteProvider, createCaddySite, createFrpAttachPlan, createFrpAttachTemplate, createFrpAttachTemplateParts, createFrpServerTemplate, createFrpcToml, createMobileAccessService, createRestrictedFrpServerTemplate, decodePairingDescriptor, encodePairingDescriptor, ensureFrpIngressCertificate, ensureIrohTls, evaluateCertificateLifetime, frpAttachVpsParts, frpIngressPaths, frpIngressSelfCheck, frpProxyName, inject, irohGatewayConfig, irohHostname, irohOrigin, irohTlsPaths, isCloudflaredRegistration, isFrpSelfSignedIngress, isGloballyRoutableIpv4, isLoopbackAddress, issueServerCertificate, mergeSavedCloudflaredTunnelSettings, mergeSavedFrpSettings, mergeSavedFrpTarget, name, normalizeWebSocketPaths, originGatewayConfig, parseAuthority, parseCidr, parseCloudflaredOrigin, parseCloudflaredTunnelSettings, parseControlFile, parseDeviceSnapshot, parseExtensionManifest, parseFrpSettings, parseGatewayConfig, parseMobileAccessControlState, parseOriginSettings, parseRemoteProviderState, probeOriginCertificate, readCertificateRenewal, resolveAuthority, resolveFrpEntryTls, resolveFrpMode, resolveFrpPublicPort, resolveFrpVhostHttpPort, resolveHostExecution, rewriteMobileIndex, validateAttachSettings, validateCloudflaredTunnelHostname, validateCloudflaredTunnelPort, validateCloudflaredTunnelToken, validateFrpEntryTls, validateFrpMode, validateFrpProxyName, validateFrpPublicOrigin, validateFrpPublicPort, validateFrpServerAddress, validateFrpServerPort, validateFrpToken, validateFrpVhostHttpPort, validateIrohOrigin, validateOriginAllowedCidrs, validateOriginListenHost, validateOriginListenPort, validateOriginPublicOrigin, validatePairingDescriptor, validateWebSocketPath, watchTaskCompletions };
|
|
25151
25275
|
|
|
25152
25276
|
//# sourceMappingURL=index.mjs.map
|