dsh-mobile 0.8.0 → 0.8.2
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 +29 -0
- package/CONTRIBUTORS.md +24 -2
- package/README.en.md +8 -5
- package/README.md +10 -5
- package/SECURITY.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/CLIENT_MODULES.en.md +6 -2
- package/docs/CLIENT_MODULES.md +6 -2
- package/docs/CONNECTION_DIAGNOSTICS.en.md +50 -0
- package/docs/CONNECTION_DIAGNOSTICS.md +50 -0
- package/docs/IROH.en.md +6 -0
- package/docs/IROH.md +6 -0
- package/docs/MOBILE_PLUGIN_ADAPTATION.en.md +18 -0
- package/docs/MOBILE_PLUGIN_ADAPTATION.md +18 -0
- package/docs/MOBILE_PLUGIN_BASELINE.en.md +117 -0
- package/docs/MOBILE_PLUGIN_BASELINE.md +117 -0
- package/docs/README.md +2 -0
- package/docs/SKIN_COMPATIBILITY.en.md +1 -1
- package/docs/SKIN_COMPATIBILITY.md +1 -1
- package/docs/SLOW_CONNECTIONS.en.md +3 -1
- package/docs/SLOW_CONNECTIONS.md +3 -1
- package/lib/cli.js +6 -12
- package/lib/cli.js.map +1 -1
- package/lib/client.js +428 -126
- 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 +16 -89
- package/lib/index.mjs +519 -253
- 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 +13 -6
- package/lib/mobile-layout.js.map +1 -1
- package/package.json +10 -6
- 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,16 @@ var ClientModulePreferenceStore = class {
|
|
|
1545
1537
|
}
|
|
1546
1538
|
}
|
|
1547
1539
|
};
|
|
1548
|
-
|
|
1549
|
-
|
|
1540
|
+
// src/control.ts
|
|
1541
|
+
/** A configuration change failed; recovered distinguishes a restored runtime from an unknown configuration. */
|
|
1542
|
+
var MobileAccessReconfigurationError = class extends Error {
|
|
1543
|
+
recovered;
|
|
1544
|
+
constructor(recovered, cause) {
|
|
1545
|
+
super(recovered ? "mobile access configuration restored" : "mobile access configuration requires recovery", { cause });
|
|
1546
|
+
this.recovered = recovered;
|
|
1547
|
+
this.name = "MobileAccessReconfigurationError";
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
1550
1550
|
/** Keeps one runtime aligned with a changing network selection. */
|
|
1551
1551
|
var FollowingMobileAccessRuntime = class {
|
|
1552
1552
|
select;
|
|
@@ -1737,6 +1737,36 @@ var MobileAccessGatewayController = class {
|
|
|
1737
1737
|
else await this.disable();
|
|
1738
1738
|
});
|
|
1739
1739
|
}
|
|
1740
|
+
/** Change configuration with the runtime stopped, without changing its durable enabled preference. */
|
|
1741
|
+
reconfigure(change, restore) {
|
|
1742
|
+
if (this.closing) return Promise.reject(/* @__PURE__ */ new Error("mobile-access control is closing"));
|
|
1743
|
+
return this.enqueue(async () => {
|
|
1744
|
+
if (!this.initialized) throw new Error("mobile-access control is not initialized");
|
|
1745
|
+
const previous = this.runtime;
|
|
1746
|
+
if (previous !== void 0) try {
|
|
1747
|
+
await previous.close();
|
|
1748
|
+
} catch (error) {
|
|
1749
|
+
throw new MobileAccessReconfigurationError(false, error);
|
|
1750
|
+
}
|
|
1751
|
+
this.runtime = void 0;
|
|
1752
|
+
try {
|
|
1753
|
+
const value = await change();
|
|
1754
|
+
if (previous !== void 0) this.runtime = await this.startRuntime();
|
|
1755
|
+
return {
|
|
1756
|
+
value,
|
|
1757
|
+
running: previous !== void 0
|
|
1758
|
+
};
|
|
1759
|
+
} catch (error) {
|
|
1760
|
+
try {
|
|
1761
|
+
await restore();
|
|
1762
|
+
if (previous !== void 0) this.runtime = await this.startRuntime();
|
|
1763
|
+
} catch (restoreError) {
|
|
1764
|
+
throw new MobileAccessReconfigurationError(false, new AggregateError([error, restoreError]));
|
|
1765
|
+
}
|
|
1766
|
+
throw new MobileAccessReconfigurationError(true, error);
|
|
1767
|
+
}
|
|
1768
|
+
});
|
|
1769
|
+
}
|
|
1740
1770
|
/** Stop the runtime after earlier transitions without changing the restart preference. */
|
|
1741
1771
|
close() {
|
|
1742
1772
|
if (this.closeTask !== void 0) return this.closeTask;
|
|
@@ -1829,8 +1859,7 @@ function isLocalAdminHostname(hostname) {
|
|
|
1829
1859
|
if (octets === void 0) return false;
|
|
1830
1860
|
return isLoopbackIpv4(octets) || isPrivateIpv4(octets) || isLinkLocalIpv4(octets);
|
|
1831
1861
|
}
|
|
1832
|
-
|
|
1833
|
-
//#region src/http-security.ts
|
|
1862
|
+
// src/http-security.ts
|
|
1834
1863
|
const DEVICE_COOKIE = "dsh_ma_device";
|
|
1835
1864
|
const SESSION_COOKIE = "dsh_ma_session";
|
|
1836
1865
|
const CSRF_COOKIE = "dsh_ma_csrf";
|
|
@@ -2014,8 +2043,7 @@ function assertLocalAdminTrust(request, requireBrowserOrigin, authenticateDeskto
|
|
|
2014
2043
|
if (requireBrowserOrigin && site !== void 0 && site !== "same-origin") throw new HttpError(403, "forbidden");
|
|
2015
2044
|
if (!loopback && !authenticateDesktop()) throw new HttpError(401, "admin_authentication_required");
|
|
2016
2045
|
}
|
|
2017
|
-
|
|
2018
|
-
//#region src/browser-auth-cookies.ts
|
|
2046
|
+
// src/browser-auth-cookies.ts
|
|
2019
2047
|
const HOST_SESSION_COOKIE = "__Host-dsh_ma_session";
|
|
2020
2048
|
const HOST_CSRF_COOKIE = "__Host-dsh_ma_csrf";
|
|
2021
2049
|
/** Non-secret browser identity; the persistent device credential keeps its narrow renewal path. */
|
|
@@ -2067,8 +2095,7 @@ function authenticatedCookie(values, authenticate, accepts = () => true) {
|
|
|
2067
2095
|
if (selected === void 0) throw new HttpError(401, "authentication_failed");
|
|
2068
2096
|
return selected;
|
|
2069
2097
|
}
|
|
2070
|
-
|
|
2071
|
-
//#region src/auth-failure-log.ts
|
|
2098
|
+
// src/auth-failure-log.ts
|
|
2072
2099
|
const LOG_INTERVAL_MS$3 = 3e4;
|
|
2073
2100
|
const MAX_LOGGED_ORIGIN_LENGTH = 512;
|
|
2074
2101
|
function requestPathCategory(path) {
|
|
@@ -2183,12 +2210,17 @@ var AuthFailureRecorder = class {
|
|
|
2183
2210
|
}
|
|
2184
2211
|
}
|
|
2185
2212
|
};
|
|
2186
|
-
|
|
2187
|
-
//#region src/http-failure-log.ts
|
|
2213
|
+
// src/http-failure-log.ts
|
|
2188
2214
|
function safeParserCode(code) {
|
|
2189
2215
|
switch (code) {
|
|
2190
2216
|
case "ERR_HTTP_REQUEST_TIMEOUT":
|
|
2191
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":
|
|
2192
2224
|
case "HPE_INVALID_CHUNK_SIZE":
|
|
2193
2225
|
case "HPE_INVALID_CONTENT_LENGTH":
|
|
2194
2226
|
case "HPE_INVALID_EOF_STATE":
|
|
@@ -2223,7 +2255,7 @@ function classifyHttpParserFailure(nodeCode) {
|
|
|
2223
2255
|
}
|
|
2224
2256
|
const LOG_INTERVAL_MS$2 = 3e4;
|
|
2225
2257
|
/**
|
|
2226
|
-
* Per-Gateway diagnostics with
|
|
2258
|
+
* Per-Gateway diagnostics with four fixed sampling slots and no timers or request-selected keys.
|
|
2227
2259
|
* Each kind records once immediately, then at most once per 30 seconds with suppressed counts.
|
|
2228
2260
|
* A sink failure cannot change a response, retry a write or expose the diagnostic sink's error.
|
|
2229
2261
|
*/
|
|
@@ -2236,6 +2268,10 @@ var HttpFailureRecorder = class {
|
|
|
2236
2268
|
nextLogAt: 0,
|
|
2237
2269
|
suppressed: 0
|
|
2238
2270
|
},
|
|
2271
|
+
transport: {
|
|
2272
|
+
nextLogAt: 0,
|
|
2273
|
+
suppressed: 0
|
|
2274
|
+
},
|
|
2239
2275
|
target: {
|
|
2240
2276
|
nextLogAt: 0,
|
|
2241
2277
|
suppressed: 0
|
|
@@ -2263,6 +2299,26 @@ var HttpFailureRecorder = class {
|
|
|
2263
2299
|
});
|
|
2264
2300
|
return failure;
|
|
2265
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
|
+
}
|
|
2266
2322
|
/** Sample an invalid request target without retaining its raw or decoded URL. */
|
|
2267
2323
|
recordTarget() {
|
|
2268
2324
|
this.record({
|
|
@@ -2306,8 +2362,7 @@ var HttpFailureRecorder = class {
|
|
|
2306
2362
|
}
|
|
2307
2363
|
}
|
|
2308
2364
|
};
|
|
2309
|
-
|
|
2310
|
-
//#region src/api-latency-log.ts
|
|
2365
|
+
// src/api-latency-log.ts
|
|
2311
2366
|
/**
|
|
2312
2367
|
* Bounded API latency samples for diagnosing slow mobile requests.
|
|
2313
2368
|
*
|
|
@@ -2414,8 +2469,7 @@ var ApiLatencyRecorder = class {
|
|
|
2414
2469
|
}
|
|
2415
2470
|
}
|
|
2416
2471
|
};
|
|
2417
|
-
|
|
2418
|
-
//#region src/upstream-auth-log.ts
|
|
2472
|
+
// src/upstream-auth-log.ts
|
|
2419
2473
|
const LOG_INTERVAL_MS = 3e4;
|
|
2420
2474
|
/** Two fixed sampling slots keep a first failure visible even after a successful exchange. */
|
|
2421
2475
|
var UpstreamAuthRecorder = class {
|
|
@@ -2480,8 +2534,7 @@ var UpstreamAuthRecorder = class {
|
|
|
2480
2534
|
}
|
|
2481
2535
|
}
|
|
2482
2536
|
};
|
|
2483
|
-
|
|
2484
|
-
//#region src/client-module-recovery.ts
|
|
2537
|
+
// src/client-module-recovery.ts
|
|
2485
2538
|
const CLIENT_MODULE_RECOVERY_COPY = {
|
|
2486
2539
|
zh: {
|
|
2487
2540
|
title: "移动页面模块需要调整",
|
|
@@ -2562,8 +2615,7 @@ function renderClientModuleRecoveryScript(locale) {
|
|
|
2562
2615
|
})
|
|
2563
2616
|
})()`;
|
|
2564
2617
|
}
|
|
2565
|
-
|
|
2566
|
-
//#region src/mobile-compat-bootstrap.ts
|
|
2618
|
+
// src/mobile-compat-bootstrap.ts
|
|
2567
2619
|
const MOBILE_COMPAT_PATH = `${AUTH_PREFIX}/compat.js`;
|
|
2568
2620
|
/**
|
|
2569
2621
|
* Name the compatibility bundle by content.
|
|
@@ -2639,8 +2691,7 @@ const EXTENSION_LIMITS = Object.freeze({
|
|
|
2639
2691
|
assetBytes: 33554432,
|
|
2640
2692
|
assetDepth: 8
|
|
2641
2693
|
});
|
|
2642
|
-
|
|
2643
|
-
//#region src/http-cache.ts
|
|
2694
|
+
// src/http-cache.ts
|
|
2644
2695
|
/** Entity-tag handling for authenticated GET and HEAD cache validation. */
|
|
2645
2696
|
const ENTITY_TAG = /^[\t ]*(?:W\/)?("[\x21\x23-\x7e\x80-\xff]*")[\t ]*$/u;
|
|
2646
2697
|
const ENTITY_TAG_LIST_ITEM = /(?:W\/)?"[\x21\x23-\x7e\x80-\xff]*"/gu;
|
|
@@ -2677,8 +2728,7 @@ function weakenEntityTag(etag) {
|
|
|
2677
2728
|
const opaque = etag === void 0 ? void 0 : ENTITY_TAG.exec(etag)?.[1];
|
|
2678
2729
|
return opaque === void 0 ? void 0 : `W/${opaque}`;
|
|
2679
2730
|
}
|
|
2680
|
-
|
|
2681
|
-
//#region src/mobile-graph-events.ts
|
|
2731
|
+
// src/mobile-graph-events.ts
|
|
2682
2732
|
/** Bounded SSE framing for the dedicated Mobile client graph stream. */
|
|
2683
2733
|
/** Maximum retained UTF-8 frame/output bytes, with CRLF normalized to LF. */
|
|
2684
2734
|
const MOBILE_GRAPH_EVENT_MAX_BYTES = 4194304;
|
|
@@ -2867,8 +2917,7 @@ var MobileGraphEventTransform = class extends Transform {
|
|
|
2867
2917
|
function createMobileGraphEventTransform(project) {
|
|
2868
2918
|
return new MobileGraphEventTransform(project);
|
|
2869
2919
|
}
|
|
2870
|
-
|
|
2871
|
-
//#region src/websocket-compression.ts
|
|
2920
|
+
// src/websocket-compression.ts
|
|
2872
2921
|
function payloadBytes(data) {
|
|
2873
2922
|
if (Array.isArray(data)) return data.reduce((bytes, part) => bytes + part.byteLength, 0);
|
|
2874
2923
|
return data.byteLength;
|
|
@@ -3088,15 +3137,13 @@ function bridgeCompressedWebSocket(request, client, upstream, head, target, upst
|
|
|
3088
3137
|
}
|
|
3089
3138
|
});
|
|
3090
3139
|
}
|
|
3091
|
-
|
|
3092
|
-
//#region src/version.ts
|
|
3140
|
+
// src/version.ts
|
|
3093
3141
|
const manifest = createRequire(import.meta.url)("../package.json");
|
|
3094
3142
|
/** Version of the installed DSH Mobile plugin package. */
|
|
3095
3143
|
const DSH_MOBILE_VERSION = typeof manifest.version === "string" ? manifest.version : "unknown";
|
|
3096
3144
|
/** Oldest Android App release supported by this plugin generation. */
|
|
3097
3145
|
const MINIMUM_ANDROID_APP_VERSION = "0.2.2";
|
|
3098
|
-
|
|
3099
|
-
//#region src/computer-images.ts
|
|
3146
|
+
// src/computer-images.ts
|
|
3100
3147
|
const MAX_ENTRIES = 500;
|
|
3101
3148
|
const MAX_IMAGE_BYTES = 20971520;
|
|
3102
3149
|
const IMAGE_TYPES = Object.freeze({
|
|
@@ -3297,8 +3344,7 @@ async function readComputerImage(path, signal) {
|
|
|
3297
3344
|
throw new HttpError(404, "file_unavailable");
|
|
3298
3345
|
}
|
|
3299
3346
|
}
|
|
3300
|
-
|
|
3301
|
-
//#region src/extension-worker.ts
|
|
3347
|
+
// src/extension-worker.ts
|
|
3302
3348
|
/** Supervise opt-in local hosts without executing extension code on the DSH thread. */
|
|
3303
3349
|
/** JSON bytes serialized and size-checked inside the worker. */
|
|
3304
3350
|
var PreparedJsonResult = class {
|
|
@@ -3951,8 +3997,7 @@ var WorkerStreamBridge = class extends Readable {
|
|
|
3951
3997
|
return super.emit(event, ...args);
|
|
3952
3998
|
}
|
|
3953
3999
|
};
|
|
3954
|
-
|
|
3955
|
-
//#region src/extensions.ts
|
|
4000
|
+
// src/extensions.ts
|
|
3956
4001
|
/** A misbehaving host activation must not wedge the local watcher forever. */
|
|
3957
4002
|
const HOST_ACTIVATION_TIMEOUT_MS = 5e3;
|
|
3958
4003
|
/** The previous Host outlives the hidden-page refresh interval and one timed refresh. */
|
|
@@ -5025,8 +5070,7 @@ function workerLoggerAdapter(context) {
|
|
|
5025
5070
|
function createMobileAccessService(ctx) {
|
|
5026
5071
|
return new MobileAccessService(ctx);
|
|
5027
5072
|
}
|
|
5028
|
-
|
|
5029
|
-
//#region src/auth-pages.ts
|
|
5073
|
+
// src/auth-pages.ts
|
|
5030
5074
|
const COPY = {
|
|
5031
5075
|
en: {
|
|
5032
5076
|
lang: "en",
|
|
@@ -5254,8 +5298,7 @@ function renderLoginScript(locale) {
|
|
|
5254
5298
|
})()
|
|
5255
5299
|
`;
|
|
5256
5300
|
}
|
|
5257
|
-
|
|
5258
|
-
//#region src/browser-ui-locale.ts
|
|
5301
|
+
// src/browser-ui-locale.ts
|
|
5259
5302
|
const UI_LOCALE_COOKIE = "dsh_ma_ui_locale";
|
|
5260
5303
|
/** Read presentation data only; malformed or ambiguous cookies keep browser negotiation. */
|
|
5261
5304
|
function readBrowserUiLocale(cookieHeader) {
|
|
@@ -5274,8 +5317,7 @@ function readBrowserUiLocale(cookieHeader) {
|
|
|
5274
5317
|
function resolveBrowserUiLocale(cookieHeader, acceptLanguage) {
|
|
5275
5318
|
return readBrowserUiLocale(cookieHeader) ?? resolveAuthPageLocale(acceptLanguage);
|
|
5276
5319
|
}
|
|
5277
|
-
|
|
5278
|
-
//#region src/mobile-push/assets.ts
|
|
5320
|
+
// src/mobile-push/assets.ts
|
|
5279
5321
|
const ICON_FILES = Object.freeze({
|
|
5280
5322
|
"/mobile-access/push/icon-180.png": "icon-180.png",
|
|
5281
5323
|
"/mobile-access/push/icon-192.png": "icon-192.png",
|
|
@@ -5327,16 +5369,14 @@ const loadFromPackage = createMobilePushAssetLoader(packageRoot);
|
|
|
5327
5369
|
function mobilePushAsset(path, root = packageRoot) {
|
|
5328
5370
|
return root === packageRoot ? loadFromPackage(path) : createMobilePushAssetLoader(root)(path);
|
|
5329
5371
|
}
|
|
5330
|
-
|
|
5331
|
-
//#region src/mobile-push/installation-id.ts
|
|
5372
|
+
// src/mobile-push/installation-id.ts
|
|
5332
5373
|
/** Browser-safe installation-id validation shared by the browser controller and the Gateway. */
|
|
5333
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;
|
|
5334
5375
|
/** True only for a UUID-shaped installation id with an allowed version and variant nibble. */
|
|
5335
5376
|
function isPushInstallationId(value) {
|
|
5336
5377
|
return typeof value === "string" && INSTALLATION_ID.test(value);
|
|
5337
5378
|
}
|
|
5338
|
-
|
|
5339
|
-
//#region src/gateway.ts
|
|
5379
|
+
// src/gateway.ts
|
|
5340
5380
|
const MAX_CONTROL_BODY_BYTES = 16384;
|
|
5341
5381
|
const MAX_HEADER_BYTES = 16384;
|
|
5342
5382
|
const DISCOVERY_QUERY = Buffer.from("DSH_MOBILE_DISCOVER_V1", "ascii");
|
|
@@ -5509,6 +5549,7 @@ const RUNTIME_MODULE = "@deepseek-ai/dsh-client-runtime";
|
|
|
5509
5549
|
const RENDERER_MODULE = "@deepseek-ai/dsh-client-ui-renderer";
|
|
5510
5550
|
const SIDEBAR_MODULE = "@deepseek-ai/dsh-client-ui-sidebar";
|
|
5511
5551
|
const SETTINGS_MODULE = "@deepseek-ai/dsh-client-ui-settings";
|
|
5552
|
+
const SETTINGS_GENERAL_MODULE = "@deepseek-ai/dsh-client-ui-settings-general";
|
|
5512
5553
|
const API_GATEWAY_MODULE = "@deepseek-ai/dsh-api-gateway";
|
|
5513
5554
|
const API_REMOTES_MODULE = "@deepseek-ai/dsh-api-remotes";
|
|
5514
5555
|
const MOBILE_BOOT_CORE_MODULES = /* @__PURE__ */ new Set([
|
|
@@ -5519,6 +5560,7 @@ const MOBILE_BOOT_CORE_MODULES = /* @__PURE__ */ new Set([
|
|
|
5519
5560
|
RENDERER_MODULE,
|
|
5520
5561
|
SIDEBAR_MODULE,
|
|
5521
5562
|
SETTINGS_MODULE,
|
|
5563
|
+
SETTINGS_GENERAL_MODULE,
|
|
5522
5564
|
API_GATEWAY_MODULE,
|
|
5523
5565
|
API_REMOTES_MODULE
|
|
5524
5566
|
]);
|
|
@@ -5639,8 +5681,8 @@ var ExcludedClientModulesError = class extends Error {
|
|
|
5639
5681
|
/** A graph-selection conflict with a bounded diagnostic safe for authenticated callers. */
|
|
5640
5682
|
var ClientModuleConflictError = class extends HttpError {
|
|
5641
5683
|
detail;
|
|
5642
|
-
constructor(detail) {
|
|
5643
|
-
super(409,
|
|
5684
|
+
constructor(detail, code = "excluded_client_modules_invalid") {
|
|
5685
|
+
super(409, code);
|
|
5644
5686
|
this.detail = detail.replace(/[\u0000-\u001f\u007f]/gu, " ").replace(/(?:https?:\/\/|[a-z]:[\\/])\S+/giu, "[redacted]").slice(0, 512);
|
|
5645
5687
|
}
|
|
5646
5688
|
};
|
|
@@ -5675,6 +5717,25 @@ function describeMobileClientModules(html) {
|
|
|
5675
5717
|
dependencies: Object.freeze(clientModuleDependencies(entry).filter((id) => ids.has(id)))
|
|
5676
5718
|
})));
|
|
5677
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
|
+
}
|
|
5678
5739
|
/** Validate graph edges before removing application rows from both manifest views. */
|
|
5679
5740
|
function excludeMobileBootEntries(entries, batches, excludedIds) {
|
|
5680
5741
|
if (excludedIds.length === 0) return;
|
|
@@ -6570,7 +6631,11 @@ var MobileAccessGateway = class {
|
|
|
6570
6631
|
});
|
|
6571
6632
|
server.on("clientError", (error, socket) => {
|
|
6572
6633
|
const nodeCode = "code" in error && typeof error.code === "string" ? error.code : void 0;
|
|
6573
|
-
const failure = this.httpFailures.
|
|
6634
|
+
const failure = this.httpFailures.recordClientError(nodeCode);
|
|
6635
|
+
if (failure.kind === "transport" || !socket.writable) {
|
|
6636
|
+
socket.destroy();
|
|
6637
|
+
return;
|
|
6638
|
+
}
|
|
6574
6639
|
rejectUpgrade(socket, failure.status, failure.code);
|
|
6575
6640
|
});
|
|
6576
6641
|
await new Promise((resolve, reject) => {
|
|
@@ -7300,18 +7365,19 @@ var MobileAccessGateway = class {
|
|
|
7300
7365
|
try {
|
|
7301
7366
|
let kind = "describe";
|
|
7302
7367
|
let excluded;
|
|
7368
|
+
let revision;
|
|
7303
7369
|
if (request.method === "POST") {
|
|
7304
7370
|
const body = await readJsonObject(request, this.config.maxBodyBytes);
|
|
7305
7371
|
const keys = Object.keys(body);
|
|
7306
|
-
if (keys.length
|
|
7307
|
-
if (keys[0] === "
|
|
7308
|
-
else if (keys
|
|
7309
|
-
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")) {
|
|
7310
7375
|
kind = "configure";
|
|
7311
7376
|
excluded = body.excludedClientModules;
|
|
7377
|
+
revision = body.revision;
|
|
7312
7378
|
} else throw new HttpError(400, "bad_request");
|
|
7313
7379
|
}
|
|
7314
|
-
const view = await this.clientModulePreferenceOperation(kind, excluded, authorization.deviceId, operation.signal);
|
|
7380
|
+
const view = await this.clientModulePreferenceOperation(kind, excluded, authorization.deviceId, operation.signal, revision);
|
|
7315
7381
|
operation.signal.throwIfAborted();
|
|
7316
7382
|
sendJson(response, 200, view, this.tlsEnabled);
|
|
7317
7383
|
} catch (error) {
|
|
@@ -7796,10 +7862,11 @@ var MobileAccessGateway = class {
|
|
|
7796
7862
|
* @param excluded - Untrusted module IDs to validate against the current boot graph.
|
|
7797
7863
|
* @param deviceId - Authenticated device identity; omitted for computer defaults.
|
|
7798
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.
|
|
7799
7866
|
* @returns The catalog and saved selection without reloading clients.
|
|
7800
7867
|
*/
|
|
7801
|
-
configureClientModules(excluded, deviceId, callerSignal) {
|
|
7802
|
-
return this.clientModulePreferenceOperation("configure", excluded, deviceId, callerSignal);
|
|
7868
|
+
configureClientModules(excluded, deviceId, callerSignal, revision) {
|
|
7869
|
+
return this.clientModulePreferenceOperation("configure", excluded, deviceId, callerSignal, revision);
|
|
7803
7870
|
}
|
|
7804
7871
|
/** Reset one override without reloading connected clients.
|
|
7805
7872
|
* @param deviceId - Authenticated device identity; omitted to reset computer defaults.
|
|
@@ -7985,7 +8052,7 @@ var MobileAccessGateway = class {
|
|
|
7985
8052
|
await upstreamClosed;
|
|
7986
8053
|
}
|
|
7987
8054
|
}
|
|
7988
|
-
async clientModulePreferenceOperation(kind, excluded, deviceId, callerSignal) {
|
|
8055
|
+
async clientModulePreferenceOperation(kind, excluded, deviceId, callerSignal, revision) {
|
|
7989
8056
|
if (this.closing || !this.started) throw new HttpError(503, "unavailable");
|
|
7990
8057
|
if (kind === "measure" && this.clientModuleMeasurementActive) throw new HttpError(429, "busy");
|
|
7991
8058
|
if (kind === "measure") this.clientModuleMeasurementActive = true;
|
|
@@ -8004,8 +8071,10 @@ var MobileAccessGateway = class {
|
|
|
8004
8071
|
const validate = async (selection) => {
|
|
8005
8072
|
html = (await this.fetchAuthenticatedUpstreamIndex(controller.signal)).html;
|
|
8006
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");
|
|
8007
8075
|
parseMobileBootPlan(html, selection);
|
|
8008
8076
|
} catch (error) {
|
|
8077
|
+
if (error instanceof ClientModuleConflictError) throw error;
|
|
8009
8078
|
if (error instanceof ExcludedClientModulesError) throw new ClientModuleConflictError(error.message);
|
|
8010
8079
|
throw new HttpError(502, "upstream_unavailable");
|
|
8011
8080
|
}
|
|
@@ -8033,8 +8102,10 @@ var MobileAccessGateway = class {
|
|
|
8033
8102
|
} catch (error) {
|
|
8034
8103
|
if (!measurementExpired || callerSignal?.aborted === true || this.closing) throw error;
|
|
8035
8104
|
}
|
|
8105
|
+
const entries = this.describeMobileModulesWithSizes(html);
|
|
8036
8106
|
return Object.freeze({
|
|
8037
|
-
entries
|
|
8107
|
+
entries,
|
|
8108
|
+
revision: clientModuleCatalogRevision(entries),
|
|
8038
8109
|
...selection,
|
|
8039
8110
|
...plan.batches === void 0 ? {
|
|
8040
8111
|
excludedClientModules: Object.freeze([]),
|
|
@@ -9193,8 +9264,7 @@ var MobileAccessGateway = class {
|
|
|
9193
9264
|
};
|
|
9194
9265
|
}
|
|
9195
9266
|
};
|
|
9196
|
-
|
|
9197
|
-
//#region src/storage.ts
|
|
9267
|
+
// src/storage.ts
|
|
9198
9268
|
function assertInteger(value, name) {
|
|
9199
9269
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new Error(`device state ${name} must be a non-negative integer`);
|
|
9200
9270
|
}
|
|
@@ -9321,8 +9391,7 @@ var MemoryDeviceStore = class {
|
|
|
9321
9391
|
return structuredClone(this.snapshot);
|
|
9322
9392
|
}
|
|
9323
9393
|
};
|
|
9324
|
-
|
|
9325
|
-
//#region src/managed-files.ts
|
|
9394
|
+
// src/managed-files.ts
|
|
9326
9395
|
/** Refuse link-shaped ancestors inside an owned state root before accessing a child. */
|
|
9327
9396
|
async function assertManagedParents(root, target, prefix) {
|
|
9328
9397
|
const parent = resolve(root);
|
|
@@ -9489,8 +9558,7 @@ async function replaceManagedDirectory(root, target, candidate, prefix, operatio
|
|
|
9489
9558
|
}
|
|
9490
9559
|
}
|
|
9491
9560
|
}
|
|
9492
|
-
|
|
9493
|
-
//#region src/component-download.ts
|
|
9561
|
+
// src/component-download.ts
|
|
9494
9562
|
/**
|
|
9495
9563
|
* Hosts a pinned GitHub release download may redirect to.
|
|
9496
9564
|
*
|
|
@@ -9601,8 +9669,7 @@ async function downloadPinnedArtifact(request) {
|
|
|
9601
9669
|
}
|
|
9602
9670
|
throw lastError;
|
|
9603
9671
|
}
|
|
9604
|
-
|
|
9605
|
-
//#region src/frp-component.ts
|
|
9672
|
+
// src/frp-component.ts
|
|
9606
9673
|
const FRP_VERSION$1 = "0.70.1";
|
|
9607
9674
|
const MAX_ARCHIVE_ENTRIES$1 = 128;
|
|
9608
9675
|
const MAX_ARCHIVE_LIST_BYTES$1 = 262144;
|
|
@@ -9886,8 +9953,7 @@ var FrpComponentManager = class {
|
|
|
9886
9953
|
return task.then(() => this.status());
|
|
9887
9954
|
}
|
|
9888
9955
|
};
|
|
9889
|
-
|
|
9890
|
-
//#region src/windows-msi-extract.ts
|
|
9956
|
+
// src/windows-msi-extract.ts
|
|
9891
9957
|
/** A failed read-only MSI extraction; causes deliberately exclude raw command arguments. */
|
|
9892
9958
|
var WindowsMsiExtractionError = class extends Error {
|
|
9893
9959
|
stage;
|
|
@@ -10371,8 +10437,7 @@ async function extractWindowsMsiBinary(msi, destination, executableName, expecte
|
|
|
10371
10437
|
await copyFile(binary, join(destination, executableName));
|
|
10372
10438
|
});
|
|
10373
10439
|
}
|
|
10374
|
-
|
|
10375
|
-
//#region src/cpolar-component.ts
|
|
10440
|
+
// src/cpolar-component.ts
|
|
10376
10441
|
const CPOLAR_VERSION = "3.3.18";
|
|
10377
10442
|
/** Pinned official cpolar release metadata for supported desktop targets. */
|
|
10378
10443
|
const CPOLAR_COMPONENT_RELEASES = Object.freeze(Object.fromEntries([
|
|
@@ -10740,8 +10805,7 @@ var CpolarComponentManager = class {
|
|
|
10740
10805
|
return task.then(() => this.status());
|
|
10741
10806
|
}
|
|
10742
10807
|
};
|
|
10743
|
-
|
|
10744
|
-
//#region src/frp-template.ts
|
|
10808
|
+
// src/frp-template.ts
|
|
10745
10809
|
/** Loopback-only HTTP vhost port used between Caddy and frps. */
|
|
10746
10810
|
const FRP_VHOST_HTTP_PORT = 7080;
|
|
10747
10811
|
/** Caddy snippet owned entirely by DSH Mobile; the main Caddyfile only imports it. */
|
|
@@ -10870,8 +10934,7 @@ function createRestrictedFrpServerTemplate(serverPort, token, publicOrigin, opti
|
|
|
10870
10934
|
if (publicIpv4Address(publicHost)) lines.push(manualIpCertificateGuide(publicHost), "");
|
|
10871
10935
|
return lines.join("\n");
|
|
10872
10936
|
}
|
|
10873
|
-
|
|
10874
|
-
//#region src/frp-config.ts
|
|
10937
|
+
// src/frp-config.ts
|
|
10875
10938
|
const MAX_SETTINGS_BYTES$1 = 8192;
|
|
10876
10939
|
/** Public entry port used by the self-signed TCP passthrough (never 443 by requirement). */
|
|
10877
10940
|
const FRP_DEFAULT_PUBLIC_PORT = 33080;
|
|
@@ -11259,8 +11322,7 @@ var FrpConfigStore = class {
|
|
|
11259
11322
|
return this.status();
|
|
11260
11323
|
}
|
|
11261
11324
|
};
|
|
11262
|
-
|
|
11263
|
-
//#region src/frp-attach.ts
|
|
11325
|
+
// src/frp-attach.ts
|
|
11264
11326
|
/** Default proof path used to confirm the public entry answered from this computer. */
|
|
11265
11327
|
const FRP_ATTACH_DISCOVERY_PATH = "/mobile-access/discovery";
|
|
11266
11328
|
/** Declaration printed above the VPS half of an attach template. */
|
|
@@ -11457,8 +11519,7 @@ function createFrpAttachTemplateParts(settings, options = {}) {
|
|
|
11457
11519
|
function createFrpAttachTemplate(settings, options = {}) {
|
|
11458
11520
|
return createFrpAttachTemplateParts(settings, options).text;
|
|
11459
11521
|
}
|
|
11460
|
-
|
|
11461
|
-
//#region src/frp-attach-plan.ts
|
|
11522
|
+
// src/frp-attach-plan.ts
|
|
11462
11523
|
const DEFAULT_PLAN_CONFIG_FILE = "<DSH Mobile 私有目录>/remote/frp/config/frpc.toml";
|
|
11463
11524
|
function planConfigFile(options) {
|
|
11464
11525
|
const file = options.configFile;
|
|
@@ -11596,8 +11657,7 @@ function createFrpAttachPlan(settings, options = {}) {
|
|
|
11596
11657
|
])
|
|
11597
11658
|
});
|
|
11598
11659
|
}
|
|
11599
|
-
|
|
11600
|
-
//#region src/cert-renewal.ts
|
|
11660
|
+
// src/cert-renewal.ts
|
|
11601
11661
|
/** Days left below which the entry certificate is reported as expiring. */
|
|
11602
11662
|
const CERT_EXPIRING_DAYS = 2;
|
|
11603
11663
|
const DAY_MS = 864e5;
|
|
@@ -11703,8 +11763,7 @@ async function probeOriginCertificate(host, port, timeoutMs = 5e3) {
|
|
|
11703
11763
|
});
|
|
11704
11764
|
});
|
|
11705
11765
|
}
|
|
11706
|
-
|
|
11707
|
-
//#region src/managed-setup.ts
|
|
11766
|
+
// src/managed-setup.ts
|
|
11708
11767
|
const VIRTUAL_INTERFACE_MARKERS = [
|
|
11709
11768
|
"bridge",
|
|
11710
11769
|
"docker",
|
|
@@ -11829,6 +11888,11 @@ async function saveManagedTrustedNetworks(setupFile, values) {
|
|
|
11829
11888
|
await restrictPrivateFile(setupFile);
|
|
11830
11889
|
return next;
|
|
11831
11890
|
}
|
|
11891
|
+
/** Persist an existing managed setup without resetting its identity, paths or extra trusted networks. */
|
|
11892
|
+
async function saveManagedSetup(setupFile, setup) {
|
|
11893
|
+
const validated = parseManagedSetup(setup);
|
|
11894
|
+
await atomicWrite(setupFile, `${JSON.stringify(validated, null, 2)}\n`);
|
|
11895
|
+
}
|
|
11832
11896
|
function privateIpv4(value) {
|
|
11833
11897
|
const parts = value.split(".").map(Number);
|
|
11834
11898
|
return parts.length === 4 && parts.every((part) => Number.isInteger(part) && part >= 0 && part <= 255) && (parts[0] === 10 || parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31 || parts[0] === 192 && parts[1] === 168);
|
|
@@ -12197,8 +12261,7 @@ async function materializeManagedSetup(setup, table) {
|
|
|
12197
12261
|
}
|
|
12198
12262
|
};
|
|
12199
12263
|
}
|
|
12200
|
-
|
|
12201
|
-
//#region src/frp-ingress.ts
|
|
12264
|
+
// src/frp-ingress.ts
|
|
12202
12265
|
/** Private ingress directory, always a sibling of the remote device file. */
|
|
12203
12266
|
function frpIngressPaths(stateFile) {
|
|
12204
12267
|
const directory = join(dirname(stateFile), "ingress");
|
|
@@ -12418,8 +12481,7 @@ async function purgeFrpIngressCertificates(stateFile, ownedRoot = dirname(stateF
|
|
|
12418
12481
|
throw error;
|
|
12419
12482
|
}
|
|
12420
12483
|
}
|
|
12421
|
-
|
|
12422
|
-
//#region src/remote.ts
|
|
12484
|
+
// src/remote.ts
|
|
12423
12485
|
const REMOTE_PROVIDERS = Object.freeze([
|
|
12424
12486
|
"tailscale",
|
|
12425
12487
|
"cpolar",
|
|
@@ -12655,8 +12717,7 @@ function configuredRemoteProvider(environment) {
|
|
|
12655
12717
|
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");
|
|
12656
12718
|
return value;
|
|
12657
12719
|
}
|
|
12658
|
-
|
|
12659
|
-
//#region src/frp.ts
|
|
12720
|
+
// src/frp.ts
|
|
12660
12721
|
const START_TIMEOUT_MS$2 = 45e3;
|
|
12661
12722
|
const DISCOVERY_REQUEST_TIMEOUT_MS = 5e3;
|
|
12662
12723
|
const DISCOVERY_RETRY_MS = 1e3;
|
|
@@ -13266,8 +13327,7 @@ var FrpController = class {
|
|
|
13266
13327
|
], "FRP resource cleanup failed");
|
|
13267
13328
|
}
|
|
13268
13329
|
};
|
|
13269
|
-
|
|
13270
|
-
//#region src/origin-proxy-config.ts
|
|
13330
|
+
// src/origin-proxy-config.ts
|
|
13271
13331
|
const DEFAULT_ORIGIN_LISTEN_PORT = 3444;
|
|
13272
13332
|
const MAX_SETTINGS_BYTES = 8192;
|
|
13273
13333
|
/**
|
|
@@ -13437,8 +13497,7 @@ var OriginConfigStore = class {
|
|
|
13437
13497
|
return this.status();
|
|
13438
13498
|
}
|
|
13439
13499
|
};
|
|
13440
|
-
|
|
13441
|
-
//#region src/origin-proxy.ts
|
|
13500
|
+
// src/origin-proxy.ts
|
|
13442
13501
|
/** Owns an authenticated HTTP gateway, with no tunnel process or public network probes. */
|
|
13443
13502
|
var OriginController = class {
|
|
13444
13503
|
options;
|
|
@@ -13588,8 +13647,7 @@ var OriginController = class {
|
|
|
13588
13647
|
});
|
|
13589
13648
|
}
|
|
13590
13649
|
};
|
|
13591
|
-
|
|
13592
|
-
//#region src/cloudflared-component.ts
|
|
13650
|
+
// src/cloudflared-component.ts
|
|
13593
13651
|
/** Upper bound for `tar -t` output; a pinned release listing is a few lines. */
|
|
13594
13652
|
const MAX_ARCHIVE_LIST_BYTES = 1048576;
|
|
13595
13653
|
const MAX_ARCHIVE_ENTRIES = 256;
|
|
@@ -13914,8 +13972,7 @@ var CloudflaredComponentManager = class {
|
|
|
13914
13972
|
return task.then(() => this.status());
|
|
13915
13973
|
}
|
|
13916
13974
|
};
|
|
13917
|
-
|
|
13918
|
-
//#region src/cloudflared.ts
|
|
13975
|
+
// src/cloudflared.ts
|
|
13919
13976
|
const MAX_LOG_BUFFER_BYTES$1 = 65536;
|
|
13920
13977
|
/**
|
|
13921
13978
|
* A quick tunnel has to be allocated and registered with the Cloudflare edge
|
|
@@ -14433,8 +14490,7 @@ var CloudflaredController = class {
|
|
|
14433
14490
|
], "cloudflared resource cleanup failed");
|
|
14434
14491
|
}
|
|
14435
14492
|
};
|
|
14436
|
-
|
|
14437
|
-
//#region src/cloudflared-tunnel.ts
|
|
14493
|
+
// src/cloudflared-tunnel.ts
|
|
14438
14494
|
const MAX_CONFIG_BYTES$1 = 8192;
|
|
14439
14495
|
const QUICK_TUNNEL_SUFFIX = ".trycloudflare.com";
|
|
14440
14496
|
/** Cloudflare's own tunnel-routing domain; it can never be a public hostname. */
|
|
@@ -14640,8 +14696,7 @@ var CloudflaredTunnelStore = class {
|
|
|
14640
14696
|
return this.status();
|
|
14641
14697
|
}
|
|
14642
14698
|
};
|
|
14643
|
-
|
|
14644
|
-
//#region src/iroh-files.ts
|
|
14699
|
+
// src/iroh-files.ts
|
|
14645
14700
|
async function renameIrohPath(root, source, destination) {
|
|
14646
14701
|
await renameManagedPath(root, source, destination, "iroh");
|
|
14647
14702
|
}
|
|
@@ -14651,8 +14706,7 @@ async function ensureIrohDirectory(root, directory) {
|
|
|
14651
14706
|
async function removeIrohTree(root, target) {
|
|
14652
14707
|
await removeManagedTree(root, target, "iroh");
|
|
14653
14708
|
}
|
|
14654
|
-
|
|
14655
|
-
//#region src/iroh-manifest.ts
|
|
14709
|
+
// src/iroh-manifest.ts
|
|
14656
14710
|
/** Checksum-gated resolution of the iroh transport host binary.
|
|
14657
14711
|
*
|
|
14658
14712
|
* The sidecar is built and published by the independent `dsh-mobile-iroh`
|
|
@@ -14756,8 +14810,7 @@ async function resolveIrohSidecarAt(companionRoot, platform, arch) {
|
|
|
14756
14810
|
if (process.platform !== "win32" && platform !== "win32" && (before.mode & 73) === 0) throw new Error("iroh_native_not_executable");
|
|
14757
14811
|
return path;
|
|
14758
14812
|
}
|
|
14759
|
-
|
|
14760
|
-
//#region src/iroh-component.ts
|
|
14813
|
+
// src/iroh-component.ts
|
|
14761
14814
|
/** Managed view of the iroh transport host sidecar.
|
|
14762
14815
|
*
|
|
14763
14816
|
* Explicit installation verifies and promotes a pinned sidecar release into
|
|
@@ -15071,8 +15124,7 @@ var IrohComponentManager = class {
|
|
|
15071
15124
|
return this.cached;
|
|
15072
15125
|
}
|
|
15073
15126
|
};
|
|
15074
|
-
|
|
15075
|
-
//#region src/iroh-sidecar.ts
|
|
15127
|
+
// src/iroh-sidecar.ts
|
|
15076
15128
|
/** Versioned parent-to-sidecar protocol for the iroh transport host.
|
|
15077
15129
|
*
|
|
15078
15130
|
* Ported from the independent `dsh-mobile-iroh` companion's sidecar
|
|
@@ -15202,8 +15254,7 @@ function decodeIrohEvent(line) {
|
|
|
15202
15254
|
}
|
|
15203
15255
|
return Object.freeze(event);
|
|
15204
15256
|
}
|
|
15205
|
-
|
|
15206
|
-
//#region src/iroh-origin.ts
|
|
15257
|
+
// src/iroh-origin.ts
|
|
15207
15258
|
/** Identity-derived browser origins for the raw TLS-over-iroh carrier.
|
|
15208
15259
|
*
|
|
15209
15260
|
* Ported from the independent `dsh-mobile-iroh` companion's origin contract.
|
|
@@ -15234,8 +15285,7 @@ function validateIrohOrigin(value, endpointId, localPort = DEFAULT_IROH_LOCAL_PO
|
|
|
15234
15285
|
if (value !== expected) throw new Error("iroh origin does not match the selected endpoint and local port");
|
|
15235
15286
|
return expected;
|
|
15236
15287
|
}
|
|
15237
|
-
|
|
15238
|
-
//#region src/iroh-pairing.ts
|
|
15288
|
+
// src/iroh-pairing.ts
|
|
15239
15289
|
/** Short-lived iroh pairing descriptors for QR transport.
|
|
15240
15290
|
*
|
|
15241
15291
|
* Byte-compatible with the independent `dsh-mobile-iroh` companion's
|
|
@@ -15343,8 +15393,7 @@ function decodePairingDescriptor(encoded, options = {}) {
|
|
|
15343
15393
|
}
|
|
15344
15394
|
/** Phone-side listener port advertised in QR codes minted by this computer. */
|
|
15345
15395
|
const PAIRING_DESCRIPTOR_LOCAL_PORT = DEFAULT_IROH_LOCAL_PORT;
|
|
15346
|
-
|
|
15347
|
-
//#region src/iroh.ts
|
|
15396
|
+
// src/iroh.ts
|
|
15348
15397
|
/** Experimental iroh remote transport as a first-class remote provider.
|
|
15349
15398
|
*
|
|
15350
15399
|
* The sidecar is distributed by this plugin itself: an explicit install
|
|
@@ -15895,8 +15944,7 @@ var IrohController = class {
|
|
|
15895
15944
|
});
|
|
15896
15945
|
}
|
|
15897
15946
|
};
|
|
15898
|
-
|
|
15899
|
-
//#region src/iroh-tls.ts
|
|
15947
|
+
// src/iroh-tls.ts
|
|
15900
15948
|
/** Self-signed TLS identity for the experimental iroh gateway.
|
|
15901
15949
|
*
|
|
15902
15950
|
* Reuses this repository's managed CA path (`managed-setup.ts`) instead of
|
|
@@ -16063,8 +16111,7 @@ function irohGatewayConfig(template, endpointId, stateFile, tls, listenPort = 0)
|
|
|
16063
16111
|
pairingTtlMs: 3e5
|
|
16064
16112
|
});
|
|
16065
16113
|
}
|
|
16066
|
-
|
|
16067
|
-
//#region src/task-events.ts
|
|
16114
|
+
// src/task-events.ts
|
|
16068
16115
|
const TASK_EVENT_DEBOUNCE_MS = 1e3;
|
|
16069
16116
|
function turnNumber(value) {
|
|
16070
16117
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : 0;
|
|
@@ -16141,8 +16188,99 @@ var TaskEventHub = class {
|
|
|
16141
16188
|
return this.sinks.size;
|
|
16142
16189
|
}
|
|
16143
16190
|
};
|
|
16144
|
-
|
|
16145
|
-
|
|
16191
|
+
// src/diagnostic-network-context.ts
|
|
16192
|
+
function addressKind(address) {
|
|
16193
|
+
if (isIP(address) === 4) {
|
|
16194
|
+
const [first, second] = address.split(".").map(Number);
|
|
16195
|
+
if (first === 100 && second >= 64 && second <= 127) return "tailnet";
|
|
16196
|
+
if (first === 198 && (second === 18 || second === 19)) return "fake-ip";
|
|
16197
|
+
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";
|
|
16198
|
+
return "public";
|
|
16199
|
+
}
|
|
16200
|
+
if (isIP(address) === 6) {
|
|
16201
|
+
const lower = address.toLowerCase();
|
|
16202
|
+
if (lower.startsWith("::ffff:") && isIP(lower.slice(7)) === 4) return addressKind(lower.slice(7));
|
|
16203
|
+
if (lower.startsWith("fd7a:115c:a1e0:")) return "tailnet";
|
|
16204
|
+
if (lower === "::" || lower === "::1" || /^(?:f[cd]|fe[89ab]|ff)/u.test(lower)) return "private";
|
|
16205
|
+
return "public";
|
|
16206
|
+
}
|
|
16207
|
+
return "unknown";
|
|
16208
|
+
}
|
|
16209
|
+
/**
|
|
16210
|
+
* Collapse system resolver results into non-identifying categories, without retaining addresses.
|
|
16211
|
+
* @param addresses - Addresses supplied by the system resolver.
|
|
16212
|
+
* @returns Address category and observed IP families; mixed answers are not treated as tailnet-only.
|
|
16213
|
+
*/
|
|
16214
|
+
function classifyDiagnosticDns(addresses) {
|
|
16215
|
+
const kinds = new Set(addresses.map((entry) => addressKind(entry.address)));
|
|
16216
|
+
const families = new Set(addresses.map((entry) => isIP(entry.address)).filter((value) => value !== 0));
|
|
16217
|
+
return {
|
|
16218
|
+
kind: kinds.size === 0 ? "unknown" : kinds.size === 1 ? [...kinds][0] : "mixed",
|
|
16219
|
+
family: families.size === 0 ? "unknown" : families.size > 1 ? "dual" : families.has(4) ? "ipv4" : "ipv6"
|
|
16220
|
+
};
|
|
16221
|
+
}
|
|
16222
|
+
/**
|
|
16223
|
+
* Read bounded system-DNS context; never consult an external DNS service or modify settings.
|
|
16224
|
+
* @param hostname - The configured endpoint's hostname.
|
|
16225
|
+
* @param budgetMs - Maximum wait; OS lookup completion itself cannot be cancelled.
|
|
16226
|
+
* @param resolve - System lookup or an instance-local controlled fixture.
|
|
16227
|
+
* @returns Unknown on failure/deadline, not a conclusion that DNS or the public service is down.
|
|
16228
|
+
*/
|
|
16229
|
+
async function probeDiagnosticDns(hostname, budgetMs, resolve = (host) => lookup(host, { all: true })) {
|
|
16230
|
+
let timer;
|
|
16231
|
+
try {
|
|
16232
|
+
return classifyDiagnosticDns(await Promise.race([resolve(hostname), new Promise((_resolve, reject) => {
|
|
16233
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error("dns_context_timeout")), Math.max(1, budgetMs));
|
|
16234
|
+
timer.unref();
|
|
16235
|
+
})]));
|
|
16236
|
+
} catch {
|
|
16237
|
+
return {
|
|
16238
|
+
kind: "unknown",
|
|
16239
|
+
family: "unknown"
|
|
16240
|
+
};
|
|
16241
|
+
} finally {
|
|
16242
|
+
clearTimeout(timer);
|
|
16243
|
+
}
|
|
16244
|
+
}
|
|
16245
|
+
/**
|
|
16246
|
+
* Classify fixed transport machine codes without reflecting messages, URLs or nested errors.
|
|
16247
|
+
* @param error - A failed endpoint request.
|
|
16248
|
+
* @returns The last observed transport stage, or unknown rather than a guessed cause.
|
|
16249
|
+
*/
|
|
16250
|
+
function diagnosticFailureStage(error) {
|
|
16251
|
+
let current = error;
|
|
16252
|
+
for (let depth = 0; depth < 4 && current !== null && typeof current === "object"; depth++) {
|
|
16253
|
+
const code = "code" in current && typeof current.code === "string" ? current.code : "";
|
|
16254
|
+
if ("name" in current && current.name === "TimeoutError") return "timeout";
|
|
16255
|
+
if (["ENOTFOUND", "EAI_AGAIN"].includes(code)) return "dns";
|
|
16256
|
+
if ([
|
|
16257
|
+
"ECONNREFUSED",
|
|
16258
|
+
"ECONNRESET",
|
|
16259
|
+
"EPIPE",
|
|
16260
|
+
"ENETUNREACH",
|
|
16261
|
+
"EHOSTUNREACH",
|
|
16262
|
+
"UND_ERR_SOCKET"
|
|
16263
|
+
].includes(code)) return "connect";
|
|
16264
|
+
if ([
|
|
16265
|
+
"ETIMEDOUT",
|
|
16266
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
16267
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
16268
|
+
"UND_ERR_BODY_TIMEOUT"
|
|
16269
|
+
].includes(code)) return "timeout";
|
|
16270
|
+
if ([
|
|
16271
|
+
"ERR_TLS_CERT_ALTNAME_INVALID",
|
|
16272
|
+
"CERT_HAS_EXPIRED",
|
|
16273
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
16274
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
16275
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
16276
|
+
"UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
|
16277
|
+
"ERR_SSL_WRONG_VERSION_NUMBER"
|
|
16278
|
+
].includes(code)) return "tls";
|
|
16279
|
+
current = "cause" in current ? current.cause : void 0;
|
|
16280
|
+
}
|
|
16281
|
+
return "unknown";
|
|
16282
|
+
}
|
|
16283
|
+
// src/diagnostics.ts
|
|
16146
16284
|
/** Recognize the active remote-web-ui boot hook without evaluating or modifying it. */
|
|
16147
16285
|
function hasCompetingRemoteChannelBoot(rows) {
|
|
16148
16286
|
return rows.some((row) => row.kind === "script" && row.text?.includes("__DSH_REMOTE_CHANNEL_BOOT__") === true);
|
|
@@ -16201,6 +16339,7 @@ function check(id, status, reason, label, detail, action, facts) {
|
|
|
16201
16339
|
id,
|
|
16202
16340
|
status,
|
|
16203
16341
|
reason,
|
|
16342
|
+
scope: id === "versions" ? "configuration" : id === "phone-network" ? "phone" : "computer",
|
|
16204
16343
|
...facts === void 0 ? {} : { facts: Object.freeze(facts) },
|
|
16205
16344
|
label,
|
|
16206
16345
|
detail,
|
|
@@ -16445,28 +16584,6 @@ async function relayProbeThroughProxy(target, proxy, budgetMs = remoteDiagnostic
|
|
|
16445
16584
|
hopRequest.end();
|
|
16446
16585
|
});
|
|
16447
16586
|
}
|
|
16448
|
-
/** Bounded supplementary DNS context for a failed direct attempt. */
|
|
16449
|
-
async function unreachableWithDnsContext(hostname, budgetMs) {
|
|
16450
|
-
let fakeIp = false;
|
|
16451
|
-
let timer;
|
|
16452
|
-
try {
|
|
16453
|
-
fakeIp = (await Promise.race([lookup(hostname, { all: true }), new Promise((_resolve, reject) => {
|
|
16454
|
-
timer = setTimeout(() => {
|
|
16455
|
-
reject(/* @__PURE__ */ new Error("dns_context_timeout"));
|
|
16456
|
-
}, Math.max(1, budgetMs));
|
|
16457
|
-
timer.unref();
|
|
16458
|
-
})])).some(({ address }) => {
|
|
16459
|
-
const [first, second] = address.split(".").map(Number);
|
|
16460
|
-
return first === 198 && (second === 18 || second === 19);
|
|
16461
|
-
});
|
|
16462
|
-
} catch {} finally {
|
|
16463
|
-
clearTimeout(timer);
|
|
16464
|
-
}
|
|
16465
|
-
return {
|
|
16466
|
-
state: "unreachable",
|
|
16467
|
-
...fakeIp ? { fakeIp: true } : {}
|
|
16468
|
-
};
|
|
16469
|
-
}
|
|
16470
16587
|
async function fetchRemoteHealth(target, budgetMs) {
|
|
16471
16588
|
const started = performance.now();
|
|
16472
16589
|
try {
|
|
@@ -16476,19 +16593,28 @@ async function fetchRemoteHealth(target, budgetMs) {
|
|
|
16476
16593
|
signal: AbortSignal.timeout(Math.max(1, budgetMs - 500))
|
|
16477
16594
|
});
|
|
16478
16595
|
const latencyMs = Math.max(0, Math.round(performance.now() - started));
|
|
16479
|
-
if (response.status === 429)
|
|
16480
|
-
|
|
16481
|
-
|
|
16482
|
-
|
|
16483
|
-
|
|
16596
|
+
if (response.status === 429) {
|
|
16597
|
+
await response.body?.cancel();
|
|
16598
|
+
return {
|
|
16599
|
+
state: "rate-limited",
|
|
16600
|
+
latencyMs
|
|
16601
|
+
};
|
|
16602
|
+
}
|
|
16603
|
+
const observation = response.ok ? {
|
|
16484
16604
|
state: "ready",
|
|
16485
16605
|
latencyMs
|
|
16486
16606
|
} : {
|
|
16487
16607
|
state: "unreachable",
|
|
16488
|
-
latencyMs
|
|
16608
|
+
latencyMs,
|
|
16609
|
+
failureStage: "http"
|
|
16610
|
+
};
|
|
16611
|
+
await response.body?.cancel();
|
|
16612
|
+
return observation;
|
|
16613
|
+
} catch (error) {
|
|
16614
|
+
return {
|
|
16615
|
+
state: "unreachable",
|
|
16616
|
+
failureStage: diagnosticFailureStage(error)
|
|
16489
16617
|
};
|
|
16490
|
-
} catch {
|
|
16491
|
-
return unreachableWithDnsContext(target.hostname, Math.max(1, budgetMs - (performance.now() - started)));
|
|
16492
16618
|
}
|
|
16493
16619
|
}
|
|
16494
16620
|
/**
|
|
@@ -16511,7 +16637,14 @@ async function probeRemoteHealth(origin, options = {}) {
|
|
|
16511
16637
|
return { state: "not-applicable" };
|
|
16512
16638
|
}
|
|
16513
16639
|
const budgetMs = remoteDiagnosticTimeoutMs(origin);
|
|
16514
|
-
const
|
|
16640
|
+
const direct = options.direct ?? fetchRemoteHealth;
|
|
16641
|
+
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)]);
|
|
16642
|
+
const context = dns === void 0 ? {} : { dns };
|
|
16643
|
+
const observation = {
|
|
16644
|
+
...observed,
|
|
16645
|
+
...context,
|
|
16646
|
+
...dns?.kind === "fake-ip" ? { fakeIp: true } : {}
|
|
16647
|
+
};
|
|
16515
16648
|
if (observation.state !== "unreachable") return observation;
|
|
16516
16649
|
const relay = configuredProxyFor(origin, options.env ?? process.env);
|
|
16517
16650
|
if (relay === void 0) return observation;
|
|
@@ -16521,12 +16654,14 @@ async function probeRemoteHealth(origin, options = {}) {
|
|
|
16521
16654
|
if (status === 429) return {
|
|
16522
16655
|
state: "rate-limited",
|
|
16523
16656
|
latencyMs,
|
|
16524
|
-
viaProxy: true
|
|
16657
|
+
viaProxy: true,
|
|
16658
|
+
...context
|
|
16525
16659
|
};
|
|
16526
16660
|
return status >= 200 && status < 300 ? {
|
|
16527
16661
|
state: "ready",
|
|
16528
16662
|
latencyMs,
|
|
16529
|
-
viaProxy: true
|
|
16663
|
+
viaProxy: true,
|
|
16664
|
+
...context
|
|
16530
16665
|
} : observation;
|
|
16531
16666
|
} catch {
|
|
16532
16667
|
return observation;
|
|
@@ -16536,28 +16671,38 @@ async function defaultRemoteProbe(origin) {
|
|
|
16536
16671
|
return probeRemoteHealth(origin);
|
|
16537
16672
|
}
|
|
16538
16673
|
function reportLine(entry) {
|
|
16539
|
-
return `[${entry.status.toUpperCase()}] ${entry.label}: ${entry.detail}${entry.action === void 0 ? "" : ` ${entry.action}`}`;
|
|
16674
|
+
return `[${entry.status.toUpperCase()}][${entry.scope}] ${entry.label}: ${entry.detail}${entry.action === void 0 ? "" : ` ${entry.action}`}`;
|
|
16540
16675
|
}
|
|
16541
16676
|
/** Run bounded read-only checks and return a report safe to paste into an issue. */
|
|
16542
16677
|
async function collectConnectionDiagnostics(snapshot, probes = {}) {
|
|
16543
16678
|
const checks = [];
|
|
16544
16679
|
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" });
|
|
16545
|
-
const [firewall, remoteObservation] = await Promise.all([(probes.firewall ?? defaultFirewallProbe())(snapshot.lan.port), remoteProbe]);
|
|
16546
|
-
checks.push(check("versions", "ok", "versions-current", "
|
|
16680
|
+
const [firewall, remoteObservation] = await Promise.all([snapshot.lan.running ? (probes.firewall ?? defaultFirewallProbe())(snapshot.lan.port) : Promise.resolve({ state: "not-applicable" }), remoteProbe]);
|
|
16681
|
+
checks.push(check("versions", "ok", "versions-current", "版本信息", `插件 ${DSH_MOBILE_VERSION},DSH ${snapshot.dshVersion},Android App 最低 ${MINIMUM_ANDROID_APP_VERSION}。`));
|
|
16547
16682
|
if (snapshot.competingRemoteChannelBoot === true) checks.push(check("remote-plugin-conflict", "warning", "competing-remote-channel", "第三方远程插件", "检测到另一套远程请求通道脚本;手机页面可能进入该插件自己的配对流程。", "在电脑端关闭 dsh-remote-web-ui 的远程访问后刷新,再使用 DSH Mobile 当前二维码配对;不要混用两种配对码。"));
|
|
16548
|
-
if (snapshot.
|
|
16549
|
-
|
|
16683
|
+
if (snapshot.indexInspectionUnavailable === true) checks.push(check("plugin-inspection", "warning", "plugin-inspection-unavailable", "页面脚本检查", "本次未能检查活动页面脚本;不能据此认定不存在其他远程通道。", "其余网络项目独立检查;保留复现环境并核对相关插件。"));
|
|
16684
|
+
if (snapshot.lan.configured === false) checks.push(check("network", snapshot.lan.running ? "error" : "info", "lan-setup-required", "局域网配置", "尚未完成局域网初始化。", "需要局域网连接时,返回局域网页选择网卡并完成配置。"));
|
|
16685
|
+
else if (snapshot.lan.networkError !== void 0) checks.push(check("network", snapshot.lan.running ? "error" : "info", "network-unavailable", "局域网网卡", "已保存的网卡当前不可用。", snapshot.lan.running ? "在局域网页重新确认网卡配置。" : "局域网未启用;此配置不用于判断远程入口。"));
|
|
16550
16686
|
else if (snapshot.lan.configuredInterface !== void 0) {
|
|
16551
16687
|
const interfaceName = snapshot.lan.interfaceName ?? snapshot.lan.configuredInterface;
|
|
16552
16688
|
checks.push(check("network", "ok", "network-interface", "局域网网卡", `正在跟随 ${interfaceName}。`, void 0, { interfaceName }));
|
|
16553
16689
|
} else checks.push(check("network", "info", "network-fixed", "局域网网卡", "当前使用固定网络配置。"));
|
|
16554
16690
|
if (snapshot.lan.running && snapshot.lan.origin !== void 0) {
|
|
16555
16691
|
const endpointSuffix = maskLanOrigin(snapshot.lan.origin);
|
|
16556
|
-
checks.push(check("lan", "ok", "lan-ready", "局域网网关",
|
|
16692
|
+
checks.push(check("lan", "ok", "lan-ready", "局域网网关", `电脑网关已监听 ${endpointSuffix};尚未验证手机能否到达。`, void 0, { endpointSuffix }));
|
|
16557
16693
|
} else checks.push(check("lan", "info", "lan-off", "局域网网关", "当前未开启。", "需要手机直连时开启局域网访问。"));
|
|
16558
|
-
if (
|
|
16559
|
-
else if (firewall.state === "
|
|
16694
|
+
if (!snapshot.lan.running) checks.push(check("firewall", "info", "firewall-inactive", "Windows 防火墙", "局域网未启用,本次未检查局域网防火墙规则。", "不据此判断远程入口;需要局域网时再启用并检查。"));
|
|
16695
|
+
else if (firewall.state === "ready") checks.push(check("firewall", "ok", "firewall-ready", "Windows 防火墙", "局域网 TCP 与发现规则已启用。"));
|
|
16696
|
+
else if (firewall.state === "missing") checks.push(check("firewall", snapshot.lan.running ? "warning" : "info", "firewall-missing", "Windows 防火墙", "未找到完整的局域网放行规则。", snapshot.lan.running ? "确认需要局域网连接,再检查相应规则;不要关闭整个防火墙。" : "局域网未启用,不据此判断远程入口失败。"));
|
|
16560
16697
|
else if (firewall.state === "unknown") checks.push(check("firewall", "info", "firewall-unknown", "Windows 防火墙", "系统未允许插件读取防火墙状态。", "若手机找不到电脑,以管理员身份重新运行 setup。"));
|
|
16698
|
+
if (snapshot.remote.running && snapshot.remote.state === "ready") checks.push(check("remote-service", "ok", "remote-service-ready", "本机通道状态", "提供方报告本机组件已就绪;不代表公网或手机路径已验证。", void 0, { provider: snapshot.remote.provider }));
|
|
16699
|
+
if (remoteObservation.dns !== void 0) {
|
|
16700
|
+
const kind = remoteObservation.dns.kind;
|
|
16701
|
+
checks.push(check("remote-dns", kind === "fake-ip" ? "warning" : "info", `dns-${kind}`, "系统 DNS", `电脑系统解析分类:${kind};IP:${remoteObservation.dns.family}。未查询公共 DNS,地址不写入报告。`, void 0, {
|
|
16702
|
+
dnsKind: kind,
|
|
16703
|
+
dnsFamily: remoteObservation.dns.family
|
|
16704
|
+
}));
|
|
16705
|
+
}
|
|
16561
16706
|
if (!snapshot.remote.running || snapshot.remote.state === "off") checks.push(check("remote", "info", "remote-off", "远程通道", "当前未启用。", void 0, { provider: snapshot.remote.provider }));
|
|
16562
16707
|
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" }));
|
|
16563
16708
|
else if (snapshot.remote.provider === "iroh" && snapshot.remote.state === "ready") checks.push(check("remote", "info", "remote-iroh-ready", "远程通道", "iroh 通道已就绪;端点身份为回环 TLS,不从电脑端探测手机可达性。", "在手机上用配对二维码验证连接;二维码过期或通道报错时重新生成并重连。", { provider: "iroh" }));
|
|
@@ -16569,10 +16714,14 @@ async function collectConnectionDiagnostics(snapshot, probes = {}) {
|
|
|
16569
16714
|
...remoteObservation.latencyMs === void 0 ? {} : { latencyMs: remoteObservation.latencyMs },
|
|
16570
16715
|
...remoteObservation.viaProxy === true ? { viaProxy: true } : {}
|
|
16571
16716
|
};
|
|
16572
|
-
if (remoteObservation.state === "ready") checks.push(check("remote", remoteObservation.viaProxy === true ? "warning" : "ok", "remote-ready", "远程通道", remoteObservation.viaProxy === true ? `电脑经代理可访问 ${endpointSuffix},但尚未验证手机能否连接。` :
|
|
16717
|
+
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));
|
|
16573
16718
|
else if (remoteObservation.state === "rate-limited") checks.push(check("remote", "warning", "remote-rate-limited", "远程通道", remoteObservation.viaProxy === true ? "电脑经代理访问时观察到限流,尚未验证手机能否连接。" : "公共地址可达,但本次检查观察到服务限流。", "稍后重试;旧会话会按需加载以减少流量。", facts));
|
|
16574
|
-
else if (snapshot.remote.provider === "tailscale" && remoteObservation.
|
|
16575
|
-
else checks.push(check("remote", "error", "remote-
|
|
16719
|
+
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));
|
|
16720
|
+
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));
|
|
16721
|
+
else {
|
|
16722
|
+
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";
|
|
16723
|
+
checks.push(check("remote", "error", reason, "电脑出口探测", "电脑当前出口的探测未通过;不代表其他网络或手机一定失败。", "按失败阶段核对 DNS、路由、证书或代理,并用手机当前网络对照。", facts));
|
|
16724
|
+
}
|
|
16576
16725
|
} else if (snapshot.remote.state === "starting" || snapshot.remote.state === "connecting" || snapshot.remote.state === "needs-login") {
|
|
16577
16726
|
const needsLogin = snapshot.remote.state === "needs-login";
|
|
16578
16727
|
checks.push(check("remote", "warning", needsLogin ? "remote-needs-login" : "remote-connecting", "远程通道", needsLogin ? "等待完成 Tailscale 登录。" : "仍在建立连接。", needsLogin ? "返回远程页继续登录。" : "等待片刻后重新检查。", { provider: snapshot.remote.provider }));
|
|
@@ -16583,15 +16732,16 @@ async function collectConnectionDiagnostics(snapshot, probes = {}) {
|
|
|
16583
16732
|
controllerCode
|
|
16584
16733
|
}));
|
|
16585
16734
|
}
|
|
16586
|
-
checks.push(check("phone-network", "info", "phone-network-unknown", "手机网络", "
|
|
16735
|
+
checks.push(check("phone-network", "info", "phone-network-unknown", "手机网络", "电脑没有验证手机网络、认证页面、资源加载或 WebSocket。", "请用手机当前网络实际连接,记录失败阶段;不要提供配对凭据或直接关闭安全隔离措施。"));
|
|
16587
16736
|
const overall = checks.some((entry) => entry.status === "error") ? "error" : checks.some((entry) => entry.status === "warning") ? "attention" : "ok";
|
|
16588
|
-
const summary = overall === "ok" ? "
|
|
16737
|
+
const summary = overall === "ok" ? "电脑端基础检查完成;手机、认证页面、资源与 WebSocket 尚未验证。" : overall === "attention" ? "电脑端检查有待确认项目;请区分测量路径与实际手机连接。" : "电脑端检查有失败项目;不据此推断所有网络均不可达。";
|
|
16589
16738
|
const report = [
|
|
16590
16739
|
"DSH Mobile 诊断报告",
|
|
16591
16740
|
`生成时间: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
16592
16741
|
`版本: plugin=${DSH_MOBILE_VERSION}; dsh=${snapshot.dshVersion}; min-app=${MINIMUM_ANDROID_APP_VERSION}`,
|
|
16593
16742
|
`LAN: ${snapshot.lan.running ? "on" : "off"}; endpoint=${maskLanOrigin(snapshot.lan.origin)}`,
|
|
16594
16743
|
`Remote: provider=${snapshot.remote.provider}; state=${snapshot.remote.state}; endpoint=${remoteSuffix(snapshot.remote.origin)}`,
|
|
16744
|
+
"Coverage: computer-side only; phone, authenticated page, resources and WebSocket remain unverified.",
|
|
16595
16745
|
...checks.map(reportLine)
|
|
16596
16746
|
].join("\n");
|
|
16597
16747
|
return Object.freeze({
|
|
@@ -16605,11 +16755,19 @@ async function collectConnectionDiagnostics(snapshot, probes = {}) {
|
|
|
16605
16755
|
}),
|
|
16606
16756
|
summary,
|
|
16607
16757
|
checks: Object.freeze(checks),
|
|
16608
|
-
report
|
|
16758
|
+
report,
|
|
16759
|
+
coverage: Object.freeze({
|
|
16760
|
+
measuredFrom: "computer",
|
|
16761
|
+
unverified: Object.freeze([
|
|
16762
|
+
"phone",
|
|
16763
|
+
"authenticated-page",
|
|
16764
|
+
"resources",
|
|
16765
|
+
"websocket"
|
|
16766
|
+
])
|
|
16767
|
+
})
|
|
16609
16768
|
});
|
|
16610
16769
|
}
|
|
16611
|
-
|
|
16612
|
-
//#region src/mobile-guide.ts
|
|
16770
|
+
// src/mobile-guide.ts
|
|
16613
16771
|
/** Compose the guide with the current customization state injected. */
|
|
16614
16772
|
function buildMobileGuide(state) {
|
|
16615
16773
|
const styleLine = state.hasCustomCss ? "存在(已检测到文件,需在已配对页面验证加载)" : "不存在(使用内置默认样式)";
|
|
@@ -16674,8 +16832,7 @@ const MOBILE_CUSTOMIZATION_GUIDE_BODY = `你在为用户定制 DSH Mobile 的手
|
|
|
16674
16832
|
- 创建或修改扩展后,确认 extension.json 的 schemaVersion 为 1、id 与目录名一致、且 id 只含小写字母数字和连字符
|
|
16675
16833
|
- 扩展的 host.mjs 若在完成前无法激活,先修正而不是留下损坏的扩展
|
|
16676
16834
|
- 完成后检查自己实际写入了哪些文件,向用户简要说明改了什么、手机端会有什么变化`;
|
|
16677
|
-
|
|
16678
|
-
//#region src/vps-deploy.ts
|
|
16835
|
+
// src/vps-deploy.ts
|
|
16679
16836
|
const FRP_VERSION = "0.70.1";
|
|
16680
16837
|
const SSH_TIMEOUT_MS = 3e5;
|
|
16681
16838
|
const LINUX_ARTIFACTS = Object.freeze({
|
|
@@ -17646,8 +17803,44 @@ async function uninstallVps(serverAddress, uninstall, input, options = {}) {
|
|
|
17646
17803
|
checks
|
|
17647
17804
|
});
|
|
17648
17805
|
}
|
|
17649
|
-
|
|
17650
|
-
|
|
17806
|
+
// src/lan-rebind.ts
|
|
17807
|
+
/** An unreadable setup is rejected before stopping the listener or replacing any file. */
|
|
17808
|
+
var ManagedLanConfigurationError = class extends Error {
|
|
17809
|
+
constructor(cause) {
|
|
17810
|
+
super("managed LAN configuration is unreadable", { cause });
|
|
17811
|
+
this.name = "ManagedLanConfigurationError";
|
|
17812
|
+
}
|
|
17813
|
+
};
|
|
17814
|
+
/** Rebind an existing listener; callers serialize this call with all other setup-file writes. */
|
|
17815
|
+
async function rebindManagedLan(options) {
|
|
17816
|
+
let previous;
|
|
17817
|
+
try {
|
|
17818
|
+
previous = parseManagedSetup(JSON.parse(await readFile(options.setupFile, "utf8")));
|
|
17819
|
+
await readManagedCa(previous.tls);
|
|
17820
|
+
} catch (error) {
|
|
17821
|
+
throw new ManagedLanConfigurationError(error);
|
|
17822
|
+
}
|
|
17823
|
+
const result = await options.controller.reconfigure(async () => {
|
|
17824
|
+
const next = Object.freeze({
|
|
17825
|
+
...previous,
|
|
17826
|
+
networkInterface: options.network.name
|
|
17827
|
+
});
|
|
17828
|
+
await refreshManagedServerCertificate(next, options.network.address);
|
|
17829
|
+
await saveManagedSetup(options.setupFile, next);
|
|
17830
|
+
options.publish(next);
|
|
17831
|
+
return next;
|
|
17832
|
+
}, async () => {
|
|
17833
|
+
await saveManagedSetup(options.setupFile, previous);
|
|
17834
|
+
options.publish(previous);
|
|
17835
|
+
const network = options.selectPrevious?.(previous) ?? selectLanNetwork(void 0, previous.networkInterface);
|
|
17836
|
+
await refreshManagedServerCertificate(previous, network.address);
|
|
17837
|
+
});
|
|
17838
|
+
return {
|
|
17839
|
+
setup: result.value,
|
|
17840
|
+
running: result.running
|
|
17841
|
+
};
|
|
17842
|
+
}
|
|
17843
|
+
// src/funnel.ts
|
|
17651
17844
|
const MAX_PROTOCOL_LINE_BYTES = 16384;
|
|
17652
17845
|
const FUNNEL_START_TIMEOUT_MS = 45e3;
|
|
17653
17846
|
function publicStatus$1(status) {
|
|
@@ -18161,8 +18354,7 @@ function funnelExecutable(importMetaUrl, environment = process.env) {
|
|
|
18161
18354
|
const file = `dsh-mobile-funnel-${process.platform}-${process.arch}${suffix}`;
|
|
18162
18355
|
return resolve(fileURLToPath(new URL(`../bin/${file}`, importMetaUrl)));
|
|
18163
18356
|
}
|
|
18164
|
-
|
|
18165
|
-
//#region src/cpolar.ts
|
|
18357
|
+
// src/cpolar.ts
|
|
18166
18358
|
const MAX_LOG_BUFFER_BYTES = 65536;
|
|
18167
18359
|
const START_TIMEOUT_MS = 45e3;
|
|
18168
18360
|
const CPOLAR_HOST_SUFFIXES = Object.freeze([
|
|
@@ -18578,8 +18770,7 @@ var CpolarController = class {
|
|
|
18578
18770
|
], "cpolar resource cleanup failed");
|
|
18579
18771
|
}
|
|
18580
18772
|
};
|
|
18581
|
-
|
|
18582
|
-
//#region src/caddy-files.ts
|
|
18773
|
+
// src/caddy-files.ts
|
|
18583
18774
|
/** Refuse link-shaped ancestors within Caddy's owned state root. */
|
|
18584
18775
|
async function assertCaddyParents(root, target) {
|
|
18585
18776
|
await assertManagedParents(root, target, "caddy");
|
|
@@ -18600,8 +18791,7 @@ async function writeCaddyPrivateFile(root, file, body) {
|
|
|
18600
18791
|
async function removeCaddyTree(root, target) {
|
|
18601
18792
|
await removeManagedTree(root, target, "caddy");
|
|
18602
18793
|
}
|
|
18603
|
-
|
|
18604
|
-
//#region src/caddy-component.ts
|
|
18794
|
+
// src/caddy-component.ts
|
|
18605
18795
|
const CADDY_VERSION = "2.11.7";
|
|
18606
18796
|
const CADDY_DNS_PLUGIN_VERSION = "v0.4.3";
|
|
18607
18797
|
const CADDY_DOWNLOAD_PAGE = "https://github.com/saya-ch/dsh-mobile/releases/tag/caddy-component-2.11.7-tencentcloud-0.4.3-r1";
|
|
@@ -19120,8 +19310,7 @@ var CaddyConfigStore = class {
|
|
|
19120
19310
|
return task;
|
|
19121
19311
|
}
|
|
19122
19312
|
};
|
|
19123
|
-
|
|
19124
|
-
//#region src/caddy.ts
|
|
19313
|
+
// src/caddy.ts
|
|
19125
19314
|
/** Private directories override Caddy's platform defaults; ambient API keys and admin/proxy overrides are omitted. */
|
|
19126
19315
|
function caddyProcessEnvironment(config, settings, environment = process.env) {
|
|
19127
19316
|
const credentials = config.credentials();
|
|
@@ -19451,8 +19640,7 @@ var CaddyController = class {
|
|
|
19451
19640
|
}
|
|
19452
19641
|
}
|
|
19453
19642
|
};
|
|
19454
|
-
|
|
19455
|
-
//#region src/managed-origin.ts
|
|
19643
|
+
// src/managed-origin.ts
|
|
19456
19644
|
/** Children never independently restore a durable enabled switch. */
|
|
19457
19645
|
function createVolatileRemoteControlStore() {
|
|
19458
19646
|
let value = {
|
|
@@ -19633,8 +19821,7 @@ var ManagedOriginController = class {
|
|
|
19633
19821
|
return task;
|
|
19634
19822
|
}
|
|
19635
19823
|
};
|
|
19636
|
-
|
|
19637
|
-
//#region src/release-update.ts
|
|
19824
|
+
// src/release-update.ts
|
|
19638
19825
|
const PACKAGE_NAME = "dsh-mobile";
|
|
19639
19826
|
const NPM_LATEST_URL = "https://registry.npmjs.org/dsh-mobile/latest";
|
|
19640
19827
|
const ANDROID_RELEASE_DESCRIPTOR_URL = "https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/apps/mobile/release.json";
|
|
@@ -20097,8 +20284,7 @@ var PluginReleaseManager = class {
|
|
|
20097
20284
|
});
|
|
20098
20285
|
}
|
|
20099
20286
|
};
|
|
20100
|
-
|
|
20101
|
-
//#region src/file-logger.ts
|
|
20287
|
+
// src/file-logger.ts
|
|
20102
20288
|
const MAX_LOG_BYTES = 5242880;
|
|
20103
20289
|
const MAX_QUEUED_BYTES = 262144;
|
|
20104
20290
|
const PATH_PREFIX = "mobile_log";
|
|
@@ -20208,14 +20394,12 @@ async function installMobileFileLogger(ctx, stateDirectory, options = {}) {
|
|
|
20208
20394
|
}
|
|
20209
20395
|
return file;
|
|
20210
20396
|
}
|
|
20211
|
-
|
|
20212
|
-
//#region src/mobile-push/contracts.ts
|
|
20397
|
+
// src/mobile-push/contracts.ts
|
|
20213
20398
|
const OUTCOME_COALESCE_MS = 1e3;
|
|
20214
20399
|
const ATTENTION_TTL_MS = 3e5;
|
|
20215
20400
|
const OUTCOME_TTL_MS = 18e5;
|
|
20216
20401
|
const ATTEMPT_TIMEOUT_MS = 1e4;
|
|
20217
|
-
|
|
20218
|
-
//#region src/mobile-push/requests.ts
|
|
20402
|
+
// src/mobile-push/requests.ts
|
|
20219
20403
|
var RequestLifetimes = class {
|
|
20220
20404
|
#records = /* @__PURE__ */ new Map();
|
|
20221
20405
|
#byCall = /* @__PURE__ */ new Map();
|
|
@@ -20395,8 +20579,7 @@ var RequestLifetimes = class {
|
|
|
20395
20579
|
}, 250);
|
|
20396
20580
|
}
|
|
20397
20581
|
};
|
|
20398
|
-
|
|
20399
|
-
//#region src/mobile-push/outcomes.ts
|
|
20582
|
+
// src/mobile-push/outcomes.ts
|
|
20400
20583
|
const isCompletedGoalPhase = (phase) => phase === "complete";
|
|
20401
20584
|
var OutcomeLifetimes = class {
|
|
20402
20585
|
#ctx;
|
|
@@ -20576,8 +20759,7 @@ var OutcomeLifetimes = class {
|
|
|
20576
20759
|
return current;
|
|
20577
20760
|
}
|
|
20578
20761
|
};
|
|
20579
|
-
|
|
20580
|
-
//#region src/mobile-push/reconcile.ts
|
|
20762
|
+
// src/mobile-push/reconcile.ts
|
|
20581
20763
|
const TURN_REASONS = [
|
|
20582
20764
|
"completed",
|
|
20583
20765
|
"error",
|
|
@@ -20849,8 +21031,7 @@ var PushReconciler = class {
|
|
|
20849
21031
|
return verifyOutcome(source, values, agent);
|
|
20850
21032
|
}
|
|
20851
21033
|
};
|
|
20852
|
-
|
|
20853
|
-
//#region src/mobile-push/host.ts
|
|
21034
|
+
// src/mobile-push/host.ts
|
|
20854
21035
|
function hasContract(ctx) {
|
|
20855
21036
|
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;
|
|
20856
21037
|
}
|
|
@@ -21061,8 +21242,7 @@ function createPushHost(ctx) {
|
|
|
21061
21242
|
}
|
|
21062
21243
|
};
|
|
21063
21244
|
}
|
|
21064
|
-
|
|
21065
|
-
//#region src/mobile-push/endpoint.ts
|
|
21245
|
+
// src/mobile-push/endpoint.ts
|
|
21066
21246
|
const MAX_ENDPOINT_BYTES = 2048;
|
|
21067
21247
|
const APPLE_SUFFIX = ".push.apple.com";
|
|
21068
21248
|
const BASE64URL = /^[A-Za-z0-9_-]+$/u;
|
|
@@ -21179,8 +21359,7 @@ async function resolveAppleEndpoint(endpoint, resolver = systemResolver) {
|
|
|
21179
21359
|
addresses: Object.freeze([...addresses])
|
|
21180
21360
|
});
|
|
21181
21361
|
}
|
|
21182
|
-
|
|
21183
|
-
//#region src/mobile-push/store.ts
|
|
21362
|
+
// src/mobile-push/store.ts
|
|
21184
21363
|
const MAX_STATE_BYTES = 4194304;
|
|
21185
21364
|
const emptySnapshot = () => ({
|
|
21186
21365
|
version: 1,
|
|
@@ -21443,8 +21622,7 @@ var JsonPushStore = class {
|
|
|
21443
21622
|
}
|
|
21444
21623
|
}
|
|
21445
21624
|
};
|
|
21446
|
-
|
|
21447
|
-
//#region src/mobile-push/registry.ts
|
|
21625
|
+
// src/mobile-push/registry.ts
|
|
21448
21626
|
const empty = () => ({
|
|
21449
21627
|
version: 1,
|
|
21450
21628
|
ownerGenerations: [],
|
|
@@ -21832,8 +22010,7 @@ var PushRegistry = class {
|
|
|
21832
22010
|
};
|
|
21833
22011
|
}
|
|
21834
22012
|
};
|
|
21835
|
-
|
|
21836
|
-
//#region src/mobile-push/payload.ts
|
|
22013
|
+
// src/mobile-push/payload.ts
|
|
21837
22014
|
const messages = {
|
|
21838
22015
|
en: {
|
|
21839
22016
|
question: ["Input requested", "A response needs your attention."],
|
|
@@ -21885,8 +22062,7 @@ function buildDeclarativePayload(category, locale, navigate, tag) {
|
|
|
21885
22062
|
if (Buffer.byteLength(payload, "utf8") > 3072) throw new TypeError("push_payload_too_large");
|
|
21886
22063
|
return payload;
|
|
21887
22064
|
}
|
|
21888
|
-
|
|
21889
|
-
//#region src/mobile-push/retention.ts
|
|
22065
|
+
// src/mobile-push/retention.ts
|
|
21890
22066
|
/** Pure defer-membership retention helpers shared by delivery maintenance. */
|
|
21891
22067
|
/** Removes deferred-recheck entries whose owning pending intent no longer exists; returns how many were removed. */
|
|
21892
22068
|
function pruneOrphanedDeferrals(deferred, pending) {
|
|
@@ -21899,8 +22075,7 @@ function pruneOrphanedDeferrals(deferred, pending) {
|
|
|
21899
22075
|
}
|
|
21900
22076
|
return removed;
|
|
21901
22077
|
}
|
|
21902
|
-
|
|
21903
|
-
//#region src/mobile-push/sender.ts
|
|
22078
|
+
// src/mobile-push/sender.ts
|
|
21904
22079
|
const MAX_RESPONSE_BYTES = 8192;
|
|
21905
22080
|
const MAX_TTL_SECONDS = 1800;
|
|
21906
22081
|
const CONTENT_ENCODING = "aes128gcm";
|
|
@@ -22186,8 +22361,7 @@ var PushSender = class {
|
|
|
22186
22361
|
}
|
|
22187
22362
|
}
|
|
22188
22363
|
};
|
|
22189
|
-
|
|
22190
|
-
//#region src/mobile-push/delivery.ts
|
|
22364
|
+
// src/mobile-push/delivery.ts
|
|
22191
22365
|
const RECONCILE_INTERVAL_MS = 3e4;
|
|
22192
22366
|
const RETRY_BACKOFF_MS = [1e3, 5e3];
|
|
22193
22367
|
const MAX_TEST_QUEUE = 64;
|
|
@@ -22973,8 +23147,7 @@ var PushDelivery = class {
|
|
|
22973
23147
|
}, delay);
|
|
22974
23148
|
}
|
|
22975
23149
|
};
|
|
22976
|
-
|
|
22977
|
-
//#region src/mobile-push/gateway.ts
|
|
23150
|
+
// src/mobile-push/gateway.ts
|
|
22978
23151
|
/** One service shared by every Gateway while each Gateway owns a separate auth/origin attachment. */
|
|
22979
23152
|
var PushGatewayCoordinator = class {
|
|
22980
23153
|
host;
|
|
@@ -23097,8 +23270,7 @@ var PushGatewayCoordinator = class {
|
|
|
23097
23270
|
return true;
|
|
23098
23271
|
}
|
|
23099
23272
|
};
|
|
23100
|
-
|
|
23101
|
-
//#region src/installation-id.ts
|
|
23273
|
+
// src/installation-id.ts
|
|
23102
23274
|
const INSTALLATION_ID_PATTERN = /^[a-f0-9]{64}\n$/u;
|
|
23103
23275
|
const INSTALLATION_ID_BYTES = 65;
|
|
23104
23276
|
async function readInstallationId(file) {
|
|
@@ -23162,8 +23334,7 @@ async function loadOrCreateInstallationId(file) {
|
|
|
23162
23334
|
}
|
|
23163
23335
|
}
|
|
23164
23336
|
}
|
|
23165
|
-
|
|
23166
|
-
//#region src/gateway-identity.ts
|
|
23337
|
+
// src/gateway-identity.ts
|
|
23167
23338
|
/**
|
|
23168
23339
|
* Attach the authenticated installation identity while retaining existing entry identity rules.
|
|
23169
23340
|
* @param config - Validated gateway configuration containing the legacy identity.
|
|
@@ -23183,8 +23354,7 @@ function withInstallationIdentity(config, installationId, explicitIdentity) {
|
|
|
23183
23354
|
legacyInstanceId: config.instanceId
|
|
23184
23355
|
});
|
|
23185
23356
|
}
|
|
23186
|
-
|
|
23187
|
-
//#region src/lan-setup.ts
|
|
23357
|
+
// src/lan-setup.ts
|
|
23188
23358
|
const FIREWALL_TCP_RULE = "DSH Mobile HTTPS";
|
|
23189
23359
|
const FIREWALL_UDP_RULE = "DSH Mobile Discovery";
|
|
23190
23360
|
async function runElevatedPowerShell(script) {
|
|
@@ -23256,8 +23426,7 @@ async function prepareManagedLanSetup(options) {
|
|
|
23256
23426
|
androidCertificate
|
|
23257
23427
|
});
|
|
23258
23428
|
}
|
|
23259
|
-
|
|
23260
|
-
//#region src/plugin.ts
|
|
23429
|
+
// src/plugin.ts
|
|
23261
23430
|
/** Stable Cordis plugin name. */
|
|
23262
23431
|
const name = "dsh-mobile";
|
|
23263
23432
|
/** The stock WebServer serves the control card; Connection authenticates the loopback DSH origin. */
|
|
@@ -23614,6 +23783,9 @@ function locateIrohCompanionRoots(importMetaUrl, environment = process.env) {
|
|
|
23614
23783
|
async function apply(ctx, config) {
|
|
23615
23784
|
const dshVersion = installedDshVersion();
|
|
23616
23785
|
const loaded = await loadSetup(config);
|
|
23786
|
+
/** Rebind overrides the managed setup at runtime; the file on disk is the source of truth on restart. */
|
|
23787
|
+
let managedSetupOverride;
|
|
23788
|
+
let lanRebindRecoveryRequired = false;
|
|
23617
23789
|
const mobileAccess = createMobileAccessService(ctx);
|
|
23618
23790
|
const baseTemplate = loopbackTemplate(loaded, ctx.webServer.port);
|
|
23619
23791
|
const upstreamLoginUrl = upstreamAuthenticatedUrl(ctx, baseTemplate.upstreamOrigin);
|
|
@@ -23910,8 +24082,17 @@ async function apply(ctx, config) {
|
|
|
23910
24082
|
let lanGateway;
|
|
23911
24083
|
let preparedLanSetup;
|
|
23912
24084
|
let trustedNetworksRestartRequired = false;
|
|
23913
|
-
let
|
|
24085
|
+
let lanConfigurationWork = Promise.resolve();
|
|
23914
24086
|
let adminClosing = false;
|
|
24087
|
+
const mutateLan = (operation) => {
|
|
24088
|
+
if (adminClosing) return Promise.reject(new HttpError(503, "unavailable"));
|
|
24089
|
+
const run = lanConfigurationWork.then(() => {
|
|
24090
|
+
if (adminClosing) throw new HttpError(503, "unavailable");
|
|
24091
|
+
return operation();
|
|
24092
|
+
});
|
|
24093
|
+
lanConfigurationWork = run.then(() => {}, () => {});
|
|
24094
|
+
return run;
|
|
24095
|
+
};
|
|
23915
24096
|
const startGateway = async (candidateConfig) => {
|
|
23916
24097
|
const resolved = withInstallationIdentity(parseGatewayConfig({
|
|
23917
24098
|
...candidateConfig,
|
|
@@ -23941,12 +24122,13 @@ async function apply(ctx, config) {
|
|
|
23941
24122
|
if (loaded.kind === "unconfigured") throw new Error(preparedLanSetup === void 0 ? "lan_setup_required" : "lan_setup_restart_required");
|
|
23942
24123
|
if (loaded.kind === "fixed") return startGateway(loaded.config);
|
|
23943
24124
|
const following = new FollowingMobileAccessRuntime(async () => {
|
|
23944
|
-
const
|
|
24125
|
+
const setup = managedSetupOverride ?? loaded.setup;
|
|
24126
|
+
const network = selectLanNetwork(void 0, setup.networkInterface);
|
|
23945
24127
|
return {
|
|
23946
24128
|
key: `${network.name}\0${network.address}\0${network.cidr}`,
|
|
23947
24129
|
start: async () => startGateway({
|
|
23948
24130
|
...loaded.config,
|
|
23949
|
-
...await materializeManagedSetup(
|
|
24131
|
+
...await materializeManagedSetup(setup)
|
|
23950
24132
|
})
|
|
23951
24133
|
};
|
|
23952
24134
|
}, (error) => {
|
|
@@ -24194,6 +24376,8 @@ async function apply(ctx, config) {
|
|
|
24194
24376
|
restartRequired: preparedLanSetup !== void 0
|
|
24195
24377
|
} : {},
|
|
24196
24378
|
running: lanController.isRunning(),
|
|
24379
|
+
rebindSupported: loaded.kind === "managed",
|
|
24380
|
+
...lanRebindRecoveryRequired ? { recoveryRequired: true } : {},
|
|
24197
24381
|
origin: lanGateway?.address().origin,
|
|
24198
24382
|
...preparedLanSetup === void 0 ? {} : { pendingOrigin: preparedLanSetup.origin },
|
|
24199
24383
|
...lanGateway === void 0 ? {} : { extensions: lanGateway.extensionStatus() }
|
|
@@ -24227,7 +24411,18 @@ async function apply(ctx, config) {
|
|
|
24227
24411
|
windowsFirewall: process.platform === "win32"
|
|
24228
24412
|
};
|
|
24229
24413
|
};
|
|
24230
|
-
const diagnosticsPayload = async () => {
|
|
24414
|
+
const diagnosticsPayload = async (request) => {
|
|
24415
|
+
const admission = ctx.connection?.admit?.(request);
|
|
24416
|
+
if (admission !== void 0 && "rejection" in admission) throw new HttpError(admission.rejection, "authentication_failed");
|
|
24417
|
+
const indexCollector = ctx.webServer;
|
|
24418
|
+
let indexInjections = [];
|
|
24419
|
+
let indexInspectionUnavailable = false;
|
|
24420
|
+
if (typeof indexCollector.collectIndexInjections === "function") try {
|
|
24421
|
+
indexInjections = indexCollector.collectIndexInjections(admission?.peer);
|
|
24422
|
+
} catch (inspectionError) {
|
|
24423
|
+
indexInspectionUnavailable = true;
|
|
24424
|
+
}
|
|
24425
|
+
else indexInspectionUnavailable = true;
|
|
24231
24426
|
let interfaceName;
|
|
24232
24427
|
let networkError;
|
|
24233
24428
|
if (loaded.kind === "managed") try {
|
|
@@ -24257,7 +24452,8 @@ async function apply(ctx, config) {
|
|
|
24257
24452
|
} : void 0;
|
|
24258
24453
|
return collectConnectionDiagnostics({
|
|
24259
24454
|
dshVersion,
|
|
24260
|
-
competingRemoteChannelBoot: hasCompetingRemoteChannelBoot(
|
|
24455
|
+
competingRemoteChannelBoot: hasCompetingRemoteChannelBoot(indexInjections),
|
|
24456
|
+
indexInspectionUnavailable,
|
|
24261
24457
|
lan: {
|
|
24262
24458
|
configured: loaded.kind !== "unconfigured" || preparedLanSetup !== void 0,
|
|
24263
24459
|
running: lanController.isRunning(),
|
|
@@ -24328,12 +24524,15 @@ async function apply(ctx, config) {
|
|
|
24328
24524
|
}
|
|
24329
24525
|
if (request.method !== "POST") throw new HttpError(405, "method_not_allowed");
|
|
24330
24526
|
const body = await readJsonObject(request, template.maxBodyBytes);
|
|
24331
|
-
|
|
24332
|
-
|
|
24527
|
+
const keys = Object.keys(body);
|
|
24528
|
+
const configure = keys.every((key) => key === "excludedClientModules" || key === "revision") && Object.prototype.hasOwnProperty.call(body, "excludedClientModules") && (body.revision === void 0 || typeof body.revision === "string");
|
|
24529
|
+
const single = keys.length === 1 && (body.reset === true || body.measure === true);
|
|
24530
|
+
if (!configure && !single) throw new HttpError(400, "bad_request");
|
|
24531
|
+
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);
|
|
24333
24532
|
} catch (error) {
|
|
24334
24533
|
if (!(error instanceof ClientModuleConflictError)) throw error;
|
|
24335
24534
|
sendJson(response, 409, {
|
|
24336
|
-
error:
|
|
24535
|
+
error: error.code,
|
|
24337
24536
|
detail: error.detail
|
|
24338
24537
|
}, false);
|
|
24339
24538
|
} finally {
|
|
@@ -24356,18 +24555,15 @@ async function apply(ctx, config) {
|
|
|
24356
24555
|
} catch {
|
|
24357
24556
|
throw new HttpError(400, "lan_trusted_networks_invalid");
|
|
24358
24557
|
}
|
|
24359
|
-
|
|
24360
|
-
if (adminClosing) throw new HttpError(503, "unavailable");
|
|
24558
|
+
await mutateLan(async () => {
|
|
24361
24559
|
await saveManagedTrustedNetworks(loaded.setupFile, values);
|
|
24362
24560
|
trustedNetworksRestartRequired = true;
|
|
24363
24561
|
});
|
|
24364
|
-
trustedNetworksWrite = write.catch(() => void 0);
|
|
24365
|
-
await write;
|
|
24366
24562
|
sendJson(response, 200, await trustedNetworksPayload(), false);
|
|
24367
24563
|
return;
|
|
24368
24564
|
}
|
|
24369
24565
|
if (request.method === "GET" && target.decodedPathname === `/api/mobile-access/diagnostics`) {
|
|
24370
|
-
sendJson(response, 200, await diagnosticsPayload(), false);
|
|
24566
|
+
sendJson(response, 200, await diagnosticsPayload(request), false);
|
|
24371
24567
|
return;
|
|
24372
24568
|
}
|
|
24373
24569
|
if (request.method === "GET" && target.decodedPathname === `/api/mobile-access/release`) {
|
|
@@ -24391,10 +24587,13 @@ async function apply(ctx, config) {
|
|
|
24391
24587
|
return;
|
|
24392
24588
|
}
|
|
24393
24589
|
if (request.method === "POST" && lanControl) {
|
|
24394
|
-
const
|
|
24395
|
-
if (typeof
|
|
24396
|
-
if (
|
|
24397
|
-
await
|
|
24590
|
+
const running = (await readJsonObject(request, 4096)).running;
|
|
24591
|
+
if (typeof running !== "boolean") throw new HttpError(400, "bad_request");
|
|
24592
|
+
if (running && loaded.kind === "unconfigured") throw new HttpError(409, preparedLanSetup === void 0 ? "lan_setup_required" : "lan_setup_restart_required");
|
|
24593
|
+
await mutateLan(async () => {
|
|
24594
|
+
if (running && lanRebindRecoveryRequired) throw new HttpError(409, "lan_rebind_recovery_required");
|
|
24595
|
+
await lanController.setRunning(running);
|
|
24596
|
+
});
|
|
24398
24597
|
sendJson(response, 200, lanPayload(), false);
|
|
24399
24598
|
return;
|
|
24400
24599
|
}
|
|
@@ -24424,6 +24623,74 @@ async function apply(ctx, config) {
|
|
|
24424
24623
|
sendJson(response, 200, await lanSetupPayload(), false);
|
|
24425
24624
|
return;
|
|
24426
24625
|
}
|
|
24626
|
+
if (request.method === "GET" && target.decodedPathname === `/api/mobile-access/lan/networks`) {
|
|
24627
|
+
await lanConfigurationWork;
|
|
24628
|
+
const networks = availableLanNetworks();
|
|
24629
|
+
const preferred = await preferredLanInterfaceNames();
|
|
24630
|
+
let recommendedAddress;
|
|
24631
|
+
try {
|
|
24632
|
+
recommendedAddress = selectLanNetwork(void 0, void 0, void 0, preferred).address;
|
|
24633
|
+
} catch {}
|
|
24634
|
+
const currentInterface = loaded.kind === "managed" ? (managedSetupOverride ?? loaded.setup).networkInterface : void 0;
|
|
24635
|
+
sendJson(response, 200, {
|
|
24636
|
+
supported: loaded.kind === "managed",
|
|
24637
|
+
currentInterface,
|
|
24638
|
+
listenPort: loaded.kind === "managed" ? loaded.setup.listenPort : 3443,
|
|
24639
|
+
platform: process.platform,
|
|
24640
|
+
networks: networks.map((network) => ({
|
|
24641
|
+
name: network.name,
|
|
24642
|
+
address: network.address,
|
|
24643
|
+
cidr: network.cidr,
|
|
24644
|
+
current: network.name === currentInterface,
|
|
24645
|
+
recommended: network.address === recommendedAddress
|
|
24646
|
+
}))
|
|
24647
|
+
}, false);
|
|
24648
|
+
return;
|
|
24649
|
+
}
|
|
24650
|
+
if (request.method === "POST" && target.decodedPathname === `/api/mobile-access/lan/rebind`) {
|
|
24651
|
+
if (loaded.kind !== "managed") throw new HttpError(409, "lan_rebind_unsupported");
|
|
24652
|
+
const body = await readJsonObject(request, 4096);
|
|
24653
|
+
const networkInterface = typeof body.networkInterface === "string" ? body.networkInterface : "";
|
|
24654
|
+
if (networkInterface === "" || Object.keys(body).some((key) => key !== "networkInterface")) throw new HttpError(400, "bad_request");
|
|
24655
|
+
sendJson(response, 200, await mutateLan(async () => {
|
|
24656
|
+
let network;
|
|
24657
|
+
try {
|
|
24658
|
+
network = selectLanNetwork(void 0, networkInterface);
|
|
24659
|
+
} catch {
|
|
24660
|
+
throw new HttpError(409, "lan_rebind_network_unavailable");
|
|
24661
|
+
}
|
|
24662
|
+
const current = managedSetupOverride ?? loaded.setup;
|
|
24663
|
+
if (!lanRebindRecoveryRequired && current.networkInterface === networkInterface) throw new HttpError(409, "lan_rebind_unchanged");
|
|
24664
|
+
const previousOrigin = lanGateway?.address().origin;
|
|
24665
|
+
try {
|
|
24666
|
+
const result = await rebindManagedLan({
|
|
24667
|
+
setupFile: loaded.setupFile,
|
|
24668
|
+
controller: lanController,
|
|
24669
|
+
network,
|
|
24670
|
+
publish: (setup) => {
|
|
24671
|
+
managedSetupOverride = setup;
|
|
24672
|
+
}
|
|
24673
|
+
});
|
|
24674
|
+
trustedNetworksRestartRequired = false;
|
|
24675
|
+
lanRebindRecoveryRequired = false;
|
|
24676
|
+
return {
|
|
24677
|
+
origin: lanGateway?.address().origin ?? `https://${network.address}:${String(result.setup.listenPort)}`,
|
|
24678
|
+
previousOrigin,
|
|
24679
|
+
restartRequired: false,
|
|
24680
|
+
disconnected: result.running,
|
|
24681
|
+
ready: lanGateway !== void 0
|
|
24682
|
+
};
|
|
24683
|
+
} catch (error) {
|
|
24684
|
+
if (error instanceof ManagedLanConfigurationError) throw new HttpError(409, "lan_rebind_configuration_invalid");
|
|
24685
|
+
if (!(error instanceof MobileAccessReconfigurationError)) throw new HttpError(503, "lan_rebind_unavailable");
|
|
24686
|
+
const recovered = error.recovered;
|
|
24687
|
+
if (!recovered) lanRebindRecoveryRequired = true;
|
|
24688
|
+
logger.error("managed LAN rebind failed; prior configuration restored: %s", String(recovered));
|
|
24689
|
+
throw new HttpError(500, recovered ? "lan_rebind_failed" : "lan_rebind_recovery_required");
|
|
24690
|
+
}
|
|
24691
|
+
}), false);
|
|
24692
|
+
return;
|
|
24693
|
+
}
|
|
24427
24694
|
if (request.method === "GET" && target.decodedPathname === `/api/mobile-access/remote/control`) {
|
|
24428
24695
|
sendJson(response, 200, remotePayload(), false);
|
|
24429
24696
|
return;
|
|
@@ -24942,7 +25209,7 @@ async function apply(ctx, config) {
|
|
|
24942
25209
|
stopAdmin,
|
|
24943
25210
|
drainRemoteWork,
|
|
24944
25211
|
() => releaseManager.dispose(),
|
|
24945
|
-
() =>
|
|
25212
|
+
() => lanConfigurationWork,
|
|
24946
25213
|
disposeMobileCommand,
|
|
24947
25214
|
disposeTaskEvents,
|
|
24948
25215
|
async () => {
|
|
@@ -24965,7 +25232,7 @@ async function apply(ctx, config) {
|
|
|
24965
25232
|
stopAdmin,
|
|
24966
25233
|
drainRemoteWork,
|
|
24967
25234
|
() => releaseManager.dispose(),
|
|
24968
|
-
() =>
|
|
25235
|
+
() => lanConfigurationWork,
|
|
24969
25236
|
disposeMobileCommand,
|
|
24970
25237
|
disposeTaskEvents,
|
|
24971
25238
|
async () => {
|
|
@@ -24981,7 +25248,6 @@ async function apply(ctx, config) {
|
|
|
24981
25248
|
};
|
|
24982
25249
|
}, "dsh-mobile: independent LAN and selectable remote providers with /mobile command");
|
|
24983
25250
|
}
|
|
24984
|
-
//#endregion
|
|
24985
25251
|
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 };
|
|
24986
25252
|
|
|
24987
25253
|
//# sourceMappingURL=index.mjs.map
|