dsh-mobile 0.4.5 → 0.4.6
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 +11 -0
- package/CONTRIBUTORS.md +5 -3
- package/README.en.md +23 -11
- package/README.md +23 -11
- package/SECURITY.md +7 -5
- package/docs/ATTACH_EXISTING_FRPS.en.md +44 -0
- package/docs/ATTACH_EXISTING_FRPS.md +98 -0
- package/docs/README.md +3 -0
- package/docs/SELF_HOSTED_FRP.en.md +3 -1
- package/docs/SELF_HOSTED_FRP.md +7 -1
- package/lib/cli.js +52 -15
- package/lib/cli.js.map +1 -1
- package/lib/client.js +1235 -155
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +384 -10
- package/lib/index.mjs +2204 -637
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-compat.js.map +1 -1
- package/package.json +18 -14
package/lib/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["parseIpv4","parsePublicOrigin","execFile","regularFile","createHttpsServer","createHttpServer","requestHttp","request","FRP_VERSION","releases","inside","regularFile","sha256","defaultExtractArtifact","defaultFetchArtifact","MAX_SETTINGS_BYTES","hostname","atomicPrivateWrite","START_TIMEOUT_MS","publicStatus","DEFAULT_VHOST_HTTP_PORT","RESERVED_LAN_GATEWAY_PORT","atomicPrivateWrite","releases","DOWNLOAD_PAGE","TERMS_URL","inside","sha256","regularFile","defaultFetchArtifact","MAX_LOG_BUFFER_BYTES","START_TIMEOUT_MS","publicStatus","reserveLoopbackPort","withoutProxyEnvironment","hostname","execFile","publicStatus","execFile","execFile"],"sources":["../src/access.ts","../src/network.ts","../src/config.ts","../src/exec-file.ts","../src/private-file.ts","../src/control.ts","../src/local-admin-host.ts","../src/http-security.ts","../src/mobile-compat-bootstrap.ts","../src/version.ts","../src/computer-images.ts","../src/extensions.ts","../src/auth-pages.ts","../src/gateway.ts","../src/storage.ts","../src/frp-component.ts","../src/websocket-paths.ts","../src/frp-template.ts","../src/frp-config.ts","../src/remote.ts","../src/frp.ts","../src/origin-proxy-config.ts","../src/origin-proxy.ts","../src/component-download.ts","../src/cloudflared-component.ts","../src/cloudflared.ts","../src/cloudflared-tunnel.ts","../src/task-events.ts","../src/diagnostics.ts","../src/mobile-guide.ts","../src/vps-deploy.ts","../src/funnel.ts","../src/cpolar.ts","../src/cpolar-component.ts","../src/release-update.ts","../src/file-logger.ts","../src/managed-setup.ts","../src/lan-setup.ts","../src/plugin.ts"],"sourcesContent":["import { createHash, randomBytes, timingSafeEqual } from 'node:crypto'\nimport type { DeviceSnapshot, DeviceStore, StoredDevice } from './storage.js'\n\n/** Stable error categories converted to deliberately terse HTTP responses. */\nexport class AccessError extends Error {\n constructor(readonly status: number, readonly code: string) {\n super(code)\n this.name = 'AccessError'\n }\n}\n\n/** Resource and lifetime controls for device authentication. */\nexport interface AccessControllerOptions {\n readonly pairingTtlMs: number\n readonly deviceTtlMs: number\n readonly sessionTtlMs: number\n readonly maxDevices: number\n readonly maxSessions: number\n readonly rateLimitWindowMs: number\n readonly maxPairingAttempts: number\n readonly maxRateLimitKeys: number\n readonly now?: () => number\n}\n\n/** Values issued once after pairing; only digests survive the response. */\nexport interface PairingResult {\n readonly deviceId: string\n readonly deviceToken: string\n readonly deviceExpiresAt: number\n readonly sessionToken: string\n readonly csrfToken: string\n readonly sessionExpiresAt: number\n}\n\n/** Values issued after renewal with the persistent HttpOnly device Cookie. */\nexport interface RenewalResult {\n readonly deviceId: string\n readonly sessionToken: string\n readonly csrfToken: string\n readonly sessionExpiresAt: number\n}\n\n/** Authenticated Session identity retained only inside the gateway. */\nexport interface SessionAuthorization {\n readonly sessionKey: string\n readonly deviceId: string\n readonly expiresAt: number\n}\n\n/** Result of a persistent-device reachability check without opening a Session. */\nexport interface DeviceProbeResult {\n readonly deviceId: string\n readonly deviceExpiresAt: number\n}\n\n/** Why a short-lived Session ended; only device revocation is sent to clients. */\nexport type SessionEndReason = 'logout' | 'expired' | 'evicted' | 'revoked'\n\n/** Safe device metadata returned by the loopback administration API. */\nexport interface DeviceSummary {\n readonly id: string\n readonly label: string\n readonly createdAt: number\n readonly expiresAt: number\n readonly lastSeenAt: number\n readonly revokedAt?: number\n}\n\ninterface PairingWindow {\n readonly digest: Buffer\n readonly expiresAt: number\n}\n\ninterface SessionRecord {\n readonly key: string\n readonly deviceId: string\n readonly csrfDigest: Buffer\n readonly createdAt: number\n readonly expiresAt: number\n}\n\ninterface LimitBucket {\n count: number\n resetAt: number\n}\n\n/** Fixed-window limiter whose attacker-controlled key table is itself bounded. */\nexport class BoundedRateLimiter {\n private readonly buckets = new Map<string, LimitBucket>()\n\n constructor(\n private readonly limit: number,\n private readonly windowMs: number,\n private readonly maximumKeys: number,\n ) {}\n\n /** Consume one attempt; unknown keys fail closed when the bounded table is full. */\n take(key: string, now: number): boolean {\n for (const [candidate, bucket] of this.buckets) {\n if (bucket.resetAt <= now) this.buckets.delete(candidate)\n }\n const current = this.buckets.get(key)\n if (current === undefined) {\n if (this.buckets.size >= this.maximumKeys) return false\n this.buckets.set(key, { count: 1, resetAt: now + this.windowMs })\n return true\n }\n if (current.count >= this.limit) return false\n current.count += 1\n return true\n }\n\n /** Current table size, exposed for bounded-state assertions. */\n get size(): number {\n return this.buckets.size\n }\n}\n\nfunction opaqueToken(): string {\n return randomBytes(32).toString('base64url')\n}\n\nfunction digest(value: string): Buffer {\n return createHash('sha256').update(value, 'utf8').digest()\n}\n\nfunction digestHex(value: string): string {\n return digest(value).toString('hex')\n}\n\nfunction matchesDigest(value: string, expected: Buffer): boolean {\n return timingSafeEqual(digest(value), expected)\n}\n\nfunction normalizeLabel(value: string | undefined): string {\n const label = (value ?? 'Mobile device').normalize('NFC').trim()\n if (label.length < 1 || label.length > 64 || /[\\u0000-\\u001f\\u007f]/u.test(label)) {\n throw new AccessError(400, 'invalid_request')\n }\n return label\n}\n\nfunction publicDevice(device: StoredDevice): DeviceSummary {\n return Object.freeze({\n id: device.id,\n label: device.label,\n createdAt: device.createdAt,\n expiresAt: device.expiresAt,\n lastSeenAt: device.lastSeenAt,\n ...(device.revokedAt === undefined ? {} : { revokedAt: device.revokedAt }),\n })\n}\n\n/** Pairing, persistent-device, short-Session, revocation, and CSRF state machine. */\nexport class AccessController {\n private readonly now: () => number\n private readonly pairLimiter: BoundedRateLimiter\n private devices: StoredDevice[] = []\n private pairingWindow: PairingWindow | undefined\n private readonly sessions = new Map<string, SessionRecord>()\n private readonly sessionEndedListeners = new Set<(authorization: SessionAuthorization, reason: SessionEndReason) => void>()\n private mutation: Promise<void> = Promise.resolve()\n private initialized = false\n private closing = false\n private closeTask: Promise<void> | undefined\n\n constructor(private readonly store: DeviceStore, private readonly options: AccessControllerOptions) {\n this.now = options.now ?? Date.now\n this.pairLimiter = new BoundedRateLimiter(\n options.maxPairingAttempts,\n options.rateLimitWindowMs,\n options.maxRateLimitKeys,\n )\n }\n\n /** Load and validate digest-only durable state before accepting traffic. */\n async initialize(): Promise<void> {\n if (this.initialized || this.closing) throw new Error('access controller cannot be initialized again')\n const snapshot = await this.store.load()\n // Older releases retained revoked identities. Remove those tombstones on upgrade.\n const devices = snapshot.devices.filter(device => device.revokedAt === undefined)\n if (devices.length > this.options.maxDevices) throw new Error('device state exceeds configured maxDevices')\n if (devices.length !== snapshot.devices.length) await this.store.save(this.snapshot(devices))\n this.devices = devices\n this.initialized = true\n }\n\n private requireInitialized(): void {\n if (!this.initialized || this.closing) throw new Error('access controller is not available')\n }\n\n private async exclusive<T>(operation: () => Promise<T>): Promise<T> {\n const prior = this.mutation\n let release!: () => void\n this.mutation = new Promise<void>(resolve => { release = resolve })\n await prior\n try {\n return await operation()\n } finally {\n release()\n }\n }\n\n private snapshot(devices: readonly StoredDevice[]): DeviceSnapshot {\n return Object.freeze({ version: 1, devices: Object.freeze([...devices]) })\n }\n\n private emitSessionEnded(session: SessionRecord, reason: SessionEndReason): void {\n const authorization = Object.freeze({\n sessionKey: session.key,\n deviceId: session.deviceId,\n expiresAt: session.expiresAt,\n })\n for (const listener of this.sessionEndedListeners) listener(authorization, reason)\n }\n\n private removeSession(key: string, reason: SessionEndReason): void {\n const session = this.sessions.get(key)\n if (session === undefined) return\n this.sessions.delete(key)\n this.emitSessionEnded(session, reason)\n }\n\n private pruneSessions(now: number): void {\n for (const [key, session] of this.sessions) {\n if (session.expiresAt <= now) this.removeSession(key, 'expired')\n }\n }\n\n private createSession(deviceId: string, now: number, deviceExpiresAt: number): RenewalResult {\n this.pruneSessions(now)\n if (this.sessions.size >= this.options.maxSessions) {\n const oldest = [...this.sessions.values()].sort((left, right) => left.createdAt - right.createdAt)[0]\n if (oldest !== undefined) this.removeSession(oldest.key, 'evicted')\n }\n const sessionToken = opaqueToken()\n const csrfToken = opaqueToken()\n const key = digestHex(sessionToken)\n const record: SessionRecord = Object.freeze({\n key,\n deviceId,\n csrfDigest: digest(csrfToken),\n createdAt: now,\n expiresAt: Math.min(now + this.options.sessionTtlMs, deviceExpiresAt),\n })\n this.sessions.set(key, record)\n return Object.freeze({ deviceId, sessionToken, csrfToken, sessionExpiresAt: record.expiresAt })\n }\n\n /** Open one short pairing window and return its one-time secret to a loopback caller only. */\n async openPairing(requestedTtlMs?: number): Promise<{ token: string; expiresAt: number }> {\n this.requireInitialized()\n return this.exclusive(async () => {\n const ttl = requestedTtlMs ?? this.options.pairingTtlMs\n if (!Number.isSafeInteger(ttl) || ttl < 10_000 || ttl > this.options.pairingTtlMs) {\n throw new AccessError(400, 'invalid_request')\n }\n const token = opaqueToken()\n const expiresAt = this.now() + ttl\n this.pairingWindow = Object.freeze({ digest: digest(token), expiresAt })\n return Object.freeze({ token, expiresAt })\n })\n }\n\n /** Consume the pairing window exactly once and persist only the device-token digest. */\n async pair(sourceKey: string, token: string, label?: string): Promise<PairingResult> {\n this.requireInitialized()\n const now = this.now()\n if (!this.pairLimiter.take(sourceKey, now)) throw new AccessError(429, 'rate_limited')\n if (token.length > 512) throw new AccessError(401, 'authentication_failed')\n return this.exclusive(async () => {\n const window = this.pairingWindow\n if (window === undefined || window.expiresAt <= now || !matchesDigest(token, window.digest)) {\n if (window !== undefined && window.expiresAt <= now) this.pairingWindow = undefined\n throw new AccessError(401, 'authentication_failed')\n }\n this.pairingWindow = undefined\n const active = this.devices.filter(device => device.revokedAt === undefined && device.expiresAt > now)\n if (active.length >= this.options.maxDevices) throw new AccessError(409, 'device_limit')\n\n const deviceToken = opaqueToken()\n const device: StoredDevice = Object.freeze({\n id: randomBytes(16).toString('hex'),\n label: normalizeLabel(label),\n tokenDigest: digestHex(deviceToken),\n createdAt: now,\n expiresAt: now + this.options.deviceTtlMs,\n lastSeenAt: now,\n })\n const retained = this.devices.filter(candidate => candidate.revokedAt === undefined && candidate.expiresAt > now)\n const next = [...retained, device]\n await this.store.save(this.snapshot(next))\n this.devices = next\n const session = this.createSession(device.id, now, device.expiresAt)\n return Object.freeze({\n ...session,\n deviceToken,\n deviceExpiresAt: device.expiresAt,\n })\n })\n }\n\n /** Exchange a valid persistent device credential for a new short Session. */\n async renew(deviceToken: string): Promise<RenewalResult> {\n this.requireInitialized()\n if (deviceToken.length > 512) throw new AccessError(401, 'authentication_failed')\n return this.exclusive(async () => {\n const now = this.now()\n const tokenDigest = digest(deviceToken)\n const index = this.devices.findIndex(device => timingSafeEqual(Buffer.from(device.tokenDigest, 'hex'), tokenDigest))\n const device = this.devices[index]\n if (device === undefined) {\n throw new AccessError(401, 'authentication_failed')\n }\n if (device.revokedAt !== undefined) throw new AccessError(401, 'device_revoked')\n if (device.expiresAt <= now) throw new AccessError(401, 'device_expired')\n const updated: StoredDevice = Object.freeze({ ...device, lastSeenAt: now })\n const next = [...this.devices]\n next[index] = updated\n await this.store.save(this.snapshot(next))\n this.devices = next\n return this.createSession(device.id, now, device.expiresAt)\n })\n }\n\n /** Validate a persistent device credential without consuming a Session slot. */\n async probe(deviceToken: string): Promise<DeviceProbeResult> {\n this.requireInitialized()\n if (deviceToken.length > 512) throw new AccessError(401, 'authentication_failed')\n return this.exclusive(async () => {\n const now = this.now()\n const tokenDigest = digest(deviceToken)\n const index = this.devices.findIndex(device => timingSafeEqual(Buffer.from(device.tokenDigest, 'hex'), tokenDigest))\n const device = this.devices[index]\n if (device === undefined) throw new AccessError(401, 'authentication_failed')\n if (device.revokedAt !== undefined) throw new AccessError(401, 'device_revoked')\n if (device.expiresAt <= now) throw new AccessError(401, 'device_expired')\n const updated: StoredDevice = Object.freeze({ ...device, lastSeenAt: now })\n const next = [...this.devices]\n next[index] = updated\n await this.store.save(this.snapshot(next))\n this.devices = next\n return Object.freeze({ deviceId: device.id, deviceExpiresAt: device.expiresAt })\n })\n }\n\n /** Resolve a short Session Cookie without revealing whether device or Session failed. */\n authorizeSession(sessionToken: string): SessionAuthorization {\n this.requireInitialized()\n if (sessionToken.length > 512) throw new AccessError(401, 'authentication_failed')\n const now = this.now()\n this.pruneSessions(now)\n const key = digestHex(sessionToken)\n const session = this.sessions.get(key)\n const device = session === undefined ? undefined : this.devices.find(candidate => candidate.id === session.deviceId)\n if (session === undefined || device === undefined || device.revokedAt !== undefined || device.expiresAt <= now) {\n if (session !== undefined) this.removeSession(session.key, 'expired')\n throw new AccessError(401, 'authentication_failed')\n }\n return Object.freeze({ sessionKey: key, deviceId: session.deviceId, expiresAt: session.expiresAt })\n }\n\n /** Require the Session-bound anti-CSRF value for an authenticated mutation. */\n assertCsrf(authorization: SessionAuthorization, csrfToken: string | undefined): void {\n const session = this.sessions.get(authorization.sessionKey)\n if (session === undefined || csrfToken === undefined || csrfToken.length > 512\n || !matchesDigest(csrfToken, session.csrfDigest)) {\n throw new AccessError(403, 'forbidden')\n }\n }\n\n /** End one short Session and notify the gateway to abort its attached work. */\n logout(authorization: SessionAuthorization): void {\n this.removeSession(authorization.sessionKey, 'logout')\n }\n\n /** Durably delete the device, then end every Session owned by that device. */\n async revokeDevice(deviceId: string): Promise<boolean> {\n this.requireInitialized()\n return this.exclusive(async () => {\n const next = this.devices.filter(device => device.id !== deviceId)\n if (next.length === this.devices.length) return false\n await this.store.save(this.snapshot(next))\n this.devices = next\n for (const [key, session] of this.sessions) {\n if (session.deviceId === deviceId) this.removeSession(key, 'revoked')\n }\n return true\n })\n }\n\n /** Remove every persistent credential and terminate every active Session. */\n async resetDevices(): Promise<void> {\n this.requireInitialized()\n await this.exclusive(async () => {\n await this.store.save(this.snapshot([]))\n this.devices = []\n for (const key of [...this.sessions.keys()]) this.removeSession(key, 'revoked')\n this.pairingWindow = undefined\n })\n }\n\n /** Safe metadata for the loopback administration surface. */\n listDevices(): readonly DeviceSummary[] {\n this.requireInitialized()\n return Object.freeze(this.devices.map(publicDevice))\n }\n\n /** Pairing status without exposing the one-time secret. */\n pairingStatus(): { open: boolean; expiresAt?: number } {\n this.requireInitialized()\n const window = this.pairingWindow\n if (window === undefined || window.expiresAt <= this.now()) {\n this.pairingWindow = undefined\n return Object.freeze({ open: false })\n }\n return Object.freeze({ open: true, expiresAt: window.expiresAt })\n }\n\n /** Subscribe gateway resources to Session logout, expiry, eviction, and device revocation. */\n onSessionEnded(listener: (authorization: SessionAuthorization, reason: SessionEndReason) => void): () => void {\n this.sessionEndedListeners.add(listener)\n return () => { this.sessionEndedListeners.delete(listener) }\n }\n\n /** Stop new operations, drain durable mutations, then clear volatile credentials. */\n close(): Promise<void> {\n if (this.closeTask !== undefined) return this.closeTask\n this.closing = true\n this.closeTask = this.finishClose()\n return this.closeTask\n }\n\n private async finishClose(): Promise<void> {\n await this.mutation\n this.pairingWindow = undefined\n for (const key of [...this.sessions.keys()]) this.removeSession(key, 'expired')\n this.sessionEndedListeners.clear()\n this.initialized = false\n }\n\n /** Bounded volatile-state metrics for tests and local status. */\n metrics(): { sessions: number; rateLimitKeys: number } {\n return Object.freeze({ sessions: this.sessions.size, rateLimitKeys: this.pairLimiter.size })\n }\n}\n","import { isIP } from 'node:net'\n\n/** A parsed IP network used to authorize directly connected clients. */\nexport interface ParsedCidr {\n readonly bits: 32 | 128\n readonly network: bigint\n readonly prefix: number\n readonly source: string\n}\n\n/** A normalized public authority. A missing port is filled from the bound listener. */\nexport interface AuthoritySpec {\n readonly hostname: string\n readonly port?: number\n}\n\nfunction parseIpv4(address: string): bigint {\n const parts = address.split('.')\n if (parts.length !== 4) throw new Error(`invalid IPv4 address ${JSON.stringify(address)}`)\n let value = 0n\n for (const part of parts) {\n if (!/^\\d{1,3}$/u.test(part)) throw new Error(`invalid IPv4 address ${JSON.stringify(address)}`)\n const octet = Number(part)\n if (octet > 255) throw new Error(`invalid IPv4 address ${JSON.stringify(address)}`)\n value = (value << 8n) | BigInt(octet)\n }\n return value\n}\n\nfunction parseIpv6Part(part: string, address: string): number[] {\n if (part.includes('.')) {\n const ipv4 = parseIpv4(part)\n return [Number((ipv4 >> 16n) & 0xffffn), Number(ipv4 & 0xffffn)]\n }\n if (!/^[\\da-f]{1,4}$/iu.test(part)) throw new Error(`invalid IPv6 address ${JSON.stringify(address)}`)\n return [Number.parseInt(part, 16)]\n}\n\nfunction parseIpv6(address: string): bigint {\n const withoutZone = address.split('%', 1)[0] ?? address\n if (withoutZone.split('::').length > 2) throw new Error(`invalid IPv6 address ${JSON.stringify(address)}`)\n const [leftText, rightText] = withoutZone.split('::')\n const left = leftText === '' ? [] : leftText!.split(':').flatMap(part => parseIpv6Part(part, address))\n const right = rightText === undefined || rightText === ''\n ? []\n : rightText.split(':').flatMap(part => parseIpv6Part(part, address))\n const omitted = 8 - left.length - right.length\n if (rightText === undefined ? omitted !== 0 : omitted < 1) {\n throw new Error(`invalid IPv6 address ${JSON.stringify(address)}`)\n }\n const groups = [...left, ...Array.from({ length: omitted }, () => 0), ...right]\n if (groups.length !== 8) throw new Error(`invalid IPv6 address ${JSON.stringify(address)}`)\n return groups.reduce((value, group) => (value << 16n) | BigInt(group), 0n)\n}\n\nfunction mappedIpv4(address: string): string | undefined {\n const match = /^::ffff:(\\d{1,3}(?:\\.\\d{1,3}){3})$/iu.exec(address)\n return match?.[1]\n}\n\nfunction parseIp(address: string): { bits: 32 | 128; value: bigint } {\n const unwrapped = address.startsWith('[') && address.endsWith(']') ? address.slice(1, -1) : address\n const mapped = mappedIpv4(unwrapped)\n if (mapped !== undefined) return { bits: 32, value: parseIpv4(mapped) }\n const version = isIP(unwrapped.split('%', 1)[0] ?? unwrapped)\n if (version === 4) return { bits: 32, value: parseIpv4(unwrapped) }\n if (version === 6) return { bits: 128, value: parseIpv6(unwrapped) }\n throw new Error(`invalid IP address ${JSON.stringify(address)}`)\n}\n\n/** Parse and canonicalize one IPv4 or IPv6 CIDR. */\nexport function parseCidr(source: string): ParsedCidr {\n const slash = source.lastIndexOf('/')\n if (slash <= 0 || slash === source.length - 1) throw new Error(`invalid CIDR ${JSON.stringify(source)}`)\n const address = source.slice(0, slash)\n const parsed = parseIp(address)\n const prefixText = source.slice(slash + 1)\n if (!/^\\d{1,3}$/u.test(prefixText)) throw new Error(`invalid CIDR ${JSON.stringify(source)}`)\n const prefix = Number(prefixText)\n if (prefix > parsed.bits) throw new Error(`invalid CIDR ${JSON.stringify(source)}`)\n const hostBits = BigInt(parsed.bits - prefix)\n const mask = hostBits === BigInt(parsed.bits)\n ? 0n\n : ((1n << BigInt(parsed.bits)) - 1n) ^ ((1n << hostBits) - 1n)\n const network = parsed.value & mask\n if (network !== parsed.value) {\n throw new Error(`CIDR ${JSON.stringify(source)} has host bits set`)\n }\n return Object.freeze({ bits: parsed.bits, network, prefix, source })\n}\n\n/** Whether a directly connected socket address belongs to at least one allowed CIDR. */\nexport function addressAllowed(address: string | undefined, cidrs: readonly ParsedCidr[]): boolean {\n if (address === undefined) return false\n let parsed: ReturnType<typeof parseIp>\n try {\n parsed = parseIp(address)\n } catch {\n return false\n }\n return cidrs.some((cidr) => {\n if (cidr.bits !== parsed.bits) return false\n const hostBits = BigInt(cidr.bits - cidr.prefix)\n const mask = hostBits === BigInt(cidr.bits)\n ? 0n\n : ((1n << BigInt(cidr.bits)) - 1n) ^ ((1n << hostBits) - 1n)\n return (parsed.value & mask) === cidr.network\n })\n}\n\n/** Whether an IP literal is loopback and therefore eligible for HTTP-only development. */\nexport function isLoopbackAddress(address: string): boolean {\n try {\n const parsed = parseIp(address)\n if (parsed.bits === 32) return (parsed.value >> 24n) === 127n\n return parsed.value === 1n\n } catch {\n return false\n }\n}\n\n/**\n * IPv4 ranges that are never a public VPS endpoint (IANA special-purpose,\n * private, shared, loopback, link-local, documentation, benchmark, multicast,\n * and reserved space). Kept in sync with Android `RemoteHostPolicy`.\n */\nconst NON_ROUTABLE_IPV4_RANGES: ReadonlyArray<readonly [network: bigint, prefix: number]> = Object.freeze([\n [0x00000000n, 8], // \"This network\" / software scope\n [0x0a000000n, 8], // Private-Use (RFC 1918)\n [0x64400000n, 10], // Shared address space / CGNAT (RFC 6598)\n [0x7f000000n, 8], // Loopback (RFC 1122)\n [0xa9fe0000n, 16], // Link-local (RFC 3927)\n [0xac100000n, 12], // Private-Use (RFC 1918)\n [0xc0000000n, 24], // IETF protocol assignments (RFC 6890)\n [0xc0000200n, 24], // Documentation TEST-NET-1 (RFC 5737)\n [0xc01fc400n, 24], // AS112-v4 (RFC 7534)\n [0xc034c100n, 24], // AMT relay (RFC 7450)\n [0xc0586300n, 24], // 6to4 relay anycast, deprecated (RFC 7526)\n [0xc0a80000n, 16], // Private-Use (RFC 1918)\n [0xc0af3000n, 24], // Direct delegation AS112 (RFC 7535)\n [0xc6120000n, 15], // Benchmarking (RFC 2544)\n [0xc6336400n, 24], // Documentation TEST-NET-2 (RFC 5737)\n [0xcb007100n, 24], // Documentation TEST-NET-3 (RFC 5737)\n [0xe0000000n, 4], // Multicast (RFC 1112, incl. MCAST-TEST-NET)\n [0xf0000000n, 4], // Reserved for future use + broadcast (RFC 1112)\n])\n\nfunction parseStrictIpv4Octets(address: string): readonly [number, number, number, number] | undefined {\n const parts = address.split('.')\n if (parts.length !== 4) return undefined\n const octets: number[] = []\n for (const part of parts) {\n if (!/^(?:0|[1-9][0-9]{0,2})$/u.test(part)) return undefined\n const octet = Number(part)\n if (octet > 255) return undefined\n octets.push(octet)\n }\n return octets as [number, number, number, number]\n}\n\n/**\n * Whether a dotted-quad IPv4 literal is globally routable and therefore usable\n * as a public VPS / remote HTTPS endpoint. Rejects documentation addresses such\n * as 203.0.113.10 alongside private, shared, and reserved space.\n */\nexport function isGloballyRoutableIpv4(address: string): boolean {\n const octets = parseStrictIpv4Octets(address)\n if (octets === undefined) return false\n const value = (BigInt(octets[0]) << 24n) | (BigInt(octets[1]) << 16n) | (BigInt(octets[2]) << 8n) | BigInt(octets[3])\n return !NON_ROUTABLE_IPV4_RANGES.some(([network, prefix]) => {\n if (prefix === 0) return true\n const hostBits = BigInt(32 - prefix)\n const mask = ((1n << 32n) - 1n) ^ ((1n << hostBits) - 1n)\n return (value & mask) === network\n })\n}\n\n/** Parse a bare host or host:port authority without accepting URL components. */\nexport function parseAuthority(source: string): AuthoritySpec {\n if (source.trim() !== source || source.length === 0 || /[/?#@\\\\]/u.test(source)) {\n throw new Error(`invalid public authority ${JSON.stringify(source)}`)\n }\n let url: URL\n try {\n url = new URL(`https://${source}`)\n } catch {\n throw new Error(`invalid public authority ${JSON.stringify(source)}`)\n }\n if (url.username !== '' || url.password !== '' || url.pathname !== '/' || url.search !== '' || url.hash !== '') {\n throw new Error(`invalid public authority ${JSON.stringify(source)}`)\n }\n const explicitPort = /\\]:\\d+$/u.test(source) || (!source.startsWith('[') && /:\\d+$/u.test(source))\n const hostname = url.hostname.toLowerCase()\n const port = explicitPort ? Number(url.port === '' ? 443 : url.port) : undefined\n if (port !== undefined && (!Number.isInteger(port) || port < 1 || port > 65535)) {\n throw new Error(`invalid public authority ${JSON.stringify(source)}`)\n }\n return port === undefined ? Object.freeze({ hostname }) : Object.freeze({ hostname, port })\n}\n\nfunction formatHostname(hostname: string): string {\n return hostname.includes(':') && !hostname.startsWith('[') ? `[${hostname}]` : hostname\n}\n\n/** Resolve an authority against the actual listener port. */\nexport function resolveAuthority(spec: AuthoritySpec, listenerPort: number): string {\n return `${formatHostname(spec.hostname)}:${String(spec.port ?? listenerPort)}`\n}\n\n/** Exact Host/Origin/CIDR policy for the directly exposed listener. */\nexport class RequestTrustPolicy {\n readonly authorities: ReadonlySet<string>\n readonly origins: ReadonlySet<string>\n private readonly scheme: 'http' | 'https'\n\n constructor(\n specs: readonly AuthoritySpec[],\n listenerPort: number,\n readonly cidrs: readonly ParsedCidr[],\n tls: boolean,\n ) {\n this.scheme = tls ? 'https' : 'http'\n this.authorities = new Set(specs.map(spec => resolveAuthority(spec, listenerPort).toLowerCase()))\n this.origins = new Set([...this.authorities].map(\n authority => new URL(`${this.scheme}://${authority}`).origin.toLowerCase(),\n ))\n }\n\n /** Validate the exact Host header after WHATWG authority normalization. */\n acceptsHost(header: string | undefined): boolean {\n return this.canonicalHost(header) !== undefined\n }\n\n /** Return the canonical accepted Host authority, otherwise undefined. */\n canonicalHost(header: string | undefined): string | undefined {\n if (header === undefined || /[/?#@\\\\]/u.test(header)) return undefined\n let normalized: string\n try {\n const parsed = new URL(`${this.scheme}://${header}`)\n if (parsed.pathname !== '/' || parsed.username !== '' || parsed.password !== '') return undefined\n normalized = resolveAuthority({\n hostname: parsed.hostname,\n port: Number(parsed.port || (this.scheme === 'https' ? '443' : '80')),\n }, 80).toLowerCase()\n } catch {\n return undefined\n }\n return this.authorities.has(normalized) ? normalized : undefined\n }\n\n /** Validate an exact same-scheme browser Origin. */\n acceptsOrigin(header: string | undefined): boolean {\n return this.canonicalOrigin(header) !== undefined\n }\n\n /** Return the canonical accepted Origin, otherwise undefined. */\n canonicalOrigin(header: string | undefined): string | undefined {\n if (header === undefined) return undefined\n // 微信小程序 wx.connectSocket 会把允许的 Origin 与 undefined 用逗号合并。\n let normalized: string | undefined\n for (const part of header.split(',')) {\n const trimmed = part.trim()\n if (trimmed === 'undefined') continue\n let candidate: string\n try {\n const parsed = new URL(trimmed)\n if (parsed.pathname !== '/' || parsed.search !== '' || parsed.hash !== '' || parsed.username !== '' || parsed.password !== '') {\n return undefined\n }\n candidate = parsed.origin.toLowerCase()\n } catch {\n return undefined\n }\n if (!this.origins.has(candidate) || normalized !== undefined) return undefined\n normalized = candidate\n }\n return normalized\n }\n}\n","import { dirname, isAbsolute, join, resolve } from 'node:path'\nimport { createHash } from 'node:crypto'\nimport { fileURLToPath } from 'node:url'\nimport z from '@deepseek-ai/schemastery'\nimport { isIP } from 'node:net'\nimport { isLoopbackAddress, parseAuthority, parseCidr, type AuthoritySpec, type ParsedCidr } from './network.js'\n\n/** TLS source accepted by the LAN listener. */\nexport interface ProvidedTlsConfig {\n readonly mode: 'provided'\n /** PEM server leaf followed by any intermediate certificate chain. */\n readonly certFile: string\n readonly keyFile: string\n /** Optional PEM intermediates appended after the chain in `certFile`; roots are rejected. */\n readonly caFile?: string\n}\n\n/** HTTP is available only for an explicitly loopback-bound listener. */\nexport interface DisabledTlsConfig {\n readonly mode: 'disabled'\n}\n\nexport type TlsConfig = ProvidedTlsConfig | DisabledTlsConfig\n\n/** Operator-facing plugin configuration. */\nexport interface PluginConfig {\n /** Optional setup JSON written by the packaged CLI. */\n setupFile?: string\n /** Preferred HTTPS origin used to derive the public authority and listener port. */\n publicOrigin?: string\n listenHost?: string\n listenPort?: number\n upstreamOrigin?: string\n publicAuthorities?: string[]\n allowedCidrs?: string[]\n stateFile: string\n /** Internal persisted on/off preference managed by the DSH plugin card. */\n controlFile: string\n /** Optional user stylesheet served to the authenticated mobile UI. */\n customCssFile?: string\n /** Optional user script that mounts authenticated mobile-only Web features. */\n customScriptFile?: string\n /** Internal dedicated mobile layout browser bundle. */\n mobileLayoutFile?: string\n /** Standalone browser compatibility bundle used before DSH boot. */\n mobileCompatibilityFile?: string\n /** Stable public discovery identifier; it is not an authentication secret. */\n instanceId?: string\n /** Managed CA certificate offered to the Android installer after fingerprint binding. */\n pairingCaFile?: string\n /** First-run state used only while the control file does not exist. */\n initiallyEnabled: boolean\n tls?: {\n mode?: 'provided' | 'disabled'\n certFile?: string\n keyFile?: string\n caFile?: string\n }\n pairingTtlMs?: number\n deviceTtlMs?: number\n sessionTtlMs?: number\n maxDevices?: number\n maxSessions?: number\n maxConnections?: number\n maxActiveRequests?: number\n maxWebSockets?: number\n maxBodyBytes?: number\n upstreamTimeoutMs?: number\n rateLimitWindowMs?: number\n maxPairingAttempts?: number\n maxRateLimitKeys?: number\n}\n\n/** Resolved, validated security and resource limits. */\nexport interface ResolvedGatewayConfig {\n readonly listenHost: string\n readonly listenPort: number\n readonly upstreamOrigin: URL\n readonly authorities: readonly AuthoritySpec[]\n readonly allowedCidrs: readonly ParsedCidr[]\n readonly stateFile: string\n /** Local extension root adjacent to the mobile-access state file. */\n readonly extensionsDir: string\n readonly customCssFile: string\n readonly customScriptFile: string\n readonly mobileLayoutFile: string\n readonly mobileCompatibilityFile: string\n readonly instanceId: string\n readonly pairingCaFile?: string\n readonly tls: TlsConfig\n /** Whether the public hop is HTTPS, even when a trusted loopback proxy terminates TLS. */\n readonly publicTls: boolean\n /** LAN discovery is disabled for private proxy listeners such as Funnel ingress. */\n readonly discovery: boolean\n readonly pairingTtlMs: number\n readonly deviceTtlMs: number\n readonly sessionTtlMs: number\n readonly maxDevices: number\n readonly maxSessions: number\n readonly maxConnections: number\n readonly maxActiveRequests: number\n readonly maxWebSockets: number\n readonly maxBodyBytes: number\n readonly upstreamTimeoutMs: number\n readonly rateLimitWindowMs: number\n readonly maxPairingAttempts: number\n readonly maxRateLimitKeys: number\n}\n\n/** Loader-facing defaults; {@link parseGatewayConfig} enforces cross-field security rules. */\nexport const Config: z<PluginConfig> = z.object({\n setupFile: z.string().hidden(),\n publicOrigin: z.string(),\n listenHost: z.string(),\n listenPort: z.natural().max(65535),\n upstreamOrigin: z.string(),\n publicAuthorities: z.array(String).default(undefined as unknown as string[]),\n allowedCidrs: z.array(String).default(undefined as unknown as string[]),\n stateFile: String,\n controlFile: z.string().hidden().required(),\n customCssFile: z.string().hidden(),\n customScriptFile: z.string().hidden(),\n mobileLayoutFile: z.string().hidden(),\n mobileCompatibilityFile: z.string().hidden(),\n instanceId: z.string().hidden(),\n pairingCaFile: z.string().hidden(),\n initiallyEnabled: z.boolean().hidden().required(),\n tls: z.object({\n mode: z.union([z.const('provided'), z.const('disabled')]),\n certFile: z.string(),\n keyFile: z.string(),\n caFile: z.string(),\n }),\n pairingTtlMs: z.natural(),\n deviceTtlMs: z.natural(),\n sessionTtlMs: z.natural(),\n maxDevices: z.natural(),\n maxSessions: z.natural(),\n maxConnections: z.natural(),\n maxActiveRequests: z.natural(),\n maxWebSockets: z.natural(),\n maxBodyBytes: z.natural(),\n upstreamTimeoutMs: z.natural(),\n rateLimitWindowMs: z.natural(),\n maxPairingAttempts: z.natural(),\n maxRateLimitKeys: z.natural(),\n})\n\nfunction integer(value: unknown, name: string, fallback: number, minimum: number, maximum: number): number {\n const resolved = value ?? fallback\n if (typeof resolved !== 'number' || !Number.isSafeInteger(resolved) || resolved < minimum || resolved > maximum) {\n throw new Error(`${name} must be an integer from ${String(minimum)} through ${String(maximum)}`)\n }\n return resolved\n}\n\nfunction stringArray(value: unknown, name: string): string[] {\n if (!Array.isArray(value) || value.length === 0 || value.some(entry => typeof entry !== 'string')) {\n throw new Error(`${name} must be a non-empty string array`)\n }\n return value as string[]\n}\n\nfunction absoluteFile(value: unknown, name: string): string {\n if (typeof value !== 'string' || value.length === 0 || !isAbsolute(value)) {\n throw new Error(`${name} must be an absolute file path`)\n }\n return resolve(value)\n}\n\n/** Resolve the hidden runtime-control file independently from gateway configuration. */\nexport function parseControlFile(value: unknown): string {\n return absoluteFile(value, 'controlFile')\n}\n\nfunction parseUpstream(value: unknown): URL {\n const source = value ?? 'http://127.0.0.1:3080'\n if (typeof source !== 'string') throw new Error('upstreamOrigin must be a string')\n let url: URL\n try {\n url = new URL(source)\n } catch {\n throw new Error('upstreamOrigin must be an HTTP loopback origin')\n }\n if (url.protocol !== 'http:' || !isLoopbackAddress(url.hostname) || url.username !== '' || url.password !== ''\n || url.pathname !== '/' || url.search !== '' || url.hash !== '' || url.port === '') {\n throw new Error('upstreamOrigin must be an HTTP loopback origin with an explicit port and no path or credentials')\n }\n return url\n}\n\nfunction parsePublicOrigin(value: unknown): { readonly authority: AuthoritySpec; readonly port: number } | undefined {\n if (value === undefined) return undefined\n if (typeof value !== 'string' || value.length === 0 || value.trim() !== value) {\n throw new Error('publicOrigin must be an HTTPS origin')\n }\n let url: URL\n try {\n url = new URL(value)\n } catch {\n throw new Error('publicOrigin must be an HTTPS origin')\n }\n if (url.protocol !== 'https:' || url.username !== '' || url.password !== ''\n || url.pathname !== '/' || url.search !== '' || url.hash !== '') {\n throw new Error('publicOrigin must be an HTTPS origin with no path or credentials')\n }\n if (url.hostname === '0.0.0.0' || url.hostname === '[::]') {\n throw new Error('publicOrigin must name a reachable host')\n }\n return Object.freeze({\n authority: parseAuthority(url.host),\n port: Number(url.port || '443'),\n })\n}\n\nfunction parseTls(value: PluginConfig['tls'], listenHost: string): TlsConfig {\n const mode = value?.mode ?? 'provided'\n if (mode === 'disabled') {\n if (!isLoopbackAddress(listenHost)) throw new Error('TLS may be disabled only on an IP loopback listener')\n return Object.freeze({ mode })\n }\n return Object.freeze({\n mode,\n certFile: absoluteFile(value?.certFile, 'tls.certFile'),\n keyFile: absoluteFile(value?.keyFile, 'tls.keyFile'),\n ...(value?.caFile === undefined ? {} : { caFile: absoluteFile(value.caFile, 'tls.caFile') }),\n })\n}\n\n/** Parse configuration and reject unsafe topology, credential, and resource combinations. */\nexport function parseGatewayConfig(raw: unknown): ResolvedGatewayConfig {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) throw new Error('mobile-access config must be an object')\n const value = raw as PluginConfig\n const publicOrigin = parsePublicOrigin(value.publicOrigin)\n if (publicOrigin !== undefined && value.listenPort !== undefined) {\n throw new Error('publicOrigin cannot be combined with listenPort')\n }\n if (publicOrigin !== undefined && value.publicAuthorities !== undefined) {\n throw new Error('publicOrigin cannot be combined with publicAuthorities')\n }\n const listenHost = value.listenHost ?? (publicOrigin === undefined ? '127.0.0.1' : '0.0.0.0')\n if (isIP(listenHost) === 0) throw new Error('listenHost must be an IP literal')\n const listenPort = publicOrigin?.port ?? integer(value.listenPort, 'listenPort', 3443, 0, 65535)\n const upstreamOrigin = parseUpstream(value.upstreamOrigin)\n const tls = parseTls(value.tls, listenHost)\n if (publicOrigin !== undefined && tls.mode !== 'provided') {\n throw new Error('publicOrigin requires TLS')\n }\n\n let authorities: AuthoritySpec[]\n if (publicOrigin !== undefined) {\n authorities = [publicOrigin.authority]\n } else {\n let authoritySources = value.publicAuthorities\n if (authoritySources === undefined || authoritySources.length === 0) {\n if (!isLoopbackAddress(listenHost)) throw new Error('publicAuthorities is required for a non-loopback listener')\n authoritySources = [listenHost]\n }\n authorities = authoritySources.map(parseAuthority)\n }\n for (const authority of authorities) {\n if (listenPort === 0 && authority.port !== undefined) {\n throw new Error('explicit public authority ports require a non-zero listenPort')\n }\n if (authority.port !== undefined && listenPort !== 0 && authority.port !== listenPort) {\n throw new Error('every explicit public authority port must equal listenPort')\n }\n }\n if (new Set(authorities.map(entry => `${entry.hostname}:${String(entry.port ?? listenPort)}`)).size !== authorities.length) {\n throw new Error('publicAuthorities must not contain duplicates')\n }\n\n const cidrSources = value.allowedCidrs\n ?? (isLoopbackAddress(listenHost) ? ['127.0.0.0/8', '::1/128'] : undefined)\n const allowedCidrs = stringArray(cidrSources, 'allowedCidrs').map(parseCidr)\n if (new Set(allowedCidrs.map(entry => `${String(entry.bits)}:${entry.network.toString(16)}:${String(entry.prefix)}`)).size !== allowedCidrs.length) {\n throw new Error('allowedCidrs must not contain duplicates')\n }\n const deviceTtlMs = integer(value.deviceTtlMs, 'deviceTtlMs', 90 * 24 * 60 * 60_000, 60_000, 366 * 24 * 60 * 60_000)\n const sessionTtlMs = integer(value.sessionTtlMs, 'sessionTtlMs', 8 * 60 * 60_000, 30_000, 24 * 60 * 60_000)\n if (sessionTtlMs > deviceTtlMs) throw new Error('sessionTtlMs must not exceed deviceTtlMs')\n\n return Object.freeze({\n listenHost,\n listenPort,\n upstreamOrigin,\n authorities: Object.freeze(authorities),\n allowedCidrs: Object.freeze(allowedCidrs),\n stateFile: absoluteFile(value.stateFile, 'stateFile'),\n extensionsDir: join(dirname(absoluteFile(value.stateFile, 'stateFile')), 'extensions'),\n customCssFile: value.customCssFile === undefined\n ? join(dirname(absoluteFile(value.stateFile, 'stateFile')), 'mobile.css')\n : absoluteFile(value.customCssFile, 'customCssFile'),\n customScriptFile: value.customScriptFile === undefined\n ? join(dirname(absoluteFile(value.stateFile, 'stateFile')), 'mobile.js')\n : absoluteFile(value.customScriptFile, 'customScriptFile'),\n mobileLayoutFile: value.mobileLayoutFile === undefined\n ? fileURLToPath(new URL('./mobile-layout.js', import.meta.url))\n : absoluteFile(value.mobileLayoutFile, 'mobileLayoutFile'),\n mobileCompatibilityFile: value.mobileCompatibilityFile === undefined\n ? fileURLToPath(new URL('./mobile-compat.js', import.meta.url))\n : absoluteFile(value.mobileCompatibilityFile, 'mobileCompatibilityFile'),\n instanceId: value.instanceId === undefined\n ? createHash('sha256').update(absoluteFile(value.stateFile, 'stateFile')).digest('hex')\n : /^[a-f\\d]{64}$/u.test(value.instanceId)\n ? value.instanceId\n : (() => { throw new Error('instanceId must be a lowercase SHA-256 value') })(),\n ...(value.pairingCaFile === undefined ? {} : { pairingCaFile: absoluteFile(value.pairingCaFile, 'pairingCaFile') }),\n tls,\n publicTls: tls.mode === 'provided',\n discovery: true,\n pairingTtlMs: integer(value.pairingTtlMs, 'pairingTtlMs', 120_000, 10_000, 600_000),\n deviceTtlMs,\n sessionTtlMs,\n maxDevices: integer(value.maxDevices, 'maxDevices', 32, 1, 256),\n maxSessions: integer(value.maxSessions, 'maxSessions', 64, 1, 1024),\n maxConnections: integer(value.maxConnections, 'maxConnections', 64, 1, 1024),\n maxActiveRequests: integer(value.maxActiveRequests, 'maxActiveRequests', 32, 1, 1024),\n maxWebSockets: integer(value.maxWebSockets, 'maxWebSockets', 16, 1, 256),\n maxBodyBytes: integer(value.maxBodyBytes, 'maxBodyBytes', 160 * 1024 * 1024, 1024, 256 * 1024 * 1024),\n upstreamTimeoutMs: integer(value.upstreamTimeoutMs, 'upstreamTimeoutMs', 30_000, 1_000, 300_000),\n rateLimitWindowMs: integer(value.rateLimitWindowMs, 'rateLimitWindowMs', 60_000, 1_000, 3_600_000),\n maxPairingAttempts: integer(value.maxPairingAttempts, 'maxPairingAttempts', 8, 1, 100),\n maxRateLimitKeys: integer(value.maxRateLimitKeys, 'maxRateLimitKeys', 256, 1, 4096),\n })\n}\n","import { execFile, type ExecFileOptions } from 'node:child_process'\n\n/** Capture both output streams even when a desktop host wraps execFile without Node's promisify metadata. */\nexport function execFileText(\n file: string,\n args: readonly string[],\n options: Omit<ExecFileOptions, 'encoding'> & { encoding?: 'utf8' } = {},\n): Promise<{ stdout: string; stderr: string }> {\n return new Promise((resolve, reject) => {\n execFile(file, [...args], { windowsHide: true, ...options, encoding: 'utf8' }, (error, stdout, stderr) => {\n if (error !== null) { reject(error); return }\n if (typeof stdout !== 'string' || typeof stderr !== 'string') {\n reject(new Error('subprocess returned invalid text output'))\n return\n }\n resolve({ stdout, stderr })\n })\n })\n}\n","import { chmod } from 'node:fs/promises'\nimport { execFileText as execFile } from './exec-file.js'\n\nlet userSidTask: Promise<string> | undefined\n\nasync function currentWindowsUserSid(): Promise<string> {\n userSidTask ??= execFile('whoami.exe', ['/user', '/fo', 'csv', '/nh'], {\n encoding: 'utf8',\n windowsHide: true,\n timeout: 10_000,\n }).then(({ stdout }) => {\n const match = /,\"(S-\\d(?:-\\d+)+)\"\\s*$/u.exec(stdout.trim())\n if (match?.[1] === undefined) throw new Error('unable to resolve the current Windows user SID')\n return match[1]\n }).catch((error: unknown) => {\n userSidTask = undefined\n throw error\n })\n return userSidTask\n}\n\n/** Restrict a sensitive regular file to the current user and Windows administrators. */\nexport async function restrictPrivateFile(file: string, mode = 0o600): Promise<void> {\n await chmod(file, mode)\n if (process.platform !== 'win32') return\n const userSid = await currentWindowsUserSid()\n await execFile('icacls.exe', [\n file,\n '/inheritance:r',\n '/grant:r',\n `*${userSid}:(F)`,\n '*S-1-5-18:(F)',\n '*S-1-5-32-544:(F)',\n '/remove:g',\n '*S-1-1-0',\n '*S-1-5-11',\n '*S-1-5-32-545',\n ], { encoding: 'utf8', windowsHide: true, timeout: 10_000 })\n}\n","import { randomBytes } from 'node:crypto'\nimport { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, join } from 'node:path'\nimport { restrictPrivateFile } from './private-file.js'\n\n/** Versioned durable preference for the resident mobile-access runtime. */\nexport interface MobileAccessControlState {\n readonly version: 1\n readonly enabled: boolean\n}\n\n/** Persistence seam for the runtime preference. */\nexport interface MobileAccessControlStore {\n load(): Promise<MobileAccessControlState>\n save(state: MobileAccessControlState): Promise<void>\n}\n\n/** One started gateway runtime owned by the controller. */\nexport interface MobileAccessRuntime {\n close(): Promise<void>\n}\n\n/** One address-specific runtime selected from the current LAN state. */\nexport interface MobileAccessRuntimeSelection {\n readonly key: string\n start(): Promise<MobileAccessRuntime>\n}\n\n/** Keeps one runtime aligned with a changing network selection. */\nexport class FollowingMobileAccessRuntime implements MobileAccessRuntime {\n private runtime: MobileAccessRuntime | undefined\n private key: string | undefined\n private queue: Promise<void> = Promise.resolve()\n private closed = false\n private timer: ReturnType<typeof setInterval> | undefined\n\n constructor(\n private readonly select: () => Promise<MobileAccessRuntimeSelection>,\n private readonly onRefreshError: (error: unknown) => void,\n ) {}\n\n /** Start the current selection and optionally poll for later changes. */\n async initialize(refreshIntervalMs?: number): Promise<void> {\n await this.refresh()\n if (refreshIntervalMs === undefined) return\n this.beginPolling(refreshIntervalMs)\n }\n\n /**\n * Arm the refresh poller without requiring an initial selection.\n * Boot-degraded path: the first selection already failed (e.g. the saved\n * LAN is down), but later refreshes must still get their chance so a\n * returning network recovers without a restart. Failures keep flowing to\n * onRefreshError; close() stays the single teardown.\n */\n beginPolling(refreshIntervalMs: number): void {\n if (this.timer !== undefined) return\n this.timer = setInterval(() => {\n void this.refresh().catch(this.onRefreshError)\n }, refreshIntervalMs)\n this.timer.unref()\n }\n\n /** Reconcile the active runtime with the latest selection. */\n refresh(): Promise<void> {\n return this.enqueue(async () => {\n if (this.closed) return\n const selection = await this.select()\n if (this.closed || (this.runtime !== undefined && this.key === selection.key)) return\n const previous = this.runtime\n this.runtime = undefined\n this.key = undefined\n if (previous !== undefined) await previous.close()\n this.runtime = await selection.start()\n this.key = selection.key\n })\n }\n\n /** Stop polling and close the most recently selected runtime. */\n close(): Promise<void> {\n if (this.closed) return this.queue\n this.closed = true\n if (this.timer !== undefined) clearInterval(this.timer)\n return this.enqueue(async () => {\n const runtime = this.runtime\n this.runtime = undefined\n this.key = undefined\n if (runtime !== undefined) await runtime.close()\n })\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const run = this.queue.then(operation, operation)\n this.queue = run.then(() => {}, () => {})\n return run\n }\n}\n\n/** Validate control state loaded across the filesystem boundary. */\nexport function parseMobileAccessControlState(value: unknown): MobileAccessControlState {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error('mobile-access control state must be an object')\n }\n const record = value as Record<string, unknown>\n if (record.version !== 1 || typeof record.enabled !== 'boolean'\n || Reflect.ownKeys(record).some(key => key !== 'version' && key !== 'enabled')) {\n throw new Error('mobile-access control state has an unsupported format')\n }\n return Object.freeze({ version: 1, enabled: record.enabled })\n}\n\n/** Atomic JSON store whose absent-file state comes from the installation-time default. */\nexport class JsonMobileAccessControlStore implements MobileAccessControlStore {\n constructor(private readonly file: string, private readonly initiallyEnabled: boolean) {}\n\n async load(): Promise<MobileAccessControlState> {\n let stat\n try {\n stat = await lstat(this.file)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return Object.freeze({ version: 1, enabled: this.initiallyEnabled })\n }\n throw error\n }\n if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 4096) {\n throw new Error('mobile-access control state must be a regular file no larger than 4 KiB')\n }\n await restrictPrivateFile(this.file)\n let parsed: unknown\n try {\n parsed = JSON.parse(await readFile(this.file, 'utf8')) as unknown\n } catch (error) {\n throw new Error('mobile-access control state is not valid JSON', { cause: error })\n }\n return parseMobileAccessControlState(parsed)\n }\n\n async save(state: MobileAccessControlState): Promise<void> {\n const validated = parseMobileAccessControlState(state)\n const directory = dirname(this.file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(this.file)\n if (!current.isFile() || current.isSymbolicLink()) {\n throw new Error('mobile-access control state target must remain a regular file')\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, `.${basename(this.file)}.${randomBytes(12).toString('hex')}.tmp`)\n try {\n await writeFile(temporary, `${JSON.stringify(validated)}\\n`, {\n encoding: 'utf8',\n flag: 'wx',\n mode: 0o600,\n })\n await rename(temporary, this.file)\n await restrictPrivateFile(this.file)\n } catch (error) {\n try {\n await rm(temporary, { force: true })\n } catch (cleanupError) {\n throw new AggregateError([error, cleanupError], 'control state write and temporary cleanup both failed')\n }\n throw error\n }\n }\n}\n\n/** Serialized persistent lifecycle for the gateway behind the always-loaded Cordis entry. */\nexport class MobileAccessGatewayController {\n private runtime: MobileAccessRuntime | undefined\n private initialized = false\n private closing = false\n private queue: Promise<void> = Promise.resolve()\n private closeTask: Promise<void> | undefined\n\n constructor(\n private readonly store: MobileAccessControlStore,\n private readonly startRuntime: () => Promise<MobileAccessRuntime>,\n ) {}\n\n /** Load the durable preference and start the first runtime when enabled. */\n initialize(): Promise<void> {\n return this.enqueue(async () => {\n if (this.initialized) throw new Error('mobile-access control is already initialized')\n if (this.closing) throw new Error('mobile-access control is closing')\n const state = await this.store.load()\n if (state.enabled) this.runtime = await this.startRuntime()\n this.initialized = true\n })\n }\n\n /** Return the committed in-process runtime state. */\n isRunning(): boolean {\n return this.runtime !== undefined\n }\n\n /** Start or stop the runtime and persist only a successfully committed transition. */\n setRunning(running: boolean): Promise<void> {\n if (this.closing) return Promise.reject(new Error('mobile-access control is closing'))\n return this.enqueue(async () => {\n if (!this.initialized) throw new Error('mobile-access control is not initialized')\n if (this.isRunning() === running) return\n if (running) {\n await this.enable()\n } else {\n await this.disable()\n }\n })\n }\n\n /** Stop the runtime after earlier transitions without changing the restart preference. */\n close(): Promise<void> {\n if (this.closeTask !== undefined) return this.closeTask\n this.closing = true\n this.closeTask = this.enqueue(async () => {\n const runtime = this.runtime\n if (runtime === undefined) return\n await runtime.close()\n this.runtime = undefined\n })\n return this.closeTask\n }\n\n private async enable(): Promise<void> {\n const candidate = await this.startRuntime()\n try {\n await this.store.save({ version: 1, enabled: true })\n } catch (error) {\n try {\n await candidate.close()\n } catch (rollbackError) {\n throw new AggregateError([error, rollbackError], 'enabling mobile access failed and runtime rollback also failed')\n }\n throw error\n }\n this.runtime = candidate\n }\n\n private async disable(): Promise<void> {\n const previous = this.runtime\n if (previous === undefined) return\n await previous.close()\n try {\n await this.store.save({ version: 1, enabled: false })\n } catch (error) {\n try {\n this.runtime = await this.startRuntime()\n } catch (rollbackError) {\n this.runtime = undefined\n throw new AggregateError([error, rollbackError], 'disabling mobile access failed and runtime rollback also failed')\n }\n throw error\n }\n this.runtime = undefined\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const run = this.queue.then(operation, operation)\n this.queue = run.then(() => {}, () => {})\n return run\n }\n}\n","/** Browser-safe desktop-admin Host checks. Do not import Node APIs here. */\n\nfunction unwrapBrackets(hostname: string): string {\n return hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname\n}\n\nfunction parseIpv4(hostname: string): readonly [number, number, number, number] | undefined {\n const parts = hostname.split('.')\n if (parts.length !== 4) return undefined\n const octets: number[] = []\n for (const part of parts) {\n if (!/^(?:0|[1-9]\\d{0,2})$/u.test(part)) return undefined\n const value = Number(part)\n if (value > 255) return undefined\n octets.push(value)\n }\n return octets as [number, number, number, number]\n}\n\nfunction isLoopbackIpv4(octets: readonly [number, number, number, number]): boolean {\n return octets[0] === 127\n}\n\nfunction isPrivateIpv4(octets: readonly [number, number, number, number]): boolean {\n return octets[0] === 10\n || (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31)\n || (octets[0] === 192 && octets[1] === 168)\n}\n\nfunction isLinkLocalIpv4(octets: readonly [number, number, number, number]): boolean {\n return octets[0] === 169 && octets[1] === 254\n}\n\n/**\n * Hostnames that may appear on the DSH desktop admin surface.\n * Loopback, RFC1918, and IPv4 link-local are accepted. Public IPs,\n * CGNAT, and arbitrary DNS names stay rejected so DNS rebinding\n * cannot reach `/api/mobile-access`.\n */\nexport function isLocalAdminHostname(hostname: string): boolean {\n const host = unwrapBrackets(hostname).trim().toLowerCase()\n if (host === 'localhost' || host === '::1') return true\n const octets = parseIpv4(host)\n if (octets === undefined) return false\n return isLoopbackIpv4(octets) || isPrivateIpv4(octets) || isLinkLocalIpv4(octets)\n}\n\n/**\n * Whether the current browser document should mount the desktop Mobile access\n * control. Dedicated Mobile HTTPS (the phone surface) stays native even when\n * the Host is a private LAN address.\n */\nexport function isDesktopAdminSurface(\n hostname: string,\n search = '',\n frontend?: string,\n): boolean {\n const query = search.startsWith('?') ? search.slice(1) : search\n return isLocalAdminHostname(hostname)\n && frontend !== 'dedicated'\n && !new URLSearchParams(query).has('dsh-mobile-preview')\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { addressAllowed, isLoopbackAddress, RequestTrustPolicy } from './network.js'\nimport { isLocalAdminHostname } from './local-admin-host.js'\n\nexport const DEVICE_COOKIE = 'dsh_ma_device'\nexport const SESSION_COOKIE = 'dsh_ma_session'\nexport const CSRF_COOKIE = 'dsh_ma_csrf'\nexport const CSRF_HEADER = 'x-dsh-mobile-csrf'\nexport const LOCAL_ADMIN_PREFIX = '/api/mobile-access'\nexport const AUTH_PREFIX = '/mobile-access'\nexport const WS_PATHS = new Set([\n '/api/events.mux',\n '/api/events.host',\n '/api/remote.mux',\n // DSH desktop UI's sidebar terminal (renderer-v2) upgrades through this\n // exact path with a session query string; it is a first-party DSH surface.\n '/sidebar/ws/terminal',\n])\n\n/** Terse request failure safe to expose without internal diagnostics. */\nexport class HttpError extends Error {\n constructor(readonly status: number, readonly code: string) {\n super(code)\n this.name = 'HttpError'\n }\n}\n\n/** Parsed origin-form request target with a decoded path for protected-prefix checks. */\nexport interface RequestTarget {\n readonly raw: string\n readonly pathname: string\n readonly decodedPathname: string\n readonly search: string\n}\n\n/** Parse only origin-form request targets and reject ambiguous slash encodings. */\nexport function parseRequestTarget(raw: string | undefined): RequestTarget {\n if (raw === undefined || !raw.startsWith('/') || raw.startsWith('//') || raw.includes('\\\\') || /[\\u0000-\\u001f\\u007f]/u.test(raw)) {\n throw new HttpError(400, 'bad_request')\n }\n let parsed: URL\n let decodedPathname: string\n try {\n parsed = new URL(raw, 'http://gateway.invalid')\n decodedPathname = decodeURIComponent(parsed.pathname)\n } catch {\n throw new HttpError(400, 'bad_request')\n }\n if (decodedPathname.includes('\\\\') || decodedPathname.startsWith('//') || /[\\u0000-\\u001f\\u007f]/u.test(decodedPathname)) {\n throw new HttpError(400, 'bad_request')\n }\n return Object.freeze({ raw, pathname: parsed.pathname, decodedPathname, search: parsed.search })\n}\n\n/**\n * Which framing policy a response carries.\n *\n * - `gateway`: the gateway's OWN documents (login, pairing, the CA helper) and\n * its JSON/SSE responses. Nothing may frame them at all.\n * - `proxied`: the upstream DSH GUI and its routes, forwarded through the\n * gateway. The GUI must be able to frame its OWN same-origin surfaces (the\n * sidebar's HTML/diff preview routes, the browser tab pointed at the GUI\n * itself) and the sidebar browser tab must be able to frame external http(s)\n * sites plus `blob:` PDF previews — a plain `default-src 'self'` with no\n * `frame-src` refuses all of them, which is what broke the sidebar browser\n * over remote access.\n */\nexport type FramingPolicy = 'gateway' | 'proxied'\n\n/**\n * The frame sources a proxied GUI document may embed: its own routes, blob:\n * (the PDF viewer's object URL) and external http(s) pages (the sidebar\n * browser tab). The framed document runs in the tab's existing sandbox\n * (opaque origin, no same-origin privileges), so this does not hand the\n * embedded page anything it did not already have.\n */\nconst PROXIED_FRAME_SRC = \"frame-src 'self' blob: https: http:\"\n\n/**\n * Set the gateway browser protections and non-cacheability.\n * @param response - the response to decorate.\n * @param tls - whether the connection is TLS (adds HSTS).\n * @param framing - the framing policy; defaults to the strict gateway one, so\n * every existing call site keeps refusing to be framed. Only the two PROXIED\n * forwarders pass `'proxied'`.\n */\nexport function setSecurityHeaders(response: ServerResponse, tls: boolean, framing: FramingPolicy = 'gateway'): void {\n const proxied = framing === 'proxied'\n response.setHeader('Cache-Control', 'no-store')\n // DSH emits inline boot code, revives Schemastery callbacks, and applies dynamic styles.\n // These allowances provide compatibility, not XSS isolation.\n response.setHeader('Content-Security-Policy', [\n \"default-src 'self'\",\n \"base-uri 'none'\",\n \"object-src 'none'\",\n // A proxied GUI is frameable by ITS OWN origin only (its preview routes and\n // the sidebar's page routes); every third-party origin stays refused, so\n // clickjacking protection is unchanged for the gateway's own documents.\n proxied ? \"frame-ancestors 'self'\" : \"frame-ancestors 'none'\",\n \"form-action 'self'\",\n \"script-src 'self' 'unsafe-inline' 'unsafe-eval'\",\n \"style-src 'self' 'unsafe-inline'\",\n \"img-src 'self' data: blob:\",\n \"font-src 'self' data:\",\n \"connect-src 'self'\",\n \"worker-src 'self' blob:\",\n // `frame-src` has no allow-by-default: the sidebar browser tab embeds\n // cross-origin pages, which only an explicit frame-src admits.\n ...(proxied ? [PROXIED_FRAME_SRC] : []),\n ].join('; '))\n response.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), payment=(), usb=()')\n response.setHeader('Referrer-Policy', 'no-referrer')\n response.setHeader('X-Content-Type-Options', 'nosniff')\n // The legacy twin of frame-ancestors: SAMEORIGIN keeps older engines aligned\n // with the proxied policy instead of refusing the GUI's own frames.\n response.setHeader('X-Frame-Options', proxied ? 'SAMEORIGIN' : 'DENY')\n response.setHeader('Cross-Origin-Resource-Policy', 'same-origin')\n if (tls) response.setHeader('Strict-Transport-Security', 'max-age=31536000')\n}\n\n/** Send a bounded JSON response without reflecting request or upstream data. */\nexport function sendJson(response: ServerResponse, status: number, value: unknown, tls: boolean): void {\n if (response.headersSent || response.destroyed) return\n setSecurityHeaders(response, tls)\n const body = `${JSON.stringify(value)}\\n`\n response.writeHead(status, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Content-Length': Buffer.byteLength(body),\n })\n response.end(body)\n}\n\n/** Send a generic failure containing only a stable category. */\nexport function sendFailure(response: ServerResponse, status: number, code: string, tls: boolean): void {\n sendJson(response, status, { error: code }, tls)\n}\n\n/** Read and parse one bounded JSON object. */\nexport async function readJsonObject(request: IncomingMessage, maximumBytes: number): Promise<Record<string, unknown>> {\n const contentType = request.headers['content-type']?.split(';', 1)[0]?.trim().toLowerCase()\n if (contentType !== 'application/json') throw new HttpError(415, 'unsupported_media_type')\n const declared = request.headers['content-length']\n if (declared !== undefined) {\n if (!/^\\d+$/u.test(declared) || Number(declared) > maximumBytes) throw new HttpError(413, 'payload_too_large')\n }\n const chunks: Buffer[] = []\n let total = 0\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n total += buffer.length\n if (total > maximumBytes) throw new HttpError(413, 'payload_too_large')\n chunks.push(buffer)\n }\n let parsed: unknown\n try {\n parsed = JSON.parse(Buffer.concat(chunks).toString('utf8')) as unknown\n } catch {\n throw new HttpError(400, 'bad_request')\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) throw new HttpError(400, 'bad_request')\n return parsed as Record<string, unknown>\n}\n\n/** Strict cookie parser: malformed or duplicate names invalidate the whole header. */\nexport function parseCookies(header: string | undefined): ReadonlyMap<string, string> | undefined {\n if (header === undefined) return new Map()\n if (header.length > 8192) return undefined\n const cookies = new Map<string, string>()\n for (const part of header.split(';')) {\n const equals = part.indexOf('=')\n if (equals <= 0) return undefined\n const name = part.slice(0, equals).trim()\n const value = part.slice(equals + 1).trim()\n if (!/^[!#$%&'*+\\-.^_`|~\\dA-Za-z]+$/u.test(name) || !/^[\\w\\-.~+/=]*$/u.test(value) || cookies.has(name)) {\n return undefined\n }\n cookies.set(name, value)\n }\n return cookies\n}\n\n/** Serialize a host-only Cookie with no Domain attribute. */\nexport function cookie(\n name: string,\n value: string,\n options: { tls: boolean; httpOnly: boolean; path: string; maxAgeSeconds: number },\n): string {\n const parts = [\n `${name}=${value}`,\n `Path=${options.path}`,\n `Max-Age=${String(Math.max(0, Math.floor(options.maxAgeSeconds)))}`,\n 'SameSite=Strict',\n 'Priority=High',\n ]\n if (options.tls) parts.push('Secure')\n if (options.httpOnly) parts.push('HttpOnly')\n return parts.join('; ')\n}\n\n/** Enforce direct CIDR, exact Host, and browser same-origin facts. */\nexport function assertExternalTrust(request: IncomingMessage, policy: RequestTrustPolicy, requireOrigin: boolean): void {\n if (!addressAllowed(request.socket.remoteAddress, policy.cidrs) || !policy.acceptsHost(request.headers.host)) {\n throw new HttpError(403, 'forbidden')\n }\n const origin = request.headers.origin\n if (origin !== undefined && !policy.acceptsOrigin(origin)) throw new HttpError(403, 'forbidden')\n const site = request.headers['sec-fetch-site']\n // 微信小程序(wx.request / wx.connectSocket)的 Sec-Fetch-Site 由微信自动附加\n // (same-origin / same-site / cross-site),客户端无法控制,也不代表真实跨站攻击;\n // CSRF 防护的核心是下方对 Origin 的强制校验,因此这里接受全部合法取值。\n if (site !== undefined && site !== 'same-origin' && site !== 'same-site' && site !== 'cross-site' && site !== 'none') throw new HttpError(403, 'forbidden')\n // POST 只强制 Origin 正确。\n if (requireOrigin && !policy.acceptsOrigin(origin)) throw new HttpError(403, 'forbidden')\n}\n\nfunction localAuthority(header: string | undefined): { hostname: string; authority: string } | undefined {\n if (header === undefined || /[/?#@\\\\]/u.test(header)) return undefined\n try {\n const url = new URL(`http://${header}`)\n if (url.pathname !== '/' || url.username !== '' || url.password !== '') return undefined\n return { hostname: url.hostname, authority: url.host.toLowerCase() }\n } catch {\n return undefined\n }\n}\n\n/** Protect the inner management route from non-loopback and DNS-rebinding callers. */\nexport function assertLocalAdminTrust(request: IncomingMessage, requireBrowserOrigin: boolean): void {\n if (request.socket.remoteAddress === undefined || !isLoopbackAddress(request.socket.remoteAddress)) {\n throw new HttpError(403, 'forbidden')\n }\n const host = localAuthority(request.headers.host)\n if (host === undefined || !isLocalAdminHostname(host.hostname)) {\n throw new HttpError(403, 'forbidden')\n }\n const site = request.headers['sec-fetch-site']\n if (site !== undefined && site !== 'same-origin' && site !== 'none') throw new HttpError(403, 'forbidden')\n const origin = request.headers.origin\n if (origin !== undefined) {\n try {\n const parsed = new URL(origin)\n if (parsed.host.toLowerCase() !== host.authority || (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')) {\n throw new HttpError(403, 'forbidden')\n }\n } catch (error) {\n if (error instanceof HttpError) throw error\n throw new HttpError(403, 'forbidden')\n }\n }\n // Mutating admin requests are browser-only. Fetch metadata is optional on\n // older clients, so a missing Sec-Fetch-Site must not make a missing Origin\n // acceptable; Origin is the stable CSRF signal across supported browsers.\n if (requireBrowserOrigin && origin === undefined) {\n throw new HttpError(403, 'forbidden')\n }\n if (requireBrowserOrigin && site !== undefined && site !== 'same-origin') {\n throw new HttpError(403, 'forbidden')\n }\n}\n","import { AUTH_PREFIX } from './http-security.js'\n\nexport const MOBILE_COMPAT_PATH = `${AUTH_PREFIX}/compat.js`\n\n/**\n * Read one attribute out of a tag body.\n *\n * A plain `/\\snonce\\s*=/` search can be fooled by a value that merely contains the\n * text, and cannot tell a double-quoted attribute from a single-quoted one. Walking\n * `name=value` pairs consumes each quoted value whole, so the attribute that is\n * actually present is the one returned.\n */\nfunction attributeValue(tag: string, name: string): string | undefined {\n const pattern = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)\\s*=\\s*(\"[^\"]*\"|'[^']*')/gu\n for (const match of tag.matchAll(pattern)) {\n const attribute = match[1]\n const raw = match[2]\n if (attribute === undefined || raw === undefined) continue\n if (attribute.toLowerCase() === name) return raw.slice(1, -1)\n }\n return undefined\n}\n\n/** Character ranges covered by an HTML comment, where markup is inert text. */\nfunction commentRanges(html: string): readonly (readonly [number, number])[] {\n return [...html.matchAll(/<!--[\\s\\S]*?-->/gu)].map(match => [match.index, match.index + match[0].length] as const)\n}\n\n/**\n * Load the compatibility bundle synchronously, before the first DSH script.\n *\n * Returns the HTML unchanged when there is no script to anchor to. The bundle is an\n * enhancement for WebViews without Iterator helpers, so a document this function\n * cannot place it in must still be served: throwing here took the entire dedicated\n * mobile frontend down with a 502.\n */\nexport function ensureMobileCompatibility(html: string): string {\n const comments = commentRanges(html)\n const isInert = (at: number): boolean => comments.some(([start, end]) => at >= start && at < end)\n // Markup inside a comment never executes, so anchoring to it would inject the\n // bundle into inert text and silently skip the fix.\n const scripts = [...html.matchAll(/<script\\b[^>]*>/giu)].filter(match => !isInert(match.index))\n if (scripts.length === 0) return html\n if (scripts.some(match => attributeValue(match[0], 'src') === MOBILE_COMPAT_PATH)) return html\n const first = scripts[0]\n if (first === undefined) return html\n // Keep any upstream CSP nonce; do not move execution ahead of preceding CSP meta tags.\n const nonce = attributeValue(first[0], 'nonce')?.replace(/[\"'<>\\s]/gu, '') ?? ''\n const attribute = nonce === '' ? '' : ` nonce=\"${nonce}\"`\n const script = `<script src=\"${MOBILE_COMPAT_PATH}\"${attribute}></script>`\n return `${html.slice(0, first.index)}${script}${html.slice(first.index)}`\n}\n","import { createRequire } from 'node:module'\n\ninterface PackageManifest {\n readonly version?: unknown\n}\n\nconst manifest = createRequire(import.meta.url)('../package.json') as PackageManifest\n\n/** Version of the installed DSH Mobile plugin package. */\nexport const DSH_MOBILE_VERSION = typeof manifest.version === 'string' ? manifest.version : 'unknown'\n\n/** Oldest Android App release supported by this plugin generation. */\nexport const MINIMUM_ANDROID_APP_VERSION = '0.2.2'\n\n/** Public gateway metadata format understood by the Android App. */\nexport const MOBILE_METADATA_VERSION = 1\n","import { lstat, opendir, readFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { basename, dirname, extname, isAbsolute, resolve } from 'node:path'\nimport { HttpError } from './http-security.js'\n\nconst MAX_ENTRIES = 500\nconst MAX_IMAGE_BYTES = 20 * 1024 * 1024\nconst IMAGE_TYPES: Readonly<Record<string, string>> = Object.freeze({\n '.gif': 'image/gif',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.png': 'image/png',\n '.webp': 'image/webp',\n})\n\n/** One computer-side row rendered by the authenticated mobile file sheet. */\nexport interface ComputerImageEntry {\n readonly kind: 'directory' | 'image'\n readonly name: string\n readonly path: string\n}\n\n/** A bounded computer-side directory listing containing folders and supported images. */\nexport interface ComputerImageListing {\n readonly path: string\n readonly parent?: string\n readonly entries: readonly ComputerImageEntry[]\n readonly truncated: boolean\n}\n\n/** Normalize an optional mobile-browser path without rebasing relative input. */\nexport function resolveComputerImagePath(path: string | null): string {\n if (path === null || path === '') return homedir()\n if (!isAbsolute(path) || path.includes('\\0')) throw new HttpError(400, 'bad_path')\n return resolve(path)\n}\n\n/** List folders and supported image files without following symbolic links. */\nexport async function listComputerImages(path: string | null, signal?: AbortSignal): Promise<ComputerImageListing> {\n signal?.throwIfAborted()\n const target = resolveComputerImagePath(path)\n const rows: ComputerImageEntry[] = []\n let truncated = false\n let directory\n try {\n directory = await opendir(target)\n for await (const entry of directory) {\n signal?.throwIfAborted()\n if (entry.isSymbolicLink()) continue\n const kind = entry.isDirectory() ? 'directory' : IMAGE_TYPES[extname(entry.name).toLowerCase()] === undefined ? undefined : 'image'\n if (kind === undefined) continue\n if (rows.length === MAX_ENTRIES) {\n truncated = true\n break\n }\n rows.push({ kind, name: entry.name, path: resolve(target, entry.name) })\n }\n } catch (error) {\n if (signal?.aborted) throw signal.reason\n throw new HttpError(404, 'directory_unavailable')\n } finally {\n await directory?.close().catch(() => undefined)\n }\n rows.sort((left, right) => left.kind === right.kind\n ? left.name.localeCompare(right.name)\n : left.kind === 'directory' ? -1 : 1)\n const parent = dirname(target)\n return Object.freeze({\n path: target,\n ...(parent === target ? {} : { parent }),\n entries: Object.freeze(rows),\n truncated,\n })\n}\n\n/** Read one bounded regular image file selected by an authenticated device. */\nexport async function readComputerImage(path: string | null, signal?: AbortSignal): Promise<{ body: Buffer; contentType: string; name: string }> {\n signal?.throwIfAborted()\n const target = resolveComputerImagePath(path)\n const contentType = IMAGE_TYPES[extname(target).toLowerCase()]\n if (contentType === undefined) throw new HttpError(415, 'unsupported_file_type')\n let info\n try {\n info = await lstat(target)\n } catch {\n throw new HttpError(404, 'file_unavailable')\n }\n if (!info.isFile() || info.isSymbolicLink()) throw new HttpError(404, 'file_unavailable')\n if (info.size > MAX_IMAGE_BYTES) throw new HttpError(413, 'file_too_large')\n try {\n return { body: await readFile(target, { signal }), contentType, name: basename(target) }\n } catch (error) {\n if (signal?.aborted) throw signal.reason\n throw new HttpError(404, 'file_unavailable')\n }\n}\n","import { createHash } from 'node:crypto'\nimport type { Dirent } from 'node:fs'\nimport { lstat, mkdir, opendir, readFile, realpath } from 'node:fs/promises'\nimport { basename, isAbsolute, join, relative, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { Service, type Context } from '@deepseek-ai/cordis'\nimport z from '@deepseek-ai/schemastery'\nimport { finished, type Readable } from 'node:stream'\n\n/** Maximum sizes enforced at the local-extension filesystem boundary. */\nexport const EXTENSION_LIMITS = Object.freeze({\n manifest: 64 * 1024,\n script: 1024 * 1024,\n css: 512 * 1024,\n asset: 8 * 1024 * 1024,\n assetFiles: 256,\n assetBytes: 32 * 1024 * 1024,\n assetDepth: 8,\n})\n\n/** A misbehaving host activation must not wedge the local watcher forever. */\nconst HOST_ACTIVATION_TIMEOUT_MS = 5_000\n\n/** The previous Host outlives the hidden-page refresh interval and one timed refresh. */\nconst RETIRED_GENERATION_TTL_MS = 10 * 60_000\n\n/** Extension teardown is advisory and must never stop watcher progress. */\nconst HOST_TEARDOWN_TIMEOUT_MS = 2_000\n\nasync function withActivationTimeout<T>(promise: Promise<T>, id: string, signal: AbortSignal): Promise<T> {\n let timer: NodeJS.Timeout | undefined\n let onAbort: (() => void) | undefined\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new MobileExtensionError('host_load_timeout', `extension ${id} activation timed out`, 500)), HOST_ACTIVATION_TIMEOUT_MS)\n }),\n new Promise<never>((_, reject) => {\n const abort = (): void => { reject(new MobileExtensionError('host_activation_closed', `extension ${id} activation is closed`, 409)) }\n if (signal.aborted) abort()\n else { onAbort = abort; signal.addEventListener('abort', abort, { once: true }) }\n }),\n ])\n } finally {\n if (timer !== undefined) clearTimeout(timer)\n if (onAbort !== undefined) signal.removeEventListener('abort', onAbort)\n }\n}\n\n/** A controlled business failure returned by an extension action or route. */\nexport class MobileExtensionError extends Error {\n constructor(readonly code: string, message: string, readonly status = 400) {\n super(message)\n this.name = 'MobileExtensionError'\n }\n}\n\n/** One host-side action exposed by an extension. */\ntype CallableActionInput = (value?: never, options?: never) => unknown\n\nexport interface MobileHostAction {\n /** A callable Schemastery schema or an adapter exposing parse(). */\n readonly input?: CallableActionInput | { parse(value: unknown): unknown }\n readonly run: (context: MobileActionContext, input: unknown) => unknown | Promise<unknown>\n}\n\n/** Context supplied to a host action. */\nexport interface MobileActionContext {\n readonly signal: AbortSignal\n readonly deviceId: string\n}\n\n/** Safe request values supplied to a host route. */\nexport interface MobileRouteRequest {\n readonly method: string\n readonly pathname: string\n readonly query: Readonly<URLSearchParams>\n readonly headers: Readonly<Record<string, string>>\n readonly body: Uint8Array\n readonly signal: AbortSignal\n readonly deviceId: string\n}\n\n/** Values an extension route may return; status is a final HTTP code from 200 through 599. */\nexport interface MobileRouteResponse {\n readonly status?: number\n readonly contentType?: string\n readonly headers?: Readonly<Record<string, string>>\n readonly body: string | Uint8Array | Readable\n}\n\n/** One host-side route exposed by an extension. */\nexport interface MobileHostRoute {\n readonly method: string\n readonly path: string\n readonly kind?: 'exact' | 'prefix'\n readonly handle: (request: MobileRouteRequest) => MobileRouteResponse | Promise<MobileRouteResponse>\n}\n\n/** Metadata shared by local and npm-provided extensions. */\nexport interface MobileExtensionManifest {\n readonly schemaVersion: 1\n readonly id: string\n readonly name: string\n readonly version: string\n readonly description?: string\n}\n\n/** Definition registered by a normal Cordis plugin. */\nexport interface MobileExtensionDefinition extends MobileExtensionManifest {\n readonly actions?: Readonly<Record<string, MobileHostAction>>\n readonly routes?: readonly MobileHostRoute[]\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n mobileAccess: MobileAccessService\n }\n}\n\n/** A local extension manifest read from extension.json. */\nexport interface LocalExtensionManifest extends MobileExtensionManifest {}\n\n/** Public snapshot sent to the mobile browser. */\nexport interface MobileExtensionClientEntry extends MobileExtensionManifest {\n readonly generation?: string\n readonly scriptUrl?: string\n readonly styleUrl?: string\n readonly assetsUrl?: string\n}\n\ninterface LocalAssetSnapshot {\n readonly body: Buffer\n readonly digest: string\n readonly name: string\n}\n\n/** Small status summary used by the desktop mobile-access card. */\nexport interface MobileExtensionStatus {\n readonly loaded: number\n readonly failed: number\n}\n\ninterface ActiveLocalExtension {\n readonly manifest: LocalExtensionManifest\n readonly directory: string\n readonly scriptBody?: Buffer\n readonly styleBody?: Buffer\n readonly assets: ReadonlyMap<string, LocalAssetSnapshot>\n readonly host: MobileExtensionDefinition\n readonly controller: AbortController\n readonly cleanups: readonly (() => void | Promise<void>)[]\n readonly digest: string\n}\n\ninterface RegisteredExtension {\n readonly definition: MobileExtensionDefinition\n readonly dispose: () => void\n}\n\ninterface RetiredLocalExtension {\n readonly active: ActiveLocalExtension\n readonly timer: NodeJS.Timeout\n}\n\ntype HostApi = {\n readonly manifest: LocalExtensionManifest\n readonly context: Context\n readonly schema: typeof z\n readonly signal: AbortSignal\n action(name: string, spec: MobileHostAction): void\n route(spec: MobileHostRoute): void\n effect(setup: () => void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)>): void\n}\n\ntype LocalHostModule = { readonly default?: (api: HostApi) => void | Promise<void> }\n\n/** Validate user-facing extension text without allowing control characters. */\nfunction text(value: unknown, field: string, maximum: number, required: boolean): string | undefined {\n if (value === undefined && !required) return undefined\n if (typeof value !== 'string' || (required && value.length === 0) || value.length > maximum\n || /[\\u0000-\\u001f\\u007f]/u.test(value)) throw new MobileExtensionError('invalid_manifest', `${field} is invalid`)\n return value\n}\n\n/** Validate a stable extension id. */\nexport function assertExtensionId(value: unknown): string {\n if (typeof value !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/u.test(value)) {\n throw new MobileExtensionError('invalid_manifest', 'extension id is invalid')\n }\n return value\n}\n\n/** Validate a manifest from JSON or a plugin definition. */\nexport function parseExtensionManifest(value: unknown): LocalExtensionManifest {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new MobileExtensionError('invalid_manifest', 'extension.json must be an object')\n }\n const record = value as Record<string, unknown>\n if (record.schemaVersion !== 1) throw new MobileExtensionError('invalid_manifest', 'unsupported extension schema')\n const id = assertExtensionId(record.id)\n const name = text(record.name, 'name', 120, true) as string\n const version = text(record.version, 'version', 64, true) as string\n const description = text(record.description, 'description', 500, false)\n for (const key of Reflect.ownKeys(record)) {\n if (!['schemaVersion', 'id', 'name', 'version', 'description'].includes(String(key))) {\n throw new MobileExtensionError('invalid_manifest', 'extension.json has unknown fields')\n }\n }\n return Object.freeze({ schemaVersion: 1, id, name, version, ...(description === undefined ? {} : { description }) })\n}\n\nfunction normalizeRelativePath(value: string, field: string): string {\n if (value.length === 0 || value.includes('\\0') || isAbsolute(value)) throw new MobileExtensionError('invalid_extension_path', `${field} is invalid`)\n const normalized = value.replaceAll('\\\\', '/')\n if (normalized.split('/').some(part => part === '' || part === '.' || part === '..')) {\n throw new MobileExtensionError('invalid_extension_path', `${field} escapes extension directory`)\n }\n return normalized\n}\n\nasync function regularFile(path: string, maximum: number, field: string): Promise<{ readonly path: string; readonly size: number }> {\n let info\n try { info = await lstat(path) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') throw new MobileExtensionError('invalid_extension', `${field} is missing`)\n throw error\n }\n if (!info.isFile() || info.isSymbolicLink() || info.size > maximum) {\n throw new MobileExtensionError('invalid_extension', `${field} must be a regular file within its size limit`)\n }\n return { path, size: info.size }\n}\n\nasync function containedPath(root: string, relativePath: string, maximum: number, field: string): Promise<{ readonly path: string; readonly size: number }> {\n const normalized = normalizeRelativePath(relativePath, field)\n const target = resolve(root, normalized)\n const rootReal = await realpath(root)\n const targetReal = await realpath(target)\n const relation = relative(rootReal, targetReal)\n if (relation === '' || relation.startsWith('..') || isAbsolute(relation)) throw new MobileExtensionError('invalid_extension_path', `${field} escapes extension directory`)\n return regularFile(targetReal, maximum, field)\n}\n\nasync function optionalFile(root: string, name: string, maximum: number, field: string): Promise<string | undefined> {\n try {\n return (await containedPath(root, name, maximum, field)).path\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\n if (error instanceof MobileExtensionError && error.message.includes('is missing')) return undefined\n throw error\n }\n}\n\nasync function optionalBytes(root: string, name: string, maximum: number, field: string): Promise<Buffer | undefined> {\n const path = await optionalFile(root, name, maximum, field)\n return path === undefined ? undefined : readFile(path)\n}\n\nfunction assertRealPathWithin(rootReal: string, targetReal: string, field: string): void {\n const relation = relative(rootReal, targetReal)\n if (relation === '' || relation.startsWith('..') || isAbsolute(relation)) {\n throw new MobileExtensionError('invalid_extension_path', `${field} escapes extension directory`)\n }\n}\n\nasync function realExtensionRoot(directory: string): Promise<string> {\n const root = resolve(directory)\n const info = await lstat(root)\n if (!info.isDirectory() || info.isSymbolicLink()) throw new MobileExtensionError('invalid_extension', 'extension directory must be real')\n return realpath(root)\n}\n\nasync function assetSnapshot(extensionRootReal: string): Promise<ReadonlyMap<string, LocalAssetSnapshot>> {\n const assetsPath = join(extensionRootReal, 'assets')\n let assetsInfo\n try { assetsInfo = await lstat(assetsPath) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return new Map()\n throw error\n }\n if (!assetsInfo.isDirectory() || assetsInfo.isSymbolicLink()) {\n throw new MobileExtensionError('invalid_extension', 'assets must be a real directory')\n }\n const assetsReal = await realpath(assetsPath)\n assertRealPathWithin(extensionRootReal, assetsReal, 'assets')\n const snapshots = new Map<string, LocalAssetSnapshot>()\n let totalBytes = 0\n const visit = async (directoryReal: string, prefix: string, depth: number): Promise<void> => {\n if (depth > EXTENSION_LIMITS.assetDepth) {\n throw new MobileExtensionError('invalid_extension', 'asset tree exceeds its depth limit')\n }\n assertRealPathWithin(extensionRootReal, directoryReal, 'asset directory')\n const handle = await opendir(directoryReal)\n const entries: Dirent[] = []\n try { for await (const entry of handle) entries.push(entry) }\n finally { await handle.close().catch(() => undefined) }\n entries.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0)\n for (const entry of entries) {\n const path = join(directoryReal, entry.name)\n const info = await lstat(path)\n if (info.isSymbolicLink()) throw new MobileExtensionError('invalid_extension_path', 'asset escapes extension directory')\n const targetReal = await realpath(path)\n assertRealPathWithin(extensionRootReal, targetReal, 'asset')\n const key = prefix === '' ? entry.name : `${prefix}/${entry.name}`\n if (info.isDirectory()) {\n await visit(targetReal, key, depth + 1)\n continue\n }\n if (!info.isFile() || info.size > EXTENSION_LIMITS.asset) {\n throw new MobileExtensionError('invalid_extension', 'asset must be a regular file within its size limit')\n }\n const body = await readFile(targetReal)\n totalBytes += body.byteLength\n if (snapshots.size >= EXTENSION_LIMITS.assetFiles || totalBytes > EXTENSION_LIMITS.assetBytes) {\n throw new MobileExtensionError('invalid_extension', 'asset tree exceeds its aggregate limit')\n }\n snapshots.set(key, Object.freeze({ body, digest: createHash('sha256').update(body).digest('hex'), name: entry.name }))\n }\n }\n await visit(assetsReal, '', 0)\n return snapshots\n}\n\ninterface LocalExtensionFingerprint {\n readonly manifest: LocalExtensionManifest\n readonly digest: string\n readonly scriptBody?: Buffer\n readonly styleBody?: Buffer\n readonly assets: ReadonlyMap<string, LocalAssetSnapshot>\n}\n\nasync function extensionFingerprint(directory: string): Promise<LocalExtensionFingerprint> {\n const root = await realExtensionRoot(directory)\n const manifestFile = await regularFile(join(root, 'extension.json'), EXTENSION_LIMITS.manifest, 'extension.json')\n const manifestBody = await readFile(manifestFile.path)\n const manifest = parseExtensionManifest(JSON.parse(manifestBody.toString('utf8')) as unknown)\n if (manifest.id !== basename(root)) throw new MobileExtensionError('invalid_manifest', 'extension id must match its directory name')\n const [host, script, style, assets] = await Promise.all([\n optionalBytes(root, 'host.mjs', EXTENSION_LIMITS.script, 'host.mjs'),\n optionalBytes(root, 'mobile.js', EXTENSION_LIMITS.script, 'mobile.js'),\n optionalBytes(root, 'mobile.css', EXTENSION_LIMITS.css, 'mobile.css'),\n assetSnapshot(root),\n ])\n const digest = createHash('sha256').update(`manifest:${manifestBody.byteLength}:`).update(createHash('sha256').update(manifestBody).digest())\n for (const [name, body] of [['host', host], ['script', script], ['style', style]] as const) {\n digest.update(`\\0${name}:${body?.byteLength ?? -1}:`)\n if (body !== undefined) digest.update(createHash('sha256').update(body).digest())\n }\n for (const [name, asset] of assets) {\n digest.update(`\\0asset:${Buffer.byteLength(name)}:${name}:${asset.body.byteLength}:${asset.digest}`)\n }\n return {\n manifest,\n digest: digest.digest('hex'),\n assets,\n ...(script === undefined ? {} : { scriptBody: script }),\n ...(style === undefined ? {} : { styleBody: style }),\n }\n}\n\nfunction routeKey(route: MobileHostRoute): string {\n const method = route.method.toUpperCase()\n const path = normalizeRoutePath(route.path)\n return `${method} ${route.kind ?? 'exact'} ${path}`\n}\n\nfunction normalizeRoutePath(value: string): string {\n if (typeof value !== 'string' || value.length === 0 || value.length > 256\n || value.includes('?') || value.includes('#') || value.includes('\\\\') || value.includes('\\0')\n || /[\\u0000-\\u001f\\u007f]/u.test(value)) throw new MobileExtensionError('invalid_route', 'extension route path is invalid')\n const normalizedInput = value.startsWith('/') ? value : `/${value}`\n const parts = normalizedInput.split('/')\n if (parts.some(part => part === '..' || part === '.')) throw new MobileExtensionError('invalid_route', 'extension route path is invalid')\n return normalizedInput === '/' ? '/' : normalizedInput.replace(/\\/+$/u, '')\n}\n\nfunction validateDefinition(definition: MobileExtensionDefinition): MobileExtensionDefinition {\n const manifest = parseExtensionManifest({\n schemaVersion: definition.schemaVersion,\n id: definition.id,\n name: definition.name,\n version: definition.version,\n ...(definition.description === undefined ? {} : { description: definition.description }),\n })\n const actionNames = new Set<string>()\n for (const [name, action] of Object.entries(definition.actions ?? {})) {\n if (!/^[a-z][a-z0-9-]{0,63}$/u.test(name) || action === null || typeof action !== 'object' || typeof action.run !== 'function' || actionNames.has(name)) {\n throw new MobileExtensionError('invalid_action', `invalid action ${name}`)\n }\n actionNames.add(name)\n }\n const routeNames = new Set<string>()\n const routes = (definition.routes ?? []).map(route => {\n if (route === null || typeof route !== 'object' || typeof route.handle !== 'function') throw new MobileExtensionError('invalid_route', 'invalid extension route')\n const method = route.method.toUpperCase()\n if (!['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) throw new MobileExtensionError('invalid_route', 'unsupported extension route method')\n const normalized: MobileHostRoute = { ...route, method, path: normalizeRoutePath(route.path) }\n const key = routeKey(normalized)\n if (routeNames.has(key)) throw new MobileExtensionError('duplicate_route', `duplicate route ${key}`)\n routeNames.add(key)\n return normalized\n })\n return Object.freeze({ ...manifest, ...(definition.actions === undefined ? {} : { actions: Object.freeze({ ...definition.actions }) }), ...(routes.length === 0 ? {} : { routes: Object.freeze(routes) }) })\n}\n\ninterface CombinedSignalLifetime {\n readonly signal: AbortSignal\n readonly cleanup: () => void\n}\n\nfunction combineSignalLifetime(first: AbortSignal, second: AbortSignal): CombinedSignalLifetime {\n if (first.aborted || second.aborted) {\n const aborted = new AbortController()\n aborted.abort(first.aborted ? first.reason : second.reason)\n return { signal: aborted.signal, cleanup: () => undefined }\n }\n const controller = new AbortController()\n const cleanup = (): void => {\n first.removeEventListener('abort', abortFirst)\n second.removeEventListener('abort', abortSecond)\n }\n const abortFirst = (): void => { cleanup(); controller.abort(first.reason) }\n const abortSecond = (): void => { cleanup(); controller.abort(second.reason) }\n first.addEventListener('abort', abortFirst, { once: true })\n second.addEventListener('abort', abortSecond, { once: true })\n return { signal: controller.signal, cleanup }\n}\n\n/** Validate and normalize an action input through either supported schema form. */\nfunction parseActionInput(schema: MobileHostAction['input'], input: unknown): unknown {\n if (schema === undefined) return input\n if (typeof schema === 'function') return (schema as (value: unknown) => unknown)(input)\n return schema.parse(input) ?? input\n}\n\n/** Combine two abort lifetimes without relying on AbortSignal.any in older WebViews. */\nexport function combineSignals(first: AbortSignal, second: AbortSignal): AbortSignal {\n return combineSignalLifetime(first, second).signal\n}\n\n/** Host registry and service consumed by both npm plugins and local extensions. */\nexport class MobileAccessService extends Service {\n private readonly registered = new Map<string, RegisteredExtension>()\n private readonly local = new Map<string, ActiveLocalExtension>()\n private readonly retired = new Map<string, RetiredLocalExtension>()\n private readonly failures = new Map<string, string>()\n private readonly contentListeners = new Set<() => void>()\n private contentHash = createHash('sha256').update('').digest('hex')\n private localRoot: string | undefined\n private localContext: Context | undefined\n private localTimer: NodeJS.Timeout | undefined\n private localRefreshing: Promise<void> | undefined\n private localRefreshAbort: AbortController | undefined\n private localLifecycle = 0\n private localClosed = true\n\n constructor(ctx: Context) { super(ctx, 'mobileAccess') }\n\n /** Register a normal Cordis extension and return an idempotent disposer. */\n registerExtension(definition: MobileExtensionDefinition): () => void {\n const validated = validateDefinition(definition)\n if (this.registered.has(validated.id) || this.local.has(validated.id)) throw new Error(`mobile extension id already registered: ${validated.id}`)\n const dispose = (): void => {\n const current = this.registered.get(validated.id)\n if (current?.dispose === dispose) {\n this.registered.delete(validated.id)\n this.updateContentHash()\n }\n }\n this.registered.set(validated.id, { definition: validated, dispose })\n this.updateContentHash()\n return dispose\n }\n\n /** Aggregate digest covering every registered and active local extension. */\n contentDigest(): string {\n return this.contentHash\n }\n\n /** Subscribe to committed extension generation changes. */\n onContentChanged(listener: () => void): () => void {\n this.contentListeners.add(listener)\n return () => { this.contentListeners.delete(listener) }\n }\n\n private updateContentHash(): void {\n const parts = [\n ...[...this.registered.values()].map(entry => entry.definition.id),\n ...[...this.local.values()].map(active => `${active.manifest.id}:${active.digest}`),\n ]\n const next = createHash('sha256').update(parts.sort().join('|')).digest('hex')\n if (next === this.contentHash) return\n this.contentHash = next\n for (const listener of this.contentListeners) {\n try { listener() } catch { /* One observer cannot block a committed generation. */ }\n }\n }\n\n /** Return the current client-facing manifest, deterministically sorted by id. */\n manifest(): readonly MobileExtensionClientEntry[] {\n const entries = new Map<string, MobileExtensionClientEntry>()\n for (const { definition } of this.registered.values()) entries.set(definition.id, {\n schemaVersion: 1, id: definition.id, name: definition.name, version: definition.version,\n ...(definition.description === undefined ? {} : { description: definition.description }),\n })\n for (const active of this.local.values()) entries.set(active.manifest.id, {\n ...active.manifest,\n generation: active.digest,\n ...(active.scriptBody === undefined ? {} : { scriptUrl: `/mobile-access/extensions/${active.manifest.id}/mobile.js?generation=${active.digest}` }),\n ...(active.styleBody === undefined ? {} : { styleUrl: `/mobile-access/extensions/${active.manifest.id}/mobile.css?generation=${active.digest}` }),\n assetsUrl: `/mobile-access/extensions/${active.manifest.id}/assets/`,\n })\n return [...entries.values()].sort((left, right) => left.id.localeCompare(right.id))\n }\n\n /** Return loaded and failed local extension counts without exposing host errors. */\n status(): MobileExtensionStatus {\n return Object.freeze({ loaded: this.registered.size + this.local.size, failed: this.failures.size })\n }\n\n /** Locate one active extension. */\n extension(id: string, generation?: string): MobileExtensionDefinition | ActiveLocalExtension | undefined {\n if (generation !== undefined) {\n const current = this.local.get(id)\n if (current?.digest === generation) return current\n const previous = this.retired.get(id)?.active\n return previous?.digest === generation ? previous : undefined\n }\n return this.local.get(id) ?? this.registered.get(id)?.definition\n }\n\n /** Return the active local generation signal for gateway cancellation wiring. */\n signal(id: string, generation?: string): AbortSignal | undefined {\n const extension = this.extension(id, generation)\n return extension !== undefined && 'host' in extension ? extension.controller.signal : undefined\n }\n\n /** Read a local client entry after validating that it remains inside its directory. */\n async readClientFile(id: string, kind: 'script' | 'style', signal?: AbortSignal, generation?: string): Promise<{ readonly body: Buffer; readonly digest: string }> {\n signal?.throwIfAborted()\n const selected = this.extension(id, generation)\n const active = selected !== undefined && 'host' in selected ? selected : undefined\n if (active === undefined) throw new MobileExtensionError('extension_generation_not_found', 'extension generation not found', 404)\n const snapshot = kind === 'script' ? active.scriptBody : active.styleBody\n if (snapshot === undefined) throw new MobileExtensionError('extension_asset_not_found', 'extension asset not found', 404)\n const body = Buffer.from(snapshot)\n return { body, digest: createHash('sha256').update(body).digest('hex') }\n }\n\n /** Read a generation-pinned static asset from its validated snapshot. */\n async readAsset(id: string, assetPath: string, signal?: AbortSignal, generation?: string): Promise<{ readonly body: Buffer; readonly digest: string; readonly name: string }> {\n signal?.throwIfAborted()\n const selected = this.extension(id, generation)\n const active = selected !== undefined && 'host' in selected ? selected : undefined\n if (active === undefined) throw new MobileExtensionError('extension_generation_not_found', 'extension generation not found', 404)\n const normalized = normalizeRelativePath(assetPath, 'asset')\n const asset = active.assets.get(normalized)\n if (asset === undefined) throw new MobileExtensionError('extension_asset_not_found', 'extension asset not found', 404)\n return { body: Buffer.from(asset.body), digest: asset.digest, name: asset.name }\n }\n\n /** Invoke one action after parsing its input and binding the request lifetime. */\n async invoke(id: string, actionName: string, input: unknown, context: MobileActionContext, generation?: string): Promise<unknown> {\n const extension = this.extension(id, generation)\n if (extension === undefined) throw new MobileExtensionError('extension_not_found', 'extension not found', 404)\n const definition = 'host' in extension ? extension.host : extension\n const action = definition.actions?.[actionName]\n if (action === undefined) throw new MobileExtensionError('action_not_found', 'action not found', 404)\n let parsed: unknown\n try { parsed = parseActionInput(action.input, input) } catch { throw new MobileExtensionError('invalid_action_input', 'action input is invalid', 400) }\n const lifetime = 'host' in extension ? combineSignalLifetime(extension.controller.signal, context.signal) : undefined\n const signal = lifetime?.signal ?? context.signal\n try { return await action.run({ ...context, signal }, parsed) } catch (error) {\n if (error instanceof MobileExtensionError) throw error\n throw new MobileExtensionError('extension_failed', 'extension action failed', 500)\n } finally { lifetime?.cleanup() }\n }\n\n /** Match one route and invoke it with a generation-bound abort signal. */\n async route(id: string, method: string, pathname: string, request: MobileRouteRequest, generation?: string): Promise<MobileRouteResponse> {\n const extension = this.extension(id, generation)\n if (extension === undefined) throw new MobileExtensionError('extension_not_found', 'extension not found', 404)\n const definition = 'host' in extension ? extension.host : extension\n const route = definition.routes?.find((candidate: MobileHostRoute) => {\n if (candidate.method !== method) return false\n return (candidate.kind ?? 'exact') === 'exact'\n ? candidate.path === pathname\n : pathname === candidate.path || pathname.startsWith(`${candidate.path}/`)\n })\n if (route === undefined) throw new MobileExtensionError('route_not_found', 'route not found', 404)\n const lifetime = 'host' in extension ? combineSignalLifetime(extension.controller.signal, request.signal) : undefined\n let releaseLifetime = true\n try {\n const routeRequest = lifetime === undefined ? request : { ...request, signal: lifetime.signal }\n const result = await route.handle(routeRequest)\n if (result === null || typeof result !== 'object' || typeof result.body !== 'string' && !(result.body instanceof Uint8Array) && !isReadable(result.body)) {\n throw new MobileExtensionError('invalid_route_response', 'extension returned an invalid response', 500)\n }\n if (lifetime !== undefined && isReadable(result.body)) {\n releaseLifetime = false\n releaseSignalLifetimeWhenStreamSettles(result.body, lifetime.cleanup)\n }\n return result\n } catch (error) {\n if (error instanceof MobileExtensionError) throw error\n throw new MobileExtensionError('extension_failed', 'extension route failed', 500)\n } finally { if (releaseLifetime) lifetime?.cleanup() }\n }\n\n /** Start the local directory watcher; an absent directory is intentionally inert. */\n async startLocal(root: string, context: Context): Promise<void> {\n const targetRoot = resolve(root)\n if (this.localRoot !== undefined && resolve(this.localRoot) !== targetRoot) await this.stopLocal()\n if (this.localTimer !== undefined) clearInterval(this.localTimer)\n const lifecycle = ++this.localLifecycle\n this.localRoot = targetRoot; this.localContext = context; this.localClosed = false\n await mkdir(this.localRoot, { recursive: true })\n if (this.localClosed || this.localLifecycle !== lifecycle || this.localRoot !== targetRoot || this.localContext !== context) return\n await this.refreshLocal()\n if (this.localClosed || this.localLifecycle !== lifecycle || this.localRoot !== targetRoot || this.localContext !== context) return\n this.localTimer = setInterval(() => { void this.refreshLocal() }, 2_000)\n this.localTimer.unref()\n }\n\n /** Stop the watcher and abort every local host generation. */\n async stopLocal(): Promise<void> {\n this.localClosed = true\n const lifecycle = ++this.localLifecycle\n if (this.localTimer !== undefined) clearInterval(this.localTimer)\n this.localTimer = undefined\n const refreshing = this.localRefreshing\n this.localRefreshAbort?.abort()\n const previous = [...this.local.values(), ...[...this.retired.values()].map(entry => entry.active)]\n this.local.clear()\n for (const entry of this.retired.values()) clearTimeout(entry.timer)\n this.retired.clear()\n this.failures.clear()\n this.updateContentHash()\n await Promise.allSettled([\n abortAndDisposeLocal(previous),\n ...(refreshing === undefined ? [] : [refreshing]),\n ])\n if (this.localLifecycle !== lifecycle) return\n const late = [...this.local.values(), ...[...this.retired.values()].map(entry => entry.active)]\n this.local.clear()\n for (const entry of this.retired.values()) clearTimeout(entry.timer)\n this.retired.clear()\n this.failures.clear()\n this.updateContentHash()\n await abortAndDisposeLocal(late)\n if (this.localTimer !== undefined) clearInterval(this.localTimer)\n this.localTimer = undefined\n }\n\n /** Refresh all local extensions atomically; failures keep the previous snapshot. */\n refreshLocal(): Promise<void> {\n if (this.localRefreshing !== undefined) return this.localRefreshing\n const controller = new AbortController()\n this.localRefreshAbort = controller\n const refreshing = this.stageAndCommit(controller.signal).finally(() => {\n if (this.localRefreshing === refreshing) this.localRefreshing = undefined\n if (this.localRefreshAbort === controller) this.localRefreshAbort = undefined\n })\n this.localRefreshing = refreshing\n return refreshing\n }\n\n private async stageAndCommit(signal: AbortSignal): Promise<void> {\n if (this.localClosed || signal.aborted || this.localRoot === undefined || this.localContext === undefined) return\n let names: string[] = []\n try {\n const directory = await opendir(this.localRoot)\n try { for await (const entry of directory) if (entry.isDirectory() && !entry.isSymbolicLink()) names.push(entry.name) }\n finally { await directory.close().catch(() => undefined) }\n } catch { return }\n names.sort()\n const staged: ActiveLocalExtension[] = []\n const stagedFresh: ActiveLocalExtension[] = []\n let failingName = 'local'\n try {\n for (const name of names) {\n signal.throwIfAborted()\n failingName = name\n const directory = join(this.localRoot, name)\n const fingerprint = await extensionFingerprint(directory)\n const current = this.local.get(fingerprint.manifest.id)\n const retired = this.retired.get(fingerprint.manifest.id)?.active\n const previous = current?.digest === fingerprint.digest ? current : retired?.digest === fingerprint.digest ? retired : undefined\n if (previous?.digest === fingerprint.digest) staged.push(previous)\n else {\n const fresh = await loadLocalExtension(directory, this.localContext, fingerprint, signal)\n try {\n signal.throwIfAborted()\n const confirmed = await extensionFingerprint(directory)\n if (confirmed.digest !== fingerprint.digest) {\n throw new MobileExtensionError('extension_changed_during_activation', `extension ${fingerprint.manifest.id} changed during activation`, 409)\n }\n } catch (error) {\n await abortAndDisposeLocal([fresh])\n throw error\n }\n staged.push(fresh); stagedFresh.push(fresh)\n }\n }\n if (this.localClosed || signal.aborted || this.localRoot === undefined || this.localContext === undefined) {\n await abortAndDisposeLocal(stagedFresh)\n return\n }\n const duplicate = new Set<string>()\n for (const entry of staged) {\n if (duplicate.has(entry.manifest.id) || this.registered.has(entry.manifest.id)) throw new MobileExtensionError('duplicate_extension', `duplicate extension id ${entry.manifest.id}`)\n duplicate.add(entry.manifest.id)\n }\n const previous = [...this.local.values()]\n for (const entry of staged) {\n const retired = this.retired.get(entry.manifest.id)\n if (retired?.active === entry) {\n clearTimeout(retired.timer)\n this.retired.delete(entry.manifest.id)\n }\n }\n const stagedIds = new Set(staged.map(entry => entry.manifest.id))\n const removed: ActiveLocalExtension[] = []\n for (const entry of previous) {\n if (staged.includes(entry)) continue\n if (stagedIds.has(entry.manifest.id)) {\n this.retire(entry)\n continue\n }\n removed.push(entry)\n const retired = this.retired.get(entry.manifest.id)\n if (retired !== undefined) {\n clearTimeout(retired.timer)\n this.retired.delete(entry.manifest.id)\n removed.push(retired.active)\n }\n }\n this.local.clear()\n for (const entry of staged) this.local.set(entry.manifest.id, entry)\n for (const entry of staged) this.failures.delete(entry.manifest.id)\n for (const name of names) this.failures.delete(name)\n for (const failure of this.failures.keys()) {\n if (failure !== 'local' && !names.includes(failure)) this.failures.delete(failure)\n }\n this.failures.delete('local')\n if (removed.length > 0) void abortAndDisposeLocal(removed)\n this.updateContentHash()\n } catch (error) {\n await abortAndDisposeLocal(stagedFresh)\n if (this.localClosed || signal.aborted) return\n const message = error instanceof Error ? error.message : String(error)\n this.failures.set(failingName, message)\n if (!(error instanceof MobileExtensionError)) this.ctx.logger.warn(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n private retire(active: ActiveLocalExtension): void {\n const previous = this.retired.get(active.manifest.id)\n if (previous?.active === active) return\n if (previous !== undefined) {\n clearTimeout(previous.timer)\n this.retired.delete(active.manifest.id)\n void abortAndDisposeLocal([previous.active])\n }\n const timer = setTimeout(() => {\n const current = this.retired.get(active.manifest.id)\n if (current?.active !== active) return\n this.retired.delete(active.manifest.id)\n void abortAndDisposeLocal([active])\n }, RETIRED_GENERATION_TTL_MS)\n timer.unref()\n this.retired.set(active.manifest.id, { active, timer })\n }\n}\n\nfunction isReadable(value: unknown): value is Readable {\n return value !== null && typeof value === 'object' && typeof (value as { pipe?: unknown }).pipe === 'function'\n}\n\nfunction releaseSignalLifetimeWhenStreamSettles(stream: Readable, cleanup: () => void): void {\n let stopObserving: (() => void) | undefined\n stopObserving = finished(stream, () => {\n stopObserving?.()\n cleanup()\n })\n}\n\nfunction invokeCleanups(cleanups: readonly (() => void | Promise<void>)[]): Promise<unknown>[] {\n const pending: Promise<unknown>[] = []\n for (const cleanup of [...cleanups].reverse()) {\n try { pending.push(Promise.resolve(cleanup())) } catch { /* extension teardown cannot block the owner */ }\n }\n return pending\n}\n\nasync function settleBounded(pending: readonly Promise<unknown>[], timeoutMs: number): Promise<void> {\n if (pending.length === 0) return\n let timer: NodeJS.Timeout | undefined\n await Promise.race([\n Promise.allSettled(pending),\n new Promise<void>(resolveTimeout => { timer = setTimeout(resolveTimeout, timeoutMs) }),\n ])\n if (timer !== undefined) clearTimeout(timer)\n}\n\nasync function abortAndDisposeLocal(entries: readonly ActiveLocalExtension[]): Promise<void> {\n const pending: Promise<unknown>[] = []\n for (const entry of entries) {\n entry.controller.abort()\n pending.push(...invokeCleanups(entry.cleanups))\n }\n await settleBounded(pending, HOST_TEARDOWN_TIMEOUT_MS)\n}\n\nasync function loadLocalExtension(directory: string, context: Context, known?: LocalExtensionFingerprint, parentSignal?: AbortSignal): Promise<ActiveLocalExtension> {\n const root = await realExtensionRoot(directory)\n const manifestFile = await regularFile(join(root, 'extension.json'), EXTENSION_LIMITS.manifest, 'extension.json')\n const manifest = known?.manifest ?? parseExtensionManifest(JSON.parse(await readFile(manifestFile.path, 'utf8')) as unknown)\n if (manifest.id !== basename(root)) throw new MobileExtensionError('invalid_manifest', 'extension id must match its directory name')\n const scriptBody = known === undefined\n ? await optionalFile(root, 'mobile.js', EXTENSION_LIMITS.script, 'mobile.js').then(path => path === undefined ? undefined : readFile(path))\n : known.scriptBody\n const styleBody = known === undefined\n ? await optionalFile(root, 'mobile.css', EXTENSION_LIMITS.css, 'mobile.css').then(path => path === undefined ? undefined : readFile(path))\n : known.styleBody\n const assets = known?.assets ?? await assetSnapshot(root)\n const hostFile = await optionalFile(root, 'host.mjs', EXTENSION_LIMITS.script, 'host.mjs')\n const controller = new AbortController()\n const actions: Record<string, MobileHostAction> = {}\n const routes: MobileHostRoute[] = []\n const cleanups: (() => void | Promise<void>)[] = []\n const pendingEffects: Promise<void>[] = []\n let activationOpen = true\n const onParentAbort = (): void => { controller.abort(parentSignal?.reason) }\n if (parentSignal?.aborted === true) onParentAbort()\n else parentSignal?.addEventListener('abort', onParentAbort, { once: true })\n const ensureActivationOpen = (): void => {\n if (!activationOpen || controller.signal.aborted) throw new MobileExtensionError('host_activation_closed', `extension ${manifest.id} activation is closed`, 409)\n }\n const api: HostApi = {\n manifest,\n context,\n schema: z,\n signal: controller.signal,\n action(name, spec) { ensureActivationOpen(); if (actions[name] !== undefined) throw new MobileExtensionError('duplicate_action', `duplicate action ${name}`); actions[name] = spec },\n route(spec) { ensureActivationOpen(); routes.push(spec) },\n effect(setup) {\n ensureActivationOpen()\n const result = setup()\n if (result instanceof Promise) {\n pendingEffects.push(result.then(async cleanup => {\n if (typeof cleanup !== 'function') return\n if (activationOpen) cleanups.push(cleanup)\n else await cleanup()\n }))\n } else if (typeof result === 'function') {\n if (activationOpen) cleanups.push(result)\n else void Promise.resolve(result()).catch(() => undefined)\n }\n },\n }\n try {\n const activate = async (): Promise<void> => {\n controller.signal.throwIfAborted()\n if (hostFile !== undefined) {\n const digest = createHash('sha256').update(await readFile(hostFile)).digest('hex')\n controller.signal.throwIfAborted()\n let imported: LocalHostModule\n try { imported = await import(`${pathToFileURL(hostFile).href}?dsh_generation=${digest}`) as LocalHostModule }\n catch { throw new MobileExtensionError('host_load_failed', `could not load ${manifest.id}/host.mjs`, 500) }\n controller.signal.throwIfAborted()\n if (imported.default !== undefined) await imported.default(api)\n }\n await Promise.all(pendingEffects)\n }\n await withActivationTimeout(activate(), manifest.id, controller.signal)\n const host = validateDefinition({ ...manifest, actions, routes })\n activationOpen = false\n const digest = known?.digest ?? createHash('sha256').update(manifest.id).digest('hex')\n return Object.freeze({ manifest, directory: root, ...(scriptBody === undefined ? {} : { scriptBody }), ...(styleBody === undefined ? {} : { styleBody }), assets, host, controller, cleanups: Object.freeze(cleanups), digest })\n } catch (error) {\n activationOpen = false\n controller.abort()\n const cleanupPromises = invokeCleanups(cleanups.splice(0))\n await settleBounded([...pendingEffects, ...cleanupPromises], HOST_TEARDOWN_TIMEOUT_MS)\n throw error\n } finally {\n parentSignal?.removeEventListener('abort', onParentAbort)\n }\n}\n\n/** Construct the service in a Cordis plugin without importing DSH internals. */\nexport function createMobileAccessService(ctx: Context): MobileAccessService {\n return new MobileAccessService(ctx)\n}\n","/** Browser-facing pairing and reauthentication pages with language negotiation. */\nexport type AuthPageLocale = 'zh' | 'en' | 'it'\n\ninterface AuthPageCopy {\n readonly lang: string\n readonly pairTitle: string\n readonly pairHeading: string\n readonly pairingCode: string\n readonly deviceName: string\n readonly pair: string\n readonly pairing: string\n readonly pairFailed: string\n readonly unavailable: string\n readonly reconnectTitle: string\n readonly reconnectHeading: string\n readonly restoring: string\n readonly noLongerPaired: string\n readonly openPairing: string\n}\n\nconst COPY: Record<AuthPageLocale, AuthPageCopy> = {\n en: {\n lang: 'en', pairTitle: 'Pair DSH mobile access', pairHeading: 'Pair this device',\n pairingCode: 'Pairing code', deviceName: 'Device name', pair: 'Pair', pairing: 'Pairing…',\n pairFailed: 'Pairing failed', unavailable: 'The computer is unavailable.',\n reconnectTitle: 'Reconnect DSH mobile access', reconnectHeading: 'Reconnect this device',\n restoring: 'Restoring the secure Session…', noLongerPaired: 'This device is no longer paired. Open pairing on the computer, then pair it again.',\n openPairing: 'Open pairing',\n },\n zh: {\n lang: 'zh-CN', pairTitle: '配对 DSH 移动访问', pairHeading: '配对此设备',\n pairingCode: '配对码', deviceName: '设备名称', pair: '配对', pairing: '正在配对…',\n pairFailed: '配对失败', unavailable: '电脑当前不可用。',\n reconnectTitle: '重新连接 DSH 移动访问', reconnectHeading: '重新连接此设备',\n restoring: '正在恢复安全会话…', noLongerPaired: '此设备已不在配对列表中。请在电脑端打开配对,然后重新配对。',\n openPairing: '打开配对页面',\n },\n it: {\n lang: 'it-IT', pairTitle: 'Abbina accesso mobile DSH', pairHeading: 'Abbina questo dispositivo',\n pairingCode: 'Codice di abbinamento', deviceName: 'Nome dispositivo', pair: 'Abbina', pairing: 'Abbinamento…',\n pairFailed: 'Abbinamento non riuscito', unavailable: 'Il computer non è disponibile.',\n reconnectTitle: 'Riconnetti accesso mobile DSH', reconnectHeading: 'Riconnetti questo dispositivo',\n restoring: 'Ripristino della sessione sicura…', noLongerPaired: 'Questo dispositivo non è più abbinato. Apri l’abbinamento sul computer e ripeti la procedura.',\n openPairing: 'Apri abbinamento',\n },\n}\n\n/** Choose Chinese, Italian, or English from an HTTP Accept-Language header. */\nexport function resolveAuthPageLocale(header: string | undefined): AuthPageLocale {\n const values = header?.split(',').map((value, index) => {\n const [language, ...parameters] = value.split(';')\n const quality = parameters.find(parameter => parameter.trim().toLowerCase().startsWith('q='))\n const parsedQuality = quality === undefined ? 1 : Number(quality.trim().slice(2))\n return {\n language: language?.trim().toLowerCase() ?? '',\n quality: Number.isFinite(parsedQuality) ? Math.max(0, Math.min(1, parsedQuality)) : 0,\n index,\n }\n }).filter(value => value.quality > 0).sort((left, right) => right.quality - left.quality || left.index - right.index).map(value => value.language) ?? []\n for (const value of values) {\n if (value.startsWith('zh')) return 'zh'\n if (value.startsWith('it')) return 'it'\n if (value.startsWith('en')) return 'en'\n }\n return 'en'\n}\n\n/** Render the browser pairing form without reflecting request data into HTML. */\nexport function renderPairPage(locale: AuthPageLocale): string {\n const copy = COPY[locale]\n return `<!doctype html>\n<html lang=\"${copy.lang}\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\">\n<title>${copy.pairTitle}</title>\n<main>\n <h1>${copy.pairHeading}</h1>\n <form id=\"pair-form\">\n <label>${copy.pairingCode} <input id=\"pair-token\" autocomplete=\"one-time-code\" required></label>\n <label>${copy.deviceName} <input id=\"device-label\" maxlength=\"64\" autocomplete=\"off\"></label>\n <button type=\"submit\">${copy.pair}</button>\n <output id=\"pair-status\" aria-live=\"polite\"></output>\n </form>\n</main>\n<script src=\"/mobile-access/pair.js\" defer></script>\n</html>\n`\n}\n\n/** Render the pairing form script with locale-owned status messages. */\nexport function renderPairScript(locale: AuthPageLocale): string {\n const copy = COPY[locale]\n return `(() => {\n const form = document.getElementById('pair-form')\n const token = document.getElementById('pair-token')\n const label = document.getElementById('device-label')\n const status = document.getElementById('pair-status')\n const fragment = new URLSearchParams(location.hash.slice(1))\n const supplied = fragment.get('token')\n history.replaceState(null, '', location.pathname)\n if (supplied) token.value = supplied\n form.addEventListener('submit', async (event) => {\n event.preventDefault()\n status.value = ${JSON.stringify(copy.pairing)}\n try {\n const response = await fetch('/mobile-access/auth/pair', {\n method: 'POST',\n credentials: 'same-origin',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ token: token.value, label: label.value || undefined }),\n })\n if (!response.ok) {\n status.value = ${JSON.stringify(copy.pairFailed)}\n return\n }\n location.replace('/')\n } catch {\n status.value = ${JSON.stringify(copy.unavailable)}\n }\n })\n})()\n`\n}\n\n/** Render the browser reauthentication page. */\nexport function renderLoginPage(locale: AuthPageLocale): string {\n const copy = COPY[locale]\n return `<!doctype html>\n<html lang=\"${copy.lang}\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\">\n<title>${copy.reconnectTitle}</title>\n<main>\n <h1>${copy.reconnectHeading}</h1>\n <p id=\"login-progress\" aria-live=\"polite\">${copy.restoring}</p>\n <section id=\"login-failed\" hidden>\n <p>${copy.noLongerPaired}</p>\n <a href=\"/mobile-access/pair\">${copy.openPairing}</a>\n </section>\n</main>\n<script src=\"/mobile-access/login.js\" defer></script>\n</html>\n`\n}\n\n/** Render the reauthentication script with locale-owned failure text. */\nexport function renderLoginScript(locale: AuthPageLocale): string {\n const copy = COPY[locale]\n return `(() => {\n const candidate = new URL(location.href).searchParams.get('return')\n let returnPath = '/'\n if (candidate && candidate.startsWith('/')) {\n try {\n const resolved = new URL(candidate, location.origin)\n const pathname = decodeURIComponent(resolved.pathname)\n if (resolved.origin === location.origin && pathname !== '/mobile-access'\n && !pathname.startsWith('/mobile-access/') && !pathname.includes('\\\\\\\\')) {\n returnPath = resolved.pathname + resolved.search + resolved.hash\n }\n } catch {\n // Malformed untrusted return targets keep the safe root default.\n }\n }\n fetch('/mobile-access/auth/renew', {\n method: 'POST',\n credentials: 'same-origin',\n headers: { 'content-type': 'application/json' },\n body: '{}',\n }).then((response) => {\n if (response.ok) {\n location.replace(returnPath)\n return\n }\n document.getElementById('login-progress').hidden = true\n document.getElementById('login-failed').hidden = false\n }).catch(() => {\n document.getElementById('login-progress').textContent = ${JSON.stringify(copy.unavailable)}\n })\n})()\n`\n}\n","import { createHash, X509Certificate } from 'node:crypto'\nimport { createSocket, type Socket as DatagramSocket } from 'node:dgram'\nimport { lstat, readFile, stat } from 'node:fs/promises'\nimport { hostname } from 'node:os'\nimport { extname } from 'node:path'\nimport {\n createServer as createHttpServer,\n request as requestHttp,\n type ClientRequest,\n type IncomingHttpHeaders,\n type IncomingMessage,\n type OutgoingHttpHeaders,\n type Server as HttpServer,\n type ServerResponse,\n} from 'node:http'\nimport { createServer as createHttpsServer, type Server as HttpsServer, type ServerOptions } from 'node:https'\nimport { connect, isIP, type AddressInfo, type Socket } from 'node:net'\nimport { Transform, type TransformCallback } from 'node:stream'\nimport { pipeline } from 'node:stream/promises'\nimport { promisify } from 'node:util'\nimport { createGzip, gzip } from 'node:zlib'\nimport type { WebRoute } from '@deepseek-ai/dsh-host-webserver'\nimport Bonjour from 'bonjour-service'\nimport * as QRCode from 'qrcode'\nimport {\n AccessController,\n AccessError,\n BoundedRateLimiter,\n type DeviceSummary,\n type SessionAuthorization,\n} from './access.js'\nimport type { ResolvedGatewayConfig } from './config.js'\nimport { ensureMobileCompatibility, MOBILE_COMPAT_PATH } from './mobile-compat-bootstrap.js'\nimport {\n AUTH_PREFIX,\n assertExternalTrust,\n assertLocalAdminTrust,\n cookie,\n CSRF_COOKIE,\n CSRF_HEADER,\n DEVICE_COOKIE,\n HttpError,\n LOCAL_ADMIN_PREFIX,\n parseCookies,\n parseRequestTarget,\n readJsonObject,\n sendFailure,\n sendJson,\n SESSION_COOKIE,\n setSecurityHeaders,\n WS_PATHS,\n} from './http-security.js'\nimport type { BlockedUpgradePathEntry, BlockedUpgradePathLog } from './websocket-paths.js'\nimport {\n DSH_MOBILE_VERSION,\n MINIMUM_ANDROID_APP_VERSION,\n MOBILE_METADATA_VERSION,\n} from './version.js'\nimport { addressAllowed, isLoopbackAddress, type ParsedCidr, RequestTrustPolicy } from './network.js'\nimport type { DeviceStore } from './storage.js'\nimport { listComputerImages, readComputerImage } from './computer-images.js'\nimport {\n EXTENSION_LIMITS,\n MobileExtensionError,\n type MobileAccessService,\n type MobileRouteRequest,\n type MobileRouteResponse,\n} from './extensions.js'\nimport {\n renderLoginPage,\n renderLoginScript,\n renderPairPage,\n renderPairScript,\n resolveAuthPageLocale,\n} from './auth-pages.js'\n\ntype GatewayServer = HttpServer | HttpsServer\n\ninterface ActiveRequest {\n readonly sessionKey: string\n readonly deviceId: string\n readonly expiresAt: number\n readonly abort: () => void\n readonly timer: NodeJS.Timeout\n}\n\ninterface ActiveWebSocket {\n readonly sessionKey: string\n readonly deviceId: string\n readonly client: Socket\n readonly upstream: Socket\n readonly timer: NodeJS.Timeout\n}\n\nconst MAX_CONTROL_BODY_BYTES = 16 * 1024\nconst MAX_HEADER_BYTES = 16 * 1024\nconst MOBILE_HISTORY_PAGE_MESSAGES = 10\nconst SESSION_HISTORY_PATH = '/api/session.history'\nconst DISCOVERY_QUERY = Buffer.from('DSH_MOBILE_DISCOVER_V1', 'ascii')\nconst DISCOVERY_PROTOCOL = 1\nconst DISCOVERY_INTERVAL_MS = 3_000\nconst MDNS_SERVICE_TYPE = 'dsh-mobile'\nconst MOBILE_LAYOUT_MODULE = '@deepseek-ai/dsh-client-ui-layout'\nconst MOBILE_LAYOUT_PATH = `${AUTH_PREFIX}/mobile-layout.js`\nconst MOBILE_BOOT_BATCH_PREFIX = `${AUTH_PREFIX}/mobile-boot/`\nconst MAX_MOBILE_BOOT_BATCH_BYTES = 32 * 1024 * 1024\nconst MAX_MOBILE_BOOT_ENTRY_BYTES = 8 * 1024 * 1024\n/**\n * Per-merged-batch byte budget. One upstream application batch can carry dozens\n * of client bundles whose combined size exceeds the hard batch cap; the layout\n * batch is therefore chunked into multiple merged batches, each kept safely\n * below {@link MAX_MOBILE_BOOT_BATCH_BYTES} so assembly can never reject it.\n */\nconst MOBILE_BOOT_CHUNK_BYTES = 16 * 1024 * 1024\n/** Bytes the assembly emits after each entry (`body` plus `\\n;\\n`). */\nconst MOBILE_BOOT_SEPARATOR_BYTES = 3\n/** Freshness window for cached upstream bundle byte sizes. */\nconst MOBILE_BOOT_SIZE_CACHE_TTL_MS = 30_000\n/** Marker header distinguishing size probes from ordinary proxied bundle fetches. */\nconst MOBILE_BOOT_SIZE_PROBE_HEADER = 'x-dsh-mobile-size-probe'\nconst MAX_MOBILE_BOOT_BATCHES = 8\nconst MOBILE_BOOT_UPSTREAM_ATTEMPTS = 4\nconst MOBILE_BOOT_RETRY_DELAY_MS = 150\n\nfunction upstreamPluginBundleUrl(source: string, upstreamOrigin: URL): URL | undefined {\n if ((!source.startsWith('/plugins/') && !source.startsWith('plugins/')) || source.includes('#')) return undefined\n try {\n const target = new URL(source, new URL('/', upstreamOrigin))\n if (target.origin !== upstreamOrigin.origin || !target.pathname.startsWith('/plugins/')) return undefined\n return target\n } catch {\n return undefined\n }\n}\n\nconst TRANSIENT_UPSTREAM_ERROR_CODES = new Set([\n 'EAI_AGAIN',\n 'ECONNABORTED',\n 'ECONNREFUSED',\n 'ECONNRESET',\n 'EPIPE',\n 'EHOSTUNREACH',\n 'ENETDOWN',\n 'ENETRESET',\n 'ENETUNREACH',\n 'ETIMEDOUT',\n 'ERR_STREAM_PREMATURE_CLOSE',\n])\nconst UPSTREAM_AUTH_REFRESH_MARGIN_MS = 60_000\nconst UPSTREAM_COOKIE_PAIR = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+=[\\x21-\\x3A\\x3C-\\x7E]*$/u\nconst CUSTOM_STYLE_FALLBACK = '/* Add mobile overrides in the DSH home mobile-access/mobile.css file. */\\n'\nconst CUSTOM_SCRIPT_FALLBACK = 'window.dshMobile?.register(() => undefined)\\n'\nconst EXTENSION_CHANGE_POLL_MS = 2_000\nconst EXTENSION_EVENT_HEARTBEAT_MS = 15_000\nconst MOBILE_CLIENT_MODULE = 'dsh-mobile'\nconst CONNECTION_MODULE = '@deepseek-ai/dsh-client-connection'\nconst RUNTIME_MODULE = '@deepseek-ai/dsh-client-runtime'\nconst RENDERER_MODULE = '@deepseek-ai/dsh-client-ui-renderer'\nconst SIDEBAR_MODULE = '@deepseek-ai/dsh-client-ui-sidebar'\nconst SETTINGS_MODULE = '@deepseek-ai/dsh-client-ui-settings'\nconst API_GATEWAY_MODULE = '@deepseek-ai/dsh-api-gateway'\nconst API_REMOTES_MODULE = '@deepseek-ai/dsh-api-remotes'\nconst MOBILE_LAYOUT_DEPENDENCY_PROFILES = Object.freeze([\n Object.freeze({\n slots: RUNTIME_MODULE,\n dependencies: Object.freeze([RUNTIME_MODULE, '@deepseek-ai/dsh-client-ui-theme']),\n }),\n Object.freeze({\n slots: RENDERER_MODULE,\n dependencies: Object.freeze([\n '@deepseek-ai/dsh-client-locale',\n RENDERER_MODULE,\n '@deepseek-ai/dsh-client-ui-session',\n '@deepseek-ai/dsh-client-ui-theme',\n ]),\n }),\n])\nconst MOBILE_CSRF_FETCH_BOOTSTRAP = `(()=>{const nativeFetch=window.fetch.bind(window);window.fetch=(input,init)=>{const source=input instanceof Request?input:undefined;const method=String(init?.method??source?.method??'GET').toUpperCase();if(method==='GET'||method==='HEAD')return nativeFetch(input,init);const raw=typeof input==='string'?input:input instanceof URL?input.href:source?.url;if(raw===undefined||new URL(raw,location.href).origin!==location.origin)return nativeFetch(input,init);const headers=new Headers(init?.headers??source?.headers);if(!headers.has(${JSON.stringify(CSRF_HEADER)})){const prefix=${JSON.stringify(`${CSRF_COOKIE}=`)};const token=document.cookie.split(';').map(value=>value.trim()).find(value=>value.startsWith(prefix))?.slice(prefix.length);if(token!==undefined)headers.set(${JSON.stringify(CSRF_HEADER)},token)}return nativeFetch(input,{...init,headers})};})();`\n// Paired pages use the gateway's authenticated HTTP carrier; streams retain DSH's WebSocket transport.\nconst MOBILE_AUTHENTICATED_TRANSPORT_BOOTSTRAP = `(()=>{if(window.__DSH_TRANSPORT__!==undefined)throw new Error('DSH Mobile cannot replace an existing transport override');window.__DSH_TRANSPORT__={fetch:(input,init)=>window.fetch(input,init),ownsHost:true}})();`\nconst PAIR_PAGE = `<!doctype html>\n<html lang=\"en\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\">\n<title>Pair DSH mobile access</title>\n<main>\n <h1>Pair this device</h1>\n <form id=\"pair-form\">\n <label>Pairing code <input id=\"pair-token\" autocomplete=\"one-time-code\" required></label>\n <label>Device name <input id=\"device-label\" maxlength=\"64\" autocomplete=\"off\"></label>\n <button type=\"submit\">Pair</button>\n <output id=\"pair-status\"></output>\n </form>\n</main>\n<script src=\"/mobile-access/pair.js\" defer></script>\n</html>\n`\n\ninterface BootGraphEntry {\n id: string\n url: string\n rev: string\n inject?: string[]\n immediately?: boolean\n}\n\ninterface BootGraphBatch {\n phase: 'bootstrap' | 'application'\n url: string\n rev: string\n entries: string[]\n}\n\ninterface MobileBootBatchEntry {\n readonly id: string\n readonly url: string\n readonly rev: string\n}\n\ninterface MobileBootBatchPlan {\n readonly key: string\n readonly path: string\n readonly entries: readonly MobileBootBatchEntry[]\n}\n\ninterface RewrittenMobileIndex {\n readonly html: string\n /** Merged batches the gateway assembles and serves; pass-through rows are not plans. */\n readonly batches: readonly MobileBootBatchPlan[]\n}\n\n/** Parsed boot manifest with the layout entry rewired to the gateway-owned layout module. */\ninterface MobileBootPlanRef {\n readonly parsed: { rev: string; entries: BootGraphEntry[]; batches?: BootGraphBatch[] }\n readonly entries: BootGraphEntry[]\n readonly batches?: BootGraphBatch[]\n readonly slotProvider: string\n readonly assignment: string\n readonly replaceStart: number\n readonly replaceEnd: number\n readonly layoutBatch?: BootGraphBatch\n readonly planEntries?: readonly MobileBootBatchEntry[]\n}\n\ninterface SplitMobileBootBatch {\n readonly plans: readonly MobileBootBatchPlan[]\n readonly rows: readonly BootGraphBatch[]\n}\n\ninterface StoredMobileBootBatch {\n readonly plan: MobileBootBatchPlan\n body?: Buffer\n gzipBody?: Buffer\n etag?: string\n layoutMtimeMs?: number\n /** In-flight assembly shared by every concurrent requester of this batch. */\n assembly?: MobileBootBatchAssembly\n}\n\ninterface MobileBootBatchAssembly {\n readonly controller: AbortController\n readonly task: Promise<Buffer>\n}\n\nconst gzipBuffer = promisify(gzip)\n\nfunction ensureMobileViewport(html: string): string {\n const viewport = /<meta\\b(?=[^>]*\\bname\\s*=\\s*[\"']viewport[\"'])[^>]*>/iu\n const match = viewport.exec(html)\n if (match === null) {\n const head = /<head\\b[^>]*>/iu.exec(html)\n if (head?.index === undefined) return html\n const position = head.index + head[0].length\n return `${html.slice(0, position)}<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\">${html.slice(position)}`\n }\n if (/\\bviewport-fit\\s*=\\s*cover\\b/iu.test(match[0])) return html\n const content = /\\bcontent\\s*=\\s*([\"'])(.*?)\\1/iu\n const next = content.test(match[0])\n ? match[0].replace(content, (_whole, quote: string, value: string) => `content=${quote}${value},viewport-fit=cover${quote}`)\n : match[0].replace(/\\s*\\/?>$/u, ' content=\"width=device-width,initial-scale=1,viewport-fit=cover\">')\n return `${html.slice(0, match.index)}${next}${html.slice(match.index + match[0].length)}`\n}\n\nfunction orderAuthenticatedSettings(entries: BootGraphEntry[], slotsProvider: string): boolean {\n const mobile = entries.filter(entry => entry !== null && typeof entry === 'object' && entry.id === MOBILE_CLIENT_MODULE)\n const settings = entries.filter(entry => entry !== null && typeof entry === 'object' && entry.id === SETTINGS_MODULE)\n if (mobile.length === 0 || settings.length === 0) return false\n if (mobile.length !== 1 || settings.length !== 1) throw new Error('upstream DSH mobile settings graph is ambiguous')\n if (!Array.isArray(mobile[0]?.inject)\n || !mobile[0].inject.includes(CONNECTION_MODULE)\n || !mobile[0].inject.includes(SIDEBAR_MODULE)) {\n throw new Error('dsh-mobile client has unsupported dependencies')\n }\n if (!Array.isArray(settings[0]?.inject)) {\n throw new Error('upstream DSH settings module has unsupported dependencies')\n }\n const remoteSettings = !settings[0].inject.includes(CONNECTION_MODULE)\n if (remoteSettings) {\n if (!settings[0].inject.includes(API_REMOTES_MODULE) || slotsProvider !== RENDERER_MODULE) {\n throw new Error('upstream DSH settings module has unsupported dependencies')\n }\n const remotes = entries.filter(entry => entry !== null && typeof entry === 'object' && entry.id === API_REMOTES_MODULE)\n const gateway = entries.filter(entry => entry !== null && typeof entry === 'object' && entry.id === API_GATEWAY_MODULE)\n if (remotes.length !== 1 || !Array.isArray(remotes[0]?.inject) || !remotes[0].inject.includes(API_GATEWAY_MODULE)\n || gateway.length !== 1 || !Array.isArray(gateway[0]?.inject) || !gateway[0].inject.includes(CONNECTION_MODULE)) {\n throw new Error('upstream DSH settings Remote graph has unsupported dependencies')\n }\n // These package edges order factory arrival; authenticated transport trust is installed before boot.\n if (!gateway[0].inject.includes(MOBILE_CLIENT_MODULE)) gateway[0].inject = [...gateway[0].inject, MOBILE_CLIENT_MODULE]\n }\n mobile[0].inject = [CONNECTION_MODULE, slotsProvider]\n if (!settings[0].inject.includes(MOBILE_CLIENT_MODULE)) settings[0].inject = [...settings[0].inject, MOBILE_CLIENT_MODULE]\n return remoteSettings\n}\n\nfunction revisionedMobileBatchPath(entries: readonly MobileBootBatchEntry[]): { readonly key: string; readonly path: string } {\n const key = createHash('sha256')\n .update(DSH_MOBILE_VERSION)\n .update(JSON.stringify(entries))\n .digest('hex')\n return { key, path: `${MOBILE_BOOT_BATCH_PREFIX}${key}.js` }\n}\n\n/**\n * Parse one upstream boot manifest and rewire the layout entry onto the\n * gateway-owned layout module. The layout entry's URL and revision become the\n * local `mobile-layout.js` path; the dedicated frontend is authenticated in the\n * gateway, so the requested layout needs no upstream address.\n */\nexport function parseMobileBootPlan(html: string): MobileBootPlanRef {\n const assignment = /(?:window\\.__DSH_BOOT__|globalThis\\[\"__DSH_BOOT__\"\\])\\s*=\\s*/u.exec(html)\n if (assignment?.index === undefined) throw new Error('upstream DSH index has no boot manifest')\n const replaceStart = assignment.index\n const valueStart = replaceStart + assignment[0].length\n const scriptEnd = html.indexOf('</script>', valueStart)\n if (scriptEnd < 0) throw new Error('upstream DSH boot manifest script is incomplete')\n const source = html.slice(valueStart, scriptEnd).trim().replace(/;$/u, '')\n const parsed = JSON.parse(source) as { rev?: unknown; entries?: unknown; batches?: unknown }\n if (typeof parsed.rev !== 'string' || !Array.isArray(parsed.entries)) {\n throw new Error('upstream DSH boot manifest is malformed')\n }\n const entries = parsed.entries as BootGraphEntry[]\n const layout = entries.filter(entry => entry !== null && typeof entry === 'object' && entry.id === MOBILE_LAYOUT_MODULE)\n if (layout.length !== 1 || typeof layout[0]?.url !== 'string' || typeof layout[0].rev !== 'string') {\n throw new Error('upstream DSH boot manifest has no unique layout module')\n }\n if (!Array.isArray(layout[0].inject)) {\n throw new Error('upstream DSH layout module has unsupported dependencies')\n }\n const dependencyProfile = MOBILE_LAYOUT_DEPENDENCY_PROFILES.find(profile => (\n profile.dependencies.every(dependency => layout[0]?.inject?.includes(dependency))\n ))\n if (dependencyProfile === undefined) throw new Error('upstream DSH layout module has unsupported dependencies')\n layout[0].url = MOBILE_LAYOUT_PATH\n layout[0].rev = `dsh-mobile-layout-${DSH_MOBILE_VERSION}`\n const parsedBatches = parsed.batches\n if (parsedBatches === undefined) {\n return Object.freeze({\n parsed: parsed as { rev: string; entries: BootGraphEntry[] },\n entries,\n slotProvider: dependencyProfile.slots,\n assignment: assignment[0],\n replaceStart,\n replaceEnd: scriptEnd,\n })\n }\n if (!Array.isArray(parsedBatches)) throw new Error('upstream DSH boot manifest batches are malformed')\n const batches = parsedBatches as BootGraphBatch[]\n const entryById = new Map(entries.map(entry => [entry.id, entry]))\n if (entryById.size !== entries.length) throw new Error('upstream DSH boot manifest has duplicate entries')\n const layoutBatches: BootGraphBatch[] = []\n for (const batch of batches) {\n if (batch === null || typeof batch !== 'object'\n || (batch.phase !== 'bootstrap' && batch.phase !== 'application')\n || typeof batch.url !== 'string' || typeof batch.rev !== 'string'\n || !Array.isArray(batch.entries) || batch.entries.length === 0\n || batch.entries.some(id => typeof id !== 'string' || !entryById.has(id))) {\n throw new Error('upstream DSH boot manifest batches are malformed')\n }\n if (batch.entries.includes(MOBILE_LAYOUT_MODULE)) layoutBatches.push(batch)\n }\n if (layoutBatches.length !== 1 || layoutBatches[0]?.phase !== 'application') {\n throw new Error('upstream DSH boot manifest has no unique application layout batch')\n }\n const layoutBatch = layoutBatches[0]\n const planEntries = layoutBatch.entries.map((id): MobileBootBatchEntry => {\n const entry = entryById.get(id)\n if (entry === undefined || typeof entry.url !== 'string' || typeof entry.rev !== 'string') {\n throw new Error('upstream DSH boot manifest batches are malformed')\n }\n return Object.freeze({ id, url: entry.url, rev: entry.rev })\n })\n return Object.freeze({\n parsed: parsed as { rev: string; entries: BootGraphEntry[]; batches: BootGraphBatch[] },\n entries,\n batches,\n slotProvider: dependencyProfile.slots,\n assignment: assignment[0],\n replaceStart,\n replaceEnd: scriptEnd,\n layoutBatch,\n planEntries,\n })\n}\n\n/**\n * Partition one upstream application batch into merged batches plus pass-through\n * rows. Every entry whose bundle is at or above the per-entry cap keeps its own\n * upstream `/plugins` row (the gateway proxies it verbatim), because a merged\n * batch cannot carry it. The remaining entries are greedily packed into merged\n * batches under {@link MOBILE_BOOT_CHUNK_BYTES}; the layout module always merges,\n * since its body is the gateway-owned layout file, never an upstream fetch.\n */\nexport function splitMobileBootBatch(\n entries: readonly MobileBootBatchEntry[],\n sizes: ReadonlyMap<string, number>,\n passThrough: ReadonlySet<string>,\n): SplitMobileBootBatch {\n const rows: BootGraphBatch[] = []\n const plans: MobileBootBatchPlan[] = []\n let chunk: { ids: string[]; entries: MobileBootBatchEntry[]; bytes: number } | undefined\n const flush = (): void => {\n if (chunk === undefined || chunk.entries.length === 0) return\n const revision = revisionedMobileBatchPath(chunk.entries)\n rows.push({ phase: 'application', url: revision.path, rev: revision.key, entries: chunk.ids })\n plans.push(Object.freeze({ key: revision.key, path: revision.path, entries: Object.freeze(chunk.entries) }))\n chunk = undefined\n }\n for (const entry of entries) {\n const size = sizes.get(entry.url)\n const oversized = passThrough.has(entry.url)\n || (size !== undefined && size >= MAX_MOBILE_BOOT_ENTRY_BYTES)\n if (oversized && entry.id !== MOBILE_LAYOUT_MODULE) {\n // A pass-through row never joins a merged batch; it also must not split\n // the open chunk, because chunk contents are independent of row order.\n rows.push({ phase: 'application', url: entry.url, rev: entry.rev, entries: [entry.id] })\n continue\n }\n const bytes = (size ?? 0) + MOBILE_BOOT_SEPARATOR_BYTES\n if (chunk !== undefined && chunk.entries.length > 0 && chunk.bytes + bytes > MOBILE_BOOT_CHUNK_BYTES) flush()\n chunk ??= { ids: [], entries: [], bytes: 0 }\n chunk.ids.push(entry.id)\n chunk.entries.push(entry)\n chunk.bytes += bytes\n }\n flush()\n return { plans, rows }\n}\n\nfunction rewriteMobileIndexWithBatch(\n html: string,\n options: { readonly sizes?: ReadonlyMap<string, number>; readonly passThrough?: ReadonlySet<string> } = {},\n): RewrittenMobileIndex {\n const plan = parseMobileBootPlan(html)\n const remoteSettings = orderAuthenticatedSettings(plan.entries, plan.slotProvider)\n let batches: readonly MobileBootBatchPlan[] = []\n if (plan.batches !== undefined && plan.layoutBatch !== undefined && plan.planEntries !== undefined) {\n const split = splitMobileBootBatch(\n plan.planEntries,\n options.sizes ?? new Map(),\n options.passThrough ?? new Set(),\n )\n const at = plan.batches.indexOf(plan.layoutBatch)\n if (at < 0) throw new Error('upstream DSH boot manifest has no unique application layout batch')\n plan.batches.splice(at, 1, ...split.rows)\n batches = Object.freeze(split.plans)\n plan.parsed.rev = createHash('sha256').update(JSON.stringify({ entries: plan.entries, batches: plan.batches })).digest('hex').slice(0, 16)\n }\n const transportBootstrap = remoteSettings ? MOBILE_AUTHENTICATED_TRANSPORT_BOOTSTRAP : ''\n const replacement = `${transportBootstrap}${MOBILE_CSRF_FETCH_BOOTSTRAP}window.__DSH_MOBILE_FRONTEND__=\"dedicated\";${plan.assignment}${JSON.stringify(plan.parsed)};`\n return Object.freeze({\n html: ensureMobileViewport(ensureMobileCompatibility(`${html.slice(0, plan.replaceStart)}${replacement}${html.slice(plan.replaceEnd)}`)),\n batches,\n })\n}\n\n/** Replace only DSH's layout client module while retaining its complete plugin graph. */\nexport function rewriteMobileIndex(html: string): string {\n return rewriteMobileIndexWithBatch(html).html\n}\n\nclass ByteLimitTransform extends Transform {\n private total = 0\n\n constructor(private readonly maximum: number) {\n super()\n }\n\n override _transform(chunk: Buffer, encoding: BufferEncoding, callback: TransformCallback): void {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding)\n this.total += buffer.length\n if (this.total > this.maximum) {\n callback(new HttpError(413, 'payload_too_large'))\n return\n }\n callback(null, buffer)\n }\n}\n\nfunction stripIpv6Brackets(hostname: string): string {\n return hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname\n}\n\ninterface PemCertificate {\n readonly pem: string\n readonly certificate: X509Certificate\n}\n\nfunction parsePemCertificates(contents: Buffer, source: string): PemCertificate[] {\n const text = contents.toString('utf8')\n const pattern = /-----BEGIN CERTIFICATE-----[\\s\\S]*?-----END CERTIFICATE-----/gu\n const blocks = text.match(pattern) ?? []\n if (blocks.length === 0 || text.replace(pattern, '').trim() !== '') {\n throw new Error(`${source} must contain only PEM certificates`)\n }\n return blocks.map((pem) => {\n let certificate: X509Certificate\n try {\n certificate = new X509Certificate(pem)\n } catch (error) {\n throw new Error(`${source} contains an invalid certificate`, { cause: error })\n }\n return Object.freeze({ pem: `${pem}\\n`, certificate })\n })\n}\n\nfunction validateServerChain(chain: readonly PemCertificate[]): void {\n const now = Date.now()\n for (const [index, entry] of chain.entries()) {\n if (Date.parse(entry.certificate.validFrom) > now || Date.parse(entry.certificate.validTo) <= now) {\n throw new Error('TLS certificate chain contains a certificate that is not currently valid')\n }\n if (index === 0) continue\n if (entry.certificate.subject === entry.certificate.issuer\n && entry.certificate.verify(entry.certificate.publicKey)) {\n throw new Error('TLS server certificate chain must not include a self-signed root')\n }\n const child = chain[index - 1]!.certificate\n if (!entry.certificate.ca || !child.checkIssued(entry.certificate)\n || !child.verify(entry.certificate.publicKey)) {\n throw new Error('TLS server certificate chain is not an ordered leaf-to-intermediate chain')\n }\n }\n}\n\nasync function tlsOptions(config: ResolvedGatewayConfig): Promise<ServerOptions> {\n if (config.tls.mode === 'disabled') throw new Error('TLS options requested for a disabled listener')\n const [certFile, key, additionalChainFile] = await Promise.all([\n readFile(config.tls.certFile),\n readFile(config.tls.keyFile),\n config.tls.caFile === undefined ? Promise.resolve(undefined) : readFile(config.tls.caFile),\n ])\n const chain = [\n ...parsePemCertificates(certFile, 'tls.certFile'),\n ...(additionalChainFile === undefined ? [] : parsePemCertificates(additionalChainFile, 'tls.caFile')),\n ]\n validateServerChain(chain)\n const leaf = chain[0]!.certificate\n for (const authority of config.authorities) {\n const hostname = stripIpv6Brackets(authority.hostname)\n const match = isIP(hostname) === 0 ? leaf.checkHost(hostname) : leaf.checkIP(hostname)\n if (match === undefined) throw new Error(`TLS certificate does not cover configured authority ${hostname}`)\n }\n return {\n cert: chain.map(entry => entry.pem).join(''),\n key,\n requestCert: false,\n minVersion: 'TLSv1.2',\n maxHeaderSize: MAX_HEADER_BYTES,\n }\n}\n\nfunction websocketAccept(key: string): string {\n return createHash('sha1').update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`, 'ascii').digest('base64')\n}\n\nfunction headerValue(headers: IncomingHttpHeaders, name: string): string | undefined {\n const value = headers[name]\n return Array.isArray(value) ? undefined : value\n}\n\nfunction hasToken(header: string | undefined, token: string): boolean {\n return header?.split(',').some(value => value.trim().toLowerCase() === token) ?? false\n}\n\nfunction rejectUpgrade(socket: Socket, status: number, code: string): void {\n if (socket.destroyed) return\n const body = `${JSON.stringify({ error: code })}\\n`\n socket.end([\n `HTTP/1.1 ${String(status)} ${status === 401 ? 'Unauthorized' : status === 403 ? 'Forbidden' : 'Bad Request'}`,\n 'Connection: close',\n 'Cache-Control: no-store',\n 'Content-Type: application/json; charset=utf-8',\n 'Referrer-Policy: no-referrer',\n 'X-Content-Type-Options: nosniff',\n `Content-Length: ${String(Buffer.byteLength(body))}`,\n '',\n body,\n ].join('\\r\\n'))\n}\n\nfunction sanitizeRequestHeaders(\n request: IncomingMessage,\n upstream: URL,\n): OutgoingHttpHeaders {\n const headers: OutgoingHttpHeaders = {\n host: upstream.host,\n }\n if (request.headers.origin !== undefined) headers.origin = upstream.origin\n if (request.headers['sec-fetch-site'] !== undefined) headers['sec-fetch-site'] = 'same-origin'\n const allowed = [\n 'accept', 'accept-encoding', 'accept-language', 'content-encoding', 'content-length', 'content-type',\n 'if-match', 'if-modified-since', 'if-none-match', 'if-unmodified-since', 'range', 'user-agent',\n ] as const\n for (const name of allowed) {\n const value = request.headers[name]\n if (value !== undefined) headers[name] = value\n }\n return headers\n}\n\nconst BLOCKED_RESPONSE_HEADERS = new Set([\n 'alt-svc', 'cache-control', 'connection', 'content-security-policy', 'content-security-policy-report-only',\n 'cross-origin-embedder-policy', 'cross-origin-opener-policy', 'cross-origin-resource-policy', 'expires',\n 'keep-alive', 'nel', 'permissions-policy', 'pragma', 'proxy-authenticate', 'referrer-policy',\n 'report-to', 'reporting-endpoints', 'server', 'set-cookie', 'strict-transport-security', 'trailer',\n 'transfer-encoding', 'upgrade', 'via', 'x-content-type-options', 'x-frame-options', 'x-powered-by',\n])\n\nfunction sanitizeResponseHeaders(headers: IncomingHttpHeaders, upstream: URL): OutgoingHttpHeaders {\n const clean: OutgoingHttpHeaders = {}\n for (const [name, value] of Object.entries(headers)) {\n const lower = name.toLowerCase()\n if (value === undefined || BLOCKED_RESPONSE_HEADERS.has(lower) || lower.startsWith('access-control-')) continue\n if (lower === 'location' && typeof value === 'string') {\n try {\n const location = new URL(value, upstream)\n clean.location = location.origin === upstream.origin\n ? `${location.pathname}${location.search}${location.hash}`\n : value\n } catch {\n continue\n }\n continue\n }\n clean[lower] = value\n }\n return clean\n}\n\nfunction acceptsGzip(header: string | undefined): boolean {\n if (header === undefined) return false\n let wildcard: boolean | undefined\n for (const entry of header.split(',')) {\n const [rawName, ...parameters] = entry.split(';')\n const name = rawName?.trim().toLowerCase()\n if (name === undefined || name === '') continue\n let quality = 1\n for (const parameter of parameters) {\n const match = /^\\s*q\\s*=\\s*(0(?:\\.\\d+)?|1(?:\\.0+)?)\\s*$/iu.exec(parameter)\n if (match !== null) quality = Number(match[1])\n }\n if (name === 'gzip') return quality > 0\n if (name === '*') wildcard = quality > 0\n }\n return wildcard ?? false\n}\n\nfunction isCompressibleContentType(value: string | string[] | undefined): boolean {\n const contentType = Array.isArray(value) ? value[0] : value\n if (contentType === undefined) return false\n const mediaType = contentType.split(';', 1)[0]?.trim().toLowerCase() ?? ''\n return mediaType.startsWith('text/')\n || /^(?:application\\/(?:javascript|json|xml|x-javascript)|image\\/svg\\+xml)$/u.test(mediaType)\n}\n\nfunction shouldCompressResponse(request: IncomingMessage, response: IncomingMessage): boolean {\n const pathname = request.url?.split('?', 1)[0] ?? ''\n const compressibleRequest = (request.method === 'GET'\n && (pathname.startsWith('/plugins/') || pathname.startsWith('/assets/')))\n || (request.method === 'POST' && pathname === SESSION_HISTORY_PATH)\n return compressibleRequest\n && response.statusCode === 200\n && request.headers.range === undefined\n && response.headers['content-range'] === undefined\n && response.headers['content-encoding'] === undefined\n && acceptsGzip(request.headers['accept-encoding'])\n && isCompressibleContentType(response.headers['content-type'])\n}\n\nfunction revisionedStaticCacheControl(\n request: IncomingMessage,\n statusCode: number | undefined,\n): string | undefined {\n if (request.method !== 'GET' && request.method !== 'HEAD') return undefined\n // Only a delivered artifact is immutable. A rev-bearing URL is unique per\n // build, so a 200 for it can never change — but an error for the same URL is\n // not that artifact. Stamping `max-age=31536000, immutable` onto a failure\n // lets the WebView cache the rejection for a year: `rev` is a per-host-start\n // nonce, so once the host restarts the bundle URL the page already holds is\n // rejected, and the poisoned entry makes the plugin fail on every later boot\n // until the browser cache is cleared. Upstream DSH only attaches its\n // immutable directive on the success path; failures there carry no\n // cache-control at all, so the gateway keeps `no-store` from\n // setSecurityHeaders instead.\n if (statusCode !== 200) return undefined\n let target: URL\n try { target = new URL(request.url ?? '/', 'https://dsh-mobile.invalid') } catch { return undefined }\n const revision = target.searchParams.get('rev')\n const hasRevision = revision !== null && /^[a-z0-9_-]{4,128}$/iu.test(revision)\n const hashedAsset = /^\\/assets\\/.*-[a-z0-9_-]{8,}\\.[a-z0-9]+$/iu.test(target.pathname)\n if (!(target.pathname.startsWith('/plugins/') && hasRevision)\n && !(target.pathname.startsWith('/assets/') && (hasRevision || hashedAsset))) return undefined\n return 'private, max-age=31536000, immutable'\n}\n\nfunction isJsonRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction mobileHistoryRequestBody(request: IncomingMessage, body: Buffer): Buffer {\n if (request.method !== 'POST' || request.url?.split('?', 1)[0] !== SESSION_HISTORY_PATH) return body\n let parsed: unknown\n try {\n parsed = JSON.parse(body.toString('utf8'))\n } catch {\n return body\n }\n if (!isJsonRecord(parsed) || parsed.method !== 'session.history' || !isJsonRecord(parsed.payload)) return body\n const requested = parsed.payload.maxMessages\n if (typeof requested === 'number' && Number.isInteger(requested) && requested > 0 && requested <= MOBILE_HISTORY_PAGE_MESSAGES) {\n return body\n }\n return Buffer.from(JSON.stringify({\n ...parsed,\n payload: { ...parsed.payload, maxMessages: MOBILE_HISTORY_PAGE_MESSAGES },\n }))\n}\n\nfunction addVaryAcceptEncoding(headers: OutgoingHttpHeaders): void {\n const existing = headers.vary\n const rawValues: string[] = Array.isArray(existing)\n ? existing.map(value => String(value))\n : existing === undefined ? [] : [String(existing)]\n const values = rawValues.flatMap(value => value.split(',').map(part => part.trim()).filter(Boolean))\n if (!values.some(value => value.toLowerCase() === 'accept-encoding')) values.push('Accept-Encoding')\n headers.vary = values.join(', ')\n}\n\nfunction requestCookies(request: IncomingMessage): ReadonlyMap<string, string> {\n const cookies = parseCookies(request.headers.cookie)\n if (cookies === undefined) throw new HttpError(401, 'authentication_failed')\n return cookies\n}\n\nfunction mapError(error: unknown): HttpError {\n if (error instanceof HttpError) return error\n if (error instanceof AccessError) return new HttpError(error.status, error.code)\n if (error instanceof MobileExtensionError) return new HttpError(error.status, error.code)\n return new HttpError(500, 'internal_error')\n}\n\nfunction requestAbortedError(): Error {\n const error = new Error('request aborted')\n error.name = 'AbortError'\n return error\n}\n\nfunction isTransientUpstreamError(error: unknown): boolean {\n if (!(error instanceof Error)) return false\n const code = (error as NodeJS.ErrnoException).code\n return typeof code === 'string' && TRANSIENT_UPSTREAM_ERROR_CODES.has(code)\n}\n\nfunction upstreamTimeoutError(): NodeJS.ErrnoException {\n const error = new Error('upstream timeout') as NodeJS.ErrnoException\n error.code = 'ETIMEDOUT'\n return error\n}\n\nfunction waitForAbortableDelay(delayMs: number, signal: AbortSignal): Promise<void> {\n if (signal.aborted) return Promise.reject(requestAbortedError())\n return new Promise((resolve, reject) => {\n const aborted = (): void => {\n clearTimeout(timer)\n reject(requestAbortedError())\n }\n const timer = setTimeout(() => {\n signal.removeEventListener('abort', aborted)\n resolve()\n }, delayMs)\n signal.addEventListener('abort', aborted, { once: true })\n })\n}\n\nfunction waitForRequestTask<T>(task: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) return Promise.reject(requestAbortedError())\n return new Promise((resolve, reject) => {\n const aborted = (): void => {\n reject(requestAbortedError())\n }\n signal.addEventListener('abort', aborted, { once: true })\n void task.then(\n value => {\n signal.removeEventListener('abort', aborted)\n resolve(value)\n },\n error => {\n signal.removeEventListener('abort', aborted)\n reject(error)\n },\n )\n })\n}\n\nfunction discoveryDeviceName(): string {\n const value = hostname().trim().replaceAll(/[\\u0000-\\u001f\\u007f]/gu, '')\n return (value === '' ? 'DeepSeek Harness' : value).slice(0, 63)\n}\n\nfunction discoveryMdnsHost(instanceId: string): string {\n const label = hostname().toLowerCase().replaceAll(/[^a-z0-9-]/gu, '-').replaceAll(/^-+|-+$/gu, '').slice(0, 40)\n return `${label === '' ? 'dsh' : label}-${instanceId.slice(0, 8)}.local`\n}\n\nfunction discoveryBroadcastTargets(cidrs: readonly ParsedCidr[]): readonly string[] {\n const targets = new Set<string>(['255.255.255.255'])\n for (const cidr of cidrs) {\n if (cidr.bits !== 32 || cidr.prefix >= 32) continue\n const hostBits = BigInt(32 - cidr.prefix)\n const broadcast = cidr.network | ((1n << hostBits) - 1n)\n targets.add([24n, 16n, 8n, 0n].map(shift => Number((broadcast >> shift) & 0xffn)).join('.'))\n }\n return [...targets]\n}\n\n/** Keep Node's upgrade-owned socket safe after the HTTP parser removes its listener. */\nfunction guardUpgradeSocket(socket: Socket): void {\n socket.on('error', () => {\n if (!socket.destroyed) socket.destroy()\n })\n}\n\nfunction discoveryFailure(error: unknown): { readonly code: string; readonly message: string } {\n const code = error instanceof Error && typeof (error as NodeJS.ErrnoException).code === 'string'\n ? (error as NodeJS.ErrnoException).code!\n : 'unknown'\n return Object.freeze({ code, message: error instanceof Error ? error.message : String(error) })\n}\n\nfunction extensionTarget(pathname: string):\n | { readonly kind: 'manifest' }\n | { readonly kind: 'events' }\n | { readonly kind: 'script' | 'style' | 'asset'; readonly id: string; readonly path?: string }\n | { readonly kind: 'action'; readonly id: string; readonly action: string }\n | { readonly kind: 'route'; readonly id: string; readonly path: string }\n | undefined {\n const prefix = `${AUTH_PREFIX}/extensions`\n if (pathname === prefix || pathname === `${prefix}/` || pathname === `${prefix}/manifest`) return { kind: 'manifest' }\n if (pathname === `${prefix}/events`) return { kind: 'events' }\n if (!pathname.startsWith(`${prefix}/`)) return undefined\n const parts = pathname.slice(prefix.length + 1).split('/')\n const id = parts.shift()\n if (id === undefined || !/^[a-z][a-z0-9-]{0,63}$/u.test(id)) return undefined\n const leaf = parts.shift()\n if (leaf === 'mobile.js' && parts.length === 0) return { kind: 'script', id }\n if (leaf === 'mobile.css' && parts.length === 0) return { kind: 'style', id }\n if (leaf === 'assets' && parts.length > 0) return { kind: 'asset', id, path: parts.join('/') }\n if (leaf === 'actions' && parts.length === 1 && /^[a-z][a-z0-9-]{0,63}$/u.test(parts[0]!)) return { kind: 'action', id, action: parts[0]! }\n if (leaf === 'routes') return { kind: 'route', id, path: `/${parts.join('/')}`.replace(/\\/{2,}/gu, '/') }\n return undefined\n}\n\nconst EXTENSION_GENERATION_HEADER = 'x-dsh-mobile-extension-generation'\n\nfunction extensionGeneration(value: string | undefined): string | undefined {\n if (value === undefined) return undefined\n if (!/^[a-f\\d]{64}$/u.test(value)) throw new HttpError(400, 'invalid_extension_generation')\n return value\n}\n\nfunction mobileBootBatchKey(pathname: string): string | undefined {\n const match = new RegExp(`^${MOBILE_BOOT_BATCH_PREFIX.replaceAll('/', '\\\\/')}([a-f\\\\d]{64})\\\\.js$`, 'u').exec(pathname)\n return match?.[1]\n}\n\nfunction assertBoundedContentLength(request: IncomingMessage, maximum: number): void {\n const declared = request.headers['content-length']\n if (declared !== undefined && (!/^\\d+$/u.test(declared) || Number(declared) > maximum)) {\n throw new HttpError(413, 'payload_too_large')\n }\n}\n\nasync function readBoundedBody(request: IncomingMessage, maximum: number): Promise<Buffer> {\n assertBoundedContentLength(request, maximum)\n const chunks: Buffer[] = []\n let total = 0\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n total += buffer.length\n if (total > maximum) throw new HttpError(413, 'payload_too_large')\n chunks.push(buffer)\n }\n return Buffer.concat(chunks)\n}\n\nfunction extensionRequestHeaders(headers: IncomingHttpHeaders): Readonly<Record<string, string>> {\n const allowed = new Set(['accept', 'content-type', 'content-length', 'content-range', 'range', 'if-none-match', 'if-modified-since'])\n const output: Record<string, string> = {}\n for (const [name, value] of Object.entries(headers)) {\n if (!allowed.has(name) || typeof value !== 'string') continue\n output[name] = value\n }\n return Object.freeze(output)\n}\n\nfunction extensionContentType(path: string): string {\n const type = {\n '.css': 'text/css; charset=utf-8',\n '.csv': 'text/csv; charset=utf-8',\n '.gif': 'image/gif',\n '.html': 'text/html; charset=utf-8',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.js': 'text/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n }[extname(path).toLowerCase()]\n return type ?? 'application/octet-stream'\n}\n\n/** Authenticated TLS edge in front of the ordinary loopback-only DSH Web server. */\nexport class MobileAccessGateway {\n readonly access: AccessController\n private readonly listenerTlsEnabled: boolean\n private readonly tlsEnabled: boolean\n private policy: RequestTrustPolicy | undefined\n private server: GatewayServer | undefined\n private discoverySocket: DatagramSocket | undefined\n private discoveryTimer: NodeJS.Timeout | undefined\n /**\n * Why broadcast discovery is unavailable, if it is. Windows keeps separate TCP and\n * UDP port-exclusion tables, so the port the OS handed the TCP listener can be\n * refused for UDP. That degradation is survivable but must stay observable: without\n * it, \"the phone cannot find this computer\" has no diagnosable cause on the host.\n */\n private discoveryError: { readonly code: string; readonly message: string } | undefined\n private mdnsError: { readonly code: string; readonly message: string } | undefined\n /** Names of the bundled mobile assets that could not be read, if any. */\n private mobileAssetError: string | undefined\n private bonjour: Bonjour | undefined\n private pairingCaCertificate: string | undefined\n private listenerPort: number | undefined\n private readonly connectedSockets = new Set<Socket>()\n private readonly activeRequests = new Map<number, ActiveRequest>()\n private readonly activeWebSockets = new Map<number, ActiveWebSocket>()\n private readonly mobileBootBatches = new Map<string, StoredMobileBootBatch>()\n /** Cached upstream bundle byte sizes (HEAD probes), keyed by resource URL. */\n private readonly bootEntrySizeCache = new Map<string, { size: number; at: number }>()\n private readonly extensionEventListeners = new Set<(revision: number) => void>()\n private extensionEventRevision = 0\n private readonly taskEventListeners = new Set<(payload: string) => void>()\n private readonly deviceEventListeners = new Map<string, Set<(payload: string) => void>>()\n private readonly pendingDeviceRevocations = new Set<string>()\n private extensionChangeTimer: NodeJS.Timeout | undefined\n private extensionChangeTask: Promise<void> | undefined\n private legacyCustomDigest = ''\n private upstreamCookie: string | undefined\n private upstreamCookieExpiresAt = 0\n private upstreamCookieTask: Promise<string> | undefined\n private upstreamAuthRequest: ClientRequest | undefined\n private nextOperationId = 1\n private closing = false\n private started = false\n private closeTask: Promise<void> | undefined\n private readonly removeSessionListener: () => void\n private readonly removeExtensionContentListener: () => void\n private readonly renewLimiter: BoundedRateLimiter\n private readonly probeLimiter: BoundedRateLimiter\n\n constructor(\n readonly config: ResolvedGatewayConfig,\n store: DeviceStore,\n private readonly extensions?: MobileAccessService,\n private readonly upstreamAuthenticatedUrl?: string,\n private readonly extraWebSocketPaths?: { has(pathname: string): boolean },\n private readonly blockedUpgradeLog?: BlockedUpgradePathLog,\n private readonly onDiscoveryDegraded?: (source: 'broadcast' | 'mdns', code: string) => void,\n ) {\n this.listenerTlsEnabled = config.tls.mode === 'provided'\n this.tlsEnabled = config.publicTls\n this.access = new AccessController(store, {\n pairingTtlMs: config.pairingTtlMs,\n deviceTtlMs: config.deviceTtlMs,\n sessionTtlMs: config.sessionTtlMs,\n maxDevices: config.maxDevices,\n maxSessions: config.maxSessions,\n rateLimitWindowMs: config.rateLimitWindowMs,\n maxPairingAttempts: config.maxPairingAttempts,\n maxRateLimitKeys: config.maxRateLimitKeys,\n })\n this.renewLimiter = new BoundedRateLimiter(\n Math.min(100, config.maxPairingAttempts * 4),\n config.rateLimitWindowMs,\n config.maxRateLimitKeys,\n )\n // Reachability checks do not create Sessions; allow one short poll for\n // each configured device without weakening the Session-creating limit.\n this.probeLimiter = new BoundedRateLimiter(\n Math.min(1_000, config.maxDevices * 4),\n config.rateLimitWindowMs,\n config.maxRateLimitKeys,\n )\n this.removeSessionListener = this.access.onSessionEnded((authorization, reason) => {\n if (reason === 'revoked') {\n if (!this.pendingDeviceRevocations.has(authorization.deviceId)) {\n this.pendingDeviceRevocations.add(authorization.deviceId)\n this.broadcastDeviceRevoked(authorization.deviceId)\n }\n // Let the final SSE frame enter the socket before revocation closes\n // the session-owned stream and WebSockets.\n setImmediate(() => {\n this.abortSessionResources(authorization.sessionKey)\n this.pendingDeviceRevocations.delete(authorization.deviceId)\n })\n } else {\n this.abortSessionResources(authorization.sessionKey)\n }\n })\n this.removeExtensionContentListener = this.extensions?.onContentChanged(() => {\n this.broadcastExtensionChange()\n }) ?? (() => undefined)\n }\n\n /** Initialize durable state, validate TLS, and bind the externally reachable listener. */\n async start(): Promise<void> {\n if (this.started || this.server !== undefined) throw new Error('mobile-access gateway cannot be started twice')\n this.started = true\n await this.access.initialize()\n try {\n if (this.config.pairingCaFile !== undefined) {\n const certificate = new X509Certificate(await readFile(this.config.pairingCaFile))\n const fingerprint = certificate.fingerprint256.replaceAll(':', '').toLowerCase()\n if (!certificate.ca || certificate.subject !== certificate.issuer\n || !certificate.verify(certificate.publicKey) || fingerprint !== this.config.instanceId) {\n throw new Error('pairingCaFile must be the self-signed CA identified by instanceId')\n }\n this.pairingCaCertificate = certificate.raw.toString('base64')\n }\n // Both mobile assets are injected into the served index, so a missing one would\n // otherwise surface only as a 503 subresource: the page still renders and the\n // compatibility bundle silently never applies. Record it instead of failing, so an\n // enhancement cannot take the whole listener down, and expose it for diagnosis.\n const missingAssets: string[] = []\n for (const [label, file] of [\n ['compatibility', this.config.mobileCompatibilityFile],\n ['layout', this.config.mobileLayoutFile],\n ] as const) {\n if (!(await lstat(file).catch(() => undefined))?.isFile()) missingAssets.push(label)\n }\n this.mobileAssetError = missingAssets.length === 0 ? undefined : `mobile_assets_missing_${missingAssets.join('_')}`\n const handler = (request: IncomingMessage, response: ServerResponse): void => {\n void this.handleExternalRequest(request, response).catch((error: unknown) => {\n const mapped = mapError(error)\n if (response.headersSent) response.destroy()\n else sendFailure(response, mapped.status, mapped.code, this.tlsEnabled)\n })\n }\n const server = this.listenerTlsEnabled\n ? createHttpsServer(await tlsOptions(this.config), handler)\n : createHttpServer({ maxHeaderSize: MAX_HEADER_BYTES }, handler)\n this.server = server\n server.maxHeadersCount = 64\n server.maxConnections = this.config.maxConnections\n server.headersTimeout = 10_000\n server.requestTimeout = this.config.upstreamTimeoutMs\n server.keepAliveTimeout = 5_000\n server.on('connection', (socket: Socket) => {\n if (this.connectedSockets.size >= this.config.maxConnections) {\n socket.destroy()\n return\n }\n this.connectedSockets.add(socket)\n socket.on('error', () => { socket.destroy() })\n socket.once('close', () => { this.connectedSockets.delete(socket) })\n })\n server.on('connect', (_request, socket) => { socket.destroy() })\n server.on('upgrade', (request, socket, head) => {\n const client = socket as Socket\n // Node removes its normal connection error listener when it hands this\n // socket to the upgrade event. Install ours before any sync validation\n // or await so a stale TLS/WebSocket connection cannot terminate DSH.\n guardUpgradeSocket(client)\n void this.handleUpgrade(request, client, head).catch((error: unknown) => {\n const mapped = mapError(error)\n rejectUpgrade(client, mapped.status, mapped.code)\n })\n })\n server.on('clientError', (_error, socket) => { rejectUpgrade(socket as Socket, 400, 'bad_request') })\n await new Promise<void>((resolve, reject) => {\n const failed = (error: Error): void => { reject(error) }\n server.once('error', failed)\n server.listen(this.config.listenPort, this.config.listenHost, () => {\n server.off('error', failed)\n resolve()\n })\n })\n const address = server.address()\n if (address === null || typeof address === 'string') throw new Error('gateway listener has no TCP address')\n this.listenerPort = address.port\n this.policy = new RequestTrustPolicy(\n this.config.authorities,\n address.port,\n this.config.allowedCidrs,\n this.tlsEnabled,\n )\n if (this.config.discovery) await this.startDiscovery(address.port)\n await this.pollLegacyCustomChanges()\n this.extensionChangeTimer = setInterval(() => { void this.pollLegacyCustomChanges() }, EXTENSION_CHANGE_POLL_MS)\n this.extensionChangeTimer.unref()\n } catch (error) {\n await this.closeFailedStart()\n throw error\n }\n }\n\n private async startDiscovery(port: number): Promise<void> {\n const socket = createSocket('udp4')\n this.discoverySocket = socket\n const announcement = this.discoveryAnnouncement(port)\n let binding = true\n socket.on('error', error => {\n if (!binding) this.recordBroadcastFailure(error, true)\n })\n const sendAnnouncement = (targetPort: number, address: string): void => {\n if (this.closing || this.discoveryError !== undefined) return\n try {\n socket.send(announcement, targetPort, address, error => {\n if (error !== null) this.recordBroadcastFailure(error, true)\n })\n } catch (error) {\n this.recordBroadcastFailure(error, true)\n }\n }\n socket.on('message', (message, remote) => {\n if (this.closing || this.discoveryError !== undefined || !message.equals(DISCOVERY_QUERY)\n || !addressAllowed(remote.address, this.config.allowedCidrs)) return\n sendAnnouncement(remote.port, remote.address)\n })\n // The UDP socket is IPv4-only; only a literal IPv4 loopback address is bindable, never ::1.\n const bindHost = isIP(this.config.listenHost) === 4 && isLoopbackAddress(this.config.listenHost) ? this.config.listenHost : '0.0.0.0'\n // Windows keeps separate TCP and UDP port-exclusion tables, so the port the OS handed\n // the TCP listener can be unavailable for UDP; another process may also already hold\n // it. Broadcast discovery is a convenience, so a failed bind must not take the whole\n // gateway down — the mDNS publication below still advertises this origin.\n const bound = await new Promise<boolean>(resolve => {\n const onBindError = (error: NodeJS.ErrnoException): void => {\n socket.off('error', onBindError)\n this.recordBroadcastFailure(error, false)\n resolve(false)\n }\n socket.once('error', onBindError)\n try {\n socket.bind(port, bindHost, () => {\n socket.off('error', onBindError)\n binding = false\n try {\n socket.setBroadcast(true)\n resolve(true)\n } catch (error) {\n this.recordBroadcastFailure(error, true)\n resolve(false)\n }\n })\n } catch (error) {\n onBindError(error as NodeJS.ErrnoException)\n }\n })\n if (bound && this.discoveryError === undefined) {\n const announce = (): void => {\n for (const target of discoveryBroadcastTargets(this.config.allowedCidrs)) {\n sendAnnouncement(port, target)\n }\n }\n announce()\n if (this.discoveryError === undefined) {\n this.discoveryTimer = setInterval(announce, DISCOVERY_INTERVAL_MS)\n this.discoveryTimer.unref()\n }\n } else {\n this.discoverySocket = undefined\n // A socket whose bind failed was never running, so close() throws synchronously.\n try { socket.close() } catch { /* the socket never started */ }\n }\n\n const deviceName = discoveryDeviceName()\n const onMdnsError = (error: unknown): void => { this.recordMdnsFailure(error) }\n const bonjour = new Bonjour({ disableIPv6: true }, onMdnsError)\n this.bonjour = bonjour\n // bonjour-service 1.4.4 does not forward multicast-dns EventEmitter errors\n // through errorCallback. Keep a listener on its underlying emitter for its lifetime.\n const mdns = (bonjour as unknown as { server: { mdns: { on(event: 'error', listener: (error: Error) => void): void } } }).server.mdns\n mdns.on('error', onMdnsError)\n bonjour.publish({\n name: `${deviceName} (${this.config.instanceId.slice(0, 8)})`,\n type: MDNS_SERVICE_TYPE,\n protocol: 'tcp',\n port,\n host: discoveryMdnsHost(this.config.instanceId),\n disableIPv6: true,\n txt: {\n deviceName,\n origin: this.address().origin,\n instanceId: this.config.instanceId,\n protocol: String(DISCOVERY_PROTOCOL),\n },\n })\n }\n\n private reportDiscoveryDegraded(source: 'broadcast' | 'mdns', code: string): void {\n try {\n this.onDiscoveryDegraded?.(source, code)\n } catch (error) {\n process.emitWarning(`DSH Mobile could not log ${source} discovery error: ${String(error)}`, {\n code: 'DSH_MOBILE_DISCOVERY_LOG_FAILED',\n })\n }\n }\n\n private recordBroadcastFailure(error: unknown, notify: boolean): void {\n if (this.closing || this.discoveryError !== undefined) return\n this.discoveryError = discoveryFailure(error)\n if (this.discoveryTimer !== undefined) clearInterval(this.discoveryTimer)\n this.discoveryTimer = undefined\n if (notify) this.reportDiscoveryDegraded('broadcast', this.discoveryError.code)\n }\n\n private recordMdnsFailure(error: unknown): void {\n if (this.closing || this.mdnsError !== undefined) return\n this.mdnsError = discoveryFailure(error)\n this.reportDiscoveryDegraded('mdns', this.mdnsError.code)\n }\n\n private discoveryAnnouncement(port: number): Buffer {\n return Buffer.from(JSON.stringify({\n deviceName: discoveryDeviceName(),\n origin: this.address().origin,\n port,\n protocol: DISCOVERY_PROTOCOL,\n instanceId: this.config.instanceId,\n }), 'utf8')\n }\n\n private async closeFailedStart(): Promise<void> {\n if (this.extensionChangeTimer !== undefined) clearInterval(this.extensionChangeTimer)\n this.extensionChangeTimer = undefined\n this.removeExtensionContentListener()\n if (this.discoveryTimer !== undefined) clearInterval(this.discoveryTimer)\n this.discoveryTimer = undefined\n await this.closeBonjour()\n this.discoverySocket?.close()\n this.discoverySocket = undefined\n for (const socket of this.connectedSockets) socket.destroy()\n const server = this.server\n this.server = undefined\n if (server?.listening === true) {\n await new Promise<void>(resolve => { server.close(() => resolve()) })\n }\n await this.access.close()\n }\n\n private async closeBonjour(): Promise<void> {\n const bonjour = this.bonjour\n this.bonjour = undefined\n if (bonjour === undefined) return\n await new Promise<void>(resolve => {\n bonjour.unpublishAll(() => { bonjour.destroy(() => resolve()) })\n })\n }\n\n /** Actual bound address, available after start and safe for loopback status output. */\n address(): { host: string; port: number; origin: string } {\n if (this.listenerPort === undefined || this.policy === undefined) throw new Error('gateway is not listening')\n const origin = this.policy.origins.values().next().value as string | undefined\n if (origin === undefined) throw new Error('gateway has no public authority')\n return Object.freeze({ host: this.config.listenHost, port: this.listenerPort, origin })\n }\n\n /**\n * Report which discovery channels this gateway actually owns.\n *\n * Broadcast discovery degrades on its own when its UDP port cannot be bound, so\n * callers need to tell \"discovery is off\" from \"discovery was never attempted\".\n * mDNS is published independently and is unaffected by that failure.\n */\n discoveryStatus(): {\n readonly broadcast: boolean\n readonly mdns: boolean\n readonly errorCode?: string\n readonly mdnsErrorCode?: string\n readonly mobileAssetsErrorCode?: string\n } {\n return Object.freeze({\n broadcast: this.discoverySocket !== undefined && this.discoveryTimer !== undefined,\n mdns: this.bonjour !== undefined && this.mdnsError === undefined,\n ...(this.discoveryError === undefined ? {} : { errorCode: `discovery_broadcast_${this.discoveryError.code}` }),\n ...(this.mdnsError === undefined ? {} : { mdnsErrorCode: `discovery_mdns_${this.mdnsError.code}` }),\n // A bundled asset that is missing would otherwise only show up as a 503 on a\n // subresource the served page still references.\n ...(this.mobileAssetError === undefined ? {} : { mobileAssetsErrorCode: this.mobileAssetError }),\n })\n }\n\n private requirePolicy(): RequestTrustPolicy {\n if (this.policy === undefined || this.closing) throw new HttpError(503, 'unavailable')\n return this.policy\n }\n\n private authorize(request: IncomingMessage): SessionAuthorization {\n const sessionToken = requestCookies(request).get(SESSION_COOKIE)\n if (sessionToken === undefined) throw new HttpError(401, 'authentication_failed')\n return this.access.authorizeSession(sessionToken)\n }\n\n private requireCsrf(request: IncomingMessage, authorization: SessionAuthorization): void {\n const value = headerValue(request.headers, CSRF_HEADER)\n this.access.assertCsrf(authorization, value)\n }\n\n private setSessionCookies(response: ServerResponse, result: {\n sessionToken: string\n csrfToken: string\n sessionExpiresAt: number\n }, now: number): void {\n const maxAge = (result.sessionExpiresAt - now) / 1000\n response.setHeader('Set-Cookie', [\n cookie(SESSION_COOKIE, result.sessionToken, { tls: this.tlsEnabled, httpOnly: true, path: '/', maxAgeSeconds: maxAge }),\n cookie(CSRF_COOKIE, result.csrfToken, { tls: this.tlsEnabled, httpOnly: false, path: '/', maxAgeSeconds: maxAge }),\n ])\n }\n\n private async handlePair(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (typeof body.token !== 'string' || (body.label !== undefined && typeof body.label !== 'string')) {\n throw new HttpError(400, 'bad_request')\n }\n const result = await this.access.pair(request.socket.remoteAddress ?? 'unknown', body.token, body.label as string | undefined)\n const now = Date.now()\n this.setSessionCookies(response, result, now)\n const sessionCookies = response.getHeader('Set-Cookie') as string[]\n response.setHeader('Set-Cookie', [\n ...sessionCookies,\n cookie(DEVICE_COOKIE, result.deviceToken, {\n tls: this.tlsEnabled,\n httpOnly: true,\n path: '/mobile-access/auth/renew',\n maxAgeSeconds: (result.deviceExpiresAt - now) / 1000,\n }),\n ])\n sendJson(response, 201, {\n paired: true,\n deviceId: result.deviceId,\n csrfToken: result.csrfToken,\n sessionExpiresAt: result.sessionExpiresAt,\n }, this.tlsEnabled)\n }\n\n private async handleRenew(request: IncomingMessage, response: ServerResponse): Promise<void> {\n if (!this.renewLimiter.take(request.socket.remoteAddress ?? 'unknown', Date.now())) {\n throw new HttpError(429, 'rate_limited')\n }\n await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n const deviceToken = requestCookies(request).get(DEVICE_COOKIE)\n if (deviceToken === undefined) throw new HttpError(401, 'authentication_failed')\n let result\n try {\n result = await this.access.renew(deviceToken)\n } catch (error) {\n if (error instanceof AccessError && error.status === 401) {\n response.setHeader('Set-Cookie', cookie(DEVICE_COOKIE, '', {\n tls: this.tlsEnabled,\n httpOnly: true,\n path: '/mobile-access/auth/renew',\n maxAgeSeconds: 0,\n }))\n }\n throw error\n }\n this.setSessionCookies(response, result, Date.now())\n sendJson(response, 200, {\n renewed: true,\n deviceId: result.deviceId,\n csrfToken: result.csrfToken,\n sessionExpiresAt: result.sessionExpiresAt,\n }, this.tlsEnabled)\n }\n\n private async handleNativePair(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (typeof body.token !== 'string' || (body.label !== undefined && typeof body.label !== 'string')) {\n throw new HttpError(400, 'bad_request')\n }\n const result = await this.access.pair(\n request.socket.remoteAddress ?? 'unknown',\n body.token,\n body.label as string | undefined,\n )\n sendJson(response, 201, {\n instanceId: this.config.instanceId,\n deviceId: result.deviceId,\n deviceToken: result.deviceToken,\n deviceExpiresAt: result.deviceExpiresAt,\n sessionToken: result.sessionToken,\n csrfToken: result.csrfToken,\n sessionExpiresAt: result.sessionExpiresAt,\n }, this.tlsEnabled)\n }\n\n private async handleNativeRenew(request: IncomingMessage, response: ServerResponse): Promise<void> {\n if (!this.renewLimiter.take(request.socket.remoteAddress ?? 'unknown', Date.now())) {\n throw new HttpError(429, 'rate_limited')\n }\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (typeof body.deviceToken !== 'string') throw new HttpError(400, 'bad_request')\n const result = await this.access.renew(body.deviceToken)\n sendJson(response, 200, {\n instanceId: this.config.instanceId,\n deviceId: result.deviceId,\n sessionToken: result.sessionToken,\n csrfToken: result.csrfToken,\n sessionExpiresAt: result.sessionExpiresAt,\n }, this.tlsEnabled)\n }\n\n private async handleNativeProbe(request: IncomingMessage, response: ServerResponse): Promise<void> {\n if (!this.probeLimiter.take(request.socket.remoteAddress ?? 'unknown', Date.now())) {\n throw new HttpError(429, 'rate_limited')\n }\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (typeof body.deviceToken !== 'string') throw new HttpError(400, 'bad_request')\n const result = await this.access.probe(body.deviceToken)\n sendJson(response, 200, {\n instanceId: this.config.instanceId,\n deviceId: result.deviceId,\n deviceExpiresAt: result.deviceExpiresAt,\n }, this.tlsEnabled)\n }\n\n private async handleLogout(request: IncomingMessage, response: ServerResponse): Promise<void> {\n await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n const authorization = this.authorize(request)\n this.requireCsrf(request, authorization)\n this.access.logout(authorization)\n response.setHeader('Set-Cookie', [\n cookie(SESSION_COOKIE, '', { tls: this.tlsEnabled, httpOnly: true, path: '/', maxAgeSeconds: 0 }),\n cookie(CSRF_COOKIE, '', { tls: this.tlsEnabled, httpOnly: false, path: '/', maxAgeSeconds: 0 }),\n ])\n sendJson(response, 200, { loggedOut: true }, this.tlsEnabled)\n }\n\n private async handleExternalRequest(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const target = parseRequestTarget(request.url)\n const policy = this.requirePolicy()\n const isMutation = request.method !== 'GET' && request.method !== 'HEAD'\n assertExternalTrust(request, policy, isMutation)\n if (target.decodedPathname === LOCAL_ADMIN_PREFIX || target.decodedPathname.startsWith(`${LOCAL_ADMIN_PREFIX}/`)) {\n throw new HttpError(404, 'not_found')\n }\n if (request.method === 'TRACE' || request.method === 'CONNECT') throw new HttpError(405, 'method_not_allowed')\n\n if (target.search === '' && request.method === 'GET' && target.decodedPathname === `${AUTH_PREFIX}/health`) {\n sendJson(response, 200, { ok: true }, this.tlsEnabled)\n return\n }\n if (target.search === '' && request.method === 'GET' && target.decodedPathname === `${AUTH_PREFIX}/metadata`) {\n sendJson(response, 200, {\n version: MOBILE_METADATA_VERSION,\n pluginVersion: DSH_MOBILE_VERSION,\n minimumAndroidAppVersion: MINIMUM_ANDROID_APP_VERSION,\n discoveryProtocol: DISCOVERY_PROTOCOL,\n }, this.tlsEnabled)\n return\n }\n if (target.search === '' && request.method === 'GET' && target.decodedPathname === `${AUTH_PREFIX}/discovery`) {\n sendJson(response, 200, {\n deviceName: discoveryDeviceName(),\n origin: this.address().origin,\n port: this.address().port,\n protocol: DISCOVERY_PROTOCOL,\n instanceId: this.config.instanceId,\n }, this.tlsEnabled)\n return\n }\n if (target.search === '' && request.method === 'GET' && target.decodedPathname === `${AUTH_PREFIX}/ca.cer`) {\n if (this.pairingCaCertificate === undefined) throw new HttpError(404, 'not_found')\n const body = Buffer.from(this.pairingCaCertificate, 'base64')\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(200, {\n 'Content-Type': 'application/pkix-cert',\n 'Content-Length': body.length,\n 'Cache-Control': 'no-store',\n })\n response.end(body)\n return\n }\n if (target.search === '' && request.method === 'GET'\n && (target.decodedPathname === `${AUTH_PREFIX}/pair` || target.decodedPathname === `${AUTH_PREFIX}/pair.js`)) {\n if (!this.access.pairingStatus().open) throw new HttpError(404, 'not_found')\n const languageHeader = request.headers['accept-language']\n const locale = resolveAuthPageLocale(typeof languageHeader === 'string' ? languageHeader : undefined)\n setSecurityHeaders(response, this.tlsEnabled)\n response.setHeader('Vary', 'Accept-Language')\n const body = target.decodedPathname.endsWith('.js') ? renderPairScript(locale) : renderPairPage(locale)\n response.writeHead(200, {\n 'Content-Type': target.decodedPathname.endsWith('.js') ? 'text/javascript; charset=utf-8' : 'text/html; charset=utf-8',\n 'Content-Length': Buffer.byteLength(body),\n })\n response.end(body)\n return\n }\n if (request.method === 'GET'\n && (target.decodedPathname === `${AUTH_PREFIX}/login` || target.decodedPathname === `${AUTH_PREFIX}/login.js`)) {\n if (target.decodedPathname.endsWith('.js') && target.search !== '') throw new HttpError(400, 'bad_request')\n const languageHeader = request.headers['accept-language']\n const locale = resolveAuthPageLocale(typeof languageHeader === 'string' ? languageHeader : undefined)\n setSecurityHeaders(response, this.tlsEnabled)\n response.setHeader('Vary', 'Accept-Language')\n const body = target.decodedPathname.endsWith('.js') ? renderLoginScript(locale) : renderLoginPage(locale)\n response.writeHead(200, {\n 'Content-Type': target.decodedPathname.endsWith('.js') ? 'text/javascript; charset=utf-8' : 'text/html; charset=utf-8',\n 'Content-Length': Buffer.byteLength(body),\n })\n response.end(body)\n return\n }\n if (target.search === '' && request.method === 'POST' && target.decodedPathname === `${AUTH_PREFIX}/auth/pair`) {\n await this.handlePair(request, response)\n return\n }\n if (target.search === '' && request.method === 'POST' && target.decodedPathname === `${AUTH_PREFIX}/auth/renew`) {\n await this.handleRenew(request, response)\n return\n }\n if (target.search === '' && request.method === 'POST' && target.decodedPathname === `${AUTH_PREFIX}/auth/native-pair`) {\n await this.handleNativePair(request, response)\n return\n }\n if (target.search === '' && request.method === 'POST' && target.decodedPathname === `${AUTH_PREFIX}/auth/native-renew`) {\n await this.handleNativeRenew(request, response)\n return\n }\n if (target.search === '' && request.method === 'POST' && target.decodedPathname === `${AUTH_PREFIX}/auth/native-probe`) {\n await this.handleNativeProbe(request, response)\n return\n }\n if (target.search === '' && request.method === 'POST' && target.decodedPathname === `${AUTH_PREFIX}/auth/logout`) {\n await this.handleLogout(request, response)\n return\n }\n const computerImages = request.method === 'GET' && target.decodedPathname === `${AUTH_PREFIX}/computer-images`\n const computerImage = request.method === 'GET' && target.decodedPathname === `${AUTH_PREFIX}/computer-image`\n const requestedExtension = extensionTarget(target.decodedPathname)\n const requestedMobileBootBatch = mobileBootBatchKey(target.decodedPathname)\n const customAsset = request.method === 'GET'\n ? target.decodedPathname === `${AUTH_PREFIX}/custom.css`\n ? {\n file: this.config.customCssFile,\n contentType: 'text/css; charset=utf-8',\n fallback: CUSTOM_STYLE_FALLBACK,\n }\n : target.decodedPathname === `${AUTH_PREFIX}/custom.js`\n ? {\n file: this.config.customScriptFile,\n contentType: 'text/javascript; charset=utf-8',\n fallback: CUSTOM_SCRIPT_FALLBACK,\n }\n : target.decodedPathname === MOBILE_LAYOUT_PATH\n ? {\n file: this.config.mobileLayoutFile,\n contentType: 'text/javascript; charset=utf-8',\n fallback: undefined,\n }\n : target.decodedPathname === MOBILE_COMPAT_PATH\n ? {\n file: this.config.mobileCompatibilityFile,\n contentType: 'text/javascript; charset=utf-8',\n fallback: undefined,\n }\n : undefined\n : undefined\n if (customAsset === undefined && requestedMobileBootBatch === undefined && !computerImages && !computerImage\n && extensionTarget(target.decodedPathname) === undefined\n && (target.decodedPathname === AUTH_PREFIX || target.decodedPathname.startsWith(`${AUTH_PREFIX}/`))) {\n throw new HttpError(404, 'not_found')\n }\n\n if (request.method !== 'GET' && request.method !== 'HEAD' && request.method !== 'POST'\n && requestedExtension?.kind !== 'route') {\n throw new HttpError(405, 'method_not_allowed')\n }\n let authorization: SessionAuthorization\n try {\n authorization = this.authorize(request)\n } catch (error) {\n const mapped = mapError(error)\n const acceptsHtml = request.headers.accept?.split(',').some(value => value.trim().split(';', 1)[0] === 'text/html') ?? false\n const topLevel = request.method === 'GET'\n && acceptsHtml\n && (request.headers['sec-fetch-dest'] === undefined || request.headers['sec-fetch-dest'] === 'document')\n && target.decodedPathname !== '/api'\n && !target.decodedPathname.startsWith('/api/')\n if (mapped.status === 401 && topLevel) {\n const returnPath = target.raw.length <= 2048 ? target.raw : '/'\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(302, {\n Location: `${AUTH_PREFIX}/login?return=${encodeURIComponent(returnPath)}`,\n 'Content-Length': 0,\n })\n response.end()\n return\n }\n throw error\n }\n if (isMutation) this.requireCsrf(request, authorization)\n const extension = requestedExtension\n if (extension !== undefined) {\n await this.handleExtensionRequest(extension, target, request, response, authorization)\n return\n }\n if (requestedMobileBootBatch !== undefined) {\n await this.serveMobileBootBatch(requestedMobileBootBatch, request, response, authorization)\n return\n }\n if (customAsset !== undefined) {\n const operation = this.allocateRequest(authorization, response, {})\n try {\n let body: Buffer\n let mtime: Date | undefined\n try {\n body = await readFile(customAsset.file, { signal: operation.signal })\n try {\n const fileStat = await stat(customAsset.file)\n mtime = fileStat.mtime\n } catch { /* keep undefined */ }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n if (customAsset.fallback === undefined) throw new HttpError(503, 'mobile_frontend_unavailable')\n body = Buffer.from(customAsset.fallback)\n }\n if (body.byteLength > 256 * 1024) throw new HttpError(413, 'payload_too_large')\n const etag = createHash('sha256').update(body).digest('hex')\n const ifNoneMatch = headerValue(request.headers, 'if-none-match')\n if (ifNoneMatch !== undefined && ifNoneMatch === etag) {\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(304)\n response.end()\n return\n }\n setSecurityHeaders(response, this.tlsEnabled)\n const responseHeaders: Record<string, string | number> = {\n 'Content-Type': customAsset.contentType,\n 'Content-Length': body.byteLength,\n 'ETag': etag,\n }\n if (mtime !== undefined) responseHeaders['Last-Modified'] = mtime.toUTCString()\n response.writeHead(200, responseHeaders)\n response.end(body)\n return\n } finally {\n operation.release()\n }\n }\n if (computerImages) {\n const operation = this.allocateRequest(authorization, response, {})\n try {\n const query = new URL(target.raw, this.address().origin).searchParams\n sendJson(response, 200, await listComputerImages(query.get('path'), operation.signal), this.tlsEnabled)\n return\n } finally {\n operation.release()\n }\n }\n if (computerImage) {\n const operation = this.allocateRequest(authorization, response, {})\n try {\n const query = new URL(target.raw, this.address().origin).searchParams\n const image = await readComputerImage(query.get('path'), operation.signal)\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(200, {\n 'Content-Type': image.contentType,\n 'Content-Length': image.body.byteLength,\n 'Content-Disposition': `inline; filename*=UTF-8''${encodeURIComponent(image.name)}`,\n })\n response.end(image.body)\n return\n } finally {\n operation.release()\n }\n }\n const stockFrontend = new URL(target.raw, this.address().origin).searchParams.get('frontend') === 'stock'\n const acceptsHtml = request.headers.accept?.split(',').some(value => value.trim().split(';', 1)[0] === 'text/html') ?? false\n if (request.method === 'GET' && acceptsHtml && !stockFrontend) {\n await this.proxyMobileIndex(request, response, authorization)\n return\n }\n if (stockFrontend && target.decodedPathname === '/') request.url = '/'\n await this.proxyHttp(request, response, authorization)\n }\n\n private async handleExtensionRequest(\n targetInfo: NonNullable<ReturnType<typeof extensionTarget>>,\n target: ReturnType<typeof parseRequestTarget>,\n request: IncomingMessage,\n response: ServerResponse,\n authorization: SessionAuthorization,\n ): Promise<void> {\n const extensions = this.extensions\n if (extensions === undefined) throw new HttpError(404, 'not_found')\n if (targetInfo.kind === 'events') {\n if (request.method !== 'GET' || target.search !== '') throw new HttpError(request.method === 'GET' ? 400 : 405, request.method === 'GET' ? 'bad_request' : 'method_not_allowed')\n this.openExtensionEventStream(request, response, authorization)\n return\n }\n if (targetInfo.kind === 'manifest') {\n if (request.method !== 'GET' && request.method !== 'HEAD') throw new HttpError(405, 'method_not_allowed')\n const operation = this.allocateRequest(authorization, response, {})\n try {\n operation.signal.throwIfAborted()\n const customRevision = async (file: string, fallback: string): Promise<string> => {\n let source: Buffer\n try {\n source = await readFile(file, { signal: operation.signal })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n source = Buffer.from(fallback)\n }\n if (source.byteLength > 256 * 1024) throw new HttpError(413, 'payload_too_large')\n return createHash('sha256').update(source).digest('hex')\n }\n const [scriptRevision, styleRevision] = await Promise.all([\n customRevision(this.config.customScriptFile, CUSTOM_SCRIPT_FALLBACK),\n customRevision(this.config.customCssFile, CUSTOM_STYLE_FALLBACK),\n ])\n const body = Buffer.from(JSON.stringify({\n protocol: 1,\n extensions: extensions.manifest(),\n legacy: { scriptRevision, styleRevision },\n }))\n // The ETag must cover extension content, not just the manifest body, so\n // editing mobile.js/css alone invalidates the client's cached manifest.\n const etag = createHash('sha256').update(body).update(extensions.contentDigest()).digest('hex')\n if (headerValue(request.headers, 'if-none-match') === etag) {\n setSecurityHeaders(response, this.tlsEnabled); response.writeHead(304); response.end(); return\n }\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': body.byteLength, ETag: etag })\n if (request.method === 'HEAD') response.end(); else response.end(body)\n return\n } finally {\n operation.release()\n }\n }\n if (targetInfo.kind === 'script' || targetInfo.kind === 'style' || targetInfo.kind === 'asset') {\n if (request.method !== 'GET' && request.method !== 'HEAD') throw new HttpError(405, 'method_not_allowed')\n const generation = extensionGeneration(new URLSearchParams(target.search).get('generation') ?? undefined)\n const operation = this.allocateRequest(authorization, response, {})\n try {\n const file = targetInfo.kind === 'script'\n ? await extensions.readClientFile(targetInfo.id, 'script', operation.signal, generation)\n : targetInfo.kind === 'style'\n ? await extensions.readClientFile(targetInfo.id, 'style', operation.signal, generation)\n : await extensions.readAsset(targetInfo.id, targetInfo.path ?? '', operation.signal, generation)\n if (headerValue(request.headers, 'if-none-match') === file.digest) {\n setSecurityHeaders(response, this.tlsEnabled); response.writeHead(304); response.end(); return\n }\n const contentType = targetInfo.kind === 'script'\n ? 'text/javascript; charset=utf-8'\n : targetInfo.kind === 'style' ? 'text/css; charset=utf-8' : extensionContentType(targetInfo.path ?? '')\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(200, { 'Content-Type': contentType, 'Content-Length': file.body.byteLength, ETag: file.digest })\n if (request.method === 'HEAD') response.end(); else response.end(file.body)\n return\n } finally {\n operation.release()\n }\n }\n if (targetInfo.kind === 'action') {\n if (request.method !== 'POST') throw new HttpError(405, 'method_not_allowed')\n const maximum = 1024 * 1024\n assertBoundedContentLength(request, maximum)\n const generation = extensionGeneration(headerValue(request.headers, EXTENSION_GENERATION_HEADER))\n const operation = this.allocateRequest(authorization, response, {})\n const abort = new AbortController()\n response.once('close', () => { abort.abort() })\n const generationSignal = extensions.signal(targetInfo.id, generation)\n const onGenerationAbort = (): void => { abort.abort(); if (!response.destroyed) response.destroy() }\n generationSignal?.addEventListener('abort', onGenerationAbort, { once: true })\n try {\n const body = await readJsonObject(request, maximum)\n const result = await extensions.invoke(targetInfo.id, targetInfo.action, body, { signal: abort.signal, deviceId: authorization.deviceId }, generation)\n let serialized: Buffer\n try { serialized = Buffer.from(JSON.stringify(result)) } catch { throw new MobileExtensionError('extension_failed', 'extension action failed', 500) }\n if (serialized.byteLength > 4 * 1024 * 1024) throw new MobileExtensionError('extension_result_too_large', 'extension result is too large', 500)\n sendJson(response, 200, result, this.tlsEnabled)\n } finally {\n generationSignal?.removeEventListener('abort', onGenerationAbort)\n abort.abort(); operation.release()\n }\n return\n }\n if (targetInfo.kind === 'route') {\n const method = request.method ?? 'GET'\n if (!['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) throw new HttpError(405, 'method_not_allowed')\n const hasBody = method !== 'GET' && method !== 'HEAD'\n if (hasBody) assertBoundedContentLength(request, this.config.maxBodyBytes)\n const generation = extensionGeneration(headerValue(request.headers, EXTENSION_GENERATION_HEADER))\n const operation = this.allocateRequest(authorization, response, {})\n const abort = new AbortController()\n response.once('close', () => { abort.abort() })\n const generationSignal = extensions.signal(targetInfo.id, generation)\n const onGenerationAbort = (): void => { abort.abort(); if (!response.destroyed) response.destroy() }\n generationSignal?.addEventListener('abort', onGenerationAbort, { once: true })\n try {\n const body = hasBody ? await readBoundedBody(request, this.config.maxBodyBytes) : Buffer.alloc(0)\n const parsed = new URL(target.raw, this.address().origin)\n const routeRequest: MobileRouteRequest = {\n method, pathname: targetInfo.path, query: parsed.searchParams,\n headers: extensionRequestHeaders(request.headers), body, signal: abort.signal, deviceId: authorization.deviceId,\n }\n const result = await extensions.route(targetInfo.id, method, targetInfo.path, routeRequest, generation)\n await this.sendExtensionResponse(response, result, request.method === 'HEAD')\n } finally {\n generationSignal?.removeEventListener('abort', onGenerationAbort)\n abort.abort(); operation.release()\n }\n }\n }\n\n private async sendExtensionResponse(response: ServerResponse, result: MobileRouteResponse, head: boolean): Promise<void> {\n const status = result.status ?? 200\n if (!Number.isSafeInteger(status) || status < 200 || status > 599) {\n throw new MobileExtensionError('invalid_route_response', 'extension returned an invalid HTTP status', 500)\n }\n const contentType = result.contentType ?? 'application/octet-stream'\n if (contentType.length > 1024\n || !/^[\\x20-\\x7e]+$/u.test(contentType)\n || !/^[\\w!#$&+.^-]+\\/[\\w!#$&+.^-]+(?:;[\\x20-\\x7e]*)?$/u.test(contentType)) {\n throw new MobileExtensionError('invalid_route_response', 'extension returned an invalid content type', 500)\n }\n const safeHeaders: Record<string, string> = {}\n for (const [name, value] of Object.entries(result.headers ?? {})) {\n if (!/^(?:content-disposition|cache-control|etag)$/iu.test(name) || /[\\r\\n]/u.test(value)) continue\n safeHeaders[name] = value\n }\n setSecurityHeaders(response, this.tlsEnabled)\n if (typeof result.body === 'string' || result.body instanceof Uint8Array) {\n const body = typeof result.body === 'string' ? Buffer.from(result.body) : Buffer.from(result.body)\n if (body.byteLength > 4 * 1024 * 1024) throw new MobileExtensionError('extension_result_too_large', 'extension response is too large', 500)\n response.writeHead(status, { ...safeHeaders, 'Content-Type': contentType, 'Content-Length': body.byteLength })\n if (head) response.end(); else response.end(body)\n return\n }\n response.writeHead(status, { ...safeHeaders, 'Content-Type': contentType })\n if (head) { result.body.destroy(); response.end(); return }\n await pipeline(result.body, new ByteLimitTransform(4 * 1024 * 1024), response)\n }\n\n /** Exchange DSH's process-local launch token for an authority-bound cookie kept inside this gateway. */\n private async upstreamCookieHeader(): Promise<string | undefined> {\n if (this.upstreamAuthenticatedUrl === undefined) return undefined\n if (this.upstreamCookie !== undefined\n && this.upstreamCookieExpiresAt > Date.now() + UPSTREAM_AUTH_REFRESH_MARGIN_MS) {\n return this.upstreamCookie\n }\n if (this.upstreamCookieTask !== undefined) return this.upstreamCookieTask\n const task = this.exchangeUpstreamCookie()\n this.upstreamCookieTask = task\n try {\n return await task\n } finally {\n if (this.upstreamCookieTask === task) this.upstreamCookieTask = undefined\n }\n }\n\n private async exchangeUpstreamCookie(): Promise<string> {\n const authenticatedUrl = this.upstreamAuthenticatedUrl\n if (authenticatedUrl === undefined) throw new HttpError(502, 'upstream_unavailable')\n let target: URL\n try {\n target = new URL(authenticatedUrl)\n } catch {\n throw new HttpError(502, 'upstream_unavailable')\n }\n if (target.origin !== this.config.upstreamOrigin.origin || target.pathname !== '/'\n || target.hash !== '' || target.search === '') {\n throw new HttpError(502, 'upstream_unavailable')\n }\n try {\n const proxied = await new Promise<IncomingMessage>((resolve, reject) => {\n const upstreamRequest = requestHttp({\n protocol: 'http:',\n hostname: stripIpv6Brackets(this.config.upstreamOrigin.hostname),\n port: Number(this.config.upstreamOrigin.port),\n method: 'GET',\n path: `${target.pathname}${target.search}`,\n headers: {\n host: this.config.upstreamOrigin.host,\n accept: 'text/html',\n 'accept-encoding': 'identity',\n },\n agent: false,\n })\n this.upstreamAuthRequest = upstreamRequest\n upstreamRequest.setTimeout(this.config.upstreamTimeoutMs, () => {\n upstreamRequest.destroy(new Error('upstream timeout'))\n })\n upstreamRequest.once('response', resolve)\n upstreamRequest.once('error', reject)\n upstreamRequest.end()\n })\n await new Promise<void>((resolve, reject) => {\n proxied.once('end', resolve)\n proxied.once('error', reject)\n proxied.resume()\n })\n const setCookie = proxied.headers['set-cookie']?.[0]\n const pair = setCookie?.split(';', 1)[0]\n const maxAgeText = setCookie === undefined\n ? undefined\n : /(?:^|;\\s*)Max-Age=(\\d+)(?:;|$)/iu.exec(setCookie)?.[1]\n const maxAgeSeconds = maxAgeText === undefined ? Number.NaN : Number(maxAgeText)\n const expiresAt = Date.now() + maxAgeSeconds * 1000\n if (proxied.statusCode !== 303 || pair === undefined || pair.length > 4096\n || !UPSTREAM_COOKIE_PAIR.test(pair) || !Number.isSafeInteger(expiresAt)\n || maxAgeSeconds <= 0) {\n throw new HttpError(502, 'upstream_unavailable')\n }\n this.upstreamCookie = pair\n this.upstreamCookieExpiresAt = expiresAt\n return pair\n } catch (error) {\n if (error instanceof HttpError) throw error\n throw new HttpError(502, 'upstream_unavailable')\n } finally {\n this.upstreamAuthRequest?.destroy()\n this.upstreamAuthRequest = undefined\n }\n }\n\n private async proxyMobileIndex(\n request: IncomingMessage,\n response: ServerResponse,\n authorization: SessionAuthorization,\n ): Promise<void> {\n const holder: { request?: ClientRequest } = {}\n const operation = this.allocateRequest(authorization, response, holder)\n try {\n const upstreamHeaders = sanitizeRequestHeaders(request, this.config.upstreamOrigin)\n const upstreamCookie = await this.upstreamCookieHeader()\n if (upstreamCookie !== undefined) upstreamHeaders.cookie = upstreamCookie\n upstreamHeaders['accept-encoding'] = 'identity'\n const proxied = await new Promise<IncomingMessage>((resolve, reject) => {\n const upstreamRequest = requestHttp({\n protocol: 'http:',\n hostname: stripIpv6Brackets(this.config.upstreamOrigin.hostname),\n port: Number(this.config.upstreamOrigin.port),\n method: 'GET',\n path: '/',\n headers: upstreamHeaders,\n agent: false,\n })\n holder.request = upstreamRequest\n upstreamRequest.setTimeout(this.config.upstreamTimeoutMs, () => {\n upstreamRequest.destroy(new Error('upstream timeout'))\n })\n upstreamRequest.once('response', resolve)\n upstreamRequest.once('error', reject)\n upstreamRequest.end()\n })\n if ((proxied.statusCode ?? 502) !== 200) throw new HttpError(502, 'upstream_unavailable')\n const chunks: Buffer[] = []\n let bytes = 0\n for await (const chunk of proxied) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n bytes += buffer.byteLength\n if (bytes > 4 * 1024 * 1024) throw new HttpError(502, 'upstream_unavailable')\n chunks.push(buffer)\n }\n let body: Buffer\n try {\n const html = Buffer.concat(chunks).toString('utf8')\n const plan = parseMobileBootPlan(html)\n const options = plan.planEntries === undefined\n ? {}\n : await this.resolveMobileBootSizes(plan.planEntries)\n const rewritten = rewriteMobileIndexWithBatch(html, options)\n for (const batch of rewritten.batches) this.rememberMobileBootBatch(batch)\n body = Buffer.from(rewritten.html)\n } catch {\n throw new HttpError(502, 'upstream_unavailable')\n }\n const headers = sanitizeResponseHeaders(proxied.headers, this.config.upstreamOrigin)\n delete headers['content-length']\n delete headers['content-encoding']\n delete headers.etag\n // The proxied GUI document: it must be able to frame its own surfaces.\n setSecurityHeaders(response, this.tlsEnabled, 'proxied')\n response.writeHead(200, {\n ...headers,\n 'Content-Type': 'text/html; charset=utf-8',\n 'Content-Length': body.byteLength,\n })\n response.end(body)\n } catch (error) {\n holder.request?.destroy()\n if (error instanceof HttpError) throw error\n if (response.headersSent) response.destroy()\n else throw new HttpError(502, 'upstream_unavailable')\n } finally {\n operation.release()\n }\n }\n\n private rememberMobileBootBatch(plan: MobileBootBatchPlan): void {\n const existing = this.mobileBootBatches.get(plan.key)\n this.mobileBootBatches.delete(plan.key)\n this.mobileBootBatches.set(plan.key, existing ?? { plan })\n while (this.mobileBootBatches.size > MAX_MOBILE_BOOT_BATCHES) {\n const oldest = this.mobileBootBatches.keys().next().value as string | undefined\n if (oldest === undefined) break\n this.mobileBootBatches.get(oldest)?.assembly?.controller.abort()\n this.mobileBootBatches.delete(oldest)\n }\n }\n\n /**\n * Determine which layout-batch entries can share a merged boot batch and which\n * must pass through. An entry at or above the per-entry cap, or one whose size\n * could not be probed, keeps its own upstream `/plugins` row: a merged batch\n * cannot carry it, and an unknown bundle must not risk the whole batch.\n */\n private async resolveMobileBootSizes(\n planEntries: readonly MobileBootBatchEntry[],\n ): Promise<{ sizes: Map<string, number>; passThrough: Set<string> }> {\n const sizes = new Map<string, number>()\n const passThrough = new Set<string>()\n const candidates = planEntries.filter(entry => entry.id !== MOBILE_LAYOUT_MODULE\n && upstreamPluginBundleUrl(entry.url, this.config.upstreamOrigin) !== undefined)\n let cursor = 0\n const worker = async (): Promise<void> => {\n while (cursor < candidates.length) {\n const index = cursor++\n const source = candidates[index]!.url\n const size = await this.upstreamBundleSize(source)\n if (size === undefined || size >= MAX_MOBILE_BOOT_ENTRY_BYTES) passThrough.add(source)\n else sizes.set(source, size)\n }\n }\n await Promise.all(Array.from({ length: Math.min(8, candidates.length) }, worker))\n return { sizes, passThrough }\n }\n\n /**\n * Measure one upstream bundle by reading its body. The upstream serves\n * `/plugins` bundles as chunked streams without a Content-Length, so a probe\n * counts bytes; it aborts instantly past the per-entry cap. Measurements are\n * cached for a short window.\n */\n private async upstreamBundleSize(source: string): Promise<number | undefined> {\n const target = upstreamPluginBundleUrl(source, this.config.upstreamOrigin)\n if (target === undefined) return undefined\n const cached = this.bootEntrySizeCache.get(source)\n if (cached !== undefined && Date.now() - cached.at < MOBILE_BOOT_SIZE_CACHE_TTL_MS) return cached.size\n const upstreamCookie = await this.upstreamCookieHeader()\n let upstreamRequest: ClientRequest | undefined\n try {\n const proxied = await new Promise<IncomingMessage>((resolve, reject) => {\n upstreamRequest = requestHttp({\n protocol: 'http:',\n hostname: stripIpv6Brackets(this.config.upstreamOrigin.hostname),\n port: Number(this.config.upstreamOrigin.port),\n method: 'GET',\n path: `${target.pathname}${target.search}`,\n headers: {\n host: this.config.upstreamOrigin.host,\n accept: 'text/javascript',\n 'accept-encoding': 'identity',\n [MOBILE_BOOT_SIZE_PROBE_HEADER]: '1',\n ...(upstreamCookie === undefined ? {} : { cookie: upstreamCookie }),\n },\n agent: false,\n })\n upstreamRequest.setTimeout(this.config.upstreamTimeoutMs, () => {\n upstreamRequest?.destroy(upstreamTimeoutError())\n })\n upstreamRequest.once('response', resolve)\n upstreamRequest.once('error', reject)\n upstreamRequest.end()\n })\n if (proxied.statusCode !== 200) {\n proxied.resume()\n return undefined\n }\n const size = await new Promise<number | undefined>((resolve, reject) => {\n let bytes = 0\n proxied.on('data', (chunk: Buffer) => {\n bytes += chunk.length\n if (bytes > MAX_MOBILE_BOOT_ENTRY_BYTES) {\n proxied.destroy()\n resolve(undefined)\n }\n })\n proxied.once('end', () => resolve(bytes))\n proxied.once('error', reject)\n })\n if (size === undefined) return undefined\n this.bootEntrySizeCache.set(source, { size, at: Date.now() })\n return size\n } catch {\n return undefined\n } finally {\n upstreamRequest?.destroy()\n }\n }\n\n private async serveMobileBootBatch(\n key: string,\n request: IncomingMessage,\n response: ServerResponse,\n authorization: SessionAuthorization,\n ): Promise<void> {\n if (request.method !== 'GET' && request.method !== 'HEAD') throw new HttpError(405, 'method_not_allowed')\n const stored = this.mobileBootBatches.get(key)\n if (stored === undefined) throw new HttpError(404, 'not_found')\n const operation = this.allocateRequest(authorization, response, {})\n response.once('close', operation.abort)\n try {\n const layoutStat = await stat(this.config.mobileLayoutFile)\n if (stored.body === undefined || stored.etag === undefined || stored.layoutMtimeMs !== layoutStat.mtimeMs) {\n operation.signal.throwIfAborted()\n const assembly = stored.assembly ?? this.startMobileBootBatchAssembly(stored, layoutStat.mtimeMs)\n await waitForRequestTask(assembly.task, operation.signal)\n }\n const compressed = acceptsGzip(request.headers['accept-encoding'])\n // The assembly above assigns stored.body; TypeScript cannot narrow a\n // property written inside an awaited closure, so read it once here.\n const assembled = stored.body\n if (assembled === undefined) throw new HttpError(502, 'upstream_unavailable')\n const body = compressed\n ? stored.gzipBody ??= await gzipBuffer(assembled)\n : assembled\n const etag = compressed ? `${stored.etag}-gzip` : stored.etag\n const headers: OutgoingHttpHeaders = {\n 'Content-Type': 'text/javascript; charset=utf-8',\n 'Content-Length': body.byteLength,\n 'Cache-Control': 'private, no-cache',\n ETag: etag,\n }\n if (compressed) headers['Content-Encoding'] = 'gzip'\n addVaryAcceptEncoding(headers)\n if (headerValue(request.headers, 'if-none-match') === etag) {\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(304, { ETag: etag, 'Cache-Control': 'private, no-cache', Vary: String(headers.vary) })\n response.end()\n return\n }\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(200, headers)\n if (request.method === 'HEAD') response.end()\n else response.end(body)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') throw new HttpError(503, 'mobile_frontend_unavailable')\n throw error\n } finally {\n response.removeListener('close', operation.abort)\n operation.release()\n }\n }\n\n private startMobileBootBatchAssembly(stored: StoredMobileBootBatch, layoutMtimeMs: number): MobileBootBatchAssembly {\n const controller = new AbortController()\n const task = (async (): Promise<Buffer> => {\n const body = await this.assembleMobileBootBatch(stored.plan, controller.signal)\n stored.body = body\n delete stored.gzipBody\n stored.etag = createHash('sha256').update(body).digest('hex')\n stored.layoutMtimeMs = layoutMtimeMs\n return body\n })()\n const assembly = Object.freeze({ controller, task })\n stored.assembly = assembly\n void task.then(\n () => { if (stored.assembly === assembly) delete stored.assembly },\n () => { if (stored.assembly === assembly) delete stored.assembly },\n )\n return assembly\n }\n\n private async assembleMobileBootBatch(plan: MobileBootBatchPlan, signal: AbortSignal): Promise<Buffer> {\n const bodies = new Array<Buffer>(plan.entries.length)\n let cursor = 0\n const worker = async (): Promise<void> => {\n while (cursor < plan.entries.length) {\n const index = cursor++\n const entry = plan.entries[index]!\n bodies[index] = entry.id === MOBILE_LAYOUT_MODULE\n ? await readFile(this.config.mobileLayoutFile, { signal })\n : await this.readUpstreamClientBundleWithRetry(entry.url, signal)\n if (bodies[index]!.byteLength > MAX_MOBILE_BOOT_ENTRY_BYTES) throw new HttpError(502, 'upstream_unavailable')\n }\n }\n // Three workers keep the fan-out gentle: the upstream resets a fraction of\n // connections when this many entries are pulled at once, and every reset\n // used to fail the whole batch.\n await Promise.all(Array.from({ length: Math.min(3, plan.entries.length) }, worker))\n const total = bodies.reduce((bytes, body) => bytes + body.byteLength + MOBILE_BOOT_SEPARATOR_BYTES, 0)\n if (total > MAX_MOBILE_BOOT_BATCH_BYTES) throw new HttpError(502, 'upstream_unavailable')\n return Buffer.concat(bodies.flatMap(body => [body, Buffer.from('\\n;\\n')]))\n }\n\n /**\n * Read one upstream bundle, retrying transient connection failures.\n *\n * Assembling a batch fans out over every client entry, and the upstream\n * resets a fraction of those connections before sending a byte\n * (`read ECONNRESET`, recv=0) — randomly, on any entry, at any concurrency.\n * A single such reset used to fail the whole batch with 502\n * `upstream_unavailable`, surfacing in the browser as \"bundle script\n * /mobile-access/mobile-boot/<hash>.js failed to load\". Retrying recovers\n * every observed reset; a genuine upstream error still fails.\n */\n private async readUpstreamClientBundleWithRetry(source: string, signal: AbortSignal): Promise<Buffer> {\n for (let attempt = 1; attempt <= MOBILE_BOOT_UPSTREAM_ATTEMPTS; attempt++) {\n signal.throwIfAborted()\n try {\n return await this.readUpstreamClientBundle(source, signal)\n } catch (error) {\n if (signal.aborted) throw error\n if (!isTransientUpstreamError(error)) throw error\n if (attempt === MOBILE_BOOT_UPSTREAM_ATTEMPTS) throw new HttpError(502, 'upstream_unavailable')\n await waitForAbortableDelay(MOBILE_BOOT_RETRY_DELAY_MS * attempt, signal)\n }\n }\n throw new HttpError(502, 'upstream_unavailable')\n }\n\n private async readUpstreamClientBundle(source: string, signal: AbortSignal): Promise<Buffer> {\n signal.throwIfAborted()\n const target = upstreamPluginBundleUrl(source, this.config.upstreamOrigin)\n if (target === undefined) throw new HttpError(502, 'upstream_unavailable')\n let upstreamRequest: ClientRequest | undefined\n const aborted = (): void => { upstreamRequest?.destroy(new Error('request aborted')) }\n signal.addEventListener('abort', aborted, { once: true })\n try {\n const upstreamCookie = await this.upstreamCookieHeader()\n signal.throwIfAborted()\n const proxied = await new Promise<IncomingMessage>((resolve, reject) => {\n upstreamRequest = requestHttp({\n protocol: 'http:',\n hostname: stripIpv6Brackets(this.config.upstreamOrigin.hostname),\n port: Number(this.config.upstreamOrigin.port),\n method: 'GET',\n path: `${target.pathname}${target.search}`,\n headers: {\n host: this.config.upstreamOrigin.host,\n accept: 'text/javascript',\n 'accept-encoding': 'identity',\n ...(upstreamCookie === undefined ? {} : { cookie: upstreamCookie }),\n },\n agent: false,\n })\n upstreamRequest.setTimeout(this.config.upstreamTimeoutMs, () => {\n upstreamRequest?.destroy(upstreamTimeoutError())\n })\n upstreamRequest.once('response', resolve)\n upstreamRequest.once('error', reject)\n upstreamRequest.end()\n })\n if ((proxied.statusCode ?? 502) !== 200) throw new HttpError(502, 'upstream_unavailable')\n const chunks: Buffer[] = []\n let bytes = 0\n for await (const chunk of proxied) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n bytes += buffer.byteLength\n if (bytes > MAX_MOBILE_BOOT_ENTRY_BYTES) throw new HttpError(502, 'upstream_unavailable')\n chunks.push(buffer)\n }\n return Buffer.concat(chunks)\n } catch (error) {\n if (error instanceof HttpError) throw error\n if (signal.aborted || isTransientUpstreamError(error)) throw error\n throw new HttpError(502, 'upstream_unavailable')\n } finally {\n signal.removeEventListener('abort', aborted)\n upstreamRequest?.destroy()\n }\n }\n\n private allocateRequest(\n authorization: SessionAuthorization,\n response: ServerResponse,\n upstream: { request?: ClientRequest },\n ): { id: number; signal: AbortSignal; abort: () => void; release: () => void } {\n if (this.activeRequests.size >= this.config.maxActiveRequests) throw new HttpError(429, 'busy')\n const id = this.nextOperationId++\n const controller = new AbortController()\n const abort = (): void => {\n controller.abort()\n upstream.request?.destroy()\n if (!response.destroyed) response.destroy()\n }\n const timer = setTimeout(abort, Math.max(1, authorization.expiresAt - Date.now()))\n timer.unref()\n this.activeRequests.set(id, Object.freeze({ ...authorization, abort, timer }))\n return {\n id,\n signal: controller.signal,\n abort,\n release: () => {\n const entry = this.activeRequests.get(id)\n if (entry !== undefined) clearTimeout(entry.timer)\n this.activeRequests.delete(id)\n },\n }\n }\n\n /**\n * Proxy one request upstream. Pass-through client bundles (`GET /plugins`)\n * receive bounded transient retries — the upstream resets a fraction of fresh\n * connections — matching the resilience the merged-batch assembly already has.\n * A request is only retried before any byte reached the client.\n */\n private async proxyHttp(\n request: IncomingMessage,\n response: ServerResponse,\n authorization: SessionAuthorization,\n ): Promise<void> {\n const retryable = request.method === 'GET'\n && request.url?.split('?', 1)[0]?.startsWith('/plugins/') === true\n if (!retryable) {\n try {\n await this.proxyHttpOnce(request, response, authorization)\n } catch (error) {\n if (error instanceof HttpError) throw error\n if (response.headersSent) response.destroy()\n else throw new HttpError(502, 'upstream_unavailable')\n }\n return\n }\n const delay = (attempt: number): Promise<void> => (\n new Promise(resolve => setTimeout(resolve, MOBILE_BOOT_RETRY_DELAY_MS * attempt))\n )\n for (let attempt = 1; attempt <= MOBILE_BOOT_UPSTREAM_ATTEMPTS; attempt++) {\n try {\n await this.proxyHttpOnce(request, response, authorization)\n return\n } catch (error) {\n if (response.headersSent) {\n response.destroy()\n return\n }\n if (error instanceof HttpError || !isTransientUpstreamError(error)) {\n throw error instanceof HttpError ? error : new HttpError(502, 'upstream_unavailable')\n }\n if (attempt === MOBILE_BOOT_UPSTREAM_ATTEMPTS) throw new HttpError(502, 'upstream_unavailable')\n await delay(attempt)\n }\n }\n throw new HttpError(502, 'upstream_unavailable')\n }\n\n private async proxyHttpOnce(\n request: IncomingMessage,\n response: ServerResponse,\n authorization: SessionAuthorization,\n ): Promise<void> {\n const declared = request.headers['content-length']\n if (declared !== undefined && (!/^\\d+$/u.test(declared) || Number(declared) > this.config.maxBodyBytes)) {\n throw new HttpError(413, 'payload_too_large')\n }\n const holder: { request?: ClientRequest } = {}\n const operation = this.allocateRequest(authorization, response, holder)\n let bodyDone: Promise<void> | undefined\n try {\n const bufferedBody = request.method === 'POST' && request.url?.split('?', 1)[0] === SESSION_HISTORY_PATH\n ? mobileHistoryRequestBody(request, await readBoundedBody(request, this.config.maxBodyBytes))\n : undefined\n const upstreamHeaders = sanitizeRequestHeaders(request, this.config.upstreamOrigin)\n const upstreamCookie = await this.upstreamCookieHeader()\n if (upstreamCookie !== undefined) upstreamHeaders.cookie = upstreamCookie\n if (bufferedBody !== undefined) upstreamHeaders['content-length'] = String(bufferedBody.byteLength)\n const upstreamResponse = new Promise<IncomingMessage>((resolve, reject) => {\n const upstreamRequest = requestHttp({\n protocol: 'http:',\n hostname: stripIpv6Brackets(this.config.upstreamOrigin.hostname),\n port: Number(this.config.upstreamOrigin.port),\n method: request.method,\n path: request.url,\n headers: upstreamHeaders,\n agent: false,\n })\n holder.request = upstreamRequest\n upstreamRequest.setTimeout(this.config.upstreamTimeoutMs, () => {\n upstreamRequest.destroy(new Error('upstream timeout'))\n })\n upstreamRequest.once('response', resolve)\n upstreamRequest.once('error', reject)\n if (bufferedBody === undefined) {\n bodyDone = pipeline(request, new ByteLimitTransform(this.config.maxBodyBytes), upstreamRequest)\n } else {\n upstreamRequest.end(bufferedBody)\n bodyDone = Promise.resolve()\n }\n void bodyDone.catch(reject)\n })\n const proxied = await upstreamResponse\n // Proxied upstream routes (static assets, the GUI's own /sidebar routes,\n // the API): the GUI frames some of its own routes (HTML/diff previews),\n // so they follow the proxied framing policy.\n setSecurityHeaders(response, this.tlsEnabled, 'proxied')\n const headers = sanitizeResponseHeaders(proxied.headers, this.config.upstreamOrigin)\n const statusCode = proxied.statusCode ?? 502\n const cacheControl = revisionedStaticCacheControl(request, statusCode)\n if (cacheControl !== undefined) headers['cache-control'] = cacheControl\n const compressed = shouldCompressResponse(request, proxied)\n if (compressed) {\n delete headers['accept-ranges']\n delete headers['content-length']\n delete headers.etag\n headers['content-encoding'] = 'gzip'\n addVaryAcceptEncoding(headers)\n }\n response.writeHead(statusCode, headers)\n await Promise.all([\n bodyDone,\n compressed ? pipeline(proxied, createGzip(), response) : pipeline(proxied, response),\n ])\n } catch (error) {\n holder.request?.destroy()\n await bodyDone?.catch(() => undefined)\n if (error instanceof HttpError) throw error\n if (response.headersSent) response.destroy()\n // Raw transient errors reach the proxyHttp retry window; anything else\n // becomes the standard upstream-unavailable answer.\n throw error\n } finally {\n operation.release()\n }\n }\n\n private abortSessionResources(sessionKey: string): void {\n for (const request of this.activeRequests.values()) {\n if (request.sessionKey === sessionKey) request.abort()\n }\n for (const socket of this.activeWebSockets.values()) {\n if (socket.sessionKey === sessionKey) {\n socket.client.destroy()\n socket.upstream.destroy()\n }\n }\n }\n\n private broadcastExtensionChange(): void {\n if (this.closing) return\n this.extensionEventRevision += 1\n for (const listener of this.extensionEventListeners) listener(this.extensionEventRevision)\n }\n\n /** Fan a completed task to every phone holding this gateway's event stream. */\n broadcastTaskEvent(event: { readonly sessionId: string; readonly turn: number }): void {\n if (this.closing) return\n const payload = JSON.stringify({ sessionId: String(event.sessionId), turn: Number(event.turn) || 0 })\n for (const listener of this.taskEventListeners) listener(payload)\n }\n\n /** Notify only the device whose persistent credential was revoked by the Host. */\n private broadcastDeviceRevoked(deviceId: string): void {\n const listeners = this.deviceEventListeners.get(deviceId)\n if (listeners === undefined) return\n const payload = JSON.stringify({ reason: 'device_revoked' })\n for (const listener of [...listeners]) listener(payload)\n }\n\n private pollLegacyCustomChanges(): Promise<void> {\n if (this.extensionChangeTask !== undefined) return this.extensionChangeTask\n const digestFile = async (path: string, fallback: string): Promise<string> => {\n try {\n const info = await stat(path)\n if (!info.isFile() || info.size > 256 * 1024) return `invalid:${String(info.size)}:${String(info.mtimeMs)}`\n return createHash('sha256').update(await readFile(path)).digest('hex')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return createHash('sha256').update(fallback).digest('hex')\n return `error:${String((error as NodeJS.ErrnoException).code ?? 'unknown')}`\n }\n }\n const task = Promise.all([\n digestFile(this.config.customScriptFile, CUSTOM_SCRIPT_FALLBACK),\n digestFile(this.config.customCssFile, CUSTOM_STYLE_FALLBACK),\n ]).then(parts => {\n const next = createHash('sha256').update(parts.join('|')).digest('hex')\n if (this.legacyCustomDigest !== '' && next !== this.legacyCustomDigest) this.broadcastExtensionChange()\n this.legacyCustomDigest = next\n }).finally(() => {\n if (this.extensionChangeTask === task) this.extensionChangeTask = undefined\n })\n this.extensionChangeTask = task\n return task\n }\n\n private openExtensionEventStream(\n request: IncomingMessage,\n response: ServerResponse,\n authorization: SessionAuthorization,\n ): void {\n const operation = this.allocateRequest(authorization, response, {})\n let closed = false\n let heartbeat: NodeJS.Timeout | undefined\n const close = (): void => {\n if (closed) return\n closed = true\n if (heartbeat !== undefined) clearInterval(heartbeat)\n this.extensionEventListeners.delete(send)\n this.taskEventListeners.delete(sendTask)\n const deviceListeners = this.deviceEventListeners.get(authorization.deviceId)\n deviceListeners?.delete(sendDevice)\n if (deviceListeners?.size === 0) this.deviceEventListeners.delete(authorization.deviceId)\n request.removeListener('aborted', close)\n response.removeListener('close', close)\n operation.release()\n }\n const send = (revision: number): void => {\n if (closed || response.destroyed || response.writableEnded) return\n response.write(`id: ${String(revision)}\\nevent: extensions-changed\\ndata: {\\\"revision\\\":${String(revision)}}\\n\\n`)\n }\n const sendTask = (payload: string): void => {\n if (closed || response.destroyed || response.writableEnded) return\n response.write(`event: task-notify\\ndata: ${payload}\\n\\n`)\n }\n const sendDevice = (payload: string): void => {\n if (closed || response.destroyed || response.writableEnded) return\n response.write(`event: device-revoked\\ndata: ${payload}\\n\\n`)\n }\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(200, {\n 'Content-Type': 'text/event-stream; charset=utf-8',\n 'Cache-Control': 'no-store',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n })\n response.write('retry: 2000\\n: ready\\n\\n')\n this.extensionEventListeners.add(send)\n this.taskEventListeners.add(sendTask)\n const deviceListeners = this.deviceEventListeners.get(authorization.deviceId) ?? new Set<(payload: string) => void>()\n deviceListeners.add(sendDevice)\n this.deviceEventListeners.set(authorization.deviceId, deviceListeners)\n heartbeat = setInterval(() => {\n if (!closed && !response.destroyed && !response.writableEnded) response.write(': heartbeat\\n\\n')\n }, EXTENSION_EVENT_HEARTBEAT_MS)\n heartbeat.unref()\n request.once('aborted', close)\n response.once('close', close)\n }\n\n private async readUpgradeResponse(upstream: Socket, expectedAccept: string): Promise<{ header: string; remainder: Buffer }> {\n return new Promise((resolve, reject) => {\n let buffer = Buffer.alloc(0)\n const failed = (error: Error): void => { cleanup(); reject(error) }\n const closed = (): void => { cleanup(); reject(new Error('upstream closed during WebSocket handshake')) }\n const data = (chunk: Buffer): void => {\n buffer = Buffer.concat([buffer, chunk])\n const end = buffer.indexOf('\\r\\n\\r\\n')\n if (end < 0) {\n if (buffer.length >= MAX_HEADER_BYTES) failed(new Error('upstream WebSocket headers are too large'))\n return\n }\n if (end + 4 > MAX_HEADER_BYTES) {\n failed(new Error('upstream WebSocket headers are too large'))\n return\n }\n cleanup()\n const lines = buffer.subarray(0, end).toString('latin1').split('\\r\\n')\n if (lines.shift() !== 'HTTP/1.1 101 Switching Protocols') {\n reject(new Error('upstream refused WebSocket upgrade'))\n return\n }\n const selected = new Map<string, string>()\n for (const line of lines) {\n const colon = line.indexOf(':')\n if (colon <= 0) {\n reject(new Error('upstream returned malformed WebSocket headers'))\n return\n }\n const name = line.slice(0, colon).trim().toLowerCase()\n const value = line.slice(colon + 1).trim()\n if (selected.has(name)) {\n reject(new Error('upstream returned duplicate WebSocket headers'))\n return\n }\n selected.set(name, value)\n }\n if (selected.get('upgrade')?.toLowerCase() !== 'websocket'\n || !hasToken(selected.get('connection'), 'upgrade')\n || selected.get('sec-websocket-accept') !== expectedAccept) {\n reject(new Error('upstream returned an invalid WebSocket handshake'))\n return\n }\n const output = [\n 'HTTP/1.1 101 Switching Protocols',\n 'Upgrade: websocket',\n 'Connection: Upgrade',\n `Sec-WebSocket-Accept: ${expectedAccept}`,\n ]\n const protocol = selected.get('sec-websocket-protocol')\n const extensions = selected.get('sec-websocket-extensions')\n if (protocol !== undefined) output.push(`Sec-WebSocket-Protocol: ${protocol}`)\n if (extensions !== undefined) output.push(`Sec-WebSocket-Extensions: ${extensions}`)\n output.push('Referrer-Policy: no-referrer', 'X-Content-Type-Options: nosniff', '', '')\n resolve({ header: output.join('\\r\\n'), remainder: buffer.subarray(end + 4) })\n }\n const cleanup = (): void => {\n upstream.off('data', data)\n upstream.off('error', failed)\n upstream.off('close', closed)\n }\n upstream.on('data', data)\n upstream.once('error', failed)\n upstream.once('close', closed)\n })\n }\n\n /** Snapshot of rejected upgrade paths for the approval UI (newest first). */\n blockedUpgradePathReport(): BlockedUpgradePathEntry[] {\n return this.blockedUpgradeLog?.report() ?? []\n }\n\n private async handleUpgrade(request: IncomingMessage, client: Socket, head: Buffer): Promise<void> {\n const target = parseRequestTarget(request.url)\n const policy = this.requirePolicy()\n // Android WebView WebSockets do not consistently carry Fetch Metadata.\n // Exact Origin, direct CIDR, exact Host, and the short Session Cookie\n // remain mandatory; when Sec-Fetch-Site is present, assertExternalTrust\n // still requires it to be same-origin.\n assertExternalTrust(request, policy, false)\n if (!policy.acceptsOrigin(request.headers.origin)) throw new HttpError(403, 'forbidden')\n // Core DSH paths plus admin-approved third-party plugin paths (exact\n // pathname match; the query string rides along to the upstream plugin,\n // which owns its parsing). Everything else stays 404: paired-device\n // auth, Origin, and handshake checks below still apply to allowed paths.\n const upgradeAllowed = WS_PATHS.has(target.decodedPathname)\n || (this.extraWebSocketPaths?.has(target.decodedPathname) ?? false)\n if (!upgradeAllowed) {\n this.blockedUpgradeLog?.record(target.decodedPathname)\n throw new HttpError(404, 'not_found')\n }\n if (request.method !== 'GET' || headerValue(request.headers, 'upgrade')?.toLowerCase() !== 'websocket'\n || !hasToken(headerValue(request.headers, 'connection'), 'upgrade')) {\n throw new HttpError(400, 'bad_request')\n }\n const key = headerValue(request.headers, 'sec-websocket-key')\n if (key === undefined || headerValue(request.headers, 'sec-websocket-version') !== '13') {\n throw new HttpError(400, 'bad_request')\n }\n let decodedKey: Buffer\n try {\n decodedKey = Buffer.from(key, 'base64')\n } catch {\n throw new HttpError(400, 'bad_request')\n }\n if (decodedKey.length !== 16 || decodedKey.toString('base64') !== key) throw new HttpError(400, 'bad_request')\n const authorization = this.authorize(request)\n if (this.activeWebSockets.size >= this.config.maxWebSockets) throw new HttpError(429, 'busy')\n const upstreamCookie = await this.upstreamCookieHeader()\n if (client.destroyed) return\n\n const upstream = connect({\n host: stripIpv6Brackets(this.config.upstreamOrigin.hostname),\n port: Number(this.config.upstreamOrigin.port),\n })\n client.pause()\n const id = this.nextOperationId++\n const closeBoth = (): void => {\n client.destroy()\n upstream.destroy()\n }\n client.on('error', closeBoth)\n upstream.on('error', closeBoth)\n const timer = setTimeout(closeBoth, Math.max(1, authorization.expiresAt - Date.now()))\n timer.unref()\n const record: ActiveWebSocket = Object.freeze({ ...authorization, client, upstream, timer })\n this.activeWebSockets.set(id, record)\n const cleanup = (): void => {\n const active = this.activeWebSockets.get(id)\n if (active !== undefined) clearTimeout(active.timer)\n this.activeWebSockets.delete(id)\n }\n client.once('close', () => { upstream.destroy(); cleanup() })\n upstream.once('close', () => { client.destroy(); cleanup() })\n upstream.setTimeout(this.config.upstreamTimeoutMs, closeBoth)\n try {\n await new Promise<void>((resolve, reject) => {\n const connected = (): void => {\n upstream.off('error', failed)\n resolve()\n }\n const failed = (error: Error): void => {\n upstream.off('connect', connected)\n reject(error)\n }\n upstream.once('connect', connected)\n upstream.once('error', failed)\n })\n const requestLines = [\n `GET ${target.raw} HTTP/1.1`,\n `Host: ${this.config.upstreamOrigin.host}`,\n 'Upgrade: websocket',\n 'Connection: Upgrade',\n `Origin: ${this.config.upstreamOrigin.origin}`,\n 'Sec-Fetch-Site: same-origin',\n `Sec-WebSocket-Key: ${key}`,\n 'Sec-WebSocket-Version: 13',\n ]\n if (upstreamCookie !== undefined) requestLines.push(`Cookie: ${upstreamCookie}`)\n const protocol = headerValue(request.headers, 'sec-websocket-protocol')\n const extensions = headerValue(request.headers, 'sec-websocket-extensions')\n if (protocol !== undefined) requestLines.push(`Sec-WebSocket-Protocol: ${protocol}`)\n if (extensions !== undefined) requestLines.push(`Sec-WebSocket-Extensions: ${extensions}`)\n requestLines.push('', '')\n upstream.write(requestLines.join('\\r\\n'))\n if (head.length > 0) upstream.write(head)\n const handshake = await this.readUpgradeResponse(upstream, websocketAccept(key))\n upstream.setTimeout(0)\n client.write(handshake.header)\n if (handshake.remainder.length > 0) client.write(handshake.remainder)\n upstream.pipe(client)\n client.pipe(upstream)\n client.resume()\n } catch (error) {\n closeBoth()\n if (error instanceof HttpError) throw error\n throw new HttpError(502, 'upstream_unavailable')\n }\n }\n\n /** Loopback-only DSH WebServer route for opening pairing and managing devices. */\n localAdminRoute(prefix: string = LOCAL_ADMIN_PREFIX): WebRoute {\n return {\n kind: 'prefix',\n path: prefix,\n handler: async (request, response) => {\n try {\n const target = parseRequestTarget(request.url)\n const mutation = request.method === 'POST'\n assertLocalAdminTrust(request, mutation)\n if (target.search !== '') throw new HttpError(400, 'bad_request')\n if (request.method === 'GET' && target.decodedPathname === `${prefix}/status`) {\n sendJson(response, 200, {\n gateway: this.address(),\n pairing: this.access.pairingStatus(),\n deviceCount: this.access.listDevices().length,\n resources: {\n connections: this.connectedSockets.size,\n activeRequests: this.activeRequests.size,\n webSockets: this.activeWebSockets.size,\n },\n discovery: this.discoveryStatus(),\n }, false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${prefix}/devices`) {\n sendJson(response, 200, { devices: this.access.listDevices() }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${prefix}/pairing/open`) {\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (body.ttlMs !== undefined && typeof body.ttlMs !== 'number') throw new HttpError(400, 'bad_request')\n const opened = await this.access.openPairing(body.ttlMs as number | undefined)\n const pairUrl = `${this.address().origin}/mobile-access/pair#instance=${this.config.instanceId}&token=${opened.token}`\n const appPairUrl = pairUrl\n // The QR code is an enhancement; a failed render must not waste an opened window.\n let qrSvg = ''\n try {\n qrSvg = await QRCode.toString(appPairUrl, { type: 'svg', margin: 1 })\n } catch {\n // keep qrSvg empty\n }\n sendJson(response, 201, {\n ...opened,\n appKey: `dsh1.${this.config.instanceId}.${opened.token}`,\n pairUrl,\n appPairUrl,\n qrSvg,\n }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${prefix}/devices/revoke`) {\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (typeof body.deviceId !== 'string' || !/^[a-f\\d]{32}$/u.test(body.deviceId)) {\n throw new HttpError(400, 'bad_request')\n }\n const revoked = await this.access.revokeDevice(body.deviceId)\n if (!revoked) throw new HttpError(404, 'not_found')\n sendJson(response, 200, { revoked: true }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${prefix}/devices/reset`) {\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await this.access.resetDevices()\n sendJson(response, 200, { reset: true }, false)\n return\n }\n throw new HttpError(404, 'not_found')\n } catch (error) {\n const mapped = mapError(error)\n if (response.headersSent) response.destroy()\n else sendFailure(response, mapped.status, mapped.code, false)\n }\n },\n }\n }\n\n /** Close listeners and abort all accepted work before resolving teardown. */\n async close(): Promise<void> {\n if (this.closeTask !== undefined) return this.closeTask\n this.closeTask = this.performClose()\n return this.closeTask\n }\n\n private async performClose(): Promise<void> {\n this.closing = true\n if (this.extensionChangeTimer !== undefined) clearInterval(this.extensionChangeTimer)\n this.extensionChangeTimer = undefined\n this.removeExtensionContentListener()\n this.upstreamAuthRequest?.destroy()\n this.upstreamAuthRequest = undefined\n this.removeSessionListener()\n this.pendingDeviceRevocations.clear()\n const accessClose = this.access.close()\n for (const stored of this.mobileBootBatches.values()) stored.assembly?.controller.abort()\n for (const request of this.activeRequests.values()) request.abort()\n for (const websocket of this.activeWebSockets.values()) {\n websocket.client.destroy()\n websocket.upstream.destroy()\n }\n for (const socket of this.connectedSockets) socket.destroy()\n if (this.discoveryTimer !== undefined) clearInterval(this.discoveryTimer)\n this.discoveryTimer = undefined\n await this.closeBonjour()\n const discoverySocket = this.discoverySocket\n this.discoverySocket = undefined\n if (discoverySocket !== undefined) {\n await new Promise<void>(resolve => { discoverySocket.close(() => resolve()) })\n }\n const server = this.server\n this.server = undefined\n if (server !== undefined && server.listening) {\n server.closeAllConnections()\n await new Promise<void>(resolve => { server.close(() => resolve()) })\n }\n await accessClose\n this.activeRequests.clear()\n this.activeWebSockets.clear()\n this.connectedSockets.clear()\n this.policy = undefined\n this.listenerPort = undefined\n }\n\n /** Safe metadata helper for direct loopback integrations. */\n devices(): readonly DeviceSummary[] {\n return this.access.listDevices()\n }\n\n /** Status shown by the loopback mobile-access control card. */\n extensionStatus(): { readonly loaded: number; readonly failed: number } {\n return this.extensions?.status() ?? { loaded: 0, failed: 0 }\n }\n}\n","import { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, join } from 'node:path'\nimport { randomBytes } from 'node:crypto'\nimport { restrictPrivateFile } from './private-file.js'\n\n/** Persistent record containing only a digest of the long-lived device credential. */\nexport interface StoredDevice {\n readonly id: string\n readonly label: string\n readonly tokenDigest: string\n readonly createdAt: number\n readonly expiresAt: number\n readonly lastSeenAt: number\n /** Legacy tombstone accepted on load; AccessController removes it during initialization. */\n readonly revokedAt?: number\n}\n\n/** Versioned device state. Raw device and Session credentials are never members. */\nexport interface DeviceSnapshot {\n readonly version: 1\n readonly devices: readonly StoredDevice[]\n}\n\n/** Persistence seam for device-token digests and revocation metadata. */\nexport interface DeviceStore {\n load(): Promise<DeviceSnapshot>\n save(snapshot: DeviceSnapshot): Promise<void>\n}\n\nfunction assertInteger(value: unknown, name: string): asserts value is number {\n if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {\n throw new Error(`device state ${name} must be a non-negative integer`)\n }\n}\n\nfunction parseDevice(value: unknown): StoredDevice {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('device state contains an invalid device')\n const record = value as Record<string, unknown>\n if (typeof record.id !== 'string' || !/^[a-f\\d]{32}$/u.test(record.id)) throw new Error('device state contains an invalid id')\n if (typeof record.label !== 'string' || record.label.length < 1 || record.label.length > 64 || /[\\u0000-\\u001f\\u007f]/u.test(record.label)) {\n throw new Error('device state contains an invalid label')\n }\n if (typeof record.tokenDigest !== 'string' || !/^[a-f\\d]{64}$/u.test(record.tokenDigest)) {\n throw new Error('device state contains an invalid credential digest')\n }\n assertInteger(record.createdAt, 'createdAt')\n assertInteger(record.expiresAt, 'expiresAt')\n assertInteger(record.lastSeenAt, 'lastSeenAt')\n if (record.revokedAt !== undefined) assertInteger(record.revokedAt, 'revokedAt')\n if (record.expiresAt <= record.createdAt || record.lastSeenAt < record.createdAt) {\n throw new Error('device state contains inconsistent timestamps')\n }\n return Object.freeze({\n id: record.id,\n label: record.label,\n tokenDigest: record.tokenDigest,\n createdAt: record.createdAt,\n expiresAt: record.expiresAt,\n lastSeenAt: record.lastSeenAt,\n ...(record.revokedAt === undefined ? {} : { revokedAt: record.revokedAt }),\n })\n}\n\n/** Validate durable data before it can authorize a device. */\nexport function parseDeviceSnapshot(value: unknown, maximumDevices = 256): DeviceSnapshot {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('device state must be an object')\n const snapshot = value as Record<string, unknown>\n if (snapshot.version !== 1 || !Array.isArray(snapshot.devices) || snapshot.devices.length > maximumDevices) {\n throw new Error('device state has an unsupported version or device count')\n }\n const devices = snapshot.devices.map(parseDevice)\n if (new Set(devices.map(device => device.id)).size !== devices.length\n || new Set(devices.map(device => device.tokenDigest)).size !== devices.length) {\n throw new Error('device state contains duplicate device identities')\n }\n return Object.freeze({ version: 1, devices: Object.freeze(devices) })\n}\n\n/** Atomic JSON implementation with symlink refusal and owner-only file creation. */\nexport class JsonDeviceStore implements DeviceStore {\n constructor(private readonly file: string, private readonly maximumDevices = 256) {}\n\n async load(): Promise<DeviceSnapshot> {\n let stat\n try {\n stat = await lstat(this.file)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return Object.freeze({ version: 1, devices: Object.freeze([]) })\n throw error\n }\n if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1024 * 1024) {\n throw new Error('device state must be a regular file no larger than 1 MiB')\n }\n await restrictPrivateFile(this.file)\n let parsed: unknown\n try {\n parsed = JSON.parse(await readFile(this.file, 'utf8')) as unknown\n } catch (error) {\n throw new Error('device state is not valid JSON', { cause: error })\n }\n return parseDeviceSnapshot(parsed, this.maximumDevices)\n }\n\n async save(snapshot: DeviceSnapshot): Promise<void> {\n const validated = parseDeviceSnapshot(snapshot, this.maximumDevices)\n const directory = dirname(this.file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(this.file)\n if (!current.isFile() || current.isSymbolicLink()) throw new Error('device state target must remain a regular file')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, `.${basename(this.file)}.${randomBytes(12).toString('hex')}.tmp`)\n try {\n await writeFile(temporary, `${JSON.stringify(validated)}\\n`, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, this.file)\n await restrictPrivateFile(this.file)\n } catch (error) {\n try {\n await rm(temporary, { force: true })\n } catch (cleanupError) {\n throw new AggregateError([error, cleanupError], 'device state write and temporary cleanup both failed')\n }\n throw error\n }\n }\n}\n\n/** In-memory store useful for embedding and deterministic tests. */\nexport class MemoryDeviceStore implements DeviceStore {\n private snapshot: DeviceSnapshot\n\n constructor(initial: DeviceSnapshot = { version: 1, devices: [] }) {\n this.snapshot = parseDeviceSnapshot(initial)\n }\n\n async load(): Promise<DeviceSnapshot> {\n return structuredClone(this.snapshot)\n }\n\n async save(snapshot: DeviceSnapshot): Promise<void> {\n this.snapshot = structuredClone(parseDeviceSnapshot(snapshot))\n }\n\n /** Return a defensive copy for assertions or administrative export. */\n inspect(): DeviceSnapshot {\n return structuredClone(this.snapshot)\n }\n}\n","import { createHash, randomBytes } from 'node:crypto'\nimport { execFile } from 'node:child_process'\nimport {\n chmod,\n copyFile,\n lstat,\n mkdir,\n mkdtemp,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises'\nimport { basename, isAbsolute, join, relative, resolve } from 'node:path'\n\nconst FRP_VERSION = '0.70.1'\nconst MAX_ARCHIVE_ENTRIES = 128\nconst MAX_ARCHIVE_LIST_BYTES = 256 * 1024\n\ninterface FrpArtifact {\n readonly platform: NodeJS.Platform\n readonly arch: string\n readonly downloadUrl: string\n readonly downloadBytes: number\n readonly downloadSha256: string\n readonly archiveName: string\n readonly executableName: string\n}\n\nconst releases = [\n {\n platform: 'win32', arch: 'x64', archiveName: 'frp.zip', executableName: 'frpc.exe',\n downloadBytes: 13_924_309,\n downloadSha256: '531f3cd3cc41c0b4f077b54fe6b7dd83c0ff727e7f0bf412a4c78fa279165de5',\n downloadUrl: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_windows_amd64.zip`,\n },\n {\n platform: 'win32', arch: 'arm64', archiveName: 'frp.zip', executableName: 'frpc.exe',\n downloadBytes: 12_204_751,\n downloadSha256: '74d3acaf0f03ee190dd0462f9b49861dca50b0559c5488af4b36572fc951fcca',\n downloadUrl: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_windows_arm64.zip`,\n },\n {\n platform: 'linux', arch: 'x64', archiveName: 'frp.tar.gz', executableName: 'frpc',\n downloadBytes: 13_924_042,\n downloadSha256: '333da23d1b9009d7c01638e9ba38cf4600f7d37d393f854e96ee1396adefa9a6',\n downloadUrl: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_amd64.tar.gz`,\n },\n {\n platform: 'linux', arch: 'arm64', archiveName: 'frp.tar.gz', executableName: 'frpc',\n downloadBytes: 12_371_290,\n downloadSha256: '3990f396a9a490ee7f0e5f355287750ed41520064ed999eab443b5e9a78d773d',\n downloadUrl: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_arm64.tar.gz`,\n },\n {\n platform: 'darwin', arch: 'x64', archiveName: 'frp.tar.gz', executableName: 'frpc',\n downloadBytes: 13_951_979,\n downloadSha256: 'cbf69cf26e5553e914e97d37f5d4367fa30f5f531d073a889465af4719281e25',\n downloadUrl: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_darwin_amd64.tar.gz`,\n },\n {\n platform: 'darwin', arch: 'arm64', archiveName: 'frp.tar.gz', executableName: 'frpc',\n downloadBytes: 12_670_664,\n downloadSha256: 'cfa733b5a261c1647edee3c1fc4133d2542989b28f5602e81d47fc821d25c55f',\n downloadUrl: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_darwin_arm64.tar.gz`,\n },\n] as const satisfies readonly FrpArtifact[]\n\n/** Pinned official FRP release metadata for supported desktop targets. */\nexport const FRP_COMPONENT_RELEASES: Readonly<Record<string, FrpArtifact>> = Object.freeze(Object.fromEntries(\n releases.map(release => [`${release.platform}-${release.arch}`, Object.freeze(release)]),\n))\n\n/** Public, credential-free description of the managed FRP client. */\nexport interface FrpComponentStatus {\n readonly supported: boolean\n readonly installed: boolean\n readonly version: string\n readonly downloadBytes: number\n readonly installedBytes: number\n readonly sourceUrl: string\n readonly releasePage: string\n readonly storagePath: string\n readonly errorCode?: string\n}\n\ninterface FrpComponentManagerOptions {\n readonly stateDirectory: string\n readonly platform?: NodeJS.Platform\n readonly arch?: string\n readonly fetchArtifact?: (artifact: FrpArtifact, signal: AbortSignal) => Promise<Uint8Array>\n readonly extractArtifact?: (archive: string, destination: string, executableName: string) => Promise<void>\n readonly inspectExecutable?: (executable: string) => Promise<string>\n}\n\nfunction inside(parent: string, child: string): boolean {\n const candidate = relative(parent, child)\n return candidate !== '' && !candidate.startsWith('..') && !isAbsolute(candidate)\n}\n\nasync function regularFile(file: string): Promise<boolean> {\n try {\n const entry = await lstat(file)\n return entry.isFile() && !entry.isSymbolicLink()\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false\n throw error\n }\n}\n\nasync function replaceDirectory(target: string, candidate: string): Promise<void> {\n const backup = `${target}.previous-${randomBytes(12).toString('hex')}`\n let previous = false\n try {\n try {\n await rename(target, backup)\n previous = true\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n try {\n await rename(candidate, target)\n } catch (error) {\n if (previous) {\n try { await rename(backup, target) } catch (restoreError) {\n throw new AggregateError([error, restoreError], 'frp_component_replace_failed')\n }\n }\n throw error\n }\n if (previous) await rm(backup, { recursive: true, force: true })\n } finally {\n await rm(candidate, { recursive: true, force: true })\n }\n}\n\nfunction sha256(bytes: Uint8Array): string {\n return createHash('sha256').update(bytes).digest('hex')\n}\n\nasync function runCapture(file: string, args: readonly string[]): Promise<string> {\n return new Promise<string>((resolveRun, reject) => {\n execFile(file, [...args], {\n windowsHide: true,\n timeout: 120_000,\n maxBuffer: MAX_ARCHIVE_LIST_BYTES,\n encoding: 'utf8',\n }, (error, stdout) => {\n if (error === null) resolveRun(stdout)\n else reject(error)\n })\n })\n}\n\nfunction validatedArchiveEntry(rawEntry: string): readonly string[] {\n if (rawEntry.length === 0 || rawEntry.includes('\\\\') || rawEntry.includes('\\u0000')\n || rawEntry.startsWith('/') || /^[a-zA-Z]:/u.test(rawEntry)) {\n throw new Error('frp_archive_path_invalid')\n }\n const segments = rawEntry.replace(/\\/$/u, '').split('/')\n if (segments.some(segment => segment === '' || segment === '.' || segment === '..')) {\n throw new Error('frp_archive_path_invalid')\n }\n return segments\n}\n\n/** Select exactly one nested frpc executable from a safe archive listing. */\nexport function selectFrpExecutableEntry(entries: readonly string[], executableName: string): string {\n if (entries.length === 0 || entries.length > MAX_ARCHIVE_ENTRIES) throw new Error('frp_archive_entries_invalid')\n let executableEntry: string | undefined\n for (const entry of entries) {\n const segments = validatedArchiveEntry(entry)\n if (segments.length >= 2 && segments.at(-1) === executableName) {\n if (executableEntry !== undefined) throw new Error('frp_archive_executable_ambiguous')\n executableEntry = entry.replace(/\\/$/u, '')\n }\n }\n if (executableEntry === undefined) throw new Error('frp_archive_executable_missing')\n return executableEntry\n}\n\nasync function defaultExtractArtifact(archive: string, destination: string, executableName: string): Promise<void> {\n const tar = process.platform === 'win32' ? 'tar.exe' : 'tar'\n const listing = await runCapture(tar, ['-tf', archive])\n const entries = listing.split(/\\r?\\n/u).filter(entry => entry.length > 0)\n const executableEntry = selectFrpExecutableEntry(entries, executableName)\n const unpacked = join(destination, 'archive')\n await mkdir(unpacked, { recursive: true, mode: 0o700 })\n await runCapture(tar, ['-xf', archive, '-C', unpacked, executableEntry])\n const extracted = join(unpacked, ...validatedArchiveEntry(executableEntry))\n if (!await regularFile(extracted)) throw new Error('frp_archive_executable_invalid')\n await copyFile(extracted, join(destination, executableName))\n}\n\nasync function defaultFetchArtifact(artifact: FrpArtifact, signal: AbortSignal): Promise<Uint8Array> {\n const response = await fetch(artifact.downloadUrl, { redirect: 'follow', signal })\n if (!response.ok) throw new Error(`frp_download_http_${String(response.status)}`)\n const finalUrl = new URL(response.url)\n const officialHost = finalUrl.hostname === 'github.com' || finalUrl.hostname.endsWith('.githubusercontent.com')\n if (finalUrl.protocol !== 'https:' || !officialHost) throw new Error('frp_download_origin_invalid')\n const lengthHeader = response.headers.get('content-length')\n const declaredLength = lengthHeader === null ? undefined : Number(lengthHeader)\n if (declaredLength !== undefined && (!Number.isFinite(declaredLength) || declaredLength !== artifact.downloadBytes)) {\n throw new Error('frp_download_size_mismatch')\n }\n if (response.body === null) throw new Error('frp_download_empty')\n const chunks: Uint8Array[] = []\n let received = 0\n const reader = response.body.getReader()\n while (true) {\n const result = await reader.read()\n if (result.done) break\n received += result.value.byteLength\n if (received > artifact.downloadBytes) {\n await reader.cancel()\n throw new Error('frp_download_size_mismatch')\n }\n chunks.push(result.value)\n }\n if (received !== artifact.downloadBytes) throw new Error('frp_download_size_mismatch')\n const bytes = new Uint8Array(received)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return bytes\n}\n\nasync function defaultInspectExecutable(executable: string): Promise<string> {\n return (await runCapture(executable, ['--version'])).trim()\n}\n\n/** Owns the optional official frpc binary inside the DSH Mobile state directory. */\nexport class FrpComponentManager {\n readonly executable: string\n readonly componentRoot: string\n readonly componentStorage: string\n readonly logRoot: string\n private readonly stagingRoot: string\n private readonly artifact: FrpArtifact | undefined\n private readonly fetchArtifact: (artifact: FrpArtifact, signal: AbortSignal) => Promise<Uint8Array>\n private readonly extractArtifact: (archive: string, destination: string, executableName: string) => Promise<void>\n private readonly inspectExecutable: (executable: string) => Promise<string>\n private installed = false\n private installedBytes = 0\n private errorCode: string | undefined\n private queue: Promise<void> = Promise.resolve()\n\n constructor(options: FrpComponentManagerOptions) {\n const stateDirectory = resolve(options.stateDirectory)\n if (!isAbsolute(stateDirectory)) throw new Error('frp state directory must be absolute')\n const platform = options.platform ?? process.platform\n const arch = options.arch ?? process.arch\n this.artifact = FRP_COMPONENT_RELEASES[`${platform}-${arch}`]\n this.componentRoot = join(stateDirectory, 'components', 'frp')\n this.componentStorage = join(this.componentRoot, FRP_VERSION)\n this.executable = join(this.componentStorage, platform === 'win32' ? 'frpc.exe' : 'frpc')\n this.logRoot = join(stateDirectory, 'logs', 'frp')\n this.stagingRoot = join(stateDirectory, 'staging', 'frp')\n for (const child of [this.componentRoot, this.componentStorage, this.logRoot, this.stagingRoot]) {\n if (!inside(stateDirectory, child)) throw new Error('frp component path escaped its state directory')\n }\n this.fetchArtifact = options.fetchArtifact ?? defaultFetchArtifact\n this.extractArtifact = options.extractArtifact ?? defaultExtractArtifact\n this.inspectExecutable = options.inspectExecutable ?? defaultInspectExecutable\n }\n\n /** Inspect the managed executable without relying on global FRP installations. */\n async initialize(): Promise<void> {\n this.installed = await regularFile(this.executable)\n this.installedBytes = this.installed ? (await stat(this.executable)).size : 0\n if (this.installed) {\n try {\n const version = await this.inspectExecutable(this.executable)\n if (version !== FRP_VERSION) throw new Error('frp_component_version_mismatch')\n this.errorCode = undefined\n } catch {\n this.installed = false\n this.errorCode = 'frp_component_invalid'\n }\n }\n }\n\n /** Return component metadata without exposing configuration or credentials. */\n status(): FrpComponentStatus {\n return Object.freeze({\n supported: this.artifact !== undefined,\n installed: this.installed,\n version: FRP_VERSION,\n downloadBytes: this.artifact?.downloadBytes ?? 0,\n installedBytes: this.installedBytes,\n sourceUrl: this.artifact?.downloadUrl ?? 'https://github.com/fatedier/frp/releases',\n releasePage: `https://github.com/fatedier/frp/releases/tag/v${FRP_VERSION}`,\n storagePath: this.componentRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n })\n }\n\n /** Download, verify, and extract only frpc after explicit confirmation. */\n install(): Promise<FrpComponentStatus> {\n return this.enqueue(async () => {\n const artifact = this.artifact\n if (artifact === undefined) throw new Error('frp_component_unsupported')\n await mkdir(this.stagingRoot, { recursive: true, mode: 0o700 })\n const staging = await mkdtemp(join(this.stagingRoot, 'install-'))\n try {\n const controller = new AbortController()\n const timeout = setTimeout(() => { controller.abort() }, 120_000)\n timeout.unref()\n let bytes: Uint8Array\n try { bytes = await this.fetchArtifact(artifact, controller.signal) } finally { clearTimeout(timeout) }\n if (bytes.byteLength !== artifact.downloadBytes) throw new Error('frp_download_size_mismatch')\n if (sha256(bytes) !== artifact.downloadSha256) throw new Error('frp_download_hash_mismatch')\n const archive = join(staging, artifact.archiveName)\n await writeFile(archive, bytes, { flag: 'wx', mode: 0o600 })\n await this.extractArtifact(archive, staging, artifact.executableName)\n const extracted = join(staging, artifact.executableName)\n if (!await regularFile(extracted)) throw new Error('frp_executable_missing')\n await chmod(extracted, 0o700)\n const version = await this.inspectExecutable(extracted)\n if (version !== FRP_VERSION) throw new Error('frp_component_version_mismatch')\n const candidate = join(this.componentRoot, `.install-${randomBytes(12).toString('hex')}`)\n await mkdir(candidate, { recursive: true, mode: 0o700 })\n const candidateExecutable = join(candidate, artifact.executableName)\n await copyFile(extracted, candidateExecutable)\n await chmod(candidateExecutable, 0o700)\n await replaceDirectory(this.componentStorage, candidate)\n this.installed = true\n this.installedBytes = (await stat(this.executable)).size\n this.errorCode = undefined\n } finally {\n await rm(staging, { recursive: true, force: true })\n }\n })\n }\n\n /** Remove all FRP executable, staging, and log files owned by DSH Mobile. */\n purge(): Promise<FrpComponentStatus> {\n return this.enqueue(async () => {\n await Promise.all([\n rm(this.componentRoot, { recursive: true, force: true }),\n rm(this.logRoot, { recursive: true, force: true }),\n rm(this.stagingRoot, { recursive: true, force: true }),\n ])\n this.installed = false\n this.installedBytes = 0\n this.errorCode = undefined\n })\n }\n\n private enqueue(operation: () => Promise<void>): Promise<FrpComponentStatus> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task.then(() => this.status())\n }\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\n\n/** Cap the admin-approved extra WebSocket upgrade paths (exact pathnames). */\nexport const MAX_EXTRA_WEBSOCKET_PATHS = 16\nexport const MAX_WEBSOCKET_PATH_LENGTH = 256\n\nfunction fail(code: string): never {\n throw new Error(code)\n}\n\n/**\n * Validate one exact pathname for proxying. Query strings are matched at\n * upgrade time, so only the pathname is stored. Rejects anything the\n * gateway cannot match exactly.\n */\nexport function validateWebSocketPath(value: unknown): string {\n if (typeof value !== 'string') fail('websocket_path_invalid')\n const path = value as string\n if (path.length === 0 || path.length > MAX_WEBSOCKET_PATH_LENGTH) fail('websocket_path_invalid')\n if (!path.startsWith('/')) fail('websocket_path_invalid')\n // eslint-disable-next-line no-control-regex\n if (/[\\s\\u0000-\\u001f\\u007f#?]/u.test(path)) fail('websocket_path_invalid')\n if (path.includes('..')) fail('websocket_path_invalid')\n return path\n}\n\n/** Validate a whole replacement list all-or-nothing; duplicates collapse. */\nexport function normalizeWebSocketPaths(value: unknown): string[] {\n if (!Array.isArray(value)) fail('websocket_paths_invalid')\n if (value.length > MAX_EXTRA_WEBSOCKET_PATHS) fail('websocket_paths_invalid')\n const seen = new Set<string>()\n for (const entry of value) {\n const path = validateWebSocketPath(entry)\n seen.add(path)\n }\n return [...seen]\n}\n\n/** Cap remembered rejected upgrade paths offered for one-click approval. */\nexport const MAX_BLOCKED_UPGRADE_PATHS = 32\n\nexport interface BlockedUpgradePathEntry {\n readonly path: string\n readonly attempts: number\n readonly firstSeen: number\n readonly lastSeen: number\n}\n\n/**\n * In-memory log of rejected third-party upgrade paths, shared by every\n * gateway instance so the approval UI sees attempts on any listener.\n * Bounded and newest-first; a restart clears it (attempts reappear on\n * the next blocked handshake).\n */\nexport class BlockedUpgradePathLog {\n private readonly entries = new Map<string, { attempts: number; firstSeen: number; lastSeen: number }>()\n\n record(pathname: string): void {\n const now = Date.now()\n const stats = this.entries.get(pathname)\n if (stats === undefined) {\n if (this.entries.size >= MAX_BLOCKED_UPGRADE_PATHS) {\n let oldest: string | undefined\n for (const [path, entry] of this.entries) {\n if (oldest === undefined || entry.lastSeen < (this.entries.get(oldest)?.lastSeen ?? 0)) oldest = path\n }\n if (oldest !== undefined) this.entries.delete(oldest)\n }\n this.entries.set(pathname, { attempts: 1, firstSeen: now, lastSeen: now })\n return\n }\n stats.attempts += 1\n stats.lastSeen = now\n }\n\n report(): BlockedUpgradePathEntry[] {\n return [...this.entries]\n .map(([path, stats]) => ({ path, ...stats }))\n .sort((a, b) => b.lastSeen - a.lastSeen)\n }\n}\n\n/** File-backed store shared by every gateway instance (LAN and remote). */\nexport class WebSocketPathStore {\n private paths = new Set<string>()\n private loaded = false\n\n constructor(private readonly file: string) {}\n\n /** Snapshot for the upgrade check. */\n has(pathname: string): boolean {\n return this.paths.has(pathname)\n }\n\n list(): string[] {\n return [...this.paths]\n }\n\n async load(): Promise<string[]> {\n if (!this.loaded) {\n try {\n const raw = JSON.parse(await readFile(this.file, 'utf8')) as { readonly paths?: unknown }\n this.paths = new Set(normalizeWebSocketPaths(raw.paths ?? []))\n } catch {\n // Missing, corrupt, or unreadable: fall back to the empty (most\n // restrictive) list. The panel always shows the active list, so an\n // admin notices and re-adds entries instead of silently widening.\n this.paths = new Set()\n }\n this.loaded = true\n }\n return this.list()\n }\n\n /** Replace the whole list after validating; persists atomically. */\n async replace(paths: readonly string[]): Promise<string[]> {\n const next = normalizeWebSocketPaths([...paths])\n await mkdir(dirname(this.file), { recursive: true })\n await writeFile(this.file, `${JSON.stringify({ paths: next })}\\n`, 'utf8')\n this.paths = new Set(next)\n this.loaded = true\n return this.list()\n }\n}\n","/** Loopback-only HTTP vhost port used between Caddy and frps. */\nexport const FRP_VHOST_HTTP_PORT = 7080\n\n/** Caddy snippet owned entirely by DSH Mobile; the main Caddyfile only imports it. */\nexport const FRP_CADDY_SNIPPET_PATH = '/etc/caddy/dsh-mobile-dsh.caddy'\n\n/** First line of the owned snippet; also the legacy whole-file marker. */\nexport const FRP_CADDY_SNIPPET_MARKER = '# Managed by DSH Mobile - snippet, safe to delete'\n\n/** Exact line the main Caddyfile must contain (uncommented) for the site to load. */\nexport const FRP_CADDY_IMPORT_LINE = `import ${FRP_CADDY_SNIPPET_PATH}`\n\n/** Directory holding the public-IPv4 certificates installed by certbot. */\nexport const FRP_CADDY_IP_CERT_DIR = '/var/lib/caddy/dsh-mobile-certs'\n\nfunction publicIpv4Address(value: string): boolean {\n const parts = value.split('.')\n return parts.length === 4 && parts.every(part => /^(?:0|[1-9][0-9]{0,2})$/u.test(part)\n && Number(part) <= 255)\n}\n\nfunction publicDnsHostname(value: string): boolean {\n return value.length <= 253 && value.includes('.') && !/^[0-9.]+$/u.test(value)\n && !value.includes(':') && value.split('.').every(label => label.length >= 1 && label.length <= 63\n && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label))\n}\n\nfunction parsePublicOrigin(publicOrigin: string): string {\n let url: URL\n try { url = new URL(publicOrigin) } catch { throw new Error('frp_template_input_invalid') }\n if (url.protocol !== 'https:' || url.port !== '' || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '' || (!publicIpv4Address(url.hostname) && !publicDnsHostname(url.hostname))) {\n throw new Error('frp_template_input_invalid')\n }\n return url.hostname\n}\n\n/** Build the Caddy site for one public host (without markers or import wiring). */\nexport function createCaddySite(publicHost: string, certDir: string = FRP_CADDY_IP_CERT_DIR): string {\n if (publicIpv4Address(publicHost)) {\n return [\n '{',\n ` default_sni ${publicHost}`,\n '}',\n '',\n `http://${publicHost} {`,\n ` redir https://${publicHost}{uri} permanent`,\n '}',\n '',\n `https://${publicHost} {`,\n ` tls ${certDir}/fullchain.pem ${certDir}/privkey.pem`,\n ` reverse_proxy 127.0.0.1:${String(FRP_VHOST_HTTP_PORT)}`,\n '}',\n '',\n ].join('\\n')\n }\n if (!publicDnsHostname(publicHost)) throw new Error('frp_template_input_invalid')\n return [\n `${publicHost} {`,\n ` reverse_proxy 127.0.0.1:${String(FRP_VHOST_HTTP_PORT)}`,\n '}',\n '',\n ].join('\\n')\n}\n\n/** Manual certbot steps for a public-IPv4 origin (Caddy cannot issue IP certificates itself). */\nfunction manualIpCertificateGuide(publicHost: string): string {\n return [\n '# Public-IPv4 manual HTTPS: Caddy cannot issue IP certificates by itself.',\n '# On the VPS (Ubuntu/Debian, port 80 reachable from the internet), run once as root:',\n '# apt-get install -y python3-venv',\n '# python3 -m venv /opt/dsh-mobile/certbot-venv',\n \"# /opt/dsh-mobile/certbot-venv/bin/pip install 'certbot==5.8.0'\",\n '# systemctl stop caddy || true',\n `# /opt/dsh-mobile/certbot-venv/bin/certbot certonly --standalone --preferred-profile shortlived --ip-address ${publicHost} --agree-tos --register-unsafely-without-email --non-interactive --keep-until-expiring`,\n '# install -d -m 0750 -o caddy -g caddy /var/lib/caddy/dsh-mobile-certs',\n `# install -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/fullchain.pem /var/lib/caddy/dsh-mobile-certs/fullchain.pem`,\n `# install -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/privkey.pem /var/lib/caddy/dsh-mobile-certs/privkey.pem`,\n '# systemctl start caddy',\n '# The site below already references those paths. Certificates last about 6 days: re-run certonly before expiry.',\n '#',\n ].join('\\n')\n}\n\n/** Build the only supported frps config and Caddy snippet from validated user inputs. */\nexport function createRestrictedFrpServerTemplate(serverPort: number, token: string, publicOrigin: string): string {\n if (!Number.isSafeInteger(serverPort) || serverPort < 1 || serverPort > 65_535\n || token.length < 16 || token.length > 512 || /[\\s\\u0000-\\u001f\\u007f]/u.test(token)) {\n throw new Error('frp_template_input_invalid')\n }\n const publicHost = parsePublicOrigin(publicOrigin)\n const lines = [\n '# frps.toml — save as /etc/dsh-mobile/frps.toml, then start the frps service.',\n `bindPort = ${String(serverPort)}`,\n 'proxyBindAddr = \"127.0.0.1\"',\n `vhostHTTPPort = ${String(FRP_VHOST_HTTP_PORT)}`,\n 'auth.method = \"token\"',\n `auth.token = ${JSON.stringify(token)}`,\n '',\n `# Caddy — save the site below as ${FRP_CADDY_SNIPPET_PATH},`,\n '# then make sure your Caddyfile contains exactly this line at the TOP of the file',\n '# (create the file with just this line if needed; globals must precede sites):',\n `# ${FRP_CADDY_IMPORT_LINE}`,\n '# finally run: caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy',\n '# Uninstall later removes only this snippet file and the import line; your own Caddy content is kept.',\n `${FRP_CADDY_SNIPPET_MARKER}`,\n createCaddySite(publicHost).trimEnd(),\n '',\n ]\n if (publicIpv4Address(publicHost)) lines.push(manualIpCertificateGuide(publicHost), '')\n return lines.join('\\n')\n}\n","import { randomBytes } from 'node:crypto'\nimport { isIP } from 'node:net'\nimport { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path'\nimport { isGloballyRoutableIpv4 } from './network.js'\nimport { restrictPrivateFile } from './private-file.js'\nimport { createRestrictedFrpServerTemplate, FRP_VHOST_HTTP_PORT } from './frp-template.js'\n\nconst MAX_SETTINGS_BYTES = 8 * 1024\n\n/** Credentials and endpoints required by the restricted FRP provider. */\nexport interface FrpSettings {\n readonly version: 1\n readonly serverAddress: string\n readonly serverPort: number\n readonly token: string\n readonly publicOrigin: string\n}\n\n/** Safe FRP configuration fields returned to the desktop UI. */\nexport interface FrpConfigurationStatus {\n readonly configured: boolean\n readonly serverAddress?: string\n readonly serverPort?: number\n readonly publicOrigin?: string\n readonly vhostHttpPort: number\n readonly storagePath: string\n readonly errorCode?: string\n}\n\nfunction hostname(value: string): boolean {\n if (value.length > 253 || !value.includes('.')) return false\n return value.split('.').every(label => label.length >= 1 && label.length <= 63\n && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label))\n}\n\n/** Validate the FRP server hostname or IP address. */\nexport function validateFrpServerAddress(value: unknown): string {\n if (typeof value !== 'string' || value !== value.trim() || value.length === 0 || value.length > 253\n || /[\\s\\u0000-\\u001f\\u007f/\\\\@?#]/u.test(value)) throw new Error('frp_server_address_invalid')\n const normalized = value.toLowerCase().replace(/\\.$/u, '')\n if (isIP(normalized) === 0 && !hostname(normalized)) throw new Error('frp_server_address_invalid')\n return normalized\n}\n\n/** Validate the FRP control port. */\nexport function validateFrpServerPort(value: unknown): number {\n if (!Number.isSafeInteger(value) || Number(value) < 1 || Number(value) > 65_535) {\n throw new Error('frp_server_port_invalid')\n }\n return Number(value)\n}\n\n/** Validate a high-entropy FRP token before durable storage. */\nexport function validateFrpToken(value: unknown): string {\n if (typeof value !== 'string' || value.length < 16 || value.length > 512\n || /[\\s\\u0000-\\u001f\\u007f]/u.test(value)) throw new Error('frp_token_invalid')\n return value\n}\n\n/** Validate the public HTTPS origin used by Caddy and Android pairing. */\nexport function validateFrpPublicOrigin(value: unknown): string {\n if (typeof value !== 'string' || value.length > 512) throw new Error('frp_public_origin_invalid')\n let url: URL\n try { url = new URL(value) } catch { throw new Error('frp_public_origin_invalid') }\n const publicHost = url.hostname\n if (url.protocol !== 'https:' || url.port !== '' || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '' || (isIP(publicHost) !== 4 && !hostname(publicHost))) {\n throw new Error('frp_public_origin_invalid')\n }\n // Documentation and other non-routable IPv4 literals (e.g. 203.0.113.10)\n // can never be a real VPS endpoint; reject them instead of deploying certs.\n if (isIP(publicHost) === 4 && !isGloballyRoutableIpv4(publicHost)) throw new Error('frp_public_origin_invalid')\n return url.origin\n}\n\n/** Parse FRP settings at the loopback request and filesystem boundaries. */\nexport function parseFrpSettings(value: unknown): FrpSettings {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('frp_settings_invalid')\n const record = value as Record<string, unknown>\n if (Reflect.ownKeys(record).some(key => !['version', 'serverAddress', 'serverPort', 'token', 'publicOrigin'].includes(String(key)))) {\n throw new Error('frp_settings_invalid')\n }\n if (record.version !== undefined && record.version !== 1) throw new Error('frp_settings_invalid')\n return Object.freeze({\n version: 1,\n serverAddress: validateFrpServerAddress(record.serverAddress),\n serverPort: validateFrpServerPort(record.serverPort),\n token: validateFrpToken(record.token),\n publicOrigin: validateFrpPublicOrigin(record.publicOrigin),\n })\n}\n\n/**\n * Merge a partial VPS request body with the saved configuration so a blank\n * field keeps its saved value (\"已保存时可留空\"). Every merged field is still\n * validated; with nothing saved and nothing supplied the result reports a\n * missing configuration instead of silently deploying blanks.\n */\nexport function mergeSavedFrpSettings(\n partial: Readonly<Record<string, unknown>>,\n saved: FrpSettings | undefined,\n): FrpSettings {\n const merged = {\n serverAddress: partial.serverAddress === '' || partial.serverAddress === undefined\n ? saved?.serverAddress : partial.serverAddress,\n serverPort: typeof partial.serverPort === 'number' && Number.isSafeInteger(partial.serverPort) && partial.serverPort >= 1\n ? partial.serverPort : saved?.serverPort,\n token: partial.token === '' || partial.token === undefined ? saved?.token : partial.token,\n publicOrigin: partial.publicOrigin === '' || partial.publicOrigin === undefined\n ? saved?.publicOrigin : partial.publicOrigin,\n }\n if (merged.serverAddress === undefined && merged.serverPort === undefined\n && merged.token === undefined && merged.publicOrigin === undefined) {\n throw new Error('frp_config_missing')\n }\n return parseFrpSettings(merged)\n}\n\n/** Merge a VPS target (address and control port) with the saved configuration. */\nexport function mergeSavedFrpTarget(\n partial: Readonly<Record<string, unknown>>,\n saved: FrpSettings | undefined,\n): { readonly serverAddress: string; readonly serverPort: number } {\n const serverAddress = partial.serverAddress === '' || partial.serverAddress === undefined\n ? saved?.serverAddress : partial.serverAddress\n const serverPort = typeof partial.serverPort === 'number' && Number.isSafeInteger(partial.serverPort) && partial.serverPort >= 1\n ? partial.serverPort : saved?.serverPort\n if (serverAddress === undefined || serverPort === undefined) throw new Error('frp_config_missing')\n return Object.freeze({\n serverAddress: validateFrpServerAddress(serverAddress),\n serverPort: validateFrpServerPort(serverPort),\n })\n}\n\nfunction tomlString(value: string): string {\n return JSON.stringify(value)\n}\n\n/** Build the single-purpose frpc configuration for the current loopback gateway. */\nexport function createFrpcToml(settings: FrpSettings, localPort: number): string {\n if (!Number.isSafeInteger(localPort) || localPort < 1 || localPort > 65_535) throw new Error('frp_local_port_invalid')\n const hostnameValue = new URL(settings.publicOrigin).hostname\n return [\n `serverAddr = ${tomlString(settings.serverAddress)}`,\n `serverPort = ${String(settings.serverPort)}`,\n 'auth.method = \"token\"',\n `auth.token = ${tomlString(settings.token)}`,\n 'transport.tls.enable = true',\n '',\n '[[proxies]]',\n 'name = \"dsh-mobile\"',\n 'type = \"http\"',\n 'localIP = \"127.0.0.1\"',\n `localPort = ${String(localPort)}`,\n `customDomains = [${tomlString(hostnameValue)}]`,\n 'transport.useEncryption = true',\n 'transport.useCompression = true',\n '',\n ].join('\\n')\n}\n\n/** Build the matching restricted frps and Caddy templates for one VPS. */\nexport function createFrpServerTemplate(settings: FrpSettings): string {\n return createRestrictedFrpServerTemplate(settings.serverPort, settings.token, settings.publicOrigin)\n}\n\nasync function atomicPrivateWrite(file: string, body: string): Promise<void> {\n const directory = dirname(file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(file)\n if (!current.isFile() || current.isSymbolicLink()) throw new Error('frp_config_target_invalid')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, `.${basename(file)}.${randomBytes(12).toString('hex')}.tmp`)\n try {\n await writeFile(temporary, body, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, file)\n await restrictPrivateFile(file)\n } catch (error) {\n await rm(temporary, { force: true })\n throw error\n }\n}\n\n/** Owns private FRP settings and generation-specific frpc configuration. */\nexport class FrpConfigStore {\n readonly stateRoot: string\n readonly settingsFile: string\n readonly runtimeConfigFile: string\n private settingsValue: FrpSettings | undefined\n private errorCode: string | undefined\n\n constructor(stateDirectory: string) {\n if (!isAbsolute(stateDirectory)) throw new Error('frp config state directory must be absolute')\n this.stateRoot = resolve(stateDirectory)\n this.settingsFile = join(this.stateRoot, 'settings.json')\n this.runtimeConfigFile = join(this.stateRoot, 'frpc.toml')\n }\n\n /** Load private settings while rejecting links, oversized files, and unknown fields. */\n async initialize(): Promise<void> {\n let entry\n try { entry = await lstat(this.settingsFile) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\n throw error\n }\n if (!entry.isFile() || entry.isSymbolicLink() || entry.size > MAX_SETTINGS_BYTES) {\n this.errorCode = 'frp_config_invalid'\n return\n }\n await restrictPrivateFile(this.settingsFile)\n try {\n this.settingsValue = parseFrpSettings(JSON.parse(await readFile(this.settingsFile, 'utf8')) as unknown)\n this.errorCode = undefined\n } catch {\n this.settingsValue = undefined\n this.errorCode = 'frp_config_invalid'\n }\n }\n\n /** Return configuration metadata without exposing the FRP token. */\n status(): FrpConfigurationStatus {\n const settings = this.settingsValue\n return Object.freeze({\n configured: settings !== undefined,\n ...(settings === undefined ? {} : {\n serverAddress: settings.serverAddress,\n serverPort: settings.serverPort,\n publicOrigin: settings.publicOrigin,\n }),\n vhostHttpPort: FRP_VHOST_HTTP_PORT,\n storagePath: this.stateRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n })\n }\n\n /** Return private settings only to the provider lifecycle. */\n settings(): FrpSettings | undefined {\n return this.settingsValue\n }\n\n /** Atomically replace private FRP settings. */\n async configure(value: unknown): Promise<FrpConfigurationStatus> {\n const settings = parseFrpSettings(value)\n await atomicPrivateWrite(this.settingsFile, `${JSON.stringify(settings)}\\n`)\n await rm(this.runtimeConfigFile, { force: true })\n this.settingsValue = settings\n this.errorCode = undefined\n return this.status()\n }\n\n /** Materialize the private generation-specific frpc configuration. */\n async writeRuntimeConfig(localPort: number): Promise<string> {\n const settings = this.settingsValue\n if (settings === undefined) throw new Error('frp_config_missing')\n await atomicPrivateWrite(this.runtimeConfigFile, createFrpcToml(settings, localPort))\n return this.runtimeConfigFile\n }\n\n /** Remove only configuration files owned by the FRP provider. */\n async purge(): Promise<FrpConfigurationStatus> {\n await rm(this.stateRoot, { recursive: true, force: true })\n this.settingsValue = undefined\n this.errorCode = undefined\n return this.status()\n }\n}\n\nexport { FRP_VHOST_HTTP_PORT as DEFAULT_VHOST_HTTP_PORT }\n","import { randomBytes } from 'node:crypto'\nimport type { ChildProcess } from 'node:child_process'\nimport { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, join } from 'node:path'\nimport type { MobileAccessGateway } from './gateway.js'\nimport { restrictPrivateFile } from './private-file.js'\n\n/** Remote transports supported by the desktop plugin and Android client. */\nexport type RemoteProvider = 'tailscale' | 'cpolar' | 'cloudflared' | 'frp' | 'origin'\n\n/** Common safe status returned by every remote provider controller. */\nexport interface RemoteProviderStatus {\n readonly enabled: boolean\n readonly state: string\n readonly origin?: string\n readonly backendOrigin?: string\n readonly loginUrl?: string\n readonly setupUrl?: string\n readonly errorCode?: string\n}\n\n/** Lifecycle shared by selectable remote providers. */\nexport interface RemoteProviderController {\n initialize(): Promise<void>\n gateway(): MobileAccessGateway | undefined\n status(): RemoteProviderStatus\n setEnabled(enabled: boolean): Promise<RemoteProviderStatus>\n reconnect(): Promise<RemoteProviderStatus>\n reset(): Promise<RemoteProviderStatus>\n close(): Promise<void>\n}\n\n/** Durable selection for the single active remote transport. */\nexport interface RemoteProviderState {\n readonly version: 1\n readonly provider: RemoteProvider\n}\n\nexport const REMOTE_PROVIDERS: readonly RemoteProvider[] = Object.freeze(['tailscale', 'cpolar', 'cloudflared', 'frp', 'origin'])\n\n/** Persist only the selected remote provider. */\nexport interface RemoteProviderStore {\n save(state: RemoteProviderState): Promise<void>\n}\n\nfunction aggregateErrors(errors: readonly unknown[], message: string): Error | undefined {\n if (errors.length === 0) return undefined\n if (errors.length === 1 && errors[0] instanceof Error) return errors[0]\n return new AggregateError(errors, message)\n}\n\n/** Settle independent remote cleanup work before reporting any collected failure. */\nexport async function settleRemoteResources(\n steps: readonly (() => void | Promise<void>)[],\n message = 'remote resource cleanup failed',\n): Promise<void> {\n const results = await Promise.allSettled(steps.map(async step => step()))\n const errors = results\n .filter(result => result.status === 'rejected')\n .map(result => result.reason as unknown)\n const failure = aggregateErrors(errors, message)\n if (failure !== undefined) throw failure\n}\n\n/**\n * Serialize all provider mutations and preserve the single-provider invariant.\n * Operations read the selected controller only after reaching the front of the queue.\n */\nexport class RemoteProviderCoordinator {\n private selectedValue: RemoteProvider\n private queue: Promise<void> = Promise.resolve()\n\n constructor(\n selected: RemoteProvider,\n private readonly controllers: Readonly<Record<RemoteProvider, RemoteProviderController>>,\n private readonly store: RemoteProviderStore,\n ) {\n this.selectedValue = selected\n }\n\n /** Return the durable provider currently selected by the desktop UI. */\n get selected(): RemoteProvider {\n return this.selectedValue\n }\n\n /** Return the controller selected when this method is called. */\n controller(): RemoteProviderController {\n return this.controllers[this.selectedValue]\n }\n\n /** Run a provider-owned mutation after all earlier provider work settles. */\n mutate<T>(operation: (controller: RemoteProviderController) => Promise<T>): Promise<T> {\n return this.enqueue(() => operation(this.controller()))\n }\n\n /** Disable the previous provider, persist the new selection, and retain rollback on write failure. */\n select(provider: RemoteProvider): Promise<void> {\n return this.enqueue(async () => {\n if (provider === this.selectedValue) return\n const previous = this.controllers[this.selectedValue]\n const restore = previous.status().enabled\n if (restore) await previous.setEnabled(false)\n try {\n await this.store.save({ version: 1, provider })\n this.selectedValue = provider\n } catch (error) {\n if (restore) {\n try { await previous.setEnabled(true) }\n catch (restoreError) { throw new AggregateError([error, restoreError], 'remote provider selection rollback failed') }\n }\n throw error\n }\n })\n }\n\n private enqueue<T>(operation: () => Promise<T>): Promise<T> {\n const task = this.queue.then(\n () => this.runAndEnforce(operation),\n () => this.runAndEnforce(operation),\n )\n this.queue = task.then(() => undefined, () => undefined)\n return task\n }\n\n private async runAndEnforce<T>(operation: () => Promise<T>): Promise<T> {\n let value: T | undefined\n let operationError: unknown\n try { value = await operation() } catch (error) { operationError = error }\n const results = await Promise.allSettled(\n REMOTE_PROVIDERS\n .filter(provider => provider !== this.selectedValue)\n .map(provider => this.controllers[provider].setEnabled(false)),\n )\n const errors = [\n ...(operationError === undefined ? [] : [operationError]),\n ...results.filter(result => result.status === 'rejected').map(result => result.reason as unknown),\n ]\n const failure = aggregateErrors(errors, 'remote provider operation failed')\n if (failure !== undefined) throw failure\n return value as T\n }\n}\n\n/** Stop an owned provider process and do not report completion before its close event. */\nexport async function terminateRemoteProcess(\n child: ChildProcess,\n gracefulTimeoutMs = 1_500,\n forcedTimeoutMs = 1_500,\n): Promise<void> {\n if (child.exitCode !== null || child.signalCode !== null) return\n await new Promise<void>((resolveClose, rejectClose) => {\n let gracefulTimer: NodeJS.Timeout | undefined\n let forcedTimer: NodeJS.Timeout | undefined\n let settled = false\n const finish = (error?: Error): void => {\n if (settled) return\n settled = true\n if (gracefulTimer !== undefined) clearTimeout(gracefulTimer)\n if (forcedTimer !== undefined) clearTimeout(forcedTimer)\n child.off('close', onClose)\n if (error === undefined) resolveClose()\n else rejectClose(error)\n }\n const onClose = (): void => { finish() }\n child.once('close', onClose)\n try { child.kill('SIGTERM') } catch (error) {\n finish(error instanceof Error ? error : new Error(String(error)))\n return\n }\n if (settled) return\n gracefulTimer = setTimeout(() => {\n try {\n if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL')\n } catch (error) {\n finish(error instanceof Error ? error : new Error(String(error)))\n return\n }\n if (settled) return\n forcedTimer = setTimeout(() => {\n finish(new Error('remote_process_stop_timeout'))\n }, forcedTimeoutMs)\n forcedTimer.unref()\n }, gracefulTimeoutMs)\n gracefulTimer.unref()\n })\n}\n\n/** Validate the provider selection loaded across the filesystem boundary. */\nexport function parseRemoteProviderState(value: unknown): RemoteProviderState {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error('remote provider state must be an object')\n }\n const record = value as Record<string, unknown>\n if (record.version !== 1\n || (record.provider !== 'tailscale' && record.provider !== 'cpolar' && record.provider !== 'cloudflared'\n && record.provider !== 'frp' && record.provider !== 'origin')\n || Reflect.ownKeys(record).some(key => key !== 'version' && key !== 'provider')) {\n throw new Error('remote provider state has an unsupported format')\n }\n return Object.freeze({ version: 1, provider: record.provider })\n}\n\n/** Atomic selection store whose absent-file state uses the configured default. */\nexport class JsonRemoteProviderStore {\n constructor(private readonly file: string, private readonly defaultProvider: RemoteProvider) {}\n\n async load(): Promise<RemoteProviderState> {\n let stat\n try {\n stat = await lstat(this.file)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return Object.freeze({ version: 1, provider: this.defaultProvider })\n }\n throw error\n }\n if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 4096) {\n throw new Error('remote provider state must be a regular file no larger than 4 KiB')\n }\n await restrictPrivateFile(this.file)\n let parsed: unknown\n try { parsed = JSON.parse(await readFile(this.file, 'utf8')) as unknown } catch (error) {\n throw new Error('remote provider state is not valid JSON', { cause: error })\n }\n return parseRemoteProviderState(parsed)\n }\n\n async save(state: RemoteProviderState): Promise<void> {\n const validated = parseRemoteProviderState(state)\n const directory = dirname(this.file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(this.file)\n if (!current.isFile() || current.isSymbolicLink()) {\n throw new Error('remote provider state target must remain a regular file')\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, `.${basename(this.file)}.${randomBytes(12).toString('hex')}.tmp`)\n try {\n await writeFile(temporary, `${JSON.stringify(validated)}\\n`, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, this.file)\n await restrictPrivateFile(this.file)\n } catch (error) {\n await rm(temporary, { force: true })\n throw error\n }\n }\n}\n\n/** Resolve the first-run provider without letting environment values bypass validation. */\nexport function configuredRemoteProvider(environment: NodeJS.ProcessEnv): RemoteProvider {\n const value = environment.DSH_MOBILE_REMOTE_PROVIDER ?? 'tailscale'\n if (value !== 'tailscale' && value !== 'cpolar' && value !== 'cloudflared' && value !== 'frp' && value !== 'origin') {\n throw new Error('DSH_MOBILE_REMOTE_PROVIDER must be tailscale, cpolar, cloudflared, frp, or origin')\n }\n return value\n}\n","import { spawn, execFile, type ChildProcessWithoutNullStreams } from 'node:child_process'\nimport { lstat, rm } from 'node:fs/promises'\nimport { connect } from 'node:net'\nimport { isAbsolute } from 'node:path'\nimport type { MobileAccessControlStore } from './control.js'\nimport type { FrpConfigStore } from './frp-config.js'\nimport { DEFAULT_VHOST_HTTP_PORT } from './frp-config.js'\nimport type { MobileAccessGateway } from './gateway.js'\nimport { settleRemoteResources, terminateRemoteProcess, type RemoteProviderController } from './remote.js'\n\nconst START_TIMEOUT_MS = 45_000\nconst DISCOVERY_REQUEST_TIMEOUT_MS = 5_000\nconst DISCOVERY_RETRY_MS = 1_000\nconst MAX_DISCOVERY_BYTES = 16 * 1024\nconst VHOST_PROBE_TIMEOUT_MS = 1_500\n\n/** Product-facing states for the restricted self-hosted FRP transport. */\nexport type FrpState = 'off' | 'unavailable' | 'starting' | 'connecting' | 'ready' | 'error'\n\n/** Safe FRP state returned only through the loopback DSH control route. */\nexport interface FrpStatus {\n readonly enabled: boolean\n readonly state: FrpState\n readonly origin?: string\n readonly errorCode?: string\n}\n\n/** Inputs for one FRP client process and authenticated DSH gateway. */\nexport interface FrpControllerOptions {\n readonly store: MobileAccessControlStore\n readonly executable: string\n readonly config: FrpConfigStore\n readonly instanceId: string\n readonly createGateway: (origin: string) => Promise<MobileAccessGateway>\n readonly onStatus?: (status: FrpStatus) => void\n readonly verifyConfig?: (executable: string, configFile: string) => Promise<void>\n readonly launchClient?: (executable: string, configFile: string) => ChildProcessWithoutNullStreams\n readonly probeVhostExposure?: (serverAddress: string, port: number) => Promise<boolean>\n readonly probeDiscovery?: (origin: string, expectedInstanceId: string, signal: AbortSignal) => Promise<boolean>\n readonly startTimeoutMs?: number\n readonly retryIntervalMs?: number\n}\n\nfunction publicStatus(status: FrpStatus): FrpStatus {\n return Object.freeze({\n enabled: status.enabled,\n state: status.state,\n ...(status.origin === undefined ? {} : { origin: status.origin }),\n ...(status.errorCode === undefined ? {} : { errorCode: status.errorCode }),\n })\n}\n\nasync function defaultVerifyConfig(executable: string, configFile: string): Promise<void> {\n await new Promise<void>((resolveRun, reject) => {\n execFile(executable, ['verify', '-c', configFile], {\n windowsHide: true,\n timeout: 30_000,\n maxBuffer: 64 * 1024,\n }, error => {\n if (error === null) resolveRun()\n else reject(error)\n })\n })\n}\n\nfunction defaultLaunchClient(executable: string, configFile: string): ChildProcessWithoutNullStreams {\n return spawn(executable, ['-c', configFile], {\n shell: false,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n })\n}\n\nasync function defaultProbeVhostExposure(serverAddress: string, port: number): Promise<boolean> {\n return new Promise<boolean>(resolveProbe => {\n const socket = connect({ host: serverAddress, port })\n let finished = false\n let received = ''\n const finish = (exposed: boolean): void => {\n if (finished) return\n finished = true\n clearTimeout(timer)\n socket.destroy()\n resolveProbe(exposed)\n }\n const timer = setTimeout(() => { finish(false) }, VHOST_PROBE_TIMEOUT_MS)\n timer.unref()\n socket.once('connect', () => {\n // A transparent proxy/TUN can acknowledge every TCP connect even when\n // the remote port is closed. Require an actual HTTP response from the\n // FRP vhost listener before treating the plaintext port as exposed.\n socket.write('GET /dsh-mobile-exposure-probe HTTP/1.1\\r\\nHost: invalid.example\\r\\nConnection: close\\r\\n\\r\\n')\n })\n socket.on('data', chunk => {\n received = `${received}${chunk.toString('latin1')}`.slice(0, 32)\n if (/^HTTP\\/1\\.[01] [1-5][0-9]{2}/u.test(received)) finish(true)\n })\n socket.once('close', () => { finish(false) })\n socket.once('error', () => { finish(false) })\n })\n}\n\nasync function boundedResponseBytes(response: Response): Promise<Uint8Array> {\n if (response.body === null) throw new Error('frp_discovery_invalid')\n const declaredLength = Number(response.headers.get('content-length'))\n if (Number.isFinite(declaredLength) && declaredLength > MAX_DISCOVERY_BYTES) throw new Error('frp_discovery_invalid')\n const reader = response.body.getReader()\n const chunks: Uint8Array[] = []\n let received = 0\n while (true) {\n const result = await reader.read()\n if (result.done) break\n received += result.value.byteLength\n if (received > MAX_DISCOVERY_BYTES) {\n await reader.cancel()\n throw new Error('frp_discovery_invalid')\n }\n chunks.push(result.value)\n }\n const bytes = new Uint8Array(received)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return bytes\n}\n\nasync function defaultProbeDiscovery(origin: string, expectedInstanceId: string, signal: AbortSignal): Promise<boolean> {\n const requestController = new AbortController()\n const abort = (): void => { requestController.abort() }\n signal.addEventListener('abort', abort, { once: true })\n const timeout = setTimeout(abort, DISCOVERY_REQUEST_TIMEOUT_MS)\n timeout.unref()\n try {\n const response = await fetch(`${origin}/mobile-access/discovery`, {\n method: 'GET',\n redirect: 'error',\n cache: 'no-store',\n signal: requestController.signal,\n headers: { accept: 'application/json' },\n })\n if (!response.ok) return false\n let value: unknown\n try { value = JSON.parse(new TextDecoder().decode(await boundedResponseBytes(response))) as unknown } catch {\n throw new Error('frp_discovery_invalid')\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('frp_discovery_invalid')\n const actual = (value as Record<string, unknown>).instanceId\n if (typeof actual !== 'string') throw new Error('frp_discovery_invalid')\n if (actual !== expectedInstanceId) throw new Error('frp_discovery_mismatch')\n return true\n } finally {\n clearTimeout(timeout)\n signal.removeEventListener('abort', abort)\n }\n}\n\n/** Owns frpc, its generation-specific configuration, and the remote gateway. */\nexport class FrpController implements RemoteProviderController {\n private enabled = false\n private initialized = false\n private disposed = false\n private child: ChildProcessWithoutNullStreams | undefined\n private gatewayValue: MobileAccessGateway | undefined\n private generation = 0\n private latest: FrpStatus = publicStatus({ enabled: false, state: 'off' })\n private queue: Promise<void> = Promise.resolve()\n private startupAbort: AbortController | undefined\n\n constructor(private readonly options: FrpControllerOptions) {\n if (!isAbsolute(options.executable)) throw new Error('frpc executable path must be absolute')\n if (!/^[a-f0-9]{64}$/u.test(options.instanceId)) throw new Error('FRP instance ID is invalid')\n }\n\n /** Restore the remembered FRP switch without changing LAN or other providers. */\n async initialize(): Promise<void> {\n const state = await this.options.store.load()\n this.enabled = state.enabled\n this.initialized = true\n if (this.enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n }\n\n /** Return the active FRP-backed DSH gateway. */\n gateway(): MobileAccessGateway | undefined {\n return this.gatewayValue\n }\n\n /** Return state safe for the desktop control UI. */\n status(): FrpStatus {\n return publicStatus(this.latest)\n }\n\n /** Enable or disable FRP without changing LAN or another provider. */\n async setEnabled(enabled: boolean): Promise<FrpStatus> {\n if (!this.initialized || this.disposed) throw new Error('FRP controller is unavailable')\n await this.enqueue(async () => {\n if (this.enabled === enabled && (enabled === false || this.child !== undefined)) return\n if (!enabled) await this.stop()\n this.enabled = enabled\n await this.options.store.save({ version: 1, enabled })\n if (enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Restart FRP while retaining its private server settings and devices. */\n async reconnect(): Promise<FrpStatus> {\n if (!this.initialized || this.disposed) throw new Error('FRP controller is unavailable')\n await this.enqueue(async () => {\n if (!this.enabled) {\n this.enabled = true\n await this.options.store.save({ version: 1, enabled: true })\n }\n await this.stop()\n await this.start()\n })\n return this.status()\n }\n\n /** Disable FRP without deleting its explicitly managed component or settings. */\n async reset(): Promise<FrpStatus> {\n if (!this.initialized || this.disposed) throw new Error('FRP controller is unavailable')\n await this.enqueue(async () => {\n await this.stop()\n this.enabled = false\n await this.options.store.save({ version: 1, enabled: false })\n this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Stop all FRP resources without changing the remembered switch. */\n async close(): Promise<void> {\n if (this.disposed) return\n this.disposed = true\n await this.enqueue(() => this.stop())\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task\n }\n\n private publish(status: FrpStatus): void {\n this.latest = publicStatus(status)\n try { this.options.onStatus?.(this.status()) } catch { /* UI observation cannot own runtime state. */ }\n }\n\n private async start(): Promise<void> {\n const generation = ++this.generation\n let executableEntry\n try { executableEntry = await lstat(this.options.executable) } catch {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'frp_component_missing' })\n return\n }\n if (!executableEntry.isFile() || executableEntry.isSymbolicLink()) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'frp_component_invalid' })\n return\n }\n const settings = this.options.config.settings()\n if (settings === undefined) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'frp_config_missing' })\n return\n }\n this.publish({ enabled: true, state: 'starting', origin: settings.publicOrigin })\n let exposed: boolean\n try {\n exposed = await (this.options.probeVhostExposure ?? defaultProbeVhostExposure)(\n settings.serverAddress,\n DEFAULT_VHOST_HTTP_PORT,\n )\n } catch {\n this.publish({ enabled: true, state: 'error', origin: settings.publicOrigin, errorCode: 'frp_vhost_probe_failed' })\n return\n }\n if (exposed) {\n this.publish({ enabled: true, state: 'error', origin: settings.publicOrigin, errorCode: 'frp_vhost_publicly_reachable' })\n return\n }\n let gateway: MobileAccessGateway\n try { gateway = await this.options.createGateway(settings.publicOrigin) } catch {\n this.publish({ enabled: true, state: 'error', origin: settings.publicOrigin, errorCode: 'gateway_start_failed' })\n return\n }\n if (generation !== this.generation || !this.enabled) {\n await gateway.close()\n return\n }\n this.gatewayValue = gateway\n let configFile: string\n try {\n configFile = await this.options.config.writeRuntimeConfig(gateway.address().port)\n await (this.options.verifyConfig ?? defaultVerifyConfig)(this.options.executable, configFile)\n } catch {\n await this.failGeneration(generation, 'frp_config_verify_failed')\n return\n }\n if (generation !== this.generation || !this.enabled) return\n let child: ChildProcessWithoutNullStreams\n try { child = (this.options.launchClient ?? defaultLaunchClient)(this.options.executable, configFile) } catch {\n await this.failGeneration(generation, 'frp_launch_failed')\n return\n }\n this.child = child\n child.stdout.resume()\n child.stderr.resume()\n child.once('error', () => { void this.enqueue(() => this.failGeneration(generation, 'frp_launch_failed')) })\n child.once('close', code => {\n if (generation !== this.generation || this.child !== child) return\n this.child = undefined\n if (this.enabled) void this.enqueue(() => this.failGeneration(generation, code === 0 ? 'frp_stopped' : 'frp_exited'))\n })\n this.publish({ enabled: true, state: 'connecting', origin: settings.publicOrigin })\n const controller = new AbortController()\n this.startupAbort = controller\n void this.waitForDiscovery(generation, settings.publicOrigin, controller.signal)\n }\n\n private async waitForDiscovery(generation: number, origin: string, signal: AbortSignal): Promise<void> {\n const deadline = Date.now() + (this.options.startTimeoutMs ?? START_TIMEOUT_MS)\n const probe = this.options.probeDiscovery ?? defaultProbeDiscovery\n while (!signal.aborted && Date.now() < deadline) {\n try {\n if (await probe(origin, this.options.instanceId, signal)) {\n await this.enqueue(async () => {\n if (generation !== this.generation || signal.aborted || !this.enabled) return\n this.startupAbort = undefined\n this.publish({ enabled: true, state: 'ready', origin })\n })\n return\n }\n } catch (error) {\n if (signal.aborted) return\n if (error instanceof Error && (error.message === 'frp_discovery_mismatch' || error.message === 'frp_discovery_invalid')) {\n await this.enqueue(() => this.failGeneration(generation, error.message))\n return\n }\n }\n await new Promise<void>(resolveWait => {\n let finished = false\n const finish = (): void => {\n if (finished) return\n finished = true\n clearTimeout(timer)\n signal.removeEventListener('abort', finish)\n resolveWait()\n }\n const timer = setTimeout(finish, this.options.retryIntervalMs ?? DISCOVERY_RETRY_MS)\n timer.unref()\n signal.addEventListener('abort', finish, { once: true })\n })\n }\n if (!signal.aborted) await this.enqueue(() => this.failGeneration(generation, 'frp_start_timeout'))\n }\n\n private async failGeneration(generation: number, code: string): Promise<void> {\n if (generation !== this.generation) return\n await this.stopProcessAndGateway()\n if (this.enabled) this.publish({ enabled: true, state: 'error', errorCode: code })\n }\n\n private async stop(): Promise<void> {\n ++this.generation\n await this.stopProcessAndGateway()\n }\n\n private async stopProcessAndGateway(): Promise<void> {\n this.startupAbort?.abort()\n this.startupAbort = undefined\n const child = this.child\n this.child = undefined\n const gateway = this.gatewayValue\n this.gatewayValue = undefined\n await settleRemoteResources([\n () => child !== undefined && child.exitCode === null ? terminateRemoteProcess(child) : undefined,\n () => gateway?.close(),\n () => rm(this.options.config.runtimeConfigFile, { force: true }),\n ], 'FRP resource cleanup failed')\n }\n}\n","import { randomBytes } from 'node:crypto'\nimport { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { isIP } from 'node:net'\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path'\nimport { addressAllowed, isGloballyRoutableIpv4, isLoopbackAddress, parseCidr } from './network.js'\nimport { restrictPrivateFile } from './private-file.js'\n\nexport const DEFAULT_ORIGIN_LISTEN_PORT = 3444\nconst MAX_SETTINGS_BYTES = 8 * 1024\n/**\n * Ports a user-space listener can actually hold on every supported platform. Below\n * this the bind needs privileges on Linux and macOS and fails with EACCES, which\n * surfaces as a generic start failure rather than a rejected choice.\n */\nconst MIN_ORIGIN_LISTEN_PORT = 1024\n/** The LAN gateway's HTTPS port, held for as long as DSH runs. */\nconst RESERVED_LAN_GATEWAY_PORT = 3443\n/** DSH's own WebServer port; the plugin cannot function while it is taken. */\nconst RESERVED_DSH_WEB_PORT = 3080\nconst PRIVATE_NETWORKS = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'].map(parseCidr)\nconst TRUSTED_NETWORKS = [...PRIVATE_NETWORKS, parseCidr('127.0.0.0/8')]\n\n/** A private HTTP listener behind a user-managed HTTPS reverse proxy. */\nexport interface OriginSettings {\n readonly version: 1\n readonly publicOrigin: string\n readonly listenHost: string\n readonly listenPort: number\n readonly allowedCidrs: readonly string[]\n}\n\n/** Configuration metadata returned only to the local desktop control UI. */\nexport interface OriginConfigurationStatus {\n readonly configured: boolean\n readonly publicOrigin?: string\n readonly listenHost?: string\n readonly listenPort?: number\n readonly allowedCidrs?: readonly string[]\n readonly backendOrigin?: string\n readonly storagePath: string\n readonly errorCode?: string\n}\n\n/** Require a public HTTPS origin; custom external ports are supported. */\nexport function validateOriginPublicOrigin(value: unknown): string {\n if (typeof value !== 'string' || value.length > 512 || /[\\s\\u0000-\\u001f\\u007f\\\\@?#]/u.test(value)) {\n throw new Error('origin_public_origin_invalid')\n }\n let url: URL\n try { url = new URL(value) } catch { throw new Error('origin_public_origin_invalid') }\n const host = url.hostname\n const validHostname = host.length <= 253 && host.includes('.')\n && host.split('.').every(label => label.length >= 1 && label.length <= 63\n && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label))\n && !['localhost', 'local', 'lan', 'home', 'internal', 'home.arpa']\n .some(suffix => host === suffix || host.endsWith('.' + suffix))\n if (url.protocol !== 'https:' || url.pathname !== '/' || url.username !== '' || url.password !== ''\n || url.search !== '' || url.hash !== '' || (url.port !== '' && Number(url.port) < 1)\n || (isIP(host) === 4 ? !isGloballyRoutableIpv4(host) : isIP(host) !== 0 || !validHostname)) {\n throw new Error('origin_public_origin_invalid')\n }\n return url.origin\n}\n\n/** Bind only one explicit loopback or RFC1918 IPv4 interface, never all interfaces. */\nexport function validateOriginListenHost(value: unknown): string {\n if (typeof value !== 'string' || isIP(value) !== 4\n || (!isLoopbackAddress(value) && !addressAllowed(value, PRIVATE_NETWORKS))) {\n throw new Error('origin_listen_host_invalid')\n }\n return value\n}\n\nexport function validateOriginListenPort(value: unknown): number {\n if (!Number.isSafeInteger(value) || Number(value) < MIN_ORIGIN_LISTEN_PORT || Number(value) > 65_535) {\n throw new Error('origin_listen_port_invalid')\n }\n // 3443 is the LAN gateway and 3080 is DSH's own WebServer. Neither is a transient\n // conflict, so both are refused as choices rather than left to fail on bind.\n if (value === RESERVED_LAN_GATEWAY_PORT || value === RESERVED_DSH_WEB_PORT) {\n throw new Error('origin_listen_port_reserved')\n }\n return Number(value)\n}\n\n/** Authorize direct proxy socket peers, not untrusted forwarded client headers. */\nexport function validateOriginAllowedCidrs(value: unknown, listenHost: string): readonly string[] {\n const input = value === undefined && isLoopbackAddress(listenHost) ? ['127.0.0.0/8'] : value\n if (!Array.isArray(input) || input.length === 0 || input.length > 16) {\n throw new Error('origin_allowed_cidrs_invalid')\n }\n const cidrs: string[] = []\n for (const entry of input) {\n if (typeof entry !== 'string' || entry.length > 64 || isIP(entry.split('/')[0] ?? '') !== 4) {\n throw new Error('origin_allowed_cidrs_invalid')\n }\n try {\n const cidr = parseCidr(entry)\n if (cidr.bits !== 32 || !TRUSTED_NETWORKS.some(range => cidr.prefix >= range.prefix\n && addressAllowed(entry.split('/')[0], [range]))) throw new Error('untrusted CIDR')\n cidrs.push(cidr.source)\n } catch { throw new Error('origin_allowed_cidrs_invalid') }\n }\n if (!isLoopbackAddress(listenHost) && !cidrs.some(cidr => !isLoopbackAddress(cidr.split('/')[0]!))) {\n throw new Error('origin_allowed_cidrs_invalid')\n }\n return Object.freeze([...new Set(cidrs)])\n}\n\n/** Validate both saved settings and local administrative requests. */\nexport function parseOriginSettings(value: unknown): OriginSettings {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('origin_settings_invalid')\n const record = value as Record<string, unknown>\n if (Reflect.ownKeys(record).some(key => !['version', 'publicOrigin', 'listenHost', 'listenPort', 'allowedCidrs'].includes(String(key)))\n || (record.version !== undefined && record.version !== 1)) throw new Error('origin_settings_invalid')\n const listenHost = validateOriginListenHost(record.listenHost === undefined ? '127.0.0.1' : record.listenHost)\n return Object.freeze({\n version: 1,\n publicOrigin: validateOriginPublicOrigin(record.publicOrigin),\n listenHost,\n listenPort: validateOriginListenPort(record.listenPort === undefined ? DEFAULT_ORIGIN_LISTEN_PORT : record.listenPort),\n allowedCidrs: validateOriginAllowedCidrs(record.allowedCidrs, listenHost),\n })\n}\n\nasync function atomicPrivateWrite(file: string, body: string): Promise<void> {\n const directory = dirname(file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(file)\n if (!current.isFile() || current.isSymbolicLink()) throw new Error('origin_config_target_invalid')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, '.' + basename(file) + '.' + randomBytes(12).toString('hex') + '.tmp')\n try {\n await writeFile(temporary, body, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, file)\n await restrictPrivateFile(file)\n } catch (error) {\n await rm(temporary, { force: true })\n throw error\n }\n}\n\n/** Owns only origin settings; shared paired-device storage is never removed. */\nexport class OriginConfigStore {\n readonly stateRoot: string\n readonly settingsFile: string\n private settingsValue: OriginSettings | undefined\n private errorCode: string | undefined\n\n constructor(stateDirectory: string) {\n if (!isAbsolute(stateDirectory)) throw new Error('origin config state directory must be absolute')\n this.stateRoot = resolve(stateDirectory)\n this.settingsFile = join(this.stateRoot, 'settings.json')\n }\n\n async initialize(): Promise<void> {\n this.settingsValue = undefined\n this.errorCode = undefined\n let entry\n try { entry = await lstat(this.settingsFile) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\n throw error\n }\n if (!entry.isFile() || entry.isSymbolicLink() || entry.size > MAX_SETTINGS_BYTES) {\n this.errorCode = 'origin_config_invalid'\n return\n }\n await restrictPrivateFile(this.settingsFile)\n try {\n this.settingsValue = parseOriginSettings(JSON.parse(await readFile(this.settingsFile, 'utf8')) as unknown)\n } catch { this.errorCode = 'origin_config_invalid' }\n }\n\n status(): OriginConfigurationStatus {\n const settings = this.settingsValue\n return Object.freeze({\n configured: settings !== undefined,\n ...(settings === undefined ? {} : {\n publicOrigin: settings.publicOrigin,\n listenHost: settings.listenHost,\n listenPort: settings.listenPort,\n allowedCidrs: settings.allowedCidrs,\n backendOrigin: 'http://' + settings.listenHost + ':' + String(settings.listenPort),\n }),\n storagePath: this.stateRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n })\n }\n\n settings(): OriginSettings | undefined { return this.settingsValue }\n\n async configure(value: unknown): Promise<OriginConfigurationStatus> {\n const settings = parseOriginSettings(value)\n await atomicPrivateWrite(this.settingsFile, JSON.stringify(settings) + '\\n')\n this.settingsValue = settings\n this.errorCode = undefined\n return this.status()\n }\n\n async purge(): Promise<OriginConfigurationStatus> {\n await rm(this.settingsFile, { force: true })\n this.settingsValue = undefined\n this.errorCode = undefined\n return this.status()\n }\n}\n","import type { MobileAccessControlStore } from './control.js'\nimport type { MobileAccessGateway } from './gateway.js'\nimport type { OriginConfigStore, OriginSettings } from './origin-proxy-config.js'\nimport type { RemoteProviderController } from './remote.js'\n\nexport type OriginState = 'off' | 'unavailable' | 'starting' | 'ready' | 'error'\n\n/** Ready means the private listener is ready, not that public ingress was tested. */\nexport interface OriginStatus {\n readonly enabled: boolean\n readonly state: OriginState\n readonly origin?: string\n readonly backendOrigin?: string\n readonly errorCode?: string\n}\n\nexport interface OriginControllerOptions {\n readonly store: MobileAccessControlStore\n readonly config: OriginConfigStore\n readonly createGateway: (settings: OriginSettings) => Promise<MobileAccessGateway>\n readonly onStatus?: (status: OriginStatus) => void\n}\n\n/** Owns an authenticated HTTP gateway, with no tunnel process or public network probes. */\nexport class OriginController implements RemoteProviderController {\n private enabled = false\n private initialized = false\n private disposed = false\n private gatewayValue: MobileAccessGateway | undefined\n private latest: OriginStatus = Object.freeze({ enabled: false, state: 'off' })\n private queue: Promise<void> = Promise.resolve()\n\n constructor(private readonly options: OriginControllerOptions) {}\n\n async initialize(): Promise<void> {\n await this.enqueue(async () => {\n if (this.disposed) throw new Error('origin_controller_unavailable')\n if (this.initialized) return\n this.enabled = (await this.options.store.load()).enabled\n this.initialized = true\n if (this.enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n })\n }\n\n gateway(): MobileAccessGateway | undefined { return this.gatewayValue }\n status(): OriginStatus { return this.latest }\n\n async setEnabled(enabled: boolean): Promise<OriginStatus> {\n this.assertAvailable()\n await this.enqueue(async () => {\n this.assertAvailable()\n if (this.enabled === enabled && (!enabled || this.gatewayValue !== undefined)) return\n if (!enabled) await this.stop()\n await this.options.store.save({ version: 1, enabled })\n this.enabled = enabled\n if (enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n async reconnect(): Promise<OriginStatus> {\n this.assertAvailable()\n await this.enqueue(async () => {\n this.assertAvailable()\n if (!this.enabled) {\n await this.options.store.save({ version: 1, enabled: true })\n this.enabled = true\n }\n await this.stop()\n await this.start()\n })\n return this.status()\n }\n\n /** Reset the switch, retaining both settings and shared remote device pairings. */\n async reset(): Promise<OriginStatus> { return this.setEnabled(false) }\n\n /** Stop the listener without changing the durable switch. */\n async close(): Promise<void> {\n this.disposed = true\n await this.enqueue(async () => {\n await this.stop()\n this.publish({ enabled: this.enabled, state: 'off' })\n })\n }\n\n private assertAvailable(): void {\n if (!this.initialized || this.disposed) throw new Error('origin_controller_unavailable')\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task\n }\n\n private publish(status: OriginStatus): void {\n this.latest = Object.freeze({ ...status })\n try { this.options.onStatus?.(this.latest) } catch { /* Observers do not own the listener. */ }\n }\n\n private async start(): Promise<void> {\n const settings = this.options.config.settings()\n if (settings === undefined) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: this.options.config.status().errorCode ?? 'origin_config_missing' })\n return\n }\n this.publish({ enabled: true, state: 'starting', origin: settings.publicOrigin })\n let gateway: MobileAccessGateway\n try { gateway = await this.options.createGateway(settings) } catch (error) {\n const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined\n this.publish({\n enabled: true, state: 'error', origin: settings.publicOrigin,\n errorCode: code === 'EADDRINUSE' ? 'origin_listen_port_in_use'\n : code === 'EADDRNOTAVAIL' ? 'origin_listen_address_unavailable' : 'origin_gateway_start_failed',\n })\n return\n }\n this.gatewayValue = gateway\n if (this.disposed) {\n await this.stop()\n return\n }\n this.publish({\n enabled: true, state: 'ready', origin: settings.publicOrigin,\n backendOrigin: 'http://' + settings.listenHost + ':' + String(gateway.address().port),\n })\n }\n\n private async stop(): Promise<void> {\n const gateway = this.gatewayValue\n if (gateway === undefined) return\n // Retain ownership on close failure so a later stop can retry cleanup.\n await gateway.close()\n this.gatewayValue = undefined\n this.publish({ enabled: this.enabled, state: 'off' })\n }\n}\n","/**\n * Hosts a pinned GitHub release download may redirect to.\n *\n * `github.com/<owner>/<repo>/releases/download/<tag>/<asset>` does not serve the bytes itself: it\n * answers 302 with a signed URL on a release-asset host. A fetch configured with\n * `redirect: 'error'` fails that hop as `TypeError: fetch failed` (cause: `unexpected redirect`)\n * before a single byte is transferred, which is exactly how the cloudflared component could never\n * install. Following one validated hop keeps the origin restriction while allowing the shape\n * every GitHub release download uses.\n */\nconst RELEASE_ASSET_HOSTS: readonly string[] = Object.freeze([\n 'release-assets.githubusercontent.com',\n 'objects.githubusercontent.com',\n])\n\n/** Input for {@link downloadPinnedArtifact}. */\nexport interface PinnedDownloadRequest {\n /** Absolute URL of the pinned artifact. */\n readonly url: string\n /** Exact byte length the artifact must have. */\n readonly expectedBytes: number\n /** Stable error-code prefix owned by the calling component, such as `cloudflared`. */\n readonly errorPrefix: string\n readonly signal: AbortSignal\n /** Hosts a single redirect hop may target; defaults to the GitHub release asset hosts. */\n readonly redirectHosts?: readonly string[]\n}\n\n/** Follow one redirect hop after checking its scheme and host. A second hop is refused. */\nasync function followValidatedRedirect(response: Response, request: PinnedDownloadRequest): Promise<Response> {\n const location = response.headers.get('location')\n if (location === null) throw new Error(`${request.errorPrefix}_download_redirect_missing`)\n let target: URL\n try {\n target = new URL(location, request.url)\n } catch {\n throw new Error(`${request.errorPrefix}_download_redirect_invalid`)\n }\n const hosts = request.redirectHosts ?? RELEASE_ASSET_HOSTS\n if (target.protocol !== 'https:' || !hosts.includes(target.hostname)) {\n throw new Error(`${request.errorPrefix}_download_redirect_rejected`)\n }\n return fetch(target, { redirect: 'error', signal: request.signal })\n}\n\n/** Attempts per install. A 55 MB transfer through a TUN proxy can reset mid-stream: the first\n * real run in the development environment failed after 1.5 MB and succeeded on the next attempt,\n * which is exactly the confusing \"nothing happened\" the user saw. */\nconst DOWNLOAD_ATTEMPTS = 2\nconst RETRY_DELAY_MS = 750\n\n/** Whether a failure is a transport problem worth a second attempt rather than a verdict. */\nfunction isRetryable(error: unknown): boolean {\n // undici reports network faults as TypeError('fetch failed'), and a 5xx is worth one more try.\n if (error instanceof TypeError) return true\n if (error instanceof Error && /_download_http_5\\d\\d$/u.test(error.message)) return true\n return false\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise(resolve => { setTimeout(resolve, ms) })\n}\n\nasync function attemptDownload(request: PinnedDownloadRequest): Promise<Uint8Array> {\n const first = await fetch(request.url, { redirect: 'manual', signal: request.signal })\n const redirected = first.status >= 300 && first.status < 400\n const response = redirected ? await followValidatedRedirect(first, request) : first\n if (!response.ok) throw new Error(`${request.errorPrefix}_download_http_${String(response.status)}`)\n const contentLength = response.headers.get('content-length')\n if (contentLength !== null && (!/^\\d+$/u.test(contentLength) || Number(contentLength) !== request.expectedBytes)) {\n throw new Error(`${request.errorPrefix}_download_size_mismatch`)\n }\n if (response.body === null) throw new Error(`${request.errorPrefix}_download_size_mismatch`)\n const bytes = new Uint8Array(request.expectedBytes)\n const reader = response.body.getReader()\n let received = 0\n while (true) {\n const chunk = await reader.read()\n if (chunk.done) break\n if (chunk.value.byteLength > bytes.byteLength - received) {\n await reader.cancel()\n throw new Error(`${request.errorPrefix}_download_size_mismatch`)\n }\n bytes.set(chunk.value, received)\n received += chunk.value.byteLength\n }\n if (received !== request.expectedBytes) throw new Error(`${request.errorPrefix}_download_size_mismatch`)\n return bytes\n}\n\n/**\n * Download a pinned artifact, following at most one validated redirect, and check its exact length.\n * A transport failure is retried once; a length or redirect verdict is not. The caller still\n * verifies the SHA-256 before publishing anything.\n * @param request - the pinned URL, its exact byte length and the caller's error prefix.\n * @returns the artifact bytes.\n */\nexport async function downloadPinnedArtifact(request: PinnedDownloadRequest): Promise<Uint8Array> {\n let lastError: unknown\n for (let attempt = 1; attempt <= DOWNLOAD_ATTEMPTS; attempt += 1) {\n try {\n return await attemptDownload(request)\n } catch (error) {\n lastError = error\n if (attempt === DOWNLOAD_ATTEMPTS || !isRetryable(error)) throw error\n await delay(RETRY_DELAY_MS)\n }\n }\n throw lastError\n}\n","import { createHash, randomBytes } from 'node:crypto'\nimport {\n chmod,\n copyFile,\n lstat,\n mkdir,\n mkdtemp,\n readFile,\n rename,\n rm,\n writeFile,\n} from 'node:fs/promises'\nimport { isAbsolute, join, relative, resolve } from 'node:path'\nimport { downloadPinnedArtifact } from './component-download.js'\n\n/**\n * Pinned cloudflared components fetched only after an explicit user action.\n *\n * Unlike the cpolar archive each artifact IS the executable: there is nothing\n * to unpack, so the download digest and the installed digest are the same pair.\n * `--no-autoupdate` is passed at runtime as well, so the pinned bytes stay the\n * bytes that were verified.\n */\ninterface CloudflaredArtifact {\n readonly version: string\n readonly platform: NodeJS.Platform\n readonly arch: string\n readonly downloadUrl: string\n readonly downloadBytes: number\n readonly downloadSha256: string\n readonly executableName: string\n}\n\nconst CLOUDFLARED_VERSION = '2026.9.1'\n\nconst releases = [\n {\n version: CLOUDFLARED_VERSION,\n platform: 'win32',\n arch: 'x64',\n downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-windows-amd64.exe`,\n downloadBytes: 54_976_432,\n downloadSha256: '2837888cc0f5d58f15b6dc478376de90b4d3ba5241c7947455d1e0a0df429712',\n executableName: 'cloudflared.exe',\n },\n {\n version: CLOUDFLARED_VERSION,\n platform: 'linux',\n arch: 'x64',\n downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64`,\n downloadBytes: 39_838_488,\n downloadSha256: '03f1f25d1cc93b9ad6c60569d44060bc4f17ed97075760ed8cfca4b12dcd68cc',\n executableName: 'cloudflared',\n },\n {\n version: CLOUDFLARED_VERSION,\n platform: 'linux',\n arch: 'arm64',\n downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64`,\n downloadBytes: 37_466_252,\n downloadSha256: '3d97437c71848bd8df68041e12436b484a661d95073ea1937f01a845ce88faa3',\n executableName: 'cloudflared',\n },\n] as const satisfies readonly CloudflaredArtifact[]\n\n/** Pinned official cloudflared release metadata for supported desktop targets. */\nexport const CLOUDFLARED_COMPONENT_RELEASES: Readonly<Record<string, CloudflaredArtifact>> = Object.freeze(Object.fromEntries(\n releases.map(release => [`${release.platform}-${release.arch}`, Object.freeze(release)]),\n))\n\n/**\n * Canonical release metadata. New code should select from\n * {@link CLOUDFLARED_COMPONENT_RELEASES} by platform and architecture; this\n * alias preserves the original Windows x64 entry for existing callers.\n */\nexport const CLOUDFLARED_COMPONENT_RELEASE = CLOUDFLARED_COMPONENT_RELEASES['win32-x64'] as CloudflaredArtifact\n\nconst DOWNLOAD_PAGE = 'https://github.com/cloudflare/cloudflared/releases'\nconst TERMS_URL = 'https://www.cloudflare.com/website-terms/'\n\n/**\n * Public, credential-free description of the managed cloudflared component.\n *\n * There is deliberately no `configured` flag: a quick tunnel needs no account,\n * token, or DNS record, so installation is the only precondition.\n */\nexport interface CloudflaredComponentStatus {\n readonly supported: boolean\n readonly installed: boolean\n readonly version: string\n readonly downloadBytes: number\n readonly installedBytes: number\n readonly sourceUrl: string\n readonly downloadPage: string\n readonly termsUrl: string\n readonly storagePath: string\n readonly errorCode?: string\n}\n\ninterface CloudflaredComponentManagerOptions {\n readonly stateDirectory: string\n readonly platform?: NodeJS.Platform\n readonly arch?: string\n readonly fetchArtifact?: (url: string, signal: AbortSignal) => Promise<Uint8Array>\n}\n\nfunction inside(parent: string, child: string): boolean {\n const candidate = relative(parent, child)\n return candidate !== '' && !candidate.startsWith('..') && !isAbsolute(candidate)\n}\n\nasync function sha256(file: string): Promise<string> {\n return createHash('sha256').update(await readFile(file)).digest('hex')\n}\n\nasync function regularFile(file: string, expectedBytes?: number): Promise<boolean> {\n try {\n const stat = await lstat(file)\n return stat.isFile() && !stat.isSymbolicLink() && (expectedBytes === undefined || stat.size === expectedBytes)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false\n throw error\n }\n}\n\nasync function defaultFetchArtifact(\n url: string,\n signal: AbortSignal,\n expectedBytes: number,\n): Promise<Uint8Array> {\n return downloadPinnedArtifact({\n url,\n expectedBytes,\n errorPrefix: 'cloudflared',\n signal,\n })\n}\n\n/** Select the pinned artifact for one host, or undefined where unsupported. */\nfunction lookupRelease(platform: NodeJS.Platform, arch: string): CloudflaredArtifact | undefined {\n return CLOUDFLARED_COMPONENT_RELEASES[`${platform}-${arch}`]\n}\n\n/** Owns the optional cloudflared binary inside DSH Mobile state. */\nexport class CloudflaredComponentManager {\n readonly executable: string\n readonly componentRoot: string\n readonly componentStorage: string\n readonly stateRoot: string\n readonly logRoot: string\n private readonly stagingRoot: string\n private readonly platform: NodeJS.Platform\n private readonly arch: string\n private readonly release: CloudflaredArtifact | undefined\n private readonly fetchArtifact: (url: string, signal: AbortSignal) => Promise<Uint8Array>\n private installed = false\n private errorCode: string | undefined\n private queue: Promise<void> = Promise.resolve()\n\n constructor(options: CloudflaredComponentManagerOptions) {\n const stateDirectory = resolve(options.stateDirectory)\n if (!isAbsolute(stateDirectory)) throw new Error('cloudflared state directory must be absolute')\n this.platform = options.platform ?? process.platform\n this.arch = options.arch ?? process.arch\n this.release = lookupRelease(this.platform, this.arch)\n this.componentRoot = join(stateDirectory, 'components', 'cloudflared')\n this.componentStorage = join(this.componentRoot, this.release?.version ?? CLOUDFLARED_VERSION)\n this.executable = join(this.componentStorage, this.release?.executableName ?? 'cloudflared')\n this.stateRoot = join(stateDirectory, 'state', 'cloudflared')\n this.logRoot = join(stateDirectory, 'logs', 'cloudflared')\n this.stagingRoot = join(stateDirectory, 'staging', 'cloudflared')\n for (const child of [this.componentRoot, this.componentStorage, this.stateRoot, this.logRoot, this.stagingRoot]) {\n if (!inside(stateDirectory, child)) throw new Error('cloudflared component path escaped its state directory')\n }\n const release = this.release\n this.fetchArtifact = options.fetchArtifact\n ?? ((url, signal) => defaultFetchArtifact(url, signal, release?.downloadBytes ?? 0))\n }\n\n /** Inspect the managed binary without using any global cloudflared state. */\n async initialize(): Promise<void> {\n const release = this.release\n this.installed = release !== undefined && await regularFile(this.executable, release.downloadBytes)\n if (this.installed && release !== undefined && await sha256(this.executable) !== release.downloadSha256) {\n this.installed = false\n this.errorCode = 'cloudflared_component_invalid'\n }\n }\n\n /** Return a safe status that never includes machine-specific account data. */\n status(): CloudflaredComponentStatus {\n // Unsupported hosts still report the canonical entry so the panel can show\n // what would be installed elsewhere; `supported` carries the actual gate.\n const release = this.release ?? CLOUDFLARED_COMPONENT_RELEASE\n return Object.freeze({\n supported: this.release !== undefined,\n installed: this.installed,\n version: release.version,\n downloadBytes: release.downloadBytes,\n installedBytes: release.downloadBytes,\n sourceUrl: release.downloadUrl,\n downloadPage: DOWNLOAD_PAGE,\n termsUrl: TERMS_URL,\n storagePath: this.componentRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n })\n }\n\n /** Download, verify, and install the pinned cloudflared executable after explicit confirmation. */\n install(): Promise<CloudflaredComponentStatus> {\n return this.enqueue(async () => {\n const release = this.release\n if (release === undefined) throw new Error('cloudflared_component_unsupported')\n await mkdir(this.stagingRoot, { recursive: true, mode: 0o700 })\n const staging = await mkdtemp(join(this.stagingRoot, 'install-'))\n try {\n const controller = new AbortController()\n // The pinned artifact is tens of megabytes, so the transfer budget is\n // larger than a control handshake: a slow but working link must not\n // fail the install.\n const timeout = setTimeout(() => { controller.abort() }, 300_000)\n timeout.unref()\n let bytes: Uint8Array\n try { bytes = await this.fetchArtifact(release.downloadUrl, controller.signal) } finally { clearTimeout(timeout) }\n if (bytes.byteLength !== release.downloadBytes) {\n throw new Error('cloudflared_download_size_mismatch')\n }\n const digest = createHash('sha256').update(bytes).digest('hex')\n if (digest !== release.downloadSha256) throw new Error('cloudflared_download_hash_mismatch')\n const staged = join(staging, release.executableName)\n await writeFile(staged, bytes, { flag: 'wx', mode: 0o600 })\n await chmod(staged, 0o700)\n if (!await regularFile(staged, release.downloadBytes)\n || await sha256(staged) !== release.downloadSha256) {\n throw new Error('cloudflared_executable_hash_mismatch')\n }\n const candidate = join(this.componentRoot, `.install-${randomBytes(12).toString('hex')}`)\n await mkdir(candidate, { recursive: true, mode: 0o700 })\n await copyFile(staged, join(candidate, release.executableName))\n await chmod(join(candidate, release.executableName), 0o700)\n await rm(this.componentStorage, { recursive: true, force: true })\n await rename(candidate, this.componentStorage)\n this.installed = true\n this.errorCode = undefined\n } finally {\n await rm(staging, { recursive: true, force: true })\n }\n })\n }\n\n /** Remove every cloudflared file owned by DSH Mobile without touching global state. */\n purge(): Promise<CloudflaredComponentStatus> {\n return this.enqueue(async () => {\n await Promise.all([\n rm(this.componentRoot, { recursive: true, force: true }),\n rm(this.stateRoot, { recursive: true, force: true }),\n rm(this.logRoot, { recursive: true, force: true }),\n rm(this.stagingRoot, { recursive: true, force: true }),\n ])\n this.installed = false\n this.errorCode = undefined\n })\n }\n\n private enqueue(operation: () => Promise<void>): Promise<CloudflaredComponentStatus> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task.then(() => this.status())\n }\n}\n","import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'\nimport { lstat } from 'node:fs/promises'\nimport { createServer, type Server } from 'node:net'\nimport { isAbsolute } from 'node:path'\nimport type { CloudflaredTunnelMode, CloudflaredTunnelSettings } from './cloudflared-tunnel.js'\nimport type { MobileAccessControlStore } from './control.js'\nimport type { MobileAccessGateway } from './gateway.js'\nimport { settleRemoteResources, terminateRemoteProcess, type RemoteProviderController } from './remote.js'\n\nconst MAX_LOG_BUFFER_BYTES = 64 * 1024\n/**\n * A quick tunnel has to be allocated and registered with the Cloudflare edge\n * before the public hostname appears. That is slower than a cpolar control\n * handshake but still bounded; 60 s keeps a genuinely stalled client from\n * looking healthy for longer than a user will wait.\n */\nconst START_TIMEOUT_MS = 60_000\n/**\n * How many startup-timeout rounds a named tunnel may wait through while its\n * connector process is still alive. A rebooted PC often autostarts DSH before\n * Wi-Fi / VPN / DNS is usable; cloudflared retries the edge on its own, so\n * killing it after a single 60 s window turns a slow boot into a manual\n * reconnect loop. An exited connector (bad token, bad config) still fails fast\n * on the first round.\n */\nconst NAMED_STARTUP_ROUNDS = 5\nconst CLOUDFLARED_HOST_SUFFIX = '.trycloudflare.com'\n\n/**\n * Subdomains Cloudflare reserves for its own control plane.\n *\n * The quick-tunnel banner prints `https://api.trycloudflare.com` (the registration API) before it\n * prints the tunnel's own hostname, and that address satisfies a bare suffix check. Adopting it\n * would put the API host in the pairing QR code, where the phone gets `{\"code\":10005\",\"message\":\n * \"Method Not Allowed\"}` instead of DSH. A quick tunnel is always one random label under the\n * suffix, so the reserved single labels are refused outright.\n */\nconst RESERVED_CLOUDFLARED_LABELS: readonly string[] = Object.freeze(['api', 'www', 'update', 'login'])\n\n/** Product-facing states for the optional cloudflared remote transport. */\nexport type CloudflaredState = 'off' | 'unavailable' | 'starting' | 'connecting' | 'ready' | 'error'\n\n/** Safe cloudflared state returned only through the loopback DSH control route. */\nexport interface CloudflaredStatus {\n readonly enabled: boolean\n readonly state: CloudflaredState\n readonly origin?: string\n readonly errorCode?: string\n}\n\n/** Inputs for one cloudflared process and its authenticated DSH gateway. */\nexport interface CloudflaredControllerOptions {\n readonly store: MobileAccessControlStore\n readonly executable: string\n /** Named-tunnel configuration; quick tunnels are used when it reports quick mode. */\n readonly tunnel: { settings(): CloudflaredTunnelSettings }\n readonly createGateway: (origin: string, listenPort: number) => Promise<MobileAccessGateway>\n readonly onStatus?: (status: CloudflaredStatus) => void\n /** Liveness bound for one startup-timeout round; defaults to the product timeout. */\n readonly startupTimeoutMs?: number\n /**\n * Total startup-timeout rounds before a live named connector is given up on;\n * defaults to NAMED_STARTUP_ROUNDS. Quick tunnels always use a single round.\n */\n readonly maxStartupRounds?: number\n readonly spawnProcess?: (\n executable: string,\n args: readonly string[],\n environment: NodeJS.ProcessEnv,\n ) => ChildProcessWithoutNullStreams\n}\n\ninterface PortReservation {\n readonly port: number\n readonly release: () => Promise<void>\n}\n\nfunction publicStatus(status: CloudflaredStatus): CloudflaredStatus {\n return Object.freeze({\n enabled: status.enabled,\n state: status.state,\n ...(status.origin === undefined ? {} : { origin: status.origin }),\n ...(status.errorCode === undefined ? {} : { errorCode: status.errorCode }),\n })\n}\n\n/**\n * Whether a hostname is a quick-tunnel host: exactly one random label under the provider suffix,\n * never one of the reserved control-plane labels.\n */\nfunction isCloudflaredHost(hostname: string): boolean {\n const lower = hostname.toLowerCase()\n if (!lower.endsWith(CLOUDFLARED_HOST_SUFFIX)) return false\n const label = lower.slice(0, -CLOUDFLARED_HOST_SUFFIX.length)\n return label !== '' && !label.includes('.') && !RESERVED_CLOUDFLARED_LABELS.includes(label)\n}\n\n/**\n * Extract a validated public HTTPS origin from one cloudflared output line.\n *\n * Quick tunnels print the public hostname inside a decorated banner, so the\n * candidate is taken from the line and then re-parsed as a URL: a lookalike such\n * as `https://x.trycloudflare.com.evil.test` fails the hostname check instead of\n * being truncated into an accepted origin.\n */\nexport function parseCloudflaredOrigin(line: string): string | undefined {\n if (!line.toLowerCase().includes('trycloudflare.com')) return undefined\n const match = /https?:\\/\\/[^\\s\"'|<>]+/u.exec(line)\n // A log line that merely mentions the provider suffix carries no origin, so it\n // is ignored. Only a URL that is present but fails validation is a protocol\n // violation worth failing the generation for.\n if (match === null) return undefined\n // Banner decoration can leave trailing punctuation attached to the hostname.\n const candidate = match[0].replace(/[),.;:]+$/u, '')\n let url: URL\n try { url = new URL(candidate) } catch { throw new Error('invalid_cloudflared_origin') }\n if (url.protocol !== 'https:' || url.port !== '' || !isCloudflaredHost(url.hostname)\n || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '') throw new Error('invalid_cloudflared_origin')\n return url.origin\n}\n\n/**\n * Whether one cloudflared log line reports an established edge connection.\n *\n * A named tunnel prints no banner, so registration is the only signal that the\n * connector reached Cloudflare and the public hostname can serve traffic. Both\n * the current and the older wording are accepted; anything else is ignored so a\n * chatty log line cannot be mistaken for readiness.\n */\nexport function isCloudflaredRegistration(line: string): boolean {\n return /registered tunnel connection/iu.test(line)\n || /connection\\s+[0-9a-f][0-9a-f-]{7,}\\s+registered/iu.test(line)\n}\n\nasync function reserveLoopbackPort(requestedPort?: number): Promise<PortReservation> {\n const server: Server = createServer(socket => { socket.destroy() })\n await new Promise<void>((resolveListen, reject) => {\n server.once('error', reject)\n server.listen(requestedPort ?? 0, '127.0.0.1', () => {\n server.off('error', reject)\n resolveListen()\n })\n })\n const address = server.address()\n if (address === null || typeof address === 'string') {\n server.close()\n throw new Error('cloudflared_port_reservation_failed')\n }\n let released = false\n return {\n port: address.port,\n release: async () => {\n if (released) return\n released = true\n await new Promise<void>(resolveClose => { server.close(() => resolveClose()) })\n },\n }\n}\n\nfunction spawnCloudflaredProcess(\n executable: string,\n args: readonly string[],\n environment: NodeJS.ProcessEnv,\n): ChildProcessWithoutNullStreams {\n return spawn(executable, [...args], {\n env: environment,\n shell: false,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n })\n}\n\nfunction withoutProxyEnvironment(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const blocked = new Set(['HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'NO_PROXY'])\n return Object.fromEntries(Object.entries(environment).filter(([name]) => !blocked.has(name.toUpperCase())))\n}\n\n/**\n * Owns an installed cloudflared client and a provider-specific DSH remote gateway.\n *\n * Quick tunnels allocate a random hostname and a random forward port on every\n * start. Named tunnels instead read an account token and a stable public\n * hostname from the tunnel store, forward to the configured loopback port, and\n * learn readiness from the connector's registration line rather than a banner.\n */\nexport class CloudflaredController implements RemoteProviderController {\n private enabled = false\n private initialized = false\n private disposed = false\n private child: ChildProcessWithoutNullStreams | undefined\n private gatewayValue: MobileAccessGateway | undefined\n private reservation: PortReservation | undefined\n private generation = 0\n private buffer = ''\n private mode: CloudflaredTunnelMode = 'quick'\n private namedOrigin: string | undefined\n private latest: CloudflaredStatus = publicStatus({ enabled: false, state: 'off' })\n private queue: Promise<void> = Promise.resolve()\n private startupTimer: NodeJS.Timeout | undefined\n private startupRounds = 0\n\n constructor(private readonly options: CloudflaredControllerOptions) {\n if (!isAbsolute(options.executable)) {\n throw new Error('cloudflared executable path must be absolute')\n }\n }\n\n /** Restore the remembered cloudflared switch independently from LAN and other providers. */\n async initialize(): Promise<void> {\n const state = await this.options.store.load()\n this.enabled = state.enabled\n this.initialized = true\n if (this.enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n }\n\n /** Return the active cloudflared-backed DSH gateway. */\n gateway(): MobileAccessGateway | undefined {\n return this.gatewayValue\n }\n\n /** Return state safe for the desktop control UI. */\n status(): CloudflaredStatus {\n return publicStatus(this.latest)\n }\n\n /** Enable or disable cloudflared without changing LAN or other provider state. */\n async setEnabled(enabled: boolean): Promise<CloudflaredStatus> {\n if (!this.initialized || this.disposed) throw new Error('cloudflared controller is unavailable')\n await this.enqueue(async () => {\n if (this.enabled === enabled && (enabled === false || this.child !== undefined)) return\n if (!enabled) await this.stop()\n this.enabled = enabled\n await this.options.store.save({ version: 1, enabled })\n if (enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Restart cloudflared and allocate a fresh quick tunnel. */\n async reconnect(): Promise<CloudflaredStatus> {\n if (!this.initialized || this.disposed) throw new Error('cloudflared controller is unavailable')\n await this.enqueue(async () => {\n if (!this.enabled) {\n this.enabled = true\n await this.options.store.save({ version: 1, enabled: true })\n }\n await this.stop()\n await this.start()\n })\n return this.status()\n }\n\n /** Disable cloudflared without deleting the installed component. */\n async reset(): Promise<CloudflaredStatus> {\n if (!this.initialized || this.disposed) throw new Error('cloudflared controller is unavailable')\n await this.enqueue(async () => {\n await this.stop()\n this.enabled = false\n await this.options.store.save({ version: 1, enabled: false })\n this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Stop owned resources without changing the remembered switch. */\n async close(): Promise<void> {\n if (this.disposed) return\n this.disposed = true\n await this.enqueue(() => this.stop())\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task\n }\n\n private publish(status: CloudflaredStatus): void {\n this.latest = publicStatus(status)\n try { this.options.onStatus?.(this.status()) } catch { /* UI observation cannot own runtime state. */ }\n }\n\n private async start(): Promise<void> {\n const generation = ++this.generation\n let executableEntry\n try { executableEntry = await lstat(this.options.executable) } catch {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'cloudflared_component_missing' })\n return\n }\n if (!executableEntry.isFile() || executableEntry.isSymbolicLink()) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'cloudflared_component_invalid' })\n return\n }\n\n const settings = this.options.tunnel.settings()\n const named = settings.mode === 'named' ? settings : undefined\n this.mode = settings.mode\n this.namedOrigin = named === undefined ? undefined : `https://${named.hostname}`\n\n let reservation: PortReservation\n try {\n reservation = await reserveLoopbackPort(named?.port)\n } catch (error) {\n // A bind conflict is the user's to resolve; anything else is an internal\n // reservation failure and must not be reported as a busy port.\n const conflict = (error as NodeJS.ErrnoException | undefined)?.code === 'EADDRINUSE'\n this.publish({\n enabled: true,\n state: 'error',\n errorCode: conflict\n ? (named === undefined ? 'cloudflared_port_unavailable' : 'cloudflared_tunnel_port_unavailable')\n : 'cloudflared_port_reservation_failed',\n })\n return\n }\n this.reservation = reservation\n this.buffer = ''\n this.startupRounds = 0\n this.publish({ enabled: true, state: 'starting' })\n\n if (named !== undefined) {\n // Cloudflare already routes the public hostname to this exact port, so the\n // gateway must be listening before the connector registers; the port has to\n // survive restarts, which is why it is configuration rather than a choice.\n const origin = this.namedOrigin as string\n this.publish({ enabled: true, state: 'connecting', origin })\n await reservation.release()\n if (this.reservation === reservation) this.reservation = undefined\n let gateway: MobileAccessGateway\n try {\n gateway = await this.options.createGateway(origin, reservation.port)\n } catch (error) {\n // Only a real bind conflict is a port problem; anything else (certificate,\n // configuration, permissions) must not send the user chasing the port.\n const conflict = (error as NodeJS.ErrnoException | undefined)?.code === 'EADDRINUSE'\n this.publish({\n enabled: true,\n state: 'error',\n errorCode: conflict ? 'cloudflared_tunnel_port_unavailable' : 'gateway_start_failed',\n })\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed) {\n await gateway.close()\n return\n }\n this.gatewayValue = gateway\n }\n\n // The local DSH remote gateway speaks plain HTTP, so neither flavour needs an\n // origin TLS override. A named tunnel takes its token from the environment so\n // the credential never appears in the process command line.\n const args = named === undefined\n ? ['tunnel', '--url', `http://127.0.0.1:${String(reservation.port)}`, '--no-autoupdate']\n : ['tunnel', '--no-autoupdate', 'run']\n const environment = withoutProxyEnvironment(process.env)\n if (named !== undefined) environment.TUNNEL_TOKEN = named.token\n const child = (this.options.spawnProcess ?? spawnCloudflaredProcess)(\n this.options.executable,\n args,\n environment,\n )\n this.child = child\n child.stdout.setEncoding('utf8')\n child.stderr.setEncoding('utf8')\n child.stdout.on('data', chunk => { this.consume(generation, String(chunk)) })\n child.stderr.on('data', chunk => { this.consume(generation, String(chunk)) })\n child.once('error', () => { void this.enqueue(() => this.failGeneration(generation, 'cloudflared_launch_failed')) })\n child.once('close', code => {\n if (generation !== this.generation || this.child !== child) return\n this.child = undefined\n if (this.enabled) void this.enqueue(() => this.failGeneration(generation, code === 0 ? 'cloudflared_stopped' : 'cloudflared_exited'))\n })\n this.armStartupTimer(generation)\n }\n\n private armStartupTimer(generation: number): void {\n if (this.startupTimer !== undefined) clearTimeout(this.startupTimer)\n this.startupTimer = setTimeout(() => {\n void this.enqueue(() => this.checkStartupTimeout(generation))\n }, this.options.startupTimeoutMs ?? START_TIMEOUT_MS)\n this.startupTimer.unref()\n }\n\n /**\n * Give a live named connector more time instead of killing it: it retries the\n * edge on its own and usually registers once the rebooted network is usable.\n * A dead connector, or an exhausted budget, fails the generation as before.\n */\n private async checkStartupTimeout(generation: number): Promise<void> {\n if (generation !== this.generation || !this.enabled || this.disposed) return\n if (this.latest.state === 'ready') return\n const childAlive = this.child !== undefined && this.child.exitCode === null\n const budget = this.options.maxStartupRounds ?? NAMED_STARTUP_ROUNDS\n if (this.mode === 'named' && childAlive && this.startupRounds + 1 < budget) {\n this.startupRounds += 1\n this.armStartupTimer(generation)\n return\n }\n await this.failGeneration(generation, 'cloudflared_start_timeout')\n }\n\n private consume(generation: number, chunk: string): void {\n if (generation !== this.generation) return\n this.buffer += chunk\n if (Buffer.byteLength(this.buffer, 'utf8') > MAX_LOG_BUFFER_BYTES && !this.buffer.includes('\\n')) {\n void this.enqueue(() => this.failGeneration(generation, 'cloudflared_invalid_output'))\n return\n }\n while (true) {\n const newline = this.buffer.indexOf('\\n')\n if (newline < 0) return\n const line = this.buffer.slice(0, newline).replace(/\\r$/u, '')\n this.buffer = this.buffer.slice(newline + 1)\n if (this.mode === 'named') {\n // The public origin is configuration here, so the log is only read for the\n // registration that proves the connector reached Cloudflare.\n if (isCloudflaredRegistration(line)) void this.enqueue(() => this.confirmNamedReady(generation))\n continue\n }\n let origin: string | undefined\n try { origin = parseCloudflaredOrigin(line) } catch {\n void this.enqueue(() => this.failGeneration(generation, 'cloudflared_invalid_origin'))\n return\n }\n if (origin !== undefined) void this.enqueue(() => this.attachGateway(generation, origin))\n }\n }\n\n /** Mark a named tunnel ready once the connector has registered with the edge. */\n private async confirmNamedReady(generation: number): Promise<void> {\n if (generation !== this.generation || !this.enabled || this.disposed) return\n if (this.gatewayValue === undefined || this.latest.state === 'ready') return\n if (this.startupTimer !== undefined) clearTimeout(this.startupTimer)\n this.startupTimer = undefined\n this.publish({ enabled: true, state: 'ready', ...(this.namedOrigin === undefined ? {} : { origin: this.namedOrigin }) })\n }\n\n private async attachGateway(generation: number, origin: string): Promise<void> {\n if (generation !== this.generation || !this.enabled || this.disposed) return\n const current = this.gatewayValue\n if (current !== undefined) {\n if (current.address().origin === origin) return\n await this.rotateGateway(generation, origin, current)\n return\n }\n const reservation = this.reservation\n if (reservation === undefined) return\n this.publish({ enabled: true, state: 'connecting', origin })\n await reservation.release()\n if (this.reservation === reservation) this.reservation = undefined\n let gateway: MobileAccessGateway\n try { gateway = await this.options.createGateway(origin, reservation.port) } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed || this.child === undefined) {\n await gateway.close()\n return\n }\n this.gatewayValue = gateway\n if (this.startupTimer !== undefined) clearTimeout(this.startupTimer)\n this.startupTimer = undefined\n this.publish({ enabled: true, state: 'ready', origin })\n }\n\n /** Replace the gateway authority when a quick tunnel restarts on a new hostname. */\n private async rotateGateway(\n generation: number,\n origin: string,\n current: MobileAccessGateway,\n ): Promise<void> {\n const listenPort = current.address().port\n // A replacement must bind the same port cloudflared already forwards to.\n // Stop exposing the closing instance before releasing that port.\n if (this.gatewayValue === current) this.gatewayValue = undefined\n this.publish({ enabled: true, state: 'connecting', origin })\n try {\n await current.close()\n } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed || this.child === undefined) return\n\n let replacement: MobileAccessGateway\n try { replacement = await this.options.createGateway(origin, listenPort) } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed || this.child === undefined) {\n await replacement.close()\n return\n }\n this.gatewayValue = replacement\n this.publish({ enabled: true, state: 'ready', origin })\n }\n\n private async failGeneration(generation: number, code: string): Promise<void> {\n if (generation !== this.generation) return\n await this.stopProcessAndGateway()\n if (this.enabled) this.publish({ enabled: true, state: 'error', errorCode: code })\n }\n\n private async stop(): Promise<void> {\n ++this.generation\n await this.stopProcessAndGateway()\n }\n\n private async stopProcessAndGateway(): Promise<void> {\n if (this.startupTimer !== undefined) clearTimeout(this.startupTimer)\n this.startupTimer = undefined\n const reservation = this.reservation\n this.reservation = undefined\n const child = this.child\n this.child = undefined\n const gateway = this.gatewayValue\n this.gatewayValue = undefined\n await settleRemoteResources([\n () => reservation?.release(),\n () => child !== undefined && child.exitCode === null ? terminateRemoteProcess(child) : undefined,\n () => gateway?.close(),\n ], 'cloudflared resource cleanup failed')\n }\n}\n","import { randomBytes } from 'node:crypto'\nimport { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { isIP } from 'node:net'\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path'\nimport { restrictPrivateFile } from './private-file.js'\n\nconst MAX_CONFIG_BYTES = 8 * 1024\nconst QUICK_TUNNEL_SUFFIX = '.trycloudflare.com'\n/** Cloudflare's own tunnel-routing domain; it can never be a public hostname. */\nconst TUNNEL_ROUTING_SUFFIX = '.cfargotunnel.com'\n/**\n * The bare registrable names behind those suffixes. A suffix test alone accepts\n * them, yet Cloudflare owns both apexes outright, so neither can ever be routed to\n * a customer tunnel.\n */\nconst RESERVED_APEX_NAMES: readonly string[] = Object.freeze([\n QUICK_TUNNEL_SUFFIX.slice(1),\n TUNNEL_ROUTING_SUFFIX.slice(1),\n])\n/** Ports below this need privileges on every supported platform. */\nconst MIN_PORT = 1024\nconst MAX_PORT = 65_535\n/**\n * The LAN gateway's HTTPS port, held by the plugin for as long as DSH runs. It is\n * never available to a tunnel, so a configuration naming it must be refused up\n * front rather than failing later as a generic \"port in use\".\n */\nconst RESERVED_LAN_GATEWAY_PORT = 3443\nconst MAX_TOKEN_LENGTH = 4096\n\n/** Which tunnel flavour the cloudflared provider runs. */\nexport type CloudflaredTunnelMode = 'quick' | 'named'\n\n/**\n * Cloudflared provider configuration.\n *\n * A quick tunnel owns nothing durable: cloudflared allocates a random hostname\n * and a random forward port every start. A named tunnel instead runs with an\n * account token, and its public hostname is routed by Cloudflare to the local\n * port recorded here, so that port must stay stable across restarts.\n */\nexport type CloudflaredTunnelSettings =\n | { readonly version: 1; readonly mode: 'quick' }\n | {\n readonly version: 1\n readonly mode: 'named'\n readonly token: string\n readonly hostname: string\n readonly port: number\n }\n\n/** Configuration metadata safe to hand to the desktop panel; never the token. */\nexport interface CloudflaredTunnelStatus {\n readonly mode: CloudflaredTunnelMode\n readonly configured: boolean\n readonly hostname?: string\n readonly port?: number\n readonly storagePath: string\n readonly errorCode?: string\n}\n\nfunction hostname(value: string): boolean {\n if (value.length > 253 || !value.includes('.')) return false\n return value.split('.').every(label => label.length >= 1 && label.length <= 63\n && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label))\n}\n\n/**\n * Validate the public hostname Cloudflare routes to this tunnel.\n *\n * The name must be a real DNS name, not an IP literal or a wildcard, and it\n * cannot sit under Cloudflare's own control-plane suffixes: `trycloudflare.com`\n * belongs to quick tunnels, and a `cfargotunnel.com` name is the routing target\n * rather than a routable public address.\n */\nexport function validateCloudflaredTunnelHostname(value: unknown): string {\n if (typeof value !== 'string' || value !== value.trim() || value.length === 0 || value.length > 253) {\n throw new Error('cloudflared_tunnel_hostname_invalid')\n }\n const normalized = value.toLowerCase().replace(/\\.$/u, '')\n // An IPv4 literal is dot-separated digits and would otherwise satisfy the label\n // grammar, but Cloudflare never routes a tunnel hostname to an address.\n if (isIP(normalized) !== 0 || !hostname(normalized) || normalized.includes('*')\n || RESERVED_APEX_NAMES.includes(normalized)\n || normalized.endsWith(QUICK_TUNNEL_SUFFIX) || normalized.endsWith(TUNNEL_ROUTING_SUFFIX)) {\n throw new Error('cloudflared_tunnel_hostname_invalid')\n }\n return normalized\n}\n\n/** Validate the stable loopback port Cloudflare's ingress forwards to. */\nexport function validateCloudflaredTunnelPort(value: unknown): number {\n if (!Number.isSafeInteger(value) || Number(value) < MIN_PORT || Number(value) > MAX_PORT) {\n throw new Error('cloudflared_tunnel_port_invalid')\n }\n // The LAN gateway always owns 3443, so this is not a transient conflict and the\n // user must be told which port to avoid instead of \"the port is busy\".\n if (value === RESERVED_LAN_GATEWAY_PORT) throw new Error('cloudflared_tunnel_port_reserved')\n return Number(value)\n}\n\n/**\n * Validate a connector token before durable storage.\n *\n * A token is a base64url-encoded JSON blob that carries the account, tunnel id\n * and tunnel secret; the provider passes it through the environment rather than\n * the command line, and it is never returned to any client.\n */\nexport function validateCloudflaredTunnelToken(value: unknown): string {\n if (typeof value !== 'string' || value !== value.trim()\n || value.length < 32 || value.length > MAX_TOKEN_LENGTH\n || !/^[A-Za-z0-9_=+/-]+$/u.test(value)) {\n throw new Error('cloudflared_tunnel_token_invalid')\n }\n return value\n}\n\n/** Parse tunnel settings at the request and filesystem boundaries. */\nexport function parseCloudflaredTunnelSettings(value: unknown): CloudflaredTunnelSettings {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error('cloudflared_tunnel_settings_invalid')\n }\n const record = value as Record<string, unknown>\n const allowed = ['version', 'mode', 'token', 'hostname', 'port']\n if (Reflect.ownKeys(record).some(key => !allowed.includes(String(key)))) {\n throw new Error('cloudflared_tunnel_settings_invalid')\n }\n if (record.version !== undefined && record.version !== 1) {\n throw new Error('cloudflared_tunnel_settings_invalid')\n }\n const mode = record.mode ?? 'quick'\n if (mode === 'quick') {\n if (record.token !== undefined || record.hostname !== undefined || record.port !== undefined) {\n throw new Error('cloudflared_tunnel_settings_invalid')\n }\n return Object.freeze({ version: 1, mode: 'quick' })\n }\n if (mode !== 'named') throw new Error('cloudflared_tunnel_settings_invalid')\n return Object.freeze({\n version: 1,\n mode: 'named',\n token: validateCloudflaredTunnelToken(record.token),\n hostname: validateCloudflaredTunnelHostname(record.hostname),\n port: validateCloudflaredTunnelPort(record.port),\n })\n}\n\n/**\n * Merge a partial panel request with the saved named-tunnel settings so a blank\n * field keeps its stored value. The token in particular is write-only, so the\n * panel submits an empty string to mean \"keep the existing connector token\".\n */\nexport function mergeSavedCloudflaredTunnelSettings(\n partial: Readonly<Record<string, unknown>>,\n saved: CloudflaredTunnelSettings | undefined,\n): CloudflaredTunnelSettings {\n const requested = partial.mode ?? saved?.mode ?? 'quick'\n // The named branch below hardcodes its mode, so an unrecognized value would be\n // silently rewritten into a named tunnel. Validate here as strictly as the\n // parser does, rather than letting this layer launder it.\n if (requested !== 'quick' && requested !== 'named') {\n throw new Error('cloudflared_tunnel_settings_invalid')\n }\n if (requested === 'quick') return parseCloudflaredTunnelSettings({ version: 1, mode: 'quick' })\n const previous = saved?.mode === 'named' ? saved : undefined\n const token = partial.token === '' || partial.token === undefined ? previous?.token : partial.token\n const hostname = partial.hostname === '' || partial.hostname === undefined ? previous?.hostname : partial.hostname\n const port = partial.port === undefined || partial.port === '' ? previous?.port : partial.port\n if (token === undefined || hostname === undefined || port === undefined) {\n throw new Error('cloudflared_tunnel_config_missing')\n }\n return parseCloudflaredTunnelSettings({ version: 1, mode: 'named', token, hostname, port })\n}\n\nasync function atomicPrivateWrite(file: string, body: string): Promise<void> {\n const directory = dirname(file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(file)\n if (!current.isFile() || current.isSymbolicLink()) throw new Error('cloudflared_tunnel_target_invalid')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, `.${basename(file)}.${randomBytes(12).toString('hex')}.tmp`)\n try {\n await writeFile(temporary, body, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, file)\n await restrictPrivateFile(file)\n } catch (error) {\n await rm(temporary, { force: true })\n throw error\n }\n}\n\n/** Owns the private cloudflared tunnel configuration for one DSH installation. */\nexport class CloudflaredTunnelStore {\n readonly stateRoot: string\n readonly settingsFile: string\n private settingsValue: CloudflaredTunnelSettings = Object.freeze({ version: 1, mode: 'quick' })\n private errorCode: string | undefined\n\n constructor(stateDirectory: string) {\n if (!isAbsolute(stateDirectory)) throw new Error('cloudflared tunnel state directory must be absolute')\n this.stateRoot = resolve(stateDirectory)\n this.settingsFile = join(this.stateRoot, 'tunnel.json')\n }\n\n /** Load private settings while rejecting links, oversized files and unknown fields. */\n async initialize(): Promise<void> {\n let entry\n try { entry = await lstat(this.settingsFile) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\n throw error\n }\n if (!entry.isFile() || entry.isSymbolicLink() || entry.size > MAX_CONFIG_BYTES) {\n this.errorCode = 'cloudflared_tunnel_config_invalid'\n return\n }\n await restrictPrivateFile(this.settingsFile)\n try {\n this.settingsValue = parseCloudflaredTunnelSettings(JSON.parse(await readFile(this.settingsFile, 'utf8')) as unknown)\n this.errorCode = undefined\n } catch {\n this.settingsValue = Object.freeze({ version: 1, mode: 'quick' })\n this.errorCode = 'cloudflared_tunnel_config_invalid'\n }\n }\n\n /** Return configuration metadata without exposing the connector token. */\n status(): CloudflaredTunnelStatus {\n const settings = this.settingsValue\n return Object.freeze({\n mode: settings.mode,\n configured: settings.mode === 'named',\n ...(settings.mode === 'named' ? { hostname: settings.hostname, port: settings.port } : {}),\n storagePath: this.stateRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n })\n }\n\n /** Return private settings only to the provider lifecycle. */\n settings(): CloudflaredTunnelSettings {\n return this.settingsValue\n }\n\n /** Atomically replace the tunnel configuration. */\n async configure(value: unknown): Promise<CloudflaredTunnelStatus> {\n const settings = parseCloudflaredTunnelSettings(value)\n if (settings.mode === 'quick') await rm(this.settingsFile, { force: true })\n else await atomicPrivateWrite(this.settingsFile, `${JSON.stringify(settings)}\\n`)\n this.settingsValue = settings\n this.errorCode = undefined\n return this.status()\n }\n\n /** Forget a named tunnel and its connector token. */\n async purge(): Promise<CloudflaredTunnelStatus> {\n await rm(this.settingsFile, { force: true })\n this.settingsValue = Object.freeze({ version: 1, mode: 'quick' })\n this.errorCode = undefined\n return this.status()\n }\n}\n","/**\n * Host-side task-completion fan-out for phone notifications.\n *\n * The exact moment a run ends is known only on the Host (the phone page can\n * merely infer it from UI state), so this module watches the public\n * `session/event` bus for completed root turns and hands them to a hub that\n * fans out to every live mobile gateway. Phones render the text locally, so\n * no user content crosses this boundary — only opaque session and turn ids.\n */\n\n/** A completed root turn worth announcing on paired phones. */\nexport interface TaskCompletionEvent {\n readonly sessionId: string\n readonly turn: number\n}\n\n/** Minimal session shape read off the `session/event` bus. */\nexport interface TaskEventSession {\n readonly id: unknown\n readonly header?: { readonly parentSession?: unknown } | null | undefined\n}\n\n/**\n * Minimal turn event shape read off the `session/event` bus. Fields stay\n * unknown here and are validated at runtime below so this module never\n * depends on the harness session packages.\n */\nexport interface TaskTurnEvent {\n readonly type: string\n readonly data?: unknown\n}\n\n/** Structural slice of the Host context this module needs (keeps tests cordis-free). */\nexport interface TaskEventContext {\n on(event: 'session/event', handler: (session: TaskEventSession, event: TaskTurnEvent) => void): () => void\n}\n\nexport interface TaskEventWatcherOptions {\n /** Trailing debounce per session that merges turn-boundary bursts. */\n readonly debounceMs?: number\n readonly onTaskCompleted: (event: TaskCompletionEvent) => void\n readonly log?: (event: string, fields: Readonly<Record<string, string | number | boolean>>) => void\n}\n\nexport const TASK_EVENT_DEBOUNCE_MS = 1_000\n\nfunction turnNumber(value: unknown): number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : 0\n}\n\nfunction turnData(value: unknown): { turn: number; completed: boolean } {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return { turn: 0, completed: false }\n const record = value as Record<string, unknown>\n const reason = record.reason as { readonly kind?: unknown } | string | null | undefined\n const completed = typeof reason === 'string'\n ? reason === 'completed'\n : reason !== null && typeof reason === 'object' && (reason as { readonly kind?: unknown }).kind === 'completed'\n return { turn: turnNumber(record.turn), completed }\n}\n\n/**\n * Subscribe to completed root turns. Subagent turns are skipped so one task\n * announces once, and rapid turn boundaries collapse into a single event.\n * Returns a disposer that also drops pending debounces.\n */\nexport function watchTaskCompletions(ctx: TaskEventContext, options: TaskEventWatcherOptions): () => void {\n const debounceMs = options.debounceMs ?? TASK_EVENT_DEBOUNCE_MS\n const pending = new Map<string, ReturnType<typeof setTimeout>>()\n const disposeListener = ctx.on('session/event', (session, event) => {\n if (event.type !== 'turn/end') return\n const { turn, completed } = turnData(event.data)\n if (!completed) return\n if (session.header?.parentSession !== undefined && session.header?.parentSession !== null) return\n const sessionId = String(session.id)\n options.log?.('task-completed-observed', { sessionId, turn })\n const previous = pending.get(sessionId)\n if (previous !== undefined) clearTimeout(previous)\n pending.set(sessionId, setTimeout(() => {\n pending.delete(sessionId)\n options.log?.('task-completed-announced', { sessionId, turn })\n options.onTaskCompleted(Object.freeze({ sessionId, turn }))\n }, debounceMs))\n })\n return () => {\n disposeListener()\n for (const timer of pending.values()) clearTimeout(timer)\n pending.clear()\n }\n}\n\n/** Receives fanned-out completion events (normally a mobile gateway). */\nexport interface TaskEventSink {\n broadcastTaskEvent(event: TaskCompletionEvent): void\n}\n\n/** One subscription feeding every live gateway; gateways register on start. */\nexport class TaskEventHub {\n private readonly sinks = new Set<TaskEventSink>()\n\n /** Register a sink; returns its disposer. */\n add(sink: TaskEventSink): () => void {\n this.sinks.add(sink)\n return () => { this.sinks.delete(sink) }\n }\n\n /** Fan out to a snapshot so a failing sink cannot break its siblings. */\n broadcast(event: TaskCompletionEvent): void {\n for (const sink of [...this.sinks]) {\n try { sink.broadcastTaskEvent(event) } catch { /* A failing gateway must not break its siblings. */ }\n }\n }\n\n /** Visible for tests. */\n get size(): number {\n return this.sinks.size\n }\n}\n","import { execFileText as execFile } from './exec-file.js'\nimport { lookup } from 'node:dns/promises'\n\nimport type { RemoteProvider } from './remote.js'\nimport { DSH_MOBILE_VERSION, MINIMUM_ANDROID_APP_VERSION } from './version.js'\n\nexport type DiagnosticStatus = 'ok' | 'warning' | 'error' | 'info'\nexport type DiagnosticReason =\n | 'versions-current'\n | 'lan-setup-required' | 'network-unavailable' | 'network-interface' | 'network-fixed'\n | 'lan-ready' | 'lan-off'\n | 'firewall-ready' | 'firewall-missing' | 'firewall-unknown'\n | 'remote-off' | 'remote-ready' | 'remote-origin-ready' | 'remote-rate-limited' | 'remote-fake-ip' | 'remote-unreachable'\n | 'remote-needs-login' | 'remote-connecting' | 'remote-controller-error'\n | 'phone-network-unknown'\n\nexport interface DiagnosticFacts {\n readonly provider?: RemoteProvider\n readonly latencyMs?: number\n readonly interfaceName?: string\n readonly endpointSuffix?: string\n readonly controllerCode?: string\n}\n\n/** One user-facing diagnostic result with stable localization data and server fallback copy. */\nexport interface DiagnosticCheck {\n readonly id: string\n readonly status: DiagnosticStatus\n readonly reason: DiagnosticReason\n readonly facts?: DiagnosticFacts\n readonly label: string\n readonly detail: string\n readonly action?: string\n}\n\n/** Runtime facts available without exposing credentials or local file paths. */\nexport interface DiagnosticSnapshot {\n readonly dshVersion: string\n readonly lan: {\n readonly configured?: boolean\n readonly running: boolean\n readonly origin?: string\n readonly configuredInterface?: string\n readonly interfaceName?: string\n readonly networkError?: string\n readonly port?: number\n }\n readonly remote: {\n readonly provider: RemoteProvider\n readonly running: boolean\n readonly state: string\n readonly origin?: string\n readonly errorCode?: string\n }\n}\n\ninterface FirewallObservation {\n readonly state: 'ready' | 'missing' | 'unknown' | 'not-applicable'\n}\n\ninterface RemoteObservation {\n readonly state: 'ready' | 'rate-limited' | 'unreachable' | 'not-applicable'\n readonly latencyMs?: number\n readonly fakeIp?: boolean\n}\n\n/** Injectable probes keep diagnostics deterministic in tests. */\nexport interface DiagnosticProbes {\n readonly firewall?: (port: number | undefined) => Promise<FirewallObservation>\n readonly remote?: (origin: string | undefined) => Promise<RemoteObservation>\n}\n\n/** Sanitized diagnostic response copied by the desktop UI. */\nexport interface ConnectionDiagnostics {\n readonly version: 1\n readonly generatedAt: number\n readonly overall: 'ok' | 'attention' | 'error'\n readonly versions: {\n readonly plugin: string\n readonly dsh: string\n readonly minimumAndroidApp: string\n }\n readonly summary: string\n readonly checks: readonly DiagnosticCheck[]\n readonly report: string\n}\n\nconst REMOTE_ERROR_GUIDANCE: Readonly<Record<string, string>> = Object.freeze({\n component_missing: '重新安装完整插件包。',\n funnel_permission_required: '继续完成 Tailscale Funnel 授权。',\n funnel_https_required: '继续完成 Tailscale HTTPS 授权。',\n funnel_start_failed: '重新打开授权页并允许 Funnel。',\n funnel_start_timeout: '检查网络后点击“重新连接”。',\n tailscale_dns_missing: '确认 Tailscale 登录仍有效后重新连接。',\n sidecar_launch_failed: '重新安装完整插件包后重试。',\n sidecar_stopped: '点击“重新连接”。',\n sidecar_exited: '点击“重新连接”;仍失败时复制诊断报告。',\n control_channel_failed: '点击“重新连接”。',\n cpolar_component_missing: '先安装 cpolar 官方组件。',\n cpolar_component_invalid: '彻底移除 cpolar 组件后重新安装。',\n cpolar_config_missing: '保存 cpolar Authtoken 后重试。',\n cpolar_config_invalid: '重新保存 cpolar Authtoken。',\n cpolar_start_timeout: '检查网络后点击“重新连接”。',\n cpolar_stopped: '点击“重新连接”。',\n cpolar_exited: '点击“重新连接”;仍失败时复制诊断报告。',\n cloudflared_component_missing: '先安装 cloudflared 官方组件。',\n cloudflared_component_invalid: '彻底移除 cloudflared 组件后重新安装。',\n cloudflared_port_unavailable: '无法分配本机远程网关端口,请重试。',\n cloudflared_launch_failed: '重新安装 cloudflared 官方组件后重试。',\n cloudflared_start_timeout: '检查网络后点击“重新连接”。',\n cloudflared_stopped: '点击“重新连接”。',\n cloudflared_exited: '点击“重新连接”;仍失败时复制诊断报告。',\n cloudflared_invalid_output: 'cloudflared 返回了无法识别的状态。',\n cloudflared_invalid_origin: 'cloudflared 返回的公网地址未通过校验。',\n origin_config_missing: '先保存自有反向代理配置。',\n origin_config_invalid: '重新保存自有反向代理配置。',\n origin_listen_port_in_use: '更换 HTTP 后端端口;不要使用局域网的 3443、DSH 自身的 3080,或已被 cloudflared 隧道占用的 3444。',\n origin_listen_address_unavailable: '监听地址不属于当前电脑,请重新选择本机的私网 IPv4 地址。',\n origin_gateway_start_failed: '检查 HTTP 后端监听地址和端口后重新连接。',\n frp_component_missing: '先安装 FRP 官方组件。',\n frp_component_invalid: '彻底清理 FRP 组件后重新安装。',\n frp_config_missing: '先保存自建 FRP 连接配置。',\n frp_config_verify_failed: '检查服务器地址、端口、Token 和公开域名。',\n frp_vhost_publicly_reachable: '将 frps 的 HTTP vhost 监听限制到 127.0.0.1。',\n frp_vhost_probe_failed: '确认 VPS 地址可解析后重新连接。',\n frp_launch_failed: '重新安装 FRP 官方组件后重试。',\n frp_start_timeout: '确认 frps、Caddy 和域名解析正常后重新连接。',\n frp_discovery_mismatch: '公开域名连接到了另一台电脑,请核对 Caddy 与 frps 配置。',\n frp_discovery_invalid: '公开域名返回了非 DSH Mobile 响应。',\n frp_stopped: '点击“重新连接”。',\n frp_exited: '检查 VPS 配置后重新连接;仍失败时复制诊断报告。',\n gateway_start_failed: '确认 DSH 正在运行后重新连接。',\n})\n\nfunction check(\n id: string,\n status: DiagnosticStatus,\n reason: DiagnosticReason,\n label: string,\n detail: string,\n action?: string,\n facts?: DiagnosticFacts,\n): DiagnosticCheck {\n return Object.freeze({ id, status, reason, ...(facts === undefined ? {} : { facts: Object.freeze(facts) }), label, detail, ...(action === undefined ? {} : { action }) })\n}\n\nfunction maskLanOrigin(origin: string | undefined): string {\n if (origin === undefined) return '未分配'\n try {\n const url = new URL(origin)\n const octets = url.hostname.split('.')\n const host = octets.length === 4 ? `${octets[0]}.${octets[1]}.${octets[2]}.x` : '局域网地址'\n return `${url.protocol}//${host}${url.port === '' ? '' : `:${url.port}`}`\n } catch {\n return '地址格式无效'\n }\n}\n\nfunction remoteSuffix(origin: string | undefined): string {\n if (origin === undefined) return '未分配'\n try {\n const hostname = new URL(origin).hostname\n if (hostname.endsWith('.ts.net')) return '*.ts.net'\n if (hostname.endsWith('.trycloudflare.com')) return '*.trycloudflare.com'\n for (const suffix of ['.cpolar.cn', '.cpolar.io', '.cpolar.top', '.cpolar.com']) {\n if (hostname.endsWith(suffix)) return `*${suffix}`\n }\n return '公共 HTTPS 地址'\n } catch {\n return '地址格式无效'\n }\n}\n\nfunction defaultFirewallProbe(platform: NodeJS.Platform = process.platform): (port: number | undefined) => Promise<FirewallObservation> {\n return async (port) => {\n if (platform !== 'win32') return { state: 'not-applicable' }\n if (port === undefined) return { state: 'unknown' }\n const script = [\n \"$specs = @(@{ Name = 'DSH Mobile HTTPS'; Protocol = 'TCP' }, @{ Name = 'DSH Mobile Discovery'; Protocol = 'UDP' })\",\n '$ready = $true',\n '$specs | ForEach-Object {',\n ' $spec = $_',\n \" $rule = Get-NetFirewallRule -DisplayName $spec.Name -ErrorAction SilentlyContinue | Where-Object { $_.Enabled -eq 'True' -and $_.Direction -eq 'Inbound' -and $_.Action -eq 'Allow' } | Select-Object -First 1\",\n ' if ($null -eq $rule) { $ready = $false; return }',\n ' $filters = @($rule | Get-NetFirewallPortFilter -ErrorAction SilentlyContinue)',\n ` $matching = @($filters | Where-Object { $_.Protocol -eq $spec.Protocol -and ($_.LocalPort -eq 'Any' -or $_.LocalPort -eq '${String(port)}') })`,\n ' if ($matching.Count -eq 0) { $ready = $false }',\n '}',\n \"if ($ready) { 'ready' } else { 'missing' }\",\n ].join('; ')\n try {\n const result = await execFile('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], {\n encoding: 'utf8',\n timeout: 3_000,\n windowsHide: true,\n })\n return { state: result.stdout.trim() === 'ready' ? 'ready' : 'missing' }\n } catch {\n return { state: 'unknown' }\n }\n }\n}\n\n/** Allow remote relays enough time to answer without making diagnostics unbounded. */\nexport function remoteDiagnosticTimeoutMs(origin: string): number {\n const hostname = new URL(origin).hostname.toLowerCase()\n if (hostname.endsWith('.ts.net') || hostname.includes('.cpolar.')) return 10_000\n return 10_000\n}\n\nasync function defaultRemoteProbe(origin: string | undefined): Promise<RemoteObservation> {\n if (origin === undefined) return { state: 'not-applicable' }\n const hostname = new URL(origin).hostname\n const started = performance.now()\n try {\n const response = await fetch(new URL('/mobile-access/health', origin), {\n cache: 'no-store',\n redirect: 'error',\n signal: AbortSignal.timeout(remoteDiagnosticTimeoutMs(origin)),\n })\n const latencyMs = Math.max(0, Math.round(performance.now() - started))\n if (response.status === 429) return { state: 'rate-limited', latencyMs }\n return response.ok ? { state: 'ready', latencyMs } : { state: 'unreachable', latencyMs }\n } catch {\n let fakeIp = false\n try {\n const addresses = await lookup(hostname, { all: true })\n fakeIp = addresses.some(({ address }) => {\n const [first, second] = address.split('.').map(Number)\n return first === 198 && (second === 18 || second === 19)\n })\n } catch {\n // DNS lookup is supplementary; the failed HTTPS probe remains authoritative.\n }\n return { state: 'unreachable', ...(fakeIp ? { fakeIp: true } : {}) }\n }\n}\n\nfunction reportLine(entry: DiagnosticCheck): string {\n return `[${entry.status.toUpperCase()}] ${entry.label}: ${entry.detail}${entry.action === undefined ? '' : ` ${entry.action}`}`\n}\n\n/** Run bounded read-only checks and return a report safe to paste into an issue. */\nexport async function collectConnectionDiagnostics(\n snapshot: DiagnosticSnapshot,\n probes: DiagnosticProbes = {},\n): Promise<ConnectionDiagnostics> {\n const checks: DiagnosticCheck[] = []\n const remoteProbe = snapshot.remote.provider !== 'origin' && snapshot.remote.running && snapshot.remote.state === 'ready' && snapshot.remote.origin !== undefined\n ? (probes.remote ?? defaultRemoteProbe)(snapshot.remote.origin)\n : Promise.resolve<RemoteObservation>({ state: 'not-applicable' })\n const [firewall, remoteObservation] = await Promise.all([\n (probes.firewall ?? defaultFirewallProbe())(snapshot.lan.port),\n remoteProbe,\n ])\n checks.push(check(\n 'versions',\n 'ok',\n 'versions-current',\n '版本兼容',\n `插件 ${DSH_MOBILE_VERSION},DSH ${snapshot.dshVersion},Android App 最低 ${MINIMUM_ANDROID_APP_VERSION}。`,\n ))\n\n if (snapshot.lan.configured === false) {\n checks.push(check('network', 'error', 'lan-setup-required', '局域网配置', '尚未完成局域网初始化。', '返回局域网页选择网卡并完成配置。'))\n } else if (snapshot.lan.networkError !== undefined) {\n checks.push(check('network', 'error', 'network-unavailable', '局域网网卡', '已保存的网卡当前不可用。', '重新运行 dsh-mobile setup。'))\n } else if (snapshot.lan.configuredInterface !== undefined) {\n const interfaceName = snapshot.lan.interfaceName ?? snapshot.lan.configuredInterface\n checks.push(check(\n 'network',\n 'ok',\n 'network-interface',\n '局域网网卡',\n `正在跟随 ${interfaceName}。`,\n undefined,\n { interfaceName },\n ))\n } else {\n checks.push(check('network', 'info', 'network-fixed', '局域网网卡', '当前使用固定网络配置。'))\n }\n\n if (snapshot.lan.running && snapshot.lan.origin !== undefined) {\n const endpointSuffix = maskLanOrigin(snapshot.lan.origin)\n checks.push(check('lan', 'ok', 'lan-ready', '局域网网关', `已监听 ${endpointSuffix},配对入口可用。`, undefined, { endpointSuffix }))\n } else {\n checks.push(check('lan', 'info', 'lan-off', '局域网网关', '当前未开启。', '需要手机直连时开启局域网访问。'))\n }\n\n if (firewall.state === 'ready') {\n checks.push(check('firewall', 'ok', 'firewall-ready', 'Windows 防火墙', '局域网 TCP 与发现规则已启用。'))\n } else if (firewall.state === 'missing') {\n checks.push(check('firewall', 'warning', 'firewall-missing', 'Windows 防火墙', '未找到完整的局域网放行规则。', '以管理员身份重新运行 dsh-mobile setup。'))\n } else if (firewall.state === 'unknown') {\n checks.push(check('firewall', 'info', 'firewall-unknown', 'Windows 防火墙', '系统未允许插件读取防火墙状态。', '若手机找不到电脑,以管理员身份重新运行 setup。'))\n }\n\n if (!snapshot.remote.running || snapshot.remote.state === 'off') {\n checks.push(check('remote', 'info', 'remote-off', '远程通道', '当前未启用。', undefined, { provider: snapshot.remote.provider }))\n } else if (snapshot.remote.provider === 'origin' && snapshot.remote.state === 'ready') {\n checks.push(check('remote', 'info', 'remote-origin-ready', '远程通道',\n '私网 HTTP 后端已就绪;未检测公网 HTTPS、证书或 WebSocket 连通性。',\n '在手机上经反向代理验证连接;代理须保留原始 Host(含端口)并支持 WebSocket。',\n { provider: 'origin' }))\n } else if (snapshot.remote.state === 'ready' && snapshot.remote.origin !== undefined) {\n const endpointSuffix = remoteSuffix(snapshot.remote.origin)\n const facts = { provider: snapshot.remote.provider, endpointSuffix, ...(remoteObservation.latencyMs === undefined ? {} : { latencyMs: remoteObservation.latencyMs }) }\n if (remoteObservation.state === 'ready') {\n checks.push(check('remote', 'ok', 'remote-ready', '远程通道', `${snapshot.remote.provider} 公共地址 ${endpointSuffix} 可达,往返约 ${String(remoteObservation.latencyMs ?? 0)} ms。`, undefined, facts))\n } else if (remoteObservation.state === 'rate-limited') {\n checks.push(check('remote', 'warning', 'remote-rate-limited', '远程通道', '公共地址可达,但本次检查观察到服务限流。', '稍后重试;旧会话会按需加载以减少流量。', facts))\n } else if (snapshot.remote.provider === 'tailscale' && remoteObservation.fakeIp === true) {\n checks.push(check(\n 'remote',\n 'error',\n 'remote-fake-ip',\n '远程通道',\n 'Tailscale 地址被当前 VPN 或 DNS 代理接管,但 TLS 链路未建立。',\n '切换 VPN 节点或代理模式;仍失败时改用 cpolar。',\n facts,\n ))\n } else {\n checks.push(check('remote', 'error', 'remote-unreachable', '远程通道', '提供方显示已就绪,但公共地址暂不可达。', '点击“重新连接”;仍失败时检查提供方状态。', facts))\n }\n } else if (snapshot.remote.state === 'starting' || snapshot.remote.state === 'connecting' || snapshot.remote.state === 'needs-login') {\n const needsLogin = snapshot.remote.state === 'needs-login'\n checks.push(check(\n 'remote',\n 'warning',\n needsLogin ? 'remote-needs-login' : 'remote-connecting',\n '远程通道',\n needsLogin ? '等待完成 Tailscale 登录。' : '仍在建立连接。',\n needsLogin ? '返回远程页继续登录。' : '等待片刻后重新检查。',\n { provider: snapshot.remote.provider },\n ))\n } else {\n const controllerCode = snapshot.remote.errorCode ?? snapshot.remote.state\n checks.push(check(\n 'remote',\n 'error',\n 'remote-controller-error',\n '远程通道',\n `连接未建立(${controllerCode})。`,\n REMOTE_ERROR_GUIDANCE[controllerCode] ?? '返回远程页点击“重新连接”。',\n { provider: snapshot.remote.provider, controllerCode },\n ))\n }\n\n checks.push(check(\n 'phone-network',\n 'info',\n 'phone-network-unknown',\n '手机网络',\n '电脑无法判断路由器是否隔离了手机。',\n '局域网仍失败时,确认手机与电脑在同一网络,并关闭访客网络或 AP 隔离。',\n ))\n\n const overall = checks.some(entry => entry.status === 'error')\n ? 'error'\n : checks.some(entry => entry.status === 'warning') ? 'attention' : 'ok'\n const summary = overall === 'ok' ? '连接基础检查正常。' : overall === 'attention' ? '发现需要留意的项目。' : '发现会影响连接的问题。'\n const report = [\n 'DSH Mobile 诊断报告',\n `生成时间: ${new Date().toISOString()}`,\n `版本: plugin=${DSH_MOBILE_VERSION}; dsh=${snapshot.dshVersion}; min-app=${MINIMUM_ANDROID_APP_VERSION}`,\n `LAN: ${snapshot.lan.running ? 'on' : 'off'}; endpoint=${maskLanOrigin(snapshot.lan.origin)}`,\n `Remote: provider=${snapshot.remote.provider}; state=${snapshot.remote.state}; endpoint=${remoteSuffix(snapshot.remote.origin)}`,\n ...checks.map(reportLine),\n ].join('\\n')\n return Object.freeze({\n version: 1,\n generatedAt: Date.now(),\n overall,\n versions: Object.freeze({ plugin: DSH_MOBILE_VERSION, dsh: snapshot.dshVersion, minimumAndroidApp: MINIMUM_ANDROID_APP_VERSION }),\n summary,\n checks: Object.freeze(checks),\n report,\n })\n}\n","/**\n * Instructions handed to the DSH agent when the user runs `/mobile <task>`.\n * The agent edits files under the DSH home; this text is what tells it the\n * layout of the mobile-access customization surface so it does not guess.\n *\n * A snapshot of the current customization state (whether mobile.css /\n * mobile.js exist and which extensions are installed) is injected at the\n * top of the guide so the agent does not overwrite earlier work blindly.\n */\n\n/** One installed extension as seen by the agent. */\nexport interface MobileGuideExtensionState {\n readonly id: string\n readonly name: string\n readonly version: string\n}\n\n/** Customization facts collected before steering the agent. */\nexport interface MobileGuideState {\n /** Absolute path of the mobile-access directory the agent edits. */\n readonly directory: string\n /** True when the user's mobile.css exists (a custom style override). */\n readonly hasCustomCss: boolean\n /** True when the user's mobile.js exists (a custom script override). */\n readonly hasCustomJs: boolean\n /** Extensions currently installed under extensions/. */\n readonly extensions: readonly MobileGuideExtensionState[]\n /** Extensions whose host failed to activate. */\n readonly failedExtensionCount: number\n}\n\n/** Compose the guide with the current customization state injected. */\nexport function buildMobileGuide(state: MobileGuideState): string {\n const styleLine = state.hasCustomCss ? '存在(当前生效的自定义样式)' : '不存在(使用内置默认样式)'\n const scriptLine = state.hasCustomJs ? '存在(当前生效的自定义脚本)' : '不存在(无自定义脚本)'\n const extensionLines = state.extensions.length === 0\n ? '(无)'\n : state.extensions.map(entry => `- ${entry.id}(${entry.name} v${entry.version})`).join('\\n')\n const failureLine = state.failedExtensionCount > 0\n ? `注意:${state.failedExtensionCount} 个扩展的电脑端 host 激活失败,如改动相关扩展请先检查其 host.mjs 与 extension.json。`\n : ''\n const currentState = `## 手机端当前状态(改名前必读,避免覆盖已有定制)\n\n- 定制目录:${state.directory}(所有改动只允许在这里进行)\n- mobile.css:${styleLine}\n- mobile.js:${scriptLine}\n- 已安装扩展:\n${extensionLines}\n${failureLine}\n\n“恢复默认”操作说明:当用户要求恢复默认 / 还原初始外观时,删除 mobile.css 与 mobile.js 两个文件(删除后手机端自动回到内置默认外观,无需创建占位文件),并按需删除 extensions/ 下的扩展目录。\\n\\n`\n\n return `${currentState}${MOBILE_CUSTOMIZATION_GUIDE_BODY}`\n}\n\n/**\n * Static body of the customization guide. Kept separate from the injected\n * state snapshot so the two concerns stay easy to edit independently.\n */\nconst MOBILE_CUSTOMIZATION_GUIDE_BODY = `你在为用户定制 DSH Mobile 的手机端。DSH Mobile 是一个把电脑上的 DeepSeek Harness 带到手机浏览器的插件,手机端界面和能力都来自本机文件。\n\n所有改动只允许在 $DSH_HOME/mobile-access/ 目录内进行,绝不修改 DeepSeek Harness 的源码或其他目录。$DSH_HOME 是 DeepSeek Harness 的配置目录(通常为 ~/.dsh),先确认它的实际路径再操作。\n\n手机端的能力分两层,按用户需求选择改动目标:\n\n1. 界面与交互 —— 只改外观和交互,不需要碰电脑的文件或程序:\n - $DSH_HOME/mobile-access/mobile.css:手机端样式\n - $DSH_HOME/mobile-access/mobile.js:手机端脚本,用 window.dshMobile.register(({ root }) => { ... }) 把内容挂载到 root,返回清理函数\n - 保存后手机端几秒内自动应用,无需重启\n\n2. 电脑端能力 —— 手机需要读电脑文件、执行命令或访问硬件时,创建扩展:\n - 目录:$DSH_HOME/mobile-access/extensions/<id>/,id 用小写字母数字和连字符(如 media-remote)\n - extension.json:{\"schemaVersion\":1,\"id\":\"<id>\",\"name\":\"显示名\",\"version\":\"0.1.0\",\"description\":\"说明\"}\n - host.mjs:电脑端 Node.js 代码(可信本地代码,可读写文件、执行命令)。导出默认函数 (api) => { ... },用 api.action('名称', { input, run }) 注册动作、api.route({ method, path, handle }) 注册路由、api.effect(fn) 注册清理。input 可以直接使用 api.schema.object(...) 等 Schemastery schema,也可以传入带 parse(value) 的适配器;两种形式都会在动作执行前校验并规范化输入。\n - mobile.js:手机端脚本,用 window.dshMobile.define({ apiVersion:1, id:'<id>', activate(api) { ... } }),activate 返回清理函数\n - mobile.css:手机端样式(可选)\n - assets/:手机端静态资源(可选)\n - mobile.js 里用 api.host.invoke('动作名', 输入) 调 host.mjs 的 action(请求会按 application/json 发送),api.host.fetch('/路由路径') 调 route,api.host.assetUrl('相对路径') 生成与当前版本绑定的资源地址\n - 也可以先用命令生成模板:dsh plugin --profile web exec dsh-mobile extension create <id> --name \"<名称>\",再在模板上改\n\n安全约束:\n- host.mjs 拥有电脑用户的完整权限,绝不能放入不可信代码,也不要让手机端无条件执行任意命令\n- 所有改动只限 $DSH_HOME/mobile-access/,不要动 DeepSeek Harness 源码\n\n完成前请自检:\n- 改动涉及 mobile.js 或扩展的 mobile.js / host.mjs 时,先做语法检查再保存(如 node --check <file>),确保没有语法错误\n- 创建或修改扩展后,确认 extension.json 的 schemaVersion 为 1、id 与目录名一致、且 id 只含小写字母数字和连字符\n- 扩展的 host.mjs 若在完成前无法激活,先修正而不是留下损坏的扩展\n- 完成后检查自己实际写入了哪些文件,向用户简要说明改了什么、手机端会有什么变化`\n","import { createHash, randomBytes } from 'node:crypto'\nimport { spawn } from 'node:child_process'\nimport { lstat, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'\nimport { isIP } from 'node:net'\nimport { tmpdir } from 'node:os'\nimport { isAbsolute, join, resolve } from 'node:path'\nimport { validateFrpPublicOrigin, validateFrpServerAddress, validateFrpServerPort, validateFrpToken, type FrpSettings } from './frp-config.js'\nimport {\n FRP_CADDY_IMPORT_LINE,\n FRP_CADDY_SNIPPET_MARKER,\n FRP_CADDY_SNIPPET_PATH,\n createCaddySite,\n} from './frp-template.js'\nimport { isGloballyRoutableIpv4 } from './network.js'\n\nconst FRP_VERSION = '0.70.1'\nconst SSH_TIMEOUT_MS = 300_000\nconst MAX_OUTPUT_BYTES = 96 * 1024\n\nconst LINUX_ARTIFACTS = Object.freeze({\n x64: Object.freeze({\n directory: `frp_${FRP_VERSION}_linux_amd64`,\n url: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_amd64.tar.gz`,\n sha256: '333da23d1b9009d7c01638e9ba38cf4600f7d37d393f854e96ee1396adefa9a6',\n }),\n arm64: Object.freeze({\n directory: `frp_${FRP_VERSION}_linux_arm64`,\n url: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_arm64.tar.gz`,\n sha256: '3990f396a9a490ee7f0e5f355287750ed41520064ed999eab443b5e9a78d773d',\n }),\n})\n\nexport interface VpsDeploymentInput {\n readonly sshUser: string\n readonly sshPort: number\n readonly sshKeyPath?: string\n /**\n * User-confirmed SHA256 host-key fingerprints (`SHA256:…`, one per server key).\n * The deployment aborts unless every key the server currently presents is confirmed.\n */\n readonly hostFingerprints: readonly string[]\n}\n\n/** One SSH server host key with its OpenSSH-style SHA256 fingerprint. */\nexport interface VpsHostKey {\n readonly keyType: string\n readonly fingerprint: string\n}\n\nexport interface VpsDeploymentCheck {\n readonly id: string\n readonly status: 'ok' | 'warning' | 'error'\n readonly detail: string\n}\n\nexport interface VpsDeploymentResult {\n readonly version: 1\n readonly deployed: boolean\n readonly serverAddress: string\n readonly publicOrigin: string\n readonly checks: readonly VpsDeploymentCheck[]\n}\n\nexport interface VpsDeploymentOptions {\n readonly runSsh?: (input: VpsDeploymentInput, serverAddress: string, script: string) => Promise<{ stdout: string; stderr: string }>\n readonly runKeyscan?: (input: { readonly sshUser: unknown; readonly sshPort: unknown }, serverAddress: string) => Promise<string>\n readonly runSshFetch?: (input: VpsSshFetchInput, serverAddress: string) => Promise<string>\n readonly runRemoteScript?: (input: VpsDeploymentInput, serverAddress: string, script: string) => Promise<{ stdout: string; stderr: string }>\n readonly log?: (event: string, fields: Readonly<Record<string, string | number | boolean>>) => void\n}\n\nexport class VpsSshError extends Error {\n constructor(message: string, readonly stdout: string, readonly stderr: string, options?: ErrorOptions) {\n super(message, options)\n }\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replaceAll(\"'\", \"'\\\\''\")}'`\n}\n\n/**\n * Reject loopback, private, and other non-routable IPv4 literals as VPS SSH\n * targets. A self-hosted deployment always addresses a public server; the\n * shared frpc settings stay permissive so local loopback test rigs keep working.\n */\nfunction assertPublicSshTarget(serverAddress: string): void {\n if (isIP(serverAddress) === 4 && !isGloballyRoutableIpv4(serverAddress)) {\n throw new Error('vps_server_not_public')\n }\n}\n\n/**\n * Validate a VPS address for every operation that opens a network connection\n * to it (scan, deploy, cleanup). IPv6 is unsupported by the SSH flow and\n * loopback/private targets are never valid VPS endpoints.\n */\nfunction validateVpsServerTarget(serverAddress: string): string {\n const address = validateFrpServerAddress(serverAddress)\n if (isIP(address) !== 0 && address.includes(':')) throw new Error('vps_ipv6_ssh_not_supported')\n assertPublicSshTarget(address)\n return address\n}\n\nfunction validSshUser(value: unknown): string {\n if (typeof value !== 'string' || value.length < 1 || value.length > 64 || !/^[a-z_][a-z0-9_.-]*[$]?$/iu.test(value)) {\n throw new Error('vps_ssh_user_invalid')\n }\n return value\n}\n\nfunction validSshPort(value: unknown): number {\n if (!Number.isSafeInteger(value) || Number(value) < 1 || Number(value) > 65_535) throw new Error('vps_ssh_port_invalid')\n return Number(value)\n}\n\nfunction validSshKeyPath(value: unknown): string | undefined {\n if (value === undefined || value === '') return undefined\n if (typeof value !== 'string' || !isAbsolute(value) || value.length > 4_096 || /[\\u0000-\\u001f\\u007f]/u.test(value)) {\n throw new Error('vps_ssh_key_invalid')\n }\n return resolve(value)\n}\n\nexport function parseVpsDeploymentInput(value: unknown): VpsDeploymentInput {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('vps_deploy_input_invalid')\n const record = value as Record<string, unknown>\n if (Reflect.ownKeys(record).some(key => !['sshUser', 'sshPort', 'sshKeyPath', 'hostFingerprints'].includes(String(key)))) {\n throw new Error('vps_deploy_input_invalid')\n }\n const sshKeyPath = validSshKeyPath(record.sshKeyPath)\n return Object.freeze({\n sshUser: validSshUser(record.sshUser),\n sshPort: validSshPort(record.sshPort),\n ...(sshKeyPath === undefined ? {} : { sshKeyPath }),\n hostFingerprints: Object.freeze(parseVpsHostFingerprints(record.hostFingerprints)),\n })\n}\n\n/** Validate user-confirmed SHA256 host-key fingerprints (`SHA256:…`). */\nexport function parseVpsHostFingerprints(value: unknown): string[] {\n if (!Array.isArray(value) || value.length < 1 || value.length > 8) throw new Error('vps_host_key_unconfirmed')\n const fingerprints: string[] = []\n for (const entry of value) {\n if (typeof entry !== 'string' || !/^SHA256:[A-Za-z0-9+/]{40,60}={0,2}$/u.test(entry) || entry.length > 96) {\n throw new Error('vps_host_key_unconfirmed')\n }\n fingerprints.push(entry)\n }\n return [...new Set(fingerprints)]\n}\n\nconst SUPPORTED_HOST_KEY_TYPES = new Set([\n 'ssh-rsa',\n 'ecdsa-sha2-nistp256',\n 'ecdsa-sha2-nistp384',\n 'ecdsa-sha2-nistp521',\n 'ssh-ed25519',\n])\n\n/** Format a raw host public key the way OpenSSH displays it (`SHA256:…` without padding). */\nexport function fingerprintHostPublicKey(keyType: string, base64Key: string): string {\n if (!SUPPORTED_HOST_KEY_TYPES.has(keyType)) throw new Error('vps_host_key_unsupported_type')\n if (!/^[A-Za-z0-9+/]+={0,2}$/u.test(base64Key) || base64Key.length < 24 || base64Key.length > 1_024) {\n throw new Error('vps_host_key_invalid')\n }\n const raw = Buffer.from(base64Key, 'base64')\n if (raw.length < 16 || raw.length > 768) throw new Error('vps_host_key_invalid')\n return `SHA256:${createHash('sha256').update(raw).digest('base64').replace(/=+$/u, '')}`\n}\n\nfunction parseKeyscanOutput(output: string): Array<{ keyType: string; base64Key: string }> {\n const keys: Array<{ keyType: string; base64Key: string }> = []\n for (const line of output.split(/\\r?\\n/u)) {\n const trimmed = line.trim()\n if (trimmed === '' || trimmed.startsWith('#')) continue\n // The host field is optional: ssh-cat fallback lines carry only type and key.\n // It is discarded anyway; pinned known_hosts lines are rebuilt from the\n // validated server address, so accepting host-less lines is safe.\n const match = /^(?:\\S+\\s+)?(ssh-rsa|ecdsa-sha2-nistp\\d+|ssh-ed25519)\\s+([A-Za-z0-9+/]+={0,2})(\\s|$)/u.exec(trimmed)\n if (match?.[1] === undefined || match[2] === undefined) throw new Error('vps_host_key_invalid')\n keys.push({ keyType: match[1], base64Key: match[2] })\n }\n return keys\n}\n\n/** Base SSH options shared by long deployment sessions. Keepalives survive NAT\n * middleboxes during minute-long apt/pip phases; host identity stays pinned. */\nfunction sshSessionOptions(knownHostsFile: string): string[] {\n return [\n '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=15',\n '-o', 'ServerAliveInterval=15', '-o', 'ServerAliveCountMax=8',\n '-o', 'StrictHostKeyChecking=yes', `-o UserKnownHostsFile=${knownHostsFile}`,\n ]\n}\n\n/** Lenient scan probe: garbage fails the whole fetch, comment-only output falls back. */\nfunction tryParseKeyscanOutput(output: string): Array<{ keyType: string; base64Key: string }> | undefined {\n try {\n return parseKeyscanOutput(output)\n } catch {\n return undefined\n }\n}\n\nasync function gitBundledKeyscan(): Promise<string | undefined> {\n if (process.platform !== 'win32') return undefined\n const programFiles = process.env['ProgramFiles'] ?? 'C:\\\\Program Files'\n const candidate = join(programFiles, 'Git', 'usr', 'bin', 'ssh-keyscan.exe')\n try {\n const entry = await lstat(candidate)\n if (!entry.isFile()) return undefined\n } catch {\n return undefined\n }\n return candidate\n}\n\nasync function defaultRunKeyscan(input: { readonly sshUser: unknown; readonly sshPort: unknown }, serverAddress: string): Promise<string> {\n const keyscan = process.platform === 'win32' ? 'ssh-keyscan.exe' : 'ssh-keyscan'\n const args = ['-T', '10', '-p', String(input.sshPort), '-t', 'rsa,ecdsa,ed25519', serverAddress]\n const first = await runProcess(keyscan, args, undefined, 30_000).catch(() => undefined)\n if (first !== undefined && tryParseKeyscanOutput(first.stdout)?.length) return first.stdout\n // Old keyscan binaries fail KEX against modern servers (non-zero exit, no\n // keys); retry with the Git for Windows copy when present before giving up.\n // An empty result lets the caller fall back to an authenticated read.\n const bundled = await gitBundledKeyscan()\n if (bundled !== undefined) {\n const second = await runProcess(bundled, args, undefined, 30_000).catch(() => undefined)\n if (second !== undefined && tryParseKeyscanOutput(second.stdout)?.length) return second.stdout\n }\n return first?.stdout ?? ''\n}\n\nexport interface VpsSshFetchInput {\n readonly sshUser: unknown\n readonly sshPort: unknown\n readonly sshKeyPath?: unknown\n}\n\n/**\n * Read the server's public host keys over an authenticated connection with a\n * throwaway known_hosts file. Fallback for keyscan binaries that cannot\n * negotiate with modern servers; output feeds the same confirm-and-pin pipeline.\n */\nasync function defaultRunSshFetch(input: VpsSshFetchInput, serverAddress: string): Promise<string> {\n const ssh = process.platform === 'win32' ? 'ssh.exe' : 'ssh'\n const sshUser = validSshUser(input.sshUser)\n const sshPort = validSshPort(input.sshPort)\n const sshKeyPath = validSshKeyPath(input.sshKeyPath)\n const nullDevice = process.platform === 'win32' ? 'NUL' : '/dev/null'\n const args = [\n '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=15',\n '-o', 'StrictHostKeyChecking=no', `-o UserKnownHostsFile=${nullDevice}`,\n ...(sshKeyPath === undefined ? [] : ['-i', sshKeyPath]),\n '-p', String(sshPort), `${sshUser}@${serverAddress}`, 'cat /etc/ssh/ssh_host_*_key.pub',\n ]\n const { stdout } = await runProcess(ssh, args, undefined, 30_000).catch((error: unknown) => {\n throw new VpsSshError('vps_host_key_unavailable', '', error instanceof Error ? error.message : String(error))\n })\n const lines: string[] = []\n for (const line of stdout.split(/\\r?\\n/u)) {\n const match = /^(ssh-rsa|ecdsa-sha2-nistp\\d+|ssh-ed25519)\\s+([A-Za-z0-9+/]+={0,2})(\\s|$)/u.exec(line.trim())\n if (match?.[1] !== undefined && match[2] !== undefined) lines.push(`${serverAddress} ${match[1]} ${match[2]}`)\n }\n return lines.length === 0 ? stdout : `${lines.join('\\n')}\\n`\n}\n\n/**\n * Scan host keys with keyscan first, then fall back to an authenticated read\n * when the local keyscan binary cannot negotiate with the server. Both paths\n * feed the same confirm-and-pin pipeline, so a fallback never weakens the\n * user-confirmation gate.\n */\nasync function scanHostKeys(\n input: VpsSshFetchInput,\n serverAddress: string,\n options: VpsDeploymentOptions,\n): Promise<string> {\n const runKeyscan = options.runKeyscan ?? defaultRunKeyscan\n // A rejecting keyscan (old binary failing KEX, missing binary, DNS error)\n // is equivalent to an empty scan: fall through to the authenticated read.\n const scanned = await runKeyscan({ sshUser: input.sshUser, sshPort: input.sshPort }, serverAddress).catch(() => '')\n if (tryParseKeyscanOutput(scanned)?.length) return scanned\n options.log?.('host-keys-keyscan-empty', { serverAddress })\n const runSshFetch = options.runSshFetch ?? defaultRunSshFetch\n const fetched = await runSshFetch(input, serverAddress)\n if (tryParseKeyscanOutput(fetched)?.length) {\n options.log?.('host-keys-ssh-fallback', { serverAddress })\n return fetched\n }\n throw new Error('vps_host_key_unavailable')\n}\n\n/**\n * Fetch the server's current host keys and return them with OpenSSH-style\n * fingerprints for the user to confirm out of band (for example against the\n * VPS console) before any destructive or authenticated deployment step.\n */\nexport async function fetchVpsHostKeys(\n serverAddress: string,\n input: VpsSshFetchInput,\n options: VpsDeploymentOptions = {},\n): Promise<readonly VpsHostKey[]> {\n const address = validateVpsServerTarget(serverAddress)\n const sshUser = validSshUser(input.sshUser)\n const sshPort = validSshPort(input.sshPort)\n const output = await scanHostKeys({ sshUser, sshPort, sshKeyPath: input.sshKeyPath }, address, options)\n const keys = parseKeyscanOutput(output)\n if (keys.length === 0) throw new Error('vps_host_key_unavailable')\n const seen = new Set<string>()\n const hostKeys: VpsHostKey[] = []\n for (const key of keys) {\n const fingerprint = fingerprintHostPublicKey(key.keyType, key.base64Key)\n if (seen.has(fingerprint)) continue\n seen.add(fingerprint)\n hostKeys.push(Object.freeze({ keyType: key.keyType, fingerprint }))\n }\n options.log?.('host-keys-fetched', { serverAddress: address, keyTypes: hostKeys.length })\n return Object.freeze(hostKeys)\n}\n\n/**\n * Verify that every host key the server currently presents was confirmed by the\n * user, then return a pinned known_hosts body. Fails closed on rotation,\n * replacement, or unexpected extra keys.\n */\nexport function buildPinnedKnownHosts(\n serverAddress: string,\n sshPort: number,\n keyscanOutput: string,\n confirmedFingerprints: readonly string[],\n): string {\n const address = validateFrpServerAddress(serverAddress)\n const port = validSshPort(sshPort)\n const confirmed = new Set(parseVpsHostFingerprints([...confirmedFingerprints]))\n const keys = parseKeyscanOutput(keyscanOutput)\n if (keys.length === 0) throw new Error('vps_host_key_unavailable')\n const host = port === 22 ? address : `[${address}]:${port}`\n const lines: string[] = []\n for (const key of keys) {\n const fingerprint = fingerprintHostPublicKey(key.keyType, key.base64Key)\n if (!confirmed.has(fingerprint)) throw new Error('vps_host_key_mismatch')\n lines.push(`${host} ${key.keyType} ${key.base64Key}`)\n }\n return `${lines.join('\\n')}\\n`\n}\n\nfunction safeOutput(value: string, token: string): string {\n const redacted = token === '' ? value : value.replaceAll(token, '<redacted>')\n return redacted.replace(/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f]/gu, '').slice(0, 8_192).trim()\n}\n\nfunction parseChecks(stdout: string, stderr: string, token: string): readonly VpsDeploymentCheck[] {\n const checks: VpsDeploymentCheck[] = []\n for (const line of stdout.split(/\\r?\\n/u)) {\n const match = /^DSH_MOBILE_CHECK\\s+([a-z0-9_-]+)\\s+(ok|warning|error)\\s+(.+)$/iu.exec(line)\n if (match !== null) checks.push(Object.freeze({ id: match[1]!, status: match[2]!.toLowerCase() as VpsDeploymentCheck['status'], detail: safeOutput(match[3]!, token) }))\n }\n if (checks.length === 0 && stderr.trim() !== '') {\n checks.push(Object.freeze({ id: 'remote-command', status: 'error', detail: safeOutput(stderr, token) || 'VPS 返回了未分类错误。' }))\n }\n return Object.freeze(checks)\n}\n\n/**\n * One-line failure detail for transport-level failures: prefer the failed\n * remote check, otherwise use the last stderr line (a `set -eu` abort has no\n * check line) instead of dumping the whole transcript into the UI.\n */\nfunction failureDetail(stdout: string, stderr: string, token: string): string {\n const parsed = parseChecks(`${stdout}\\n${stderr}`, '', token)\n const failed = parsed.find(check => check.status === 'error')\n if (failed !== undefined) return failed.detail\n for (const stream of [stderr, stdout]) {\n const lines = stream.split(/\\r?\\n/u).map(line => line.trim()).filter(line => line !== '')\n const last = lines[lines.length - 1]\n if (last !== undefined) return safeOutput(last, token)\n }\n return ''\n}\n\nfunction deploymentScript(settings: FrpSettings): string {\n const amd64 = LINUX_ARTIFACTS.x64\n const arm64 = LINUX_ARTIFACTS.arm64\n const config = [\n 'bindAddr = \"0.0.0.0\"',\n `bindPort = ${String(settings.serverPort)}`,\n 'proxyBindAddr = \"127.0.0.1\"',\n 'vhostHTTPPort = 7080',\n 'auth.method = \"token\"',\n `auth.token = ${JSON.stringify(settings.token)}`,\n '',\n ].join('\\n')\n const publicHost = new URL(settings.publicOrigin).hostname\n const publicIp = isGloballyRoutableIpv4(publicHost)\n // The site content is shared with the manual template; only the wiring\n // (snippet file + one import line) differs.\n const caddySite = createCaddySite(publicHost)\n const caddySnippet = `${FRP_CADDY_SNIPPET_MARKER}\\n${caddySite.trimEnd()}\\n`\n const ipCertificateSetup = publicIp ? `\nexport DEBIAN_FRONTEND=noninteractive\napt-get install -y python3-venv\nif [ ! -x /opt/dsh-mobile/certbot-venv/bin/certbot ]; then\n python3 -m venv /opt/dsh-mobile/certbot-venv\n /opt/dsh-mobile/certbot-venv/bin/pip install --disable-pip-version-check 'certbot==5.8.0'\nfi\nsystemctl stop caddy.service || true\nif ! /opt/dsh-mobile/certbot-venv/bin/certbot certonly --standalone --preferred-profile shortlived --ip-address ${publicHost} --agree-tos --register-unsafely-without-email --non-interactive --keep-until-expiring; then\n systemctl start caddy.service || true\n fail \"公网 IP HTTPS 证书申请失败;请确认 80/tcp 可从公网访问。\"\nfi\ninstall -d -m 0750 -o caddy -g caddy /var/lib/caddy/dsh-mobile-certs\ninstall -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/fullchain.pem /var/lib/caddy/dsh-mobile-certs/fullchain.pem\ninstall -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/privkey.pem /var/lib/caddy/dsh-mobile-certs/privkey.pem\ncat > /usr/local/sbin/dsh-mobile-cert-renew <<'DSH_MOBILE_CERT_RENEW'\n#!/bin/sh\nset -eu\nsystemctl stop caddy.service\ntrap 'systemctl start caddy.service' EXIT\n/opt/dsh-mobile/certbot-venv/bin/certbot renew --cert-name ${publicHost} --preferred-profile shortlived --non-interactive\ninstall -d -m 0750 -o caddy -g caddy /var/lib/caddy/dsh-mobile-certs\ninstall -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/fullchain.pem /var/lib/caddy/dsh-mobile-certs/fullchain.pem\ninstall -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/privkey.pem /var/lib/caddy/dsh-mobile-certs/privkey.pem\nDSH_MOBILE_CERT_RENEW\nchmod 0755 /usr/local/sbin/dsh-mobile-cert-renew\ncat > /etc/systemd/system/dsh-mobile-cert-renew.service <<'DSH_MOBILE_CERT_SERVICE'\n[Unit]\nDescription=Renew DSH Mobile public IP TLS certificate\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=oneshot\nExecStart=/usr/local/sbin/dsh-mobile-cert-renew\nDSH_MOBILE_CERT_SERVICE\ncat > /etc/systemd/system/dsh-mobile-cert-renew.timer <<'DSH_MOBILE_CERT_TIMER'\n[Unit]\nDescription=Daily DSH Mobile public IP TLS certificate renewal check\n\n[Timer]\nOnCalendar=daily\nRandomizedDelaySec=2h\nPersistent=true\nUnit=dsh-mobile-cert-renew.service\n\n[Install]\nWantedBy=timers.target\nDSH_MOBILE_CERT_TIMER\ncheck certificate ok \"Let's Encrypt 公网 IP 证书已安装并启用每日自动续期。\"\n` : ''\n return `#!/bin/sh\nset -eu\numask 077\n\nfail() { echo \"DSH_MOBILE_CHECK remote-command error $1\" >&2; exit 1; }\ncheck() { echo \"DSH_MOBILE_CHECK $1 $2 $3\"; }\n\n# Serialize concurrent deploys: two writers racing sed -i on the Caddyfile\n# can duplicate the import line and break validation. Uninstall takes the\n# same lock, so deploy and cleanup also exclude each other.\nif command -v flock >/dev/null 2>&1; then\n exec 9>/tmp/dsh-mobile-deploy.lock\n flock -n 9 || fail \"已有部署或清理正在进行,请稍后再试。\"\nfi\n\n[ \"$(id -u)\" = \"0\" ] || fail \"请使用 root SSH 账号。\"\ncommand -v systemctl >/dev/null 2>&1 || fail \"VPS 不支持 systemd。\"\ncommand -v tar >/dev/null 2>&1 || fail \"VPS 缺少 tar。\"\ncommand -v curl >/dev/null 2>&1 || fail \"VPS 缺少 curl。\"\ncommand -v sha256sum >/dev/null 2>&1 || fail \"VPS 缺少 sha256sum。\"\ncommand -v useradd >/dev/null 2>&1 || fail \"VPS 缺少 useradd。\"\n\nif [ -r /etc/os-release ]; then . /etc/os-release; else fail \"无法识别 VPS 系统。\"; fi\ncase \"\\${ID:-}\" in\n debian|ubuntu) ;;\n *) fail \"首版 VPS 部署只支持 Debian/Ubuntu。\" ;;\nesac\ncheck os ok \"\\${PRETTY_NAME:-Debian/Ubuntu}\"\n\nif command -v ss >/dev/null 2>&1 && ss -ltnH | awk '{print $4}' | grep -Eq '(^|:)${String(settings.serverPort)}$'; then\n systemctl is-active --quiet dsh-mobile-frps.service || fail \"端口 ${String(settings.serverPort)} 已被占用。\"\nfi\n\nif ! command -v caddy >/dev/null 2>&1; then\n export DEBIAN_FRONTEND=noninteractive\n # A previous interrupted run may have left these files unreadable because\n # the deployment uses umask 077. APT reads repositories as the _apt user.\n chmod 0644 /usr/share/keyrings/caddy-stable-archive-keyring.gpg 2>/dev/null || true\n chmod 0644 /etc/apt/sources.list.d/caddy-stable.list 2>/dev/null || true\n apt-get update\n apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl gnupg\n curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor --yes -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg\n curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' -o /etc/apt/sources.list.d/caddy-stable.list\n chmod 0644 /usr/share/keyrings/caddy-stable-archive-keyring.gpg /etc/apt/sources.list.d/caddy-stable.list\n apt-get update\n apt-get install -y caddy\nfi\ncheck caddy ok \"Caddy 已安装。\"\n\n# The site lives in our own snippet file; the main Caddyfile only gains one\n# import line, so existing user content is never rewritten or merged.\ncaddy_import='${FRP_CADDY_IMPORT_LINE}'\ninstall -d -m 0755 /etc/caddy\ncaddyfile_ready=false\nif [ ! -e /etc/caddy/Caddyfile ]; then\n printf '%s\\n' \"$caddy_import\" > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\nelif grep -Eq '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile; then\n # The snippet may carry global options (IP mode default_sni), which must\n # precede all site blocks after import inlining: rebuild the file with a\n # single import on top. Removal uses grep -v with the exact gate pattern\n # above (not sed -i, whose in-place delete proved unreliable here), and the\n # result is verified to carry exactly one import before replacing the file.\n {\n printf '%s\\n' \"$caddy_import\"\n grep -Ev '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile || true\n } > /etc/caddy/Caddyfile.dsh-new\n [ \"$(grep -Ec '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile.dsh-new)\" = 1 ] \\\n || fail \"Caddyfile import 整理失败,未做任何修改。\"\n cat /etc/caddy/Caddyfile.dsh-new > /etc/caddy/Caddyfile\n rm -f /etc/caddy/Caddyfile.dsh-new\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\nelif [ ! -s /etc/caddy/Caddyfile ]; then\n printf '%s\\n' \"$caddy_import\" > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\nelif grep -q '^# DSH Mobile removed its site' /etc/caddy/Caddyfile; then\n # Leftover placeholder from our own uninstall: drop only that line, then\n # ensure the import exists exactly once (same grep -v + count discipline).\n grep -Ev '^# DSH Mobile removed its site.*$' /etc/caddy/Caddyfile > /etc/caddy/Caddyfile.dsh-new || true\n grep -Eq '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile.dsh-new \\\n || printf '%s\\n' \"$caddy_import\" >> /etc/caddy/Caddyfile.dsh-new\n [ \"$(grep -Ec '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile.dsh-new)\" = 1 ] \\\n || fail \"Caddyfile import 整理失败,未做任何修改。\"\n cat /etc/caddy/Caddyfile.dsh-new > /etc/caddy/Caddyfile\n rm -f /etc/caddy/Caddyfile.dsh-new\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\nelse\n caddy_hash=\"$(sha256sum /etc/caddy/Caddyfile | awk '{print $1}')\"\n if [ \"$caddy_hash\" = '66177d46fa761acb07208065db9b0274cb1b12c02ac43b9bfc9857b698b1ccfe' ]; then\n printf '%s\\n' \"$caddy_import\" > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\n elif grep -q '^# Managed by DSH Mobile$' /etc/caddy/Caddyfile; then\n # Legacy whole-file layout: the entire file is ours by construction.\n printf '%s\\n' \"$caddy_import\" > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\n elif grep -q '^:80[[:space:]]*{' /etc/caddy/Caddyfile \\\n && grep -q 'root [*] /usr/share/caddy' /etc/caddy/Caddyfile \\\n && grep -q '^[[:space:]]*file_server[[:space:]]*$' /etc/caddy/Caddyfile; then\n printf '%s\\n' \"$caddy_import\" > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\n fi\nfi\nif [ \"$caddyfile_ready\" != true ]; then\n fail \"已有 Caddyfile,请先备份,然后加一行 ${FRP_CADDY_IMPORT_LINE},或手动合并站点。\"\nfi\n\n${ipCertificateSetup}\n\narch=\"$(uname -m)\"\ncase \"$arch\" in\n x86_64|amd64) url=${shellQuote(amd64.url)}; expected=${shellQuote(amd64.sha256)}; directory=${shellQuote(amd64.directory)} ;;\n aarch64|arm64) url=${shellQuote(arm64.url)}; expected=${shellQuote(arm64.sha256)}; directory=${shellQuote(arm64.directory)} ;;\n *) fail \"只支持 Linux x86_64 和 arm64。\" ;;\nesac\n\ntmp=\"$(mktemp -d /tmp/dsh-mobile-frp.XXXXXX)\"\ncleanup() { rm -rf \"$tmp\"; [ -z \"\\${DSH_MOBILE_FRP_ARCHIVE:-}\" ] || rm -f \"$DSH_MOBILE_FRP_ARCHIVE\"; }\ntrap cleanup EXIT HUP INT TERM\narchive=\"$tmp/frp.tar.gz\"\nif [ -n \"\\${DSH_MOBILE_FRP_ARCHIVE:-}\" ]; then\n [ -f \"$DSH_MOBILE_FRP_ARCHIVE\" ] || fail \"上传的 frps 安装包不存在。\"\n cp \"$DSH_MOBILE_FRP_ARCHIVE\" \"$archive\"\nelse\n curl --fail --location --proto '=https' --tlsv1.2 --output \"$archive\" \"$url\"\nfi\nactual=\"$(sha256sum \"$archive\" | awk '{print $1}')\"\n[ \"$actual\" = \"$expected\" ] || fail \"frps 下载校验失败。\"\ntar -xzf \"$archive\" -C \"$tmp\" \"$directory/frps\"\n\ninstall -d -m 0755 /usr/local/libexec/dsh-mobile/frp/${FRP_VERSION}\ninstall -m 0755 \"$tmp/$directory/frps\" /usr/local/libexec/dsh-mobile/frp/${FRP_VERSION}/frps\n# The account must exist before anything references its group below.\ndsh_mobile_created=false\nif ! id -u dsh-mobile >/dev/null 2>&1; then\n useradd --system --home-dir /nonexistent --shell /usr/sbin/nologin --no-create-home dsh-mobile\n dsh_mobile_created=true\nfi\ninstall -d -m 0750 -o root -g dsh-mobile /etc/dsh-mobile\nif [ \"$dsh_mobile_created\" = true ]; then\n # Ownership record: uninstall removes the account only when this deployment created it.\n touch /etc/dsh-mobile/.owns-account\n check account ok \"已创建 dsh-mobile 系统用户。\"\nelse\n check account ok \"复用已有的 dsh-mobile 系统用户(卸载时将保留)。\"\nfi\ncat > /etc/dsh-mobile/frps.toml <<'DSH_MOBILE_FRPS_CONFIG'\n${config}DSH_MOBILE_FRPS_CONFIG\nchown root:dsh-mobile /etc/dsh-mobile/frps.toml\nchmod 0640 /etc/dsh-mobile/frps.toml\n\ncat > /etc/systemd/system/dsh-mobile-frps.service <<'DSH_MOBILE_FRPS_UNIT'\n[Unit]\nDescription=DSH Mobile self-hosted FRP server\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nUser=dsh-mobile\nGroup=dsh-mobile\nExecStart=/usr/local/libexec/dsh-mobile/frp/${FRP_VERSION}/frps -c /etc/dsh-mobile/frps.toml\nRestart=on-failure\nRestartSec=5s\nNoNewPrivileges=true\nPrivateTmp=true\nProtectHome=true\nProtectSystem=strict\n\n[Install]\nWantedBy=multi-user.target\nDSH_MOBILE_FRPS_UNIT\n\ncat > ${FRP_CADDY_SNIPPET_PATH} <<'DSH_MOBILE_CADDY_SNIPPET'\n${caddySnippet}DSH_MOBILE_CADDY_SNIPPET\nchmod 0644 ${FRP_CADDY_SNIPPET_PATH}\ncaddy validate --config /etc/caddy/Caddyfile\nsystemctl daemon-reload\n# Restart (not just start) so a redeploy over a running previous generation\n# actually picks up the new frps token and config instead of keeping the old\n# process alive with stale credentials.\nsystemctl enable dsh-mobile-frps.service\nsystemctl restart dsh-mobile-frps.service\nsystemctl enable --now caddy.service\n${publicIp ? 'systemctl enable --now dsh-mobile-cert-renew.timer' : ''}\nsystemctl reload caddy.service || systemctl restart caddy.service\n\nif command -v ufw >/dev/null 2>&1 && ufw status | grep -q '^Status: active'; then\n ufw allow ${String(settings.serverPort)}/tcp comment 'DSH Mobile FRP control' >/dev/null\n ufw allow 80/tcp comment 'DSH Mobile HTTPS redirect' >/dev/null\n ufw allow 443/tcp comment 'DSH Mobile HTTPS' >/dev/null\n check firewall ok \"UFW 已放行 FRP 控制端口和 HTTPS。\"\nelse\n check firewall warning \"未修改系统防火墙;请确认 ${String(settings.serverPort)}/tcp、80/tcp、443/tcp 已放行。\"\nfi\n\nsystemctl is-active --quiet dsh-mobile-frps.service || fail \"frps 服务启动失败。\"\nsystemctl is-active --quiet caddy.service || fail \"Caddy 服务启动失败。\"\ncheck frps ok \"frps ${FRP_VERSION} 已启动,7080 仅绑定回环地址。\"\ncheck caddy ok \"Caddy 已加载 ${publicHost}。\"\necho DSH_MOBILE_DEPLOYMENT_OK\n`\n}\n\nasync function runProcess(command: string, args: readonly string[], stdin?: string, timeoutMs = SSH_TIMEOUT_MS): Promise<{ stdout: string; stderr: string }> {\n return new Promise((resolveRun, rejectRun) => {\n const child = spawn(command, args, { windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'] })\n let stdout = ''\n let stderr = ''\n const append = (current: string, chunk: Buffer): string => `${current}${chunk.toString('utf8')}`.slice(-MAX_OUTPUT_BYTES)\n const timer = setTimeout(() => { child.kill(); rejectRun(new VpsSshError('vps_ssh_timeout', stdout, stderr)) }, timeoutMs)\n timer.unref()\n child.stdout.on('data', chunk => { stdout = append(stdout, Buffer.from(chunk)) })\n child.stderr.on('data', chunk => { stderr = append(stderr, Buffer.from(chunk)) })\n child.once('error', error => { clearTimeout(timer); rejectRun(new VpsSshError('vps_ssh_unavailable', stdout, stderr, { cause: error })) })\n child.once('close', code => {\n clearTimeout(timer)\n if (code !== 0) rejectRun(new VpsSshError(stderr.includes('Permission denied') ? 'vps_ssh_auth_failed' : 'vps_deploy_failed', stdout, stderr))\n else resolveRun({ stdout, stderr })\n })\n child.stdin.end(stdin, 'utf8')\n })\n}\n\nasync function downloadArtifact(artifact: (typeof LINUX_ARTIFACTS)[keyof typeof LINUX_ARTIFACTS], file: string): Promise<number> {\n const curl = process.platform === 'win32' ? 'curl.exe' : 'curl'\n await runProcess(curl, [\n ...(process.platform === 'win32' ? ['--ipv4'] : []),\n '--fail', '--location', '--silent', '--show-error',\n '--connect-timeout', '15', '--max-time', '180',\n '--proto', '=https', '--tlsv1.2', '--output', file, artifact.url,\n ], undefined, 200_000)\n const bytes = await readFile(file)\n if (createHash('sha256').update(bytes).digest('hex') !== artifact.sha256) throw new Error('vps_download_hash_mismatch')\n return bytes.byteLength\n}\n\nasync function defaultRunSsh(\n input: VpsDeploymentInput,\n serverAddress: string,\n script: string,\n knownHostsFile: string,\n log?: VpsDeploymentOptions['log'],\n): Promise<{ stdout: string; stderr: string }> {\n const ssh = process.platform === 'win32' ? 'ssh.exe' : 'ssh'\n const scp = process.platform === 'win32' ? 'scp.exe' : 'scp'\n // The server identity is pinned to the user-confirmed fingerprints captured\n // before deployment. Unknown or rotated keys abort instead of being accepted.\n const common = sshSessionOptions(knownHostsFile)\n if (input.sshKeyPath !== undefined) common.push('-i', input.sshKeyPath)\n const target = `${input.sshUser}@${serverAddress}`\n const probe = await runProcess(ssh, [...common, '-p', String(input.sshPort), target, 'uname -m'])\n const architecture = probe.stdout.trim()\n const artifact = architecture === 'x86_64' || architecture === 'amd64'\n ? LINUX_ARTIFACTS.x64\n : architecture === 'aarch64' || architecture === 'arm64' ? LINUX_ARTIFACTS.arm64 : undefined\n if (artifact === undefined) throw new Error('vps_arch_unsupported')\n log?.('architecture', { architecture })\n const localDirectory = await mkdtemp(join(tmpdir(), 'dsh-mobile-frp-'))\n const localArchive = join(localDirectory, 'frp.tar.gz')\n const remoteArchive = `/tmp/dsh-mobile-frp-${randomBytes(12).toString('hex')}.tar.gz`\n try {\n log?.('download-start', { source: 'local', architecture })\n const bytes = await downloadArtifact(artifact, localArchive)\n log?.('download-complete', { source: 'local', bytes })\n const scpResult = await runProcess(scp, [...common, '-P', String(input.sshPort), localArchive, `${target}:${remoteArchive}`])\n log?.('upload-complete', { bytes, stderrBytes: Buffer.byteLength(scpResult.stderr) })\n const remoteCommand = input.sshUser === 'root'\n ? `env DSH_MOBILE_FRP_ARCHIVE=${shellQuote(remoteArchive)} sh -s`\n : `sudo -n env DSH_MOBILE_FRP_ARCHIVE=${shellQuote(remoteArchive)} sh -s`\n return await runProcess(ssh, [...common, '-p', String(input.sshPort), target, remoteCommand], script)\n } finally {\n await rm(localDirectory, { recursive: true, force: true })\n }\n}\n\nexport async function deployVps(settings: FrpSettings, input: VpsDeploymentInput, options: VpsDeploymentOptions = {}): Promise<VpsDeploymentResult> {\n const serverPort = validateFrpServerPort(settings.serverPort)\n const token = validateFrpToken(settings.token)\n const publicOrigin = validateFrpPublicOrigin(settings.publicOrigin)\n const serverAddress = validateVpsServerTarget(settings.serverAddress)\n const parsedInput = parseVpsDeploymentInput(input)\n if (parsedInput.sshKeyPath !== undefined) {\n const entry = await lstat(parsedInput.sshKeyPath).catch(() => undefined)\n if (entry === undefined || !entry.isFile() || entry.isSymbolicLink()) throw new Error('vps_ssh_key_invalid')\n }\n // Pin the server identity before any authenticated connection: re-scan the\n // host keys now and require every presented key to be user-confirmed, so a\n // rotation between the UI preview and this deployment aborts safely.\n const keyscanOutput = await scanHostKeys(\n { sshUser: parsedInput.sshUser, sshPort: parsedInput.sshPort, sshKeyPath: parsedInput.sshKeyPath },\n serverAddress,\n options,\n )\n const knownHostsBody = buildPinnedKnownHosts(serverAddress, parsedInput.sshPort, keyscanOutput, parsedInput.hostFingerprints)\n options.log?.('host-keys-verified', { serverAddress })\n const runSsh = options.runSsh ?? (async (sshInput, host, scriptBody) => {\n const workDirectory = await mkdtemp(join(tmpdir(), 'dsh-mobile-known-hosts-'))\n try {\n const knownHostsFile = join(workDirectory, 'known_hosts')\n await writeFile(knownHostsFile, knownHostsBody, { encoding: 'utf8', mode: 0o600 })\n return await defaultRunSsh(sshInput, host, scriptBody, knownHostsFile, options.log)\n } finally {\n await rm(workDirectory, { recursive: true, force: true })\n }\n })\n options.log?.('validated', { serverAddress, serverPort, publicOrigin, sshUser: parsedInput.sshUser, sshPort: parsedInput.sshPort, keyProvided: parsedInput.sshKeyPath !== undefined })\n let result: { stdout: string; stderr: string }\n try {\n options.log?.('ssh-start', { serverAddress, sshPort: parsedInput.sshPort })\n result = await runSsh(parsedInput, serverAddress, deploymentScript({ ...settings, serverAddress, serverPort, token, publicOrigin }))\n options.log?.('ssh-complete', { stdoutBytes: Buffer.byteLength(result.stdout), stderrBytes: Buffer.byteLength(result.stderr) })\n } catch (error) {\n if (error instanceof VpsSshError) {\n // Prefer the failed remote check over raw output: the script reports\n // failures through DSH_MOBILE_CHECK lines on either stream, and the raw\n // concatenation would leak protocol framing into the UI.\n const detail = failureDetail(error.stdout, error.stderr, token)\n options.log?.('ssh-failed', { code: error.message, detail: detail || 'no remote output' })\n throw new Error(detail === '' ? error.message : `${error.message}:${detail}`, { cause: error })\n }\n options.log?.('ssh-failed', { code: error instanceof Error ? error.message : 'unknown' })\n throw error\n }\n const checks = parseChecks(result.stdout, result.stderr, token)\n for (const check of checks) options.log?.('remote-check', { id: check.id, status: check.status, detail: check.detail })\n if (!result.stdout.includes('DSH_MOBILE_DEPLOYMENT_OK')) {\n if (checks.length === 0) throw new Error('vps_deploy_failed')\n throw new Error(`vps_deploy_failed:${checks.map(check => check.detail).join(' ')}`)\n }\n return Object.freeze({ version: 1, deployed: true, serverAddress, publicOrigin, checks })\n}\n\nexport function vpsDeploymentScriptForTesting(settings: FrpSettings): string {\n return deploymentScript(settings)\n}\n\nexport interface VpsUninstallInput {\n readonly serverPort: unknown\n /** Let's Encrypt certificate name to delete (public-IPv4 mode); omit for domain mode. */\n readonly certName?: unknown\n}\n\nexport interface VpsUninstallResult {\n readonly version: 1\n readonly removed: boolean\n readonly serverAddress: string\n readonly checks: readonly VpsDeploymentCheck[]\n}\n\nfunction validCertName(value: unknown): string | undefined {\n if (value === undefined || value === '') return undefined\n if (typeof value !== 'string' || value.length > 253 || !/^[a-z0-9.-]+$/u.test(value)) throw new Error('vps_cert_name_invalid')\n return value.toLowerCase()\n}\n\n/**\n * Build a reviewable uninstall script that removes only DSH Mobile-owned\n * server artifacts: its systemd units, config, binaries, venv, renew helper,\n * managed Caddy site, owned UFW rules, and optionally its IP certificate.\n * Existing non-DSH-Mobile Caddy content and firewall rules are never touched.\n */\nexport function createVpsUninstallScript(input: VpsUninstallInput): string {\n const serverPort = validateFrpServerPort(input.serverPort)\n const certName = validCertName(input.certName)\n const certCleanup = certName === undefined ? '' : `\nif [ -x /opt/dsh-mobile/certbot-venv/bin/certbot ]; then\n /opt/dsh-mobile/certbot-venv/bin/certbot delete --cert-name ${shellQuote(certName)} --non-interactive || true\nfi\n`\n return `#!/bin/sh\n# DSH Mobile VPS uninstall. Review before running: only files, services, and\n# firewall rules created by the DSH Mobile deployment are removed.\nset -eu\numask 077\n\nfail() { echo \"DSH_MOBILE_CHECK remote-command error $1\" >&2; exit 1; }\ncheck() { echo \"DSH_MOBILE_CHECK $1 $2 $3\"; }\n\n# Same lock as the deploy script: cleanup and deployment exclude each other\n# so their Caddyfile surgeries never interleave.\nif command -v flock >/dev/null 2>&1; then\n exec 9>/tmp/dsh-mobile-deploy.lock\n flock -n 9 || fail \"已有部署或清理正在进行,请稍后再试。\"\nfi\n\n[ \"$(id -u)\" = \"0\" ] || fail \"请使用 root SSH 账号。\"\ncommand -v systemctl >/dev/null 2>&1 || fail \"VPS 不支持 systemd。\"\n\n# Ownership is decided before deleting anything: only an account created by a\n# DSH Mobile deployment (marker written at useradd time) may be removed below.\nowns_account=false\nif [ -f /etc/dsh-mobile/.owns-account ]; then owns_account=true; fi\n\nsystemctl disable --now dsh-mobile-cert-renew.timer >/dev/null 2>&1 || true\nsystemctl disable --now dsh-mobile-frps.service >/dev/null 2>&1 || true\nrm -f /etc/systemd/system/dsh-mobile-frps.service\nrm -f /etc/systemd/system/dsh-mobile-cert-renew.service\nrm -f /etc/systemd/system/dsh-mobile-cert-renew.timer\nsystemctl daemon-reload\ncheck services ok \"已停止并删除 dsh-mobile-frps 服务与证书续期定时器。\"\n${certCleanup}\nrm -rf /etc/dsh-mobile\nrm -rf /usr/local/libexec/dsh-mobile\nrm -rf /opt/dsh-mobile/certbot-venv\nrm -f /usr/local/sbin/dsh-mobile-cert-renew\nrm -rf /var/lib/caddy/dsh-mobile-certs\nrm -f ${FRP_CADDY_SNIPPET_PATH}\ncheck files ok \"已删除 DSH Mobile 配置、二进制与证书文件。\"\n\nif grep -Eq '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile 2>/dev/null; then\n # Rebuild without our import line (grep -v with the gate pattern, verified\n # to remove every copy), keeping all user content byte-identical otherwise.\n grep -Ev '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile > /etc/caddy/Caddyfile.dsh-new || true\n if grep -Eq '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile.dsh-new; then\n rm -f /etc/caddy/Caddyfile.dsh-new\n fail \"Caddyfile import 移除失败,未做任何修改。\"\n fi\n cat /etc/caddy/Caddyfile.dsh-new > /etc/caddy/Caddyfile\n rm -f /etc/caddy/Caddyfile.dsh-new\n if [ ! -s /etc/caddy/Caddyfile ]; then\n printf '# DSH Mobile removed its site; the remaining Caddyfile was empty.\\n' > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n fi\n if command -v caddy >/dev/null 2>&1; then\n caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy.service || systemctl restart caddy.service || true\n fi\n check caddy ok \"已移除 DSH Mobile 站点引入;其余 Caddy 配置保持原样。\"\nelif [ -f /etc/caddy/Caddyfile ] && grep -q '^# Managed by DSH Mobile$' /etc/caddy/Caddyfile; then\n # Legacy whole-file layout (pre-snippet releases): the entire file is ours.\n printf '# DSH Mobile removed its site. Restore your own Caddyfile or reinstall the Caddy defaults.\\\\n' > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n if command -v caddy >/dev/null 2>&1; then\n caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy.service || systemctl restart caddy.service || true\n fi\n check caddy ok \"已清空旧版 DSH Mobile 管理的 Caddy 站点;请按需恢复自己的配置。\"\nelse\n check caddy ok \"Caddyfile 非 DSH Mobile 管理,保持原样。\"\nfi\n\nif command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q '^Status: active'; then\n for rule in $(ufw status numbered 2>/dev/null | grep 'DSH Mobile' | sed -E 's/^\\\\[ *([0-9]+)\\\\].*/\\\\1/' | sort -rn); do\n yes | ufw delete \"$rule\" >/dev/null 2>&1 || true\n done\n check firewall ok \"已删除带 DSH Mobile 标记的 UFW 规则(FRP 控制端口 ${String(serverPort)}、80、443)。\"\nelse\n check firewall ok \"UFW 未启用或无需调整。\"\nfi\n\nif [ \"$owns_account\" = true ]; then\n if id dsh-mobile >/dev/null 2>&1; then\n if pgrep -u dsh-mobile >/dev/null 2>&1; then\n fail \"dsh-mobile 用户仍有运行中的进程,已保留该用户;请先停止相关进程后重试。\"\n fi\n userdel dsh-mobile || fail \"删除 dsh-mobile 系统用户失败。\"\n check account ok \"已删除本次部署创建的 dsh-mobile 系统用户。\"\n else\n check account ok \"dsh-mobile 系统用户已不存在,无需删除。\"\n fi\nelse\n check account ok \"dsh-mobile 系统用户非本次部署创建,已保留。\"\nfi\n\necho DSH_MOBILE_UNINSTALL_OK\n`\n}\n\nasync function runRemoteScript(\n input: VpsDeploymentInput,\n serverAddress: string,\n script: string,\n environment: Readonly<Record<string, string>>,\n knownHostsFile: string,\n log?: VpsDeploymentOptions['log'],\n): Promise<{ stdout: string; stderr: string }> {\n const ssh = process.platform === 'win32' ? 'ssh.exe' : 'ssh'\n const parsedInput = parseVpsDeploymentInput(input)\n const common = sshSessionOptions(knownHostsFile)\n if (parsedInput.sshKeyPath !== undefined) common.push('-i', parsedInput.sshKeyPath)\n const target = `${parsedInput.sshUser}@${serverAddress}`\n const remoteCommand = parsedInput.sshUser === 'root' ? 'sh -s' : 'sudo -n sh -s'\n const envPrefix = Object.entries(environment).map(([key, value]) => `${key}=${shellQuote(value)}`).join(' ')\n log?.('uninstall-ssh-start', { serverAddress })\n return await runProcess(ssh, [...common, '-p', String(parsedInput.sshPort), target, `${envPrefix} ${remoteCommand}`.trim()], script)\n}\n\n/** Remove DSH Mobile-owned server artifacts over a pinned SSH connection. */\nexport async function uninstallVps(\n serverAddress: string,\n uninstall: VpsUninstallInput,\n input: VpsDeploymentInput,\n options: VpsDeploymentOptions = {},\n): Promise<VpsUninstallResult> {\n const address = validateVpsServerTarget(serverAddress)\n const parsedInput = parseVpsDeploymentInput(input)\n // Identity is verified with a fresh scan immediately before this destructive\n // action: every presented key must still be user-confirmed.\n const keyscanOutput = await scanHostKeys(\n { sshUser: parsedInput.sshUser, sshPort: parsedInput.sshPort, sshKeyPath: parsedInput.sshKeyPath },\n address,\n options,\n )\n const knownHostsBody = buildPinnedKnownHosts(address, parsedInput.sshPort, keyscanOutput, parsedInput.hostFingerprints)\n options.log?.('host-keys-verified', { serverAddress: address })\n const script = createVpsUninstallScript(uninstall)\n options.log?.('uninstall-start', { serverAddress: address })\n const runRemote = options.runRemoteScript ?? (async (sshInput, host, scriptBody) => {\n const workDirectory = await mkdtemp(join(tmpdir(), 'dsh-mobile-known-hosts-'))\n try {\n const knownHostsFile = join(workDirectory, 'known_hosts')\n await writeFile(knownHostsFile, knownHostsBody, { encoding: 'utf8', mode: 0o600 })\n return await runRemoteScript(sshInput, host, scriptBody, {}, knownHostsFile, options.log)\n } finally {\n await rm(workDirectory, { recursive: true, force: true })\n }\n })\n let result: { stdout: string; stderr: string }\n try {\n result = await runRemote(parsedInput, address, script)\n } catch (error) {\n if (error instanceof VpsSshError) {\n // runProcess labels transport failures as deploy errors; relabel them\n // and prefer the failed remote check over raw output (see deployVps).\n const detail = failureDetail(error.stdout, error.stderr, '')\n const code = error.message === 'vps_deploy_failed' ? 'vps_uninstall_failed' : error.message\n options.log?.('uninstall-failed', { code, detail: detail || 'no remote output' })\n throw new Error(detail === '' ? code : `${code}:${detail}`, { cause: error })\n }\n options.log?.('uninstall-failed', { code: error instanceof Error ? error.message : 'unknown' })\n throw error\n }\n const checks = parseChecks(result.stdout, result.stderr, '')\n for (const check of checks) options.log?.('remote-check', { id: check.id, status: check.status, detail: check.detail })\n if (!result.stdout.includes('DSH_MOBILE_UNINSTALL_OK')) {\n if (checks.length === 0) throw new Error('vps_uninstall_failed')\n throw new Error(`vps_uninstall_failed:${checks.map(check => check.detail).join(' ')}`)\n }\n return Object.freeze({ version: 1, removed: true, serverAddress: address, checks })\n}\n","import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'\nimport { lstat, rm } from 'node:fs/promises'\nimport { isAbsolute, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { MobileAccessControlStore } from './control.js'\nimport type { MobileAccessGateway } from './gateway.js'\nimport { settleRemoteResources, terminateRemoteProcess, type RemoteProviderController } from './remote.js'\n\nconst MAX_PROTOCOL_LINE_BYTES = 16 * 1024\nconst FUNNEL_START_TIMEOUT_MS = 45_000\n\n/** Product-facing states for the independent Tailscale Funnel transport. */\nexport type FunnelState = 'off' | 'unavailable' | 'starting' | 'needs-login' | 'connecting' | 'ready' | 'error'\n\n/** Safe state returned only through the loopback DSH control route. */\nexport interface FunnelStatus {\n readonly enabled: boolean\n readonly state: FunnelState\n readonly origin?: string\n readonly loginUrl?: string\n readonly setupUrl?: string\n readonly errorCode?: string\n}\n\ninterface FunnelEvent {\n readonly version: 1\n readonly type: 'login' | 'ready' | 'serving' | 'error'\n readonly url?: string\n readonly origin?: string\n readonly code?: string\n}\n\n/** Construction inputs for one Funnel lifecycle independent from the LAN gateway. */\nexport interface FunnelControllerOptions {\n readonly store: MobileAccessControlStore\n readonly executable: string\n readonly stateDirectory: string\n readonly hostname: string\n readonly createGateway: (origin: string) => Promise<MobileAccessGateway>\n readonly onStatus?: (status: FunnelStatus) => void\n}\n\nfunction publicStatus(status: FunnelStatus): FunnelStatus {\n return Object.freeze({\n enabled: status.enabled,\n state: status.state,\n ...(status.origin === undefined ? {} : { origin: status.origin }),\n ...(status.loginUrl === undefined ? {} : { loginUrl: status.loginUrl }),\n ...(status.setupUrl === undefined ? {} : { setupUrl: status.setupUrl }),\n ...(status.errorCode === undefined ? {} : { errorCode: status.errorCode }),\n })\n}\n\nconst FUNNEL_SETUP_URLS = new Set([\n 'https://tailscale.com/s/no-funnel',\n 'https://tailscale.com/s/https',\n])\n\nfunction parseSetupUrl(value: unknown): string | undefined {\n if (value === undefined) return undefined\n if (typeof value !== 'string' || value.length > 2048) throw new Error('invalid_sidecar_protocol')\n const url = new URL(value)\n const normalized = url.toString().replace(/\\/$/u, '')\n const officialInteractive = url.protocol === 'https:' && url.hostname === 'login.tailscale.com'\n && url.port === '' && url.username === '' && url.password === ''\n if (!FUNNEL_SETUP_URLS.has(normalized) && !officialInteractive) throw new Error('invalid_sidecar_protocol')\n return officialInteractive ? url.toString() : normalized\n}\n\nfunction parseOrigin(value: unknown): string {\n if (typeof value !== 'string' || value.length > 512) throw new Error('invalid_funnel_origin')\n let url: URL\n try { url = new URL(value) } catch { throw new Error('invalid_funnel_origin') }\n if (url.protocol !== 'https:' || !url.hostname.endsWith('.ts.net') || url.port !== ''\n || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '') throw new Error('invalid_funnel_origin')\n return url.origin\n}\n\n/** Parse one sidecar protocol line while restricting every browser-opened URL. */\nexport function parseFunnelEvent(line: string): FunnelEvent {\n if (Buffer.byteLength(line, 'utf8') === 0 || Buffer.byteLength(line, 'utf8') > MAX_PROTOCOL_LINE_BYTES) {\n throw new Error('invalid_sidecar_protocol')\n }\n let value: unknown\n try { value = JSON.parse(line) as unknown } catch { throw new Error('invalid_sidecar_protocol') }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('invalid_sidecar_protocol')\n const record = value as Record<string, unknown>\n if (record.version !== 1 || typeof record.type !== 'string') throw new Error('invalid_sidecar_protocol')\n if (record.type === 'login') {\n if (typeof record.url !== 'string' || record.url.length > 2048) throw new Error('invalid_sidecar_protocol')\n const url = new URL(record.url)\n if (url.protocol !== 'https:' || url.hostname !== 'login.tailscale.com') throw new Error('invalid_sidecar_protocol')\n return Object.freeze({ version: 1, type: 'login', url: url.toString() })\n }\n if (record.type === 'ready' || record.type === 'serving') {\n return Object.freeze({ version: 1, type: record.type, origin: parseOrigin(record.origin) })\n }\n if (record.type === 'error') {\n if (typeof record.code !== 'string' || !/^[a-z][a-z0-9_]{0,63}$/u.test(record.code)) {\n throw new Error('invalid_sidecar_protocol')\n }\n const setupUrl = parseSetupUrl(record.url)\n return Object.freeze({ version: 1, type: 'error', code: record.code, ...(setupUrl === undefined ? {} : { url: setupUrl }) })\n }\n throw new Error('invalid_sidecar_protocol')\n}\n\nfunction withoutProvisioningSecrets(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const blocked = new Set(['TS_AUTHKEY', 'TAILSCALE_AUTHKEY', 'TS_OAUTH_CLIENT_SECRET'])\n return Object.fromEntries(Object.entries(environment).filter(([name]) => !blocked.has(name.toUpperCase())))\n}\n\n/** Owns the source-built tsnet sidecar, remote gateway, and persisted remote switch. */\nexport class FunnelController implements RemoteProviderController {\n private enabled = false\n private initialized = false\n private disposed = false\n private child: ChildProcessWithoutNullStreams | undefined\n private gatewayValue: MobileAccessGateway | undefined\n private generation = 0\n private buffer = ''\n private latest: FunnelStatus = publicStatus({ enabled: false, state: 'off' })\n private queue: Promise<void> = Promise.resolve()\n private startTimer: NodeJS.Timeout | undefined\n\n constructor(private readonly options: FunnelControllerOptions) {\n if (!isAbsolute(options.executable) || !isAbsolute(options.stateDirectory)) {\n throw new Error('Funnel paths must be absolute')\n }\n }\n\n /** Restore the remote switch without coupling it to LAN availability. */\n async initialize(): Promise<void> {\n const state = await this.options.store.load()\n this.enabled = state.enabled\n this.initialized = true\n if (this.enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n }\n\n /** Return the currently attached authenticated remote gateway. */\n gateway(): MobileAccessGateway | undefined {\n return this.gatewayValue\n }\n\n /** Return state safe for the local desktop control UI. */\n status(): FunnelStatus {\n return publicStatus(this.latest)\n }\n\n /** Enable or disable Funnel without changing the LAN listener. */\n async setEnabled(enabled: boolean): Promise<FunnelStatus> {\n if (!this.initialized || this.disposed) throw new Error('Funnel controller is unavailable')\n await this.enqueue(async () => {\n if (this.enabled === enabled && (enabled === false || this.child !== undefined)) return\n if (!enabled) await this.stop()\n this.enabled = enabled\n await this.options.store.save({ version: 1, enabled })\n if (enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Restart a failed or interrupted Funnel session while retaining sign-in state. */\n async reconnect(): Promise<FunnelStatus> {\n if (!this.initialized || this.disposed) throw new Error('Funnel controller is unavailable')\n await this.enqueue(async () => {\n if (!this.enabled) {\n this.enabled = true\n await this.options.store.save({ version: 1, enabled: true })\n }\n await this.stop()\n await this.start()\n })\n return this.status()\n }\n\n /** Disable Funnel and remove only its private Tailscale node state. */\n async reset(): Promise<FunnelStatus> {\n if (!this.initialized || this.disposed) throw new Error('Funnel controller is unavailable')\n await this.enqueue(async () => {\n await this.stop()\n this.enabled = false\n await this.options.store.save({ version: 1, enabled: false })\n await rm(resolve(this.options.stateDirectory), { recursive: true, force: true })\n this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Stop all remote resources without modifying the remembered switch. */\n async close(): Promise<void> {\n if (this.disposed) return\n this.disposed = true\n await this.enqueue(() => this.stop())\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task\n }\n\n private publish(status: FunnelStatus): void {\n this.latest = publicStatus(status)\n try { this.options.onStatus?.(this.status()) } catch { /* UI observation cannot own runtime state. */ }\n }\n\n private async start(): Promise<void> {\n const generation = ++this.generation\n let entry\n try { entry = await lstat(this.options.executable) } catch {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'component_missing' })\n return\n }\n if (!entry.isFile() || entry.isSymbolicLink()) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'component_invalid' })\n return\n }\n this.buffer = ''\n this.publish({ enabled: true, state: 'starting' })\n const child = spawn(this.options.executable, [\n '--state-dir', resolve(this.options.stateDirectory),\n '--hostname', this.options.hostname,\n ], {\n env: withoutProvisioningSecrets(process.env),\n shell: false,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n })\n this.child = child\n this.clearStartTimer()\n this.startTimer = setTimeout(() => {\n void this.enqueue(() => this.failGeneration(generation, 'funnel_start_timeout'))\n }, FUNNEL_START_TIMEOUT_MS)\n this.startTimer.unref()\n child.stderr.resume()\n child.stdout.setEncoding('utf8')\n child.stdout.on('data', chunk => { this.consume(generation, String(chunk)) })\n child.once('error', () => {\n void this.enqueue(() => this.failGeneration(generation, 'sidecar_launch_failed'))\n })\n child.once('close', code => {\n if (generation !== this.generation || this.child !== child) return\n this.child = undefined\n if (this.enabled) {\n void this.enqueue(() => this.failGeneration(generation, code === 0 ? 'sidecar_stopped' : 'sidecar_exited'))\n }\n })\n }\n\n private consume(generation: number, chunk: string): void {\n if (generation !== this.generation) return\n this.buffer += chunk\n if (Buffer.byteLength(this.buffer, 'utf8') > MAX_PROTOCOL_LINE_BYTES && !this.buffer.includes('\\n')) {\n void this.enqueue(() => this.failGeneration(generation, 'invalid_sidecar_protocol'))\n return\n }\n while (true) {\n const newline = this.buffer.indexOf('\\n')\n if (newline < 0) return\n const line = this.buffer.slice(0, newline).replace(/\\r$/u, '')\n this.buffer = this.buffer.slice(newline + 1)\n let event: FunnelEvent\n try { event = parseFunnelEvent(line) } catch {\n void this.enqueue(() => this.failGeneration(generation, 'invalid_sidecar_protocol'))\n return\n }\n void this.enqueue(() => this.handleEvent(generation, event))\n }\n }\n\n private async handleEvent(generation: number, event: FunnelEvent): Promise<void> {\n if (generation !== this.generation || !this.enabled) return\n this.clearStartTimer()\n if (event.type === 'login') {\n this.publish({ enabled: true, state: 'needs-login', loginUrl: event.url! })\n return\n }\n if (event.type === 'error') {\n await this.failGeneration(generation, event.code ?? 'funnel_failed', event.url)\n return\n }\n const origin = parseOrigin(event.origin)\n if (event.type === 'ready') {\n let gateway: MobileAccessGateway\n try {\n await this.gatewayValue?.close()\n this.gatewayValue = undefined\n gateway = await this.options.createGateway(origin)\n } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled) {\n await gateway.close()\n return\n }\n this.gatewayValue = gateway\n const address = gateway.address()\n const child = this.child\n if (child === undefined) {\n await this.failGeneration(generation, 'sidecar_stopped')\n return\n }\n child.stdin.write(\n `${JSON.stringify({ version: 1, type: 'serve', target: `http://${address.host}:${String(address.port)}` })}\\n`,\n error => {\n if (error !== null && error !== undefined) {\n void this.enqueue(() => this.failGeneration(generation, 'control_channel_failed'))\n }\n },\n )\n this.publish({ enabled: true, state: 'connecting', origin })\n return\n }\n this.publish({ enabled: true, state: 'ready', origin })\n }\n\n private async failGeneration(generation: number, code: string, setupUrl?: string): Promise<void> {\n if (generation !== this.generation) return\n await this.stopProcessAndGateway()\n if (this.enabled) this.publish({ enabled: true, state: 'error', errorCode: code, ...(setupUrl === undefined ? {} : { setupUrl }) })\n }\n\n private async stop(): Promise<void> {\n ++this.generation\n await this.stopProcessAndGateway()\n }\n\n private async stopProcessAndGateway(): Promise<void> {\n this.clearStartTimer()\n const child = this.child\n this.child = undefined\n const gateway = this.gatewayValue\n this.gatewayValue = undefined\n await settleRemoteResources([\n async () => {\n child?.stdin.end()\n if (child !== undefined && child.exitCode === null) await terminateRemoteProcess(child)\n },\n () => gateway?.close(),\n ], 'Funnel resource cleanup failed')\n }\n\n private clearStartTimer(): void {\n if (this.startTimer === undefined) return\n clearTimeout(this.startTimer)\n this.startTimer = undefined\n }\n}\n\n/** Locate the current platform's bundled Funnel executable, with one local development override. */\nexport function funnelExecutable(importMetaUrl: string, environment: NodeJS.ProcessEnv = process.env): string {\n const override = environment.DSH_MOBILE_FUNNEL_SIDECAR\n if (override !== undefined) {\n if (!isAbsolute(override)) throw new Error('DSH_MOBILE_FUNNEL_SIDECAR must be an absolute path')\n return resolve(override)\n }\n const suffix = process.platform === 'win32' ? '.exe' : ''\n const file = `dsh-mobile-funnel-${process.platform}-${process.arch}${suffix}`\n return resolve(fileURLToPath(new URL(`../bin/${file}`, importMetaUrl)))\n}\n","import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'\nimport { lstat } from 'node:fs/promises'\nimport { createServer, type Server } from 'node:net'\nimport { isAbsolute, resolve } from 'node:path'\nimport type { MobileAccessControlStore } from './control.js'\nimport type { MobileAccessGateway } from './gateway.js'\nimport { settleRemoteResources, terminateRemoteProcess, type RemoteProviderController } from './remote.js'\n\nconst MAX_LOG_BUFFER_BYTES = 64 * 1024\nconst START_TIMEOUT_MS = 45_000\nconst CPOLAR_HOST_SUFFIXES = Object.freeze(['.cpolar.cn', '.cpolar.io', '.cpolar.top', '.cpolar.com'])\n\n/** Product-facing states for the optional cpolar remote transport. */\nexport type CpolarState = 'off' | 'unavailable' | 'starting' | 'connecting' | 'ready' | 'error'\n\n/** Safe cpolar state returned only through the loopback DSH control route. */\nexport interface CpolarStatus {\n readonly enabled: boolean\n readonly state: CpolarState\n readonly origin?: string\n readonly errorCode?: string\n}\n\n/** Inputs for one cpolar process and its authenticated DSH gateway. */\nexport interface CpolarControllerOptions {\n readonly store: MobileAccessControlStore\n readonly executable: string\n readonly configFile: string\n readonly region?: string\n readonly createGateway: (origin: string, listenPort: number) => Promise<MobileAccessGateway>\n readonly onStatus?: (status: CpolarStatus) => void\n readonly spawnProcess?: (\n executable: string,\n args: readonly string[],\n environment: NodeJS.ProcessEnv,\n ) => ChildProcessWithoutNullStreams\n}\n\ninterface PortReservation {\n readonly port: number\n readonly release: () => Promise<void>\n}\n\nfunction publicStatus(status: CpolarStatus): CpolarStatus {\n return Object.freeze({\n enabled: status.enabled,\n state: status.state,\n ...(status.origin === undefined ? {} : { origin: status.origin }),\n ...(status.errorCode === undefined ? {} : { errorCode: status.errorCode }),\n })\n}\n\nfunction isCpolarHost(hostname: string): boolean {\n return CPOLAR_HOST_SUFFIXES.some(suffix => hostname.endsWith(suffix))\n}\n\n/** Extract a validated public HTTPS origin from one cpolar log line. */\nexport function parseCpolarOrigin(line: string): string | undefined {\n if (!line.includes('Tunnel established at ')) return undefined\n const match = /Tunnel established at (https:\\/\\/[^\"\\s]+)/u.exec(line)\n if (match === null) return undefined\n let url: URL\n try { url = new URL(match[1]!) } catch { throw new Error('invalid_cpolar_origin') }\n if (url.protocol !== 'https:' || url.port !== '' || !isCpolarHost(url.hostname)\n || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '') throw new Error('invalid_cpolar_origin')\n return url.origin\n}\n\nasync function reserveLoopbackPort(): Promise<PortReservation> {\n const server: Server = createServer(socket => { socket.destroy() })\n await new Promise<void>((resolveListen, reject) => {\n server.once('error', reject)\n server.listen(0, '127.0.0.1', () => {\n server.off('error', reject)\n resolveListen()\n })\n })\n const address = server.address()\n if (address === null || typeof address === 'string') {\n server.close()\n throw new Error('cpolar_port_reservation_failed')\n }\n let released = false\n return {\n port: address.port,\n release: async () => {\n if (released) return\n released = true\n await new Promise<void>(resolveClose => { server.close(() => resolveClose()) })\n },\n }\n}\n\nfunction spawnCpolarProcess(\n executable: string,\n args: readonly string[],\n environment: NodeJS.ProcessEnv,\n): ChildProcessWithoutNullStreams {\n return spawn(executable, [...args], {\n env: environment,\n shell: false,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n })\n}\n\nfunction withoutProxyEnvironment(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const blocked = new Set(['HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'NO_PROXY'])\n return Object.fromEntries(Object.entries(environment).filter(([name]) => !blocked.has(name.toUpperCase())))\n}\n\n/** Owns an installed cpolar client and a provider-specific DSH remote gateway. */\nexport class CpolarController implements RemoteProviderController {\n private enabled = false\n private initialized = false\n private disposed = false\n private child: ChildProcessWithoutNullStreams | undefined\n private gatewayValue: MobileAccessGateway | undefined\n private reservation: PortReservation | undefined\n private generation = 0\n private buffer = ''\n private latest: CpolarStatus = publicStatus({ enabled: false, state: 'off' })\n private queue: Promise<void> = Promise.resolve()\n private startupTimer: NodeJS.Timeout | undefined\n\n constructor(private readonly options: CpolarControllerOptions) {\n if (!isAbsolute(options.executable) || !isAbsolute(options.configFile)) {\n throw new Error('cpolar paths must be absolute')\n }\n if (options.region !== undefined && !/^[a-z][a-z0-9_]{0,31}$/u.test(options.region)) {\n throw new Error('cpolar region is invalid')\n }\n }\n\n /** Restore the remembered cpolar switch independently from LAN and Funnel state. */\n async initialize(): Promise<void> {\n const state = await this.options.store.load()\n this.enabled = state.enabled\n this.initialized = true\n if (this.enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n }\n\n /** Return the active cpolar-backed DSH gateway. */\n gateway(): MobileAccessGateway | undefined {\n return this.gatewayValue\n }\n\n /** Return state safe for the desktop control UI. */\n status(): CpolarStatus {\n return publicStatus(this.latest)\n }\n\n /** Enable or disable cpolar without changing LAN or Tailscale state. */\n async setEnabled(enabled: boolean): Promise<CpolarStatus> {\n if (!this.initialized || this.disposed) throw new Error('cpolar controller is unavailable')\n await this.enqueue(async () => {\n if (this.enabled === enabled && (enabled === false || this.child !== undefined)) return\n if (!enabled) await this.stop()\n this.enabled = enabled\n await this.options.store.save({ version: 1, enabled })\n if (enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Restart cpolar while retaining its account configuration and DSH device store. */\n async reconnect(): Promise<CpolarStatus> {\n if (!this.initialized || this.disposed) throw new Error('cpolar controller is unavailable')\n await this.enqueue(async () => {\n if (!this.enabled) {\n this.enabled = true\n await this.options.store.save({ version: 1, enabled: true })\n }\n await this.stop()\n await this.start()\n })\n return this.status()\n }\n\n /** Disable cpolar without modifying the user's cpolar account or global tunnels. */\n async reset(): Promise<CpolarStatus> {\n if (!this.initialized || this.disposed) throw new Error('cpolar controller is unavailable')\n await this.enqueue(async () => {\n await this.stop()\n this.enabled = false\n await this.options.store.save({ version: 1, enabled: false })\n this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Stop owned resources without changing the remembered switch. */\n async close(): Promise<void> {\n if (this.disposed) return\n this.disposed = true\n await this.enqueue(() => this.stop())\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task\n }\n\n private publish(status: CpolarStatus): void {\n this.latest = publicStatus(status)\n try { this.options.onStatus?.(this.status()) } catch { /* UI observation cannot own runtime state. */ }\n }\n\n private async start(): Promise<void> {\n const generation = ++this.generation\n let executableEntry\n try { executableEntry = await lstat(this.options.executable) } catch {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'cpolar_component_missing' })\n return\n }\n if (!executableEntry.isFile() || executableEntry.isSymbolicLink()) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'cpolar_component_invalid' })\n return\n }\n let configEntry\n try { configEntry = await lstat(this.options.configFile) } catch {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'cpolar_config_missing' })\n return\n }\n if (!configEntry.isFile() || configEntry.isSymbolicLink()) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'cpolar_config_invalid' })\n return\n }\n\n let reservation: PortReservation\n try { reservation = await reserveLoopbackPort() } catch {\n this.publish({ enabled: true, state: 'error', errorCode: 'cpolar_port_unavailable' })\n return\n }\n this.reservation = reservation\n this.buffer = ''\n this.publish({ enabled: true, state: 'starting' })\n const args = [\n 'http',\n `-config=${resolve(this.options.configFile)}`,\n ...(this.options.region === undefined ? [] : [`-region=${this.options.region}`]),\n '-inspect-addr=false',\n '-redirect-https=true',\n '-log=stdout',\n '-log-level=INFO',\n String(reservation.port),\n ]\n const child = (this.options.spawnProcess ?? spawnCpolarProcess)(\n this.options.executable,\n args,\n withoutProxyEnvironment(process.env),\n )\n this.child = child\n child.stdout.setEncoding('utf8')\n child.stderr.setEncoding('utf8')\n child.stdout.on('data', chunk => { this.consume(generation, String(chunk)) })\n child.stderr.on('data', chunk => { this.consume(generation, String(chunk)) })\n child.once('error', () => { void this.enqueue(() => this.failGeneration(generation, 'cpolar_launch_failed')) })\n child.once('close', code => {\n if (generation !== this.generation || this.child !== child) return\n this.child = undefined\n if (this.enabled) void this.enqueue(() => this.failGeneration(generation, code === 0 ? 'cpolar_stopped' : 'cpolar_exited'))\n })\n this.startupTimer = setTimeout(() => {\n void this.enqueue(() => this.failGeneration(generation, 'cpolar_start_timeout'))\n }, START_TIMEOUT_MS)\n this.startupTimer.unref()\n }\n\n private consume(generation: number, chunk: string): void {\n if (generation !== this.generation) return\n this.buffer += chunk\n if (Buffer.byteLength(this.buffer, 'utf8') > MAX_LOG_BUFFER_BYTES && !this.buffer.includes('\\n')) {\n void this.enqueue(() => this.failGeneration(generation, 'cpolar_invalid_output'))\n return\n }\n while (true) {\n const newline = this.buffer.indexOf('\\n')\n if (newline < 0) return\n const line = this.buffer.slice(0, newline).replace(/\\r$/u, '')\n this.buffer = this.buffer.slice(newline + 1)\n let origin: string | undefined\n try { origin = parseCpolarOrigin(line) } catch {\n void this.enqueue(() => this.failGeneration(generation, 'cpolar_invalid_origin'))\n return\n }\n if (origin !== undefined) void this.enqueue(() => this.attachGateway(generation, origin))\n }\n }\n\n private async attachGateway(generation: number, origin: string): Promise<void> {\n if (generation !== this.generation || !this.enabled || this.disposed) return\n const current = this.gatewayValue\n if (current !== undefined) {\n if (current.address().origin === origin) return\n await this.rotateGateway(generation, origin, current)\n return\n }\n const reservation = this.reservation\n if (reservation === undefined) return\n this.publish({ enabled: true, state: 'connecting', origin })\n await reservation.release()\n if (this.reservation === reservation) this.reservation = undefined\n let gateway: MobileAccessGateway\n try { gateway = await this.options.createGateway(origin, reservation.port) } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed || this.child === undefined) {\n await gateway.close()\n return\n }\n this.gatewayValue = gateway\n if (this.startupTimer !== undefined) clearTimeout(this.startupTimer)\n this.startupTimer = undefined\n this.publish({ enabled: true, state: 'ready', origin })\n }\n\n /** Replace the gateway authority when cpolar rotates a temporary public origin. */\n private async rotateGateway(\n generation: number,\n origin: string,\n current: MobileAccessGateway,\n ): Promise<void> {\n const listenPort = current.address().port\n // A replacement must bind the same port cpolar already forwards to. Stop\n // exposing the closing instance before releasing that port.\n if (this.gatewayValue === current) this.gatewayValue = undefined\n this.publish({ enabled: true, state: 'connecting', origin })\n try {\n await current.close()\n } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed || this.child === undefined) return\n\n let replacement: MobileAccessGateway\n try { replacement = await this.options.createGateway(origin, listenPort) } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed || this.child === undefined) {\n await replacement.close()\n return\n }\n this.gatewayValue = replacement\n this.publish({ enabled: true, state: 'ready', origin })\n }\n\n private async failGeneration(generation: number, code: string): Promise<void> {\n if (generation !== this.generation) return\n await this.stopProcessAndGateway()\n if (this.enabled) this.publish({ enabled: true, state: 'error', errorCode: code })\n }\n\n private async stop(): Promise<void> {\n ++this.generation\n await this.stopProcessAndGateway()\n }\n\n private async stopProcessAndGateway(): Promise<void> {\n if (this.startupTimer !== undefined) clearTimeout(this.startupTimer)\n this.startupTimer = undefined\n const reservation = this.reservation\n this.reservation = undefined\n const child = this.child\n this.child = undefined\n const gateway = this.gatewayValue\n this.gatewayValue = undefined\n await settleRemoteResources([\n () => reservation?.release(),\n () => child !== undefined && child.exitCode === null ? terminateRemoteProcess(child) : undefined,\n () => gateway?.close(),\n ], 'cpolar resource cleanup failed')\n }\n}\n","import { createHash, randomBytes } from 'node:crypto'\nimport { execFile } from 'node:child_process'\nimport {\n chmod,\n copyFile,\n lstat,\n mkdir,\n mkdtemp,\n readFile,\n readdir,\n rename,\n rm,\n writeFile,\n} from 'node:fs/promises'\nimport { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport { downloadPinnedArtifact } from './component-download.js'\nimport { restrictPrivateFile } from './private-file.js'\n\n/** Pinned cpolar components fetched only after an explicit user action. */\ninterface CpolarArtifact {\n readonly version: string\n readonly platform: NodeJS.Platform\n readonly arch: string\n readonly downloadUrl: string\n readonly downloadBytes: number\n readonly downloadSha256: string\n readonly executableName: string\n readonly executableBytes: number\n readonly executableSha256: string\n /** Windows ships an MSI inside a zip; Linux ships the binary in a tarball. */\n readonly archiveKind: 'msi-zip' | 'tar.gz'\n}\n\nconst CPOLAR_VERSION = '3.3.18'\n\nconst releases = [\n {\n version: CPOLAR_VERSION,\n platform: 'win32',\n arch: 'x64',\n downloadUrl: `https://www.cpolar.com/static/downloads/releases/${CPOLAR_VERSION}/cpolar-stable-windows-amd64-setup.zip`,\n downloadBytes: 7_603_505,\n downloadSha256: 'fb8cf60289058ee26079f995d2eeea0b21768a742d90c93015afe96e83428830',\n executableName: 'cpolar.exe',\n executableBytes: 19_637_680,\n executableSha256: 'b2d865ee505e842d22ceca5493a872efa893a79b079a7a8ee2bd3aa5343a5c41',\n archiveKind: 'msi-zip',\n },\n {\n version: CPOLAR_VERSION,\n platform: 'linux',\n arch: 'x64',\n downloadUrl: `https://www.cpolar.com/static/downloads/releases/${CPOLAR_VERSION}/cpolar-stable-linux-amd64.tar.gz`,\n downloadBytes: 7_404_781,\n downloadSha256: '5cd3320c4369928ccb509c4f5fa2ec3b86151ead77e1b77c1694aa64c43e32e7',\n executableName: 'cpolar',\n executableBytes: 19_328_632,\n executableSha256: 'c076e1109372a3f88031841c5989030e49b6871b54aee3260c796da9122dec05',\n archiveKind: 'tar.gz',\n },\n {\n version: CPOLAR_VERSION,\n platform: 'linux',\n arch: 'arm64',\n downloadUrl: `https://www.cpolar.com/static/downloads/releases/${CPOLAR_VERSION}/cpolar-stable-linux-arm64.tar.gz`,\n downloadBytes: 6_855_666,\n downloadSha256: '8a61a97983f18ae5ffb4b8bac4c9b3d8e2399a6ee101844e7b0b30d7f326157c',\n executableName: 'cpolar',\n executableBytes: 19_017_169,\n executableSha256: '8d76a1b7e518df45f387f107c7a428079c67ecd9b971a2915c53b947ea9b443a',\n archiveKind: 'tar.gz',\n },\n] as const satisfies readonly CpolarArtifact[]\n\n/** Pinned official cpolar release metadata for supported desktop targets. */\nexport const CPOLAR_COMPONENT_RELEASES: Readonly<Record<string, CpolarArtifact>> = Object.freeze(Object.fromEntries(\n releases.map(release => [`${release.platform}-${release.arch}`, Object.freeze(release)]),\n))\n\n/**\n * Canonical release metadata. New code should select from\n * {@link CPOLAR_COMPONENT_RELEASES} by platform and architecture; this alias\n * preserves the original Windows x64 entry for existing callers.\n */\nexport const CPOLAR_COMPONENT_RELEASE = CPOLAR_COMPONENT_RELEASES['win32-x64'] as CpolarArtifact\n\nconst DOWNLOAD_PAGE = 'https://www.cpolar.com/download'\nconst SIGNUP_URL = 'https://dashboard.cpolar.com/signup'\nconst DASHBOARD_URL = 'https://dashboard.cpolar.com/auth'\nconst TERMS_URL = 'https://www.cpolar.com/tos'\n\n/** Public, credential-free description of the managed cpolar component. */\nexport interface CpolarComponentStatus {\n readonly supported: boolean\n readonly installed: boolean\n readonly configured: boolean\n readonly version: string\n readonly downloadBytes: number\n readonly installedBytes: number\n readonly sourceUrl: string\n readonly downloadPage: string\n readonly signupUrl: string\n readonly dashboardUrl: string\n readonly termsUrl: string\n readonly storagePath: string\n readonly errorCode?: string\n}\n\ninterface CpolarComponentManagerOptions {\n readonly stateDirectory: string\n readonly platform?: NodeJS.Platform\n readonly arch?: string\n readonly fetchArtifact?: (url: string, signal: AbortSignal) => Promise<Uint8Array>\n readonly extractArtifact?: (archive: string, destination: string) => Promise<void>\n}\n\nfunction inside(parent: string, child: string): boolean {\n const candidate = relative(parent, child)\n return candidate !== '' && !candidate.startsWith('..') && !isAbsolute(candidate)\n}\n\nasync function sha256(file: string): Promise<string> {\n return createHash('sha256').update(await readFile(file)).digest('hex')\n}\n\nasync function regularFile(file: string, expectedBytes?: number): Promise<boolean> {\n try {\n const stat = await lstat(file)\n return stat.isFile() && !stat.isSymbolicLink() && (expectedBytes === undefined || stat.size === expectedBytes)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false\n throw error\n }\n}\n\nasync function run(file: string, args: readonly string[]): Promise<void> {\n await new Promise<void>((resolveRun, reject) => {\n execFile(file, [...args], { windowsHide: true, timeout: 120_000 }, (error) => {\n if (error === null) resolveRun()\n else reject(error)\n })\n })\n}\n\nasync function defaultFetchArtifact(\n url: string,\n signal: AbortSignal,\n expectedBytes: number,\n): Promise<Uint8Array> {\n return downloadPinnedArtifact({\n url,\n expectedBytes,\n errorPrefix: 'cpolar',\n signal,\n })\n}\n\nasync function extractWindowsMsi(archive: string, destination: string, executableName: string): Promise<void> {\n if (process.platform !== 'win32') throw new Error('cpolar_component_unsupported')\n const unpacked = join(destination, 'archive')\n const administrative = join(destination, 'administrative')\n await mkdir(unpacked, { recursive: true, mode: 0o700 })\n await mkdir(administrative, { recursive: true, mode: 0o700 })\n await run('tar.exe', ['-xf', archive, '-C', unpacked])\n const archiveEntries = await readdir(unpacked, { recursive: true })\n const msiRelative = archiveEntries.find(entry => entry.toLowerCase().endsWith('.msi'))\n if (msiRelative === undefined) throw new Error('cpolar_installer_missing')\n await run('msiexec.exe', ['/a', join(unpacked, msiRelative), '/qn', `TARGETDIR=${administrative}`])\n const installedEntries = await readdir(administrative, { recursive: true })\n const executableRelative = installedEntries.find(entry => basename(entry).toLowerCase() === executableName.toLowerCase())\n if (executableRelative === undefined) throw new Error('cpolar_executable_missing')\n await copyFile(join(administrative, executableRelative), join(destination, executableName))\n}\n\nasync function extractLinuxTarball(archive: string, destination: string, executableName: string): Promise<void> {\n const unpacked = join(destination, 'archive')\n await mkdir(unpacked, { recursive: true, mode: 0o700 })\n // The Linux tarball carries the binary at its root; no installer involved.\n await run(process.platform === 'win32' ? 'tar.exe' : 'tar', ['-xzf', archive, '-C', unpacked])\n const archiveEntries = await readdir(unpacked, { recursive: true })\n const executableRelative = archiveEntries.find(entry => basename(entry).toLowerCase() === executableName.toLowerCase())\n if (executableRelative === undefined) throw new Error('cpolar_executable_missing')\n await copyFile(join(unpacked, executableRelative), join(destination, executableName))\n}\n\nasync function defaultExtractArtifact(archive: string, destination: string, release: CpolarArtifact): Promise<void> {\n if (release.archiveKind === 'tar.gz') return extractLinuxTarball(archive, destination, release.executableName)\n return extractWindowsMsi(archive, destination, release.executableName)\n}\n\n/** Validate a cpolar Authtoken before it crosses the durable-file boundary. */\nexport function validateCpolarAuthtoken(value: unknown): string {\n if (typeof value !== 'string' || value.length < 20 || value.length > 512\n || /[\\s\\u0000-\\u001f\\u007f]/u.test(value)) {\n throw new Error('cpolar_authtoken_invalid')\n }\n return value\n}\n\n/** Owns the optional cpolar binary and account configuration inside DSH Mobile state. */\nexport class CpolarComponentManager {\n readonly executable: string\n readonly configFile: string\n readonly componentRoot: string\n readonly componentStorage: string\n readonly stateRoot: string\n readonly logRoot: string\n private readonly stagingRoot: string\n private readonly platform: NodeJS.Platform\n private readonly arch: string\n private readonly release: CpolarArtifact | undefined\n private readonly fetchArtifact: (url: string, signal: AbortSignal) => Promise<Uint8Array>\n private readonly extractArtifact: (archive: string, destination: string) => Promise<void>\n private installed = false\n private configured = false\n private errorCode: string | undefined\n private queue: Promise<void> = Promise.resolve()\n\n constructor(options: CpolarComponentManagerOptions) {\n const stateDirectory = resolve(options.stateDirectory)\n if (!isAbsolute(stateDirectory)) throw new Error('cpolar state directory must be absolute')\n this.platform = options.platform ?? process.platform\n this.arch = options.arch ?? process.arch\n this.release = CPOLAR_COMPONENT_RELEASES[`${this.platform}-${this.arch}`]\n this.componentRoot = join(stateDirectory, 'components', 'cpolar')\n this.componentStorage = join(this.componentRoot, this.release?.version ?? CPOLAR_VERSION)\n this.executable = join(this.componentStorage, this.release?.executableName ?? 'cpolar')\n this.stateRoot = join(stateDirectory, 'state', 'cpolar')\n this.configFile = join(this.stateRoot, 'cpolar.yml')\n this.logRoot = join(stateDirectory, 'logs', 'cpolar')\n this.stagingRoot = join(stateDirectory, 'staging', 'cpolar')\n for (const child of [this.componentRoot, this.componentStorage, this.stateRoot, this.logRoot, this.stagingRoot]) {\n if (!inside(stateDirectory, child)) throw new Error('cpolar component path escaped its state directory')\n }\n const release = this.release\n this.fetchArtifact = options.fetchArtifact\n ?? ((url, signal) => defaultFetchArtifact(url, signal, release?.downloadBytes ?? 0))\n this.extractArtifact = options.extractArtifact\n ?? ((archive, destination) => {\n if (release === undefined) throw new Error('cpolar_component_unsupported')\n return defaultExtractArtifact(archive, destination, release)\n })\n }\n\n /** Inspect the managed binary and configuration without using global cpolar state. */\n async initialize(): Promise<void> {\n const release = this.release\n this.installed = release !== undefined && await regularFile(this.executable, release.executableBytes)\n if (this.installed && release !== undefined && await sha256(this.executable) !== release.executableSha256) {\n this.installed = false\n this.errorCode = 'cpolar_component_invalid'\n }\n this.configured = await regularFile(this.configFile)\n if (this.configured) await restrictPrivateFile(this.configFile)\n }\n\n /** Return a safe status that never includes the account token. */\n status(): CpolarComponentStatus {\n // Unsupported hosts still report the canonical entry so the panel can show\n // what would be installed elsewhere; `supported` carries the actual gate.\n const release = this.release ?? CPOLAR_COMPONENT_RELEASE\n return Object.freeze({\n supported: this.release !== undefined,\n installed: this.installed,\n configured: this.configured,\n version: release.version,\n downloadBytes: release.downloadBytes,\n installedBytes: release.executableBytes,\n sourceUrl: release.downloadUrl,\n downloadPage: DOWNLOAD_PAGE,\n signupUrl: SIGNUP_URL,\n dashboardUrl: DASHBOARD_URL,\n termsUrl: TERMS_URL,\n storagePath: this.componentRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n })\n }\n\n /** Download, verify, and administratively extract cpolar after explicit confirmation. */\n install(): Promise<CpolarComponentStatus> {\n return this.enqueue(async () => {\n const release = this.release\n if (release === undefined) throw new Error('cpolar_component_unsupported')\n await mkdir(this.stagingRoot, { recursive: true, mode: 0o700 })\n const staging = await mkdtemp(join(this.stagingRoot, 'install-'))\n try {\n const controller = new AbortController()\n const timeout = setTimeout(() => { controller.abort() }, 120_000)\n timeout.unref()\n let bytes: Uint8Array\n try { bytes = await this.fetchArtifact(release.downloadUrl, controller.signal) } finally { clearTimeout(timeout) }\n const digest = createHash('sha256').update(bytes).digest('hex')\n if (digest !== release.downloadSha256) throw new Error('cpolar_download_hash_mismatch')\n const archive = join(staging, release.archiveKind === 'tar.gz' ? 'cpolar.tar.gz' : 'cpolar.zip')\n await writeFile(archive, bytes, { flag: 'wx', mode: 0o600 })\n await this.extractArtifact(archive, staging)\n const extracted = join(staging, release.executableName)\n if (!await regularFile(extracted, release.executableBytes)\n || await sha256(extracted) !== release.executableSha256) {\n throw new Error('cpolar_executable_hash_mismatch')\n }\n const candidate = join(this.componentRoot, `.install-${randomBytes(12).toString('hex')}`)\n await mkdir(candidate, { recursive: true, mode: 0o700 })\n await copyFile(extracted, join(candidate, release.executableName))\n await chmod(join(candidate, release.executableName), 0o700)\n await rm(this.componentStorage, { recursive: true, force: true })\n await rename(candidate, this.componentStorage)\n this.installed = true\n this.errorCode = undefined\n } finally {\n await rm(staging, { recursive: true, force: true })\n }\n })\n }\n\n /** Store only the cpolar token in a private, self-update-disabled configuration. */\n configure(authtoken: unknown): Promise<CpolarComponentStatus> {\n return this.enqueue(async () => {\n const token = validateCpolarAuthtoken(authtoken)\n await mkdir(this.stateRoot, { recursive: true, mode: 0o700 })\n const temporary = join(this.stateRoot, `.cpolar.${randomBytes(12).toString('hex')}.tmp`)\n const body = `authtoken: ${JSON.stringify(token)}\\nconsole_ui: false\\nupdate: false\\ninspect_db_size: -1\\n`\n try {\n await writeFile(temporary, body, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, this.configFile)\n await restrictPrivateFile(this.configFile)\n } catch (error) {\n await rm(temporary, { force: true })\n throw error\n }\n this.configured = true\n this.errorCode = undefined\n })\n }\n\n /** Remove every cpolar file owned by DSH Mobile without touching global state. */\n purge(): Promise<CpolarComponentStatus> {\n return this.enqueue(async () => {\n await Promise.all([\n rm(this.componentRoot, { recursive: true, force: true }),\n rm(this.stateRoot, { recursive: true, force: true }),\n rm(this.logRoot, { recursive: true, force: true }),\n rm(this.stagingRoot, { recursive: true, force: true }),\n ])\n this.installed = false\n this.configured = false\n this.errorCode = undefined\n })\n }\n\n private enqueue(operation: () => Promise<void>): Promise<CpolarComponentStatus> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task.then(() => this.status())\n }\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport { readFile } from 'node:fs/promises'\nimport { isAbsolute, join } from 'node:path'\nimport { createRequire } from 'node:module'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { DSH_MOBILE_VERSION } from './version.js'\n\nconst PACKAGE_NAME = 'dsh-mobile'\nconst NPM_LATEST_URL = 'https://registry.npmjs.org/dsh-mobile/latest'\nconst GITHUB_LATEST_URL = 'https://github.com/saya-ch/dsh-mobile/releases/latest'\nconst GITHUB_API_LATEST_URL = 'https://api.github.com/repos/saya-ch/dsh-mobile/releases/latest'\nconst GITHUB_RELEASES_URL = 'https://github.com/saya-ch/dsh-mobile/releases'\nconst RELEASE_NOTES_MAX_CHARS = 4000\nconst STATUS_CACHE_MS = 10 * 60_000\nconst REQUEST_TIMEOUT_MS = 8_000\nconst UPDATE_TIMEOUT_MS = 120_000\nconst UPDATE_TERMINATION_GRACE_MS = 1_500\n\nconst NUMERIC_VERSION_IDENTIFIER = '(?:0|[1-9]\\\\d*)'\nconst WILDCARD_VERSION_IDENTIFIER = '(?:[xX*])'\nconst PARTIAL_VERSION = `(?:${WILDCARD_VERSION_IDENTIFIER}|${NUMERIC_VERSION_IDENTIFIER}(?:\\\\.(?:${WILDCARD_VERSION_IDENTIFIER}|${NUMERIC_VERSION_IDENTIFIER}(?:\\\\.(?:${WILDCARD_VERSION_IDENTIFIER}|${NUMERIC_VERSION_IDENTIFIER}))?))?)`\nconst FULL_VERSION = `${NUMERIC_VERSION_IDENTIFIER}\\\\.${NUMERIC_VERSION_IDENTIFIER}\\\\.${NUMERIC_VERSION_IDENTIFIER}(?:-[0-9A-Za-z-]+(?:\\\\.[0-9A-Za-z-]+)*)?(?:\\\\+[0-9A-Za-z-]+(?:\\\\.[0-9A-Za-z-]+)*)?`\nconst RANGE_VERSION = `(?:${FULL_VERSION}|${PARTIAL_VERSION})`\nconst COMPARATOR = new RegExp(`^(?:<=|>=|<|>|=|~|\\\\^)?${RANGE_VERSION}$`, 'u')\nconst HYPHEN_RANGE = new RegExp(`^${RANGE_VERSION} +[-] +${RANGE_VERSION}$`, 'u')\nconst DIST_TAG = /^[A-Za-z][A-Za-z0-9._-]{0,127}$/u\n\ninterface Semver {\n readonly core: readonly [number, number, number]\n readonly prerelease: readonly (number | string)[]\n}\n\n/** Release information safe to return through the loopback administration API. */\nexport interface PluginReleaseStatus {\n readonly installedVersion: string\n readonly latestVersion?: string\n readonly updateAvailable: boolean\n readonly updateSupported: boolean\n readonly androidVersion?: string\n readonly androidDownloadUrl: string\n /** Trimmed body of the latest GitHub release (What’s new + notices). */\n readonly releaseNotes?: string\n}\n\n/** Result returned after the profile package has been replaced successfully. */\nexport interface PluginUpdateResult {\n readonly installedVersion: string\n readonly restartRequired: true\n}\n\ninterface PluginReleaseManagerOptions {\n readonly profileDirectory: string | undefined\n readonly installedVersion?: string\n readonly fetch?: typeof globalThis.fetch\n readonly runUpdate?: (profileDirectory: string, version: string) => Promise<void>\n readonly readInstalledVersion?: (profileDirectory: string) => Promise<string | undefined>\n readonly now?: () => number\n readonly updateProcess?: PnpmUpdateRuntime\n}\n\ninterface UpdateProcessExit {\n readonly code: number | null\n readonly signal: NodeJS.Signals | null\n}\n\ninterface UpdateProcessRequest {\n readonly command: string\n readonly args: readonly string[]\n readonly cwd: string\n readonly detached: boolean\n readonly platform: NodeJS.Platform\n readonly shell: false\n}\n\ninterface ManagedUpdateProcess {\n readonly completion: Promise<UpdateProcessExit>\n readonly stderr?: NodeJS.ReadableStream\n terminateTree(): Promise<void>\n}\n\ninterface UpdateDeadline {\n readonly promise: Promise<void>\n cancel(): void\n}\n\ninterface PnpmUpdateRuntime {\n readonly platform?: NodeJS.Platform\n readonly timeoutMs?: number\n readonly windowsCommandInterpreter?: string\n readonly start?: (request: UpdateProcessRequest) => ManagedUpdateProcess\n readonly deadline?: (timeoutMs: number) => UpdateDeadline\n}\n\nfunction parseSemver(value: string): Semver | undefined {\n const match = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([0-9A-Za-z.-]+))?(?:\\+[0-9A-Za-z.-]+)?$/u.exec(value)\n if (match === null) return undefined\n const core = [Number(match[1]), Number(match[2]), Number(match[3])] as const\n if (core.some(part => !Number.isSafeInteger(part))) return undefined\n const prerelease = match[4] === undefined\n ? []\n : match[4].split('.').map((part): number | string => /^\\d+$/u.test(part) ? Number(part) : part)\n if (prerelease.some(part => typeof part === 'number' && !Number.isSafeInteger(part))) return undefined\n return Object.freeze({ core, prerelease: Object.freeze(prerelease) })\n}\n\n/** Compare two strict SemVer strings, including prerelease precedence. */\nexport function comparePluginVersions(left: string, right: string): number | undefined {\n const a = parseSemver(left)\n const b = parseSemver(right)\n if (a === undefined || b === undefined) return undefined\n for (let index = 0; index < a.core.length; index += 1) {\n const difference = a.core[index]! - b.core[index]!\n if (difference !== 0) return Math.sign(difference)\n }\n if (a.prerelease.length === 0 || b.prerelease.length === 0) {\n return a.prerelease.length === b.prerelease.length ? 0 : a.prerelease.length === 0 ? 1 : -1\n }\n const length = Math.max(a.prerelease.length, b.prerelease.length)\n for (let index = 0; index < length; index += 1) {\n const leftPart = a.prerelease[index]\n const rightPart = b.prerelease[index]\n if (leftPart === undefined || rightPart === undefined) return leftPart === undefined ? -1 : 1\n if (leftPart === rightPart) continue\n if (typeof leftPart === 'number' && typeof rightPart === 'number') return Math.sign(leftPart - rightPart)\n if (typeof leftPart === 'number') return -1\n if (typeof rightPart === 'number') return 1\n return leftPart < rightPart ? -1 : 1\n }\n return 0\n}\n\nfunction isComparatorSet(value: string): boolean {\n if (HYPHEN_RANGE.test(value)) return true\n const normalized = value.replace(/(<=|>=|<|>|=|~|\\^) +/gu, '$1')\n const comparators = normalized.split(/ +/u)\n return comparators.length > 0 && comparators.every(comparator => COMPARATOR.test(comparator))\n}\n\nfunction isNpmVersionRange(value: string): boolean {\n if (!/^[0-9xX*<>=~^|.+\\- ]+$/u.test(value)) return false\n const alternatives = value.split(/ *\\|\\| */u)\n return alternatives.length > 0 && alternatives.every(alternative => alternative !== '' && isComparatorSet(alternative))\n}\n\n/** Return whether pnpm may safely replace this profile dependency from an npm version, range, or tag. */\nexport function isRegistryPluginSpec(value: unknown): value is string {\n if (typeof value !== 'string' || value.trim() !== value || value === '' || /[\\u0000-\\u001f\\u007f]/u.test(value)) return false\n if (/\\.(?:tgz|tar(?:\\.gz)?)$/iu.test(value)) return false\n return parseSemver(value) !== undefined || isNpmVersionRange(value) || DIST_TAG.test(value)\n}\n\n/** Resolve the DSH profile named by the current launcher arguments. */\nexport function launchedProfileName(argv: readonly string[]): string {\n for (let index = 0; index < argv.length; index += 1) {\n if (argv[index] === '--profile') {\n const candidate = argv[index + 1]\n if (candidate !== undefined && /^[\\w.-]+$/u.test(candidate)) return candidate\n }\n const match = /^--profile=([\\w.-]+)$/u.exec(argv[index] ?? '')\n if (match?.[1] !== undefined) return match[1]\n }\n return 'web'\n}\n\ninterface DesktopProfiles {\n readonly current?: { readonly dir?: string }\n}\n\n/** Resolve the launcher-owned Desktop profile, or the CLI profile outside Desktop. */\nexport function releaseProfileDirectory(ctx: Pick<Context, 'get'>, dshHome: string, argv: readonly string[]): string | undefined {\n const desktopProfiles = ctx.get('desktopProfiles') as DesktopProfiles | undefined\n const desktopDirectory = desktopProfiles?.current?.dir\n if (typeof desktopDirectory === 'string' && isAbsolute(desktopDirectory)) return desktopDirectory\n // Desktop selects its profile outside argv; never update an unrelated Web profile.\n if (desktopProfiles !== undefined || ctx.get('desktopRuntime') !== undefined) return undefined\n return join(dshHome, 'profiles', launchedProfileName(argv))\n}\n\nasync function profileDependencySpec(profileDirectory: string): Promise<string | undefined> {\n try {\n const manifest = JSON.parse(await readFile(join(profileDirectory, 'package.json'), 'utf8')) as {\n readonly dependencies?: Readonly<Record<string, unknown>>\n }\n const value = manifest.dependencies?.[PACKAGE_NAME]\n return typeof value === 'string' ? value : undefined\n } catch {\n return undefined\n }\n}\n\nasync function fetchNpmVersion(fetcher: typeof globalThis.fetch): Promise<string | undefined> {\n const response = await fetcher(NPM_LATEST_URL, {\n headers: { accept: 'application/json', 'user-agent': 'dsh-mobile-release-check' },\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n })\n if (!response.ok) return undefined\n const payload = await response.json() as { readonly version?: unknown }\n return typeof payload.version === 'string' && parseSemver(payload.version) !== undefined\n ? payload.version\n : undefined\n}\n\nfunction githubReleaseVersion(location: string | null, responseUrl: string): string | undefined {\n let url: URL\n try { url = new URL(location ?? responseUrl, GITHUB_LATEST_URL) }\n catch { return undefined }\n if (url.origin !== 'https://github.com' || url.username !== '' || url.password !== '' || url.search !== '' || url.hash !== '') return undefined\n const prefix = '/saya-ch/dsh-mobile/releases/tag/v'\n if (!url.pathname.startsWith(prefix)) return undefined\n let version: string\n try { version = decodeURIComponent(url.pathname.slice(prefix.length)) } catch { return undefined }\n return parseSemver(version) === undefined ? undefined : version\n}\n\nfunction androidReleaseDownloadUrl(version: string | undefined): string {\n if (version === undefined) return GITHUB_RELEASES_URL\n const tag = `v${version}`\n return `https://github.com/saya-ch/dsh-mobile/releases/download/${encodeURIComponent(tag)}/dsh-mobile-android-${encodeURIComponent(tag)}.apk`\n}\n\nasync function fetchAndroidVersion(fetcher: typeof globalThis.fetch): Promise<string | undefined> {\n const response = await fetcher(GITHUB_LATEST_URL, {\n method: 'GET',\n redirect: 'manual',\n headers: { accept: 'text/html', 'user-agent': 'dsh-mobile-release-check' },\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n })\n return githubReleaseVersion(response.headers.get('location'), response.url)\n}\n\n/** Best-effort body of the latest GitHub release, trimmed to a bounded size. */\nasync function fetchReleaseNotes(fetcher: typeof globalThis.fetch): Promise<string | undefined> {\n const response = await fetcher(GITHUB_API_LATEST_URL, {\n headers: { accept: 'application/vnd.github+json', 'user-agent': 'dsh-mobile-release-check' },\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n })\n if (!response.ok) return undefined\n const payload = await response.json() as { readonly body?: unknown }\n if (typeof payload.body !== 'string') return undefined\n const notes = payload.body.trim()\n return notes === '' ? undefined : notes.slice(0, RELEASE_NOTES_MAX_CHARS)\n}\n\nasync function readProfileInstalledVersion(profileDirectory: string): Promise<string | undefined> {\n try {\n const manifestPath = createRequire(join(profileDirectory, 'package.json')).resolve(`${PACKAGE_NAME}/package.json`)\n const manifest = JSON.parse(await readFile(manifestPath, 'utf8')) as { readonly version?: unknown }\n return typeof manifest.version === 'string' ? manifest.version : undefined\n } catch {\n return undefined\n }\n}\n\nfunction childCompletion(child: ChildProcess): Promise<UpdateProcessExit> {\n return new Promise<UpdateProcessExit>((resolveCompletion, rejectCompletion) => {\n child.once('error', rejectCompletion)\n child.once('close', (code, signal) => { resolveCompletion({ code, signal }) })\n })\n}\n\nfunction createDeadline(timeoutMs: number): UpdateDeadline {\n let timer: NodeJS.Timeout | undefined\n const promise = new Promise<void>(resolveTimeout => {\n timer = setTimeout(resolveTimeout, timeoutMs)\n timer.unref()\n })\n return {\n promise,\n cancel: () => {\n if (timer !== undefined) clearTimeout(timer)\n timer = undefined\n },\n }\n}\n\nasync function taskkillProcessTree(pid: number): Promise<void> {\n const killer = spawn('taskkill.exe', ['/PID', String(pid), '/T', '/F'], {\n shell: false,\n windowsHide: true,\n stdio: 'ignore',\n })\n const result = await childCompletion(killer)\n if (result.code !== 0) throw new Error('plugin_update_tree_termination_failed')\n}\n\nasync function completionWithin(completion: Promise<UpdateProcessExit>, timeoutMs: number): Promise<boolean> {\n let timer: NodeJS.Timeout | undefined\n try {\n return await Promise.race([\n completion.then(() => true, () => true),\n new Promise<false>(resolveTimeout => {\n timer = setTimeout(() => { resolveTimeout(false) }, timeoutMs)\n timer.unref()\n }),\n ])\n } finally {\n if (timer !== undefined) clearTimeout(timer)\n }\n}\n\nfunction processMissing(error: unknown): boolean {\n return (error as NodeJS.ErrnoException).code === 'ESRCH'\n}\n\nasync function terminateProcessTree(child: ChildProcess, completion: Promise<UpdateProcessExit>, platform: NodeJS.Platform): Promise<void> {\n if (child.exitCode !== null || child.signalCode !== null) return\n const pid = child.pid\n if (pid === undefined) {\n child.kill('SIGKILL')\n if (!await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) {\n throw new Error('plugin_update_tree_termination_timeout')\n }\n return\n }\n if (platform === 'win32') {\n try { await taskkillProcessTree(pid) } catch (error) {\n if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL')\n if (!await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) {\n throw new AggregateError([error, new Error('plugin_update_tree_termination_timeout')], 'plugin update tree termination failed')\n }\n throw error\n }\n if (!await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) {\n if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL')\n if (!await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) {\n throw new Error('plugin_update_tree_termination_timeout')\n }\n }\n return\n }\n try { process.kill(-pid, 'SIGTERM') } catch (error) {\n if (!processMissing(error)) throw error\n if (!await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) {\n throw new Error('plugin_update_tree_termination_timeout')\n }\n return\n }\n if (await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) return\n try { process.kill(-pid, 'SIGKILL') } catch (error) {\n if (!processMissing(error)) throw error\n }\n if (!await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) {\n throw new Error('plugin_update_tree_termination_timeout')\n }\n}\n\nfunction startUpdateProcess(request: UpdateProcessRequest): ManagedUpdateProcess {\n const child = spawn(request.command, [...request.args], {\n cwd: request.cwd,\n detached: request.detached,\n shell: request.shell,\n windowsHide: true,\n stdio: ['ignore', 'ignore', 'pipe'],\n })\n const completion = childCompletion(child)\n return {\n completion,\n ...(child.stderr === null ? {} : { stderr: child.stderr }),\n terminateTree: async () => terminateProcessTree(child, completion, request.platform),\n }\n}\n\nfunction updateFailure(cause?: unknown): Error {\n return cause === undefined ? new Error('plugin_update_failed') : new Error('plugin_update_failed', { cause })\n}\n\nasync function runPnpmUpdate(profileDirectory: string, version: string, runtime: PnpmUpdateRuntime = {}): Promise<void> {\n if (parseSemver(version) === undefined) throw new Error('plugin_update_unavailable')\n const platform = runtime.platform ?? process.platform\n const packageSpec = `${PACKAGE_NAME}@${version}`\n const managed = (runtime.start ?? startUpdateProcess)({\n command: platform === 'win32'\n ? (runtime.windowsCommandInterpreter ?? process.env.ComSpec ?? 'cmd.exe')\n : 'pnpm',\n args: platform === 'win32'\n ? ['/d', '/s', '/c', 'pnpm.cmd', 'add', packageSpec]\n : ['add', packageSpec],\n cwd: profileDirectory,\n detached: platform !== 'win32',\n platform,\n shell: false,\n })\n let diagnostics = ''\n managed.stderr?.on('data', chunk => {\n if (diagnostics.length < 4096) diagnostics += Buffer.from(chunk).toString('utf8').slice(0, 4096 - diagnostics.length)\n })\n const completion = managed.completion.then(\n result => ({ kind: 'exit' as const, result }),\n error => ({ kind: 'error' as const, error }),\n )\n const deadline = (runtime.deadline ?? createDeadline)(runtime.timeoutMs ?? UPDATE_TIMEOUT_MS)\n const first = await Promise.race([\n completion,\n deadline.promise.then(() => ({ kind: 'timeout' as const })),\n ])\n deadline.cancel()\n if (first.kind === 'error') throw updateFailure(first.error)\n if (first.kind === 'exit') {\n if (first.result.code === 0) return\n const detail = diagnostics.trim() || `pnpm exited with ${first.result.signal ?? String(first.result.code)}`\n throw updateFailure(new Error(detail))\n }\n let terminationError: unknown\n try { await managed.terminateTree() } catch (error) { terminationError = error }\n if (terminationError !== undefined) throw updateFailure(terminationError)\n const stopped = await completion\n if (stopped.kind === 'error') throw updateFailure(stopped.error)\n throw updateFailure(new Error('plugin update timed out'))\n}\n\n/** Cached npm/GitHub release lookup and guarded profile-local package update. */\nexport class PluginReleaseManager {\n private readonly profileDirectory: string | undefined\n private readonly installedVersion: string\n private readonly fetcher: typeof globalThis.fetch\n private readonly runner: (profileDirectory: string, version: string) => Promise<void>\n private readonly installedVersionReader: (profileDirectory: string) => Promise<string | undefined>\n private readonly now: () => number\n private cache: { readonly expiresAt: number; readonly status: PluginReleaseStatus } | undefined\n private activeUpdate: Promise<PluginUpdateResult> | undefined\n\n constructor(options: PluginReleaseManagerOptions) {\n this.profileDirectory = options.profileDirectory\n this.installedVersion = options.installedVersion ?? DSH_MOBILE_VERSION\n this.fetcher = options.fetch ?? globalThis.fetch\n this.runner = options.runUpdate ?? ((profileDirectory, version) => runPnpmUpdate(profileDirectory, version, options.updateProcess))\n this.installedVersionReader = options.readInstalledVersion ?? readProfileInstalledVersion\n this.now = options.now ?? Date.now\n }\n\n /** Read cached release metadata and suppress external lookup failures. */\n async status(force = false): Promise<PluginReleaseStatus> {\n if (!force && this.cache !== undefined && this.cache.expiresAt > this.now()) return this.cache.status\n const dependencySpec = this.profileDirectory === undefined ? undefined : await profileDependencySpec(this.profileDirectory)\n const updateSupported = isRegistryPluginSpec(dependencySpec)\n const [npmResult, androidResult, notesResult] = await Promise.allSettled([\n fetchNpmVersion(this.fetcher),\n fetchAndroidVersion(this.fetcher),\n fetchReleaseNotes(this.fetcher),\n ])\n const latestVersion = npmResult.status === 'fulfilled' ? npmResult.value : undefined\n const androidVersion = androidResult.status === 'fulfilled' ? androidResult.value : undefined\n const releaseNotes = notesResult.status === 'fulfilled' ? notesResult.value : undefined\n const comparison = latestVersion === undefined\n ? undefined\n : comparePluginVersions(latestVersion, this.installedVersion)\n const status: PluginReleaseStatus = Object.freeze({\n installedVersion: this.installedVersion,\n ...(latestVersion === undefined ? {} : { latestVersion }),\n updateAvailable: updateSupported && comparison === 1,\n updateSupported,\n ...(androidVersion === undefined ? {} : { androidVersion }),\n androidDownloadUrl: androidReleaseDownloadUrl(androidVersion),\n ...(releaseNotes === undefined ? {} : { releaseNotes }),\n })\n this.cache = { expiresAt: this.now() + STATUS_CACHE_MS, status }\n return status\n }\n\n /** Install the latest npm release into the active profile, then require a DSH restart. */\n async update(): Promise<PluginUpdateResult> {\n if (this.activeUpdate !== undefined) return this.activeUpdate\n this.activeUpdate = this.updateOnce()\n try { return await this.activeUpdate }\n finally { this.activeUpdate = undefined }\n }\n\n private async updateOnce(): Promise<PluginUpdateResult> {\n const profileDirectory = this.profileDirectory\n if (profileDirectory === undefined) throw new Error('plugin_update_unsupported')\n const status = await this.status(true)\n if (!status.updateSupported) throw new Error('plugin_update_unsupported')\n if (!status.updateAvailable || status.latestVersion === undefined) throw new Error('plugin_update_unavailable')\n await this.runner(profileDirectory, status.latestVersion)\n const installed = await this.installedVersionReader(profileDirectory)\n if (installed !== status.latestVersion) throw new Error('plugin_update_failed')\n this.cache = undefined\n return Object.freeze({ installedVersion: installed, restartRequired: true })\n }\n}\n","import { createWriteStream, type WriteStream } from 'node:fs'\nimport { lstat, mkdir, rename, rm } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { Logger, type Context, type Exporter, type Message } from '@deepseek-ai/cordis'\n\nconst MAX_LOG_BYTES = 5 * 1024 * 1024\n\n/** Install a plugin-scoped Cordis exporter backed by a private UTF-8 log file. */\nexport async function installMobileFileLogger(ctx: Context, stateDirectory: string): Promise<string> {\n const directory = join(stateDirectory, 'logs')\n const file = join(directory, 'dsh-mobile.log')\n const previous = `${file}.1`\n await mkdir(directory, { recursive: true, mode: 0o700 })\n const entry = await lstat(file).catch(() => undefined)\n if (entry !== undefined && entry.isFile() && !entry.isSymbolicLink() && entry.size >= MAX_LOG_BYTES) {\n await rm(previous, { force: true })\n await rename(file, previous)\n }\n const stream: WriteStream = createWriteStream(file, { flags: 'a', encoding: 'utf8', mode: 0o600 })\n const exporter: Exporter = {\n colors: false,\n maxLength: 16 * 1024,\n levels: { default: -1, 'dsh-mobile': 3 },\n export(message: Message): void {\n if (message.name !== 'dsh-mobile') return\n const record = {\n timestamp: new Date(message.ts).toISOString(),\n level: message.type,\n logger: message.name,\n message: Logger.format(exporter, message),\n }\n stream.write(`${JSON.stringify(record)}\\n`)\n },\n }\n ctx.logger.exporter(exporter)\n ctx.effect(() => () => { stream.end() }, 'dsh-mobile file logger')\n return file\n}\n","import {\n X509Certificate,\n createPrivateKey,\n createPublicKey,\n} from 'node:crypto'\nimport { execFileText as execFile } from './exec-file.js'\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises'\nimport { networkInterfaces, type NetworkInterfaceInfo } from 'node:os'\nimport { basename, dirname, join } from 'node:path'\nimport { generate } from 'selfsigned'\nimport { restrictPrivateFile } from './private-file.js'\n\n/** One active private IPv4 address tied to a stable operating-system interface name. */\nexport interface LanNetwork {\n readonly name: string\n readonly address: string\n readonly cidr: string\n}\n\n/** Versioned setup that survives DHCP address changes on the selected interface. */\nexport interface ManagedSetup {\n readonly version: 2\n readonly networkInterface: string\n readonly listenPort: number\n /** Legacy setup snapshot retained for file compatibility; the active WebServer port wins at runtime. */\n readonly upstreamOrigin: string\n readonly tls: {\n readonly mode: 'managed'\n readonly caCertFile: string\n readonly caKeyFile: string\n readonly certFile: string\n readonly keyFile: string\n }\n}\n\ntype InterfaceTable = NodeJS.Dict<NetworkInterfaceInfo[]>\ntype RouteCommand = (file: string, args: readonly string[]) => Promise<string>\n\nconst VIRTUAL_INTERFACE_MARKERS = [\n 'bridge', 'docker', 'hyper-v', 'mihomo', 'radmin', 'tailscale', 'tap', 'tun',\n 'utun', 'vbox', 'veth', 'virtual', 'vmware', 'vpn', 'vethernet', 'wsl', 'zerotier',\n]\n\n/** Route inspection is advisory; never let a system command hold setup open indefinitely. */\nconst ROUTE_COMMAND_TIMEOUT_MS = 5_000\n\nfunction requiredString(value: unknown, name: string): string {\n if (typeof value !== 'string' || value.length === 0) throw new Error(`${name} must be a non-empty string`)\n return value\n}\n\n/** Validate the durable managed setup before it controls network and filesystem operations. */\nexport function parseManagedSetup(value: unknown): ManagedSetup {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error('mobile setup file must be an object')\n }\n const record = value as Record<string, unknown>\n if (record.version !== 2 || Reflect.ownKeys(record)\n .some(key => typeof key !== 'string' || !['version', 'networkInterface', 'listenPort', 'upstreamOrigin', 'tls'].includes(key))) {\n throw new Error('mobile setup file has an unsupported format')\n }\n if (!Number.isSafeInteger(record.listenPort) || (record.listenPort as number) < 1024\n || (record.listenPort as number) > 65535) {\n throw new Error('mobile setup listenPort must be from 1024 through 65535')\n }\n if (typeof record.tls !== 'object' || record.tls === null || Array.isArray(record.tls)) {\n throw new Error('mobile setup tls must be an object')\n }\n const tls = record.tls as Record<string, unknown>\n if (tls.mode !== 'managed' || Reflect.ownKeys(tls)\n .some(key => typeof key !== 'string' || !['mode', 'caCertFile', 'caKeyFile', 'certFile', 'keyFile'].includes(key))) {\n throw new Error('mobile setup tls has an unsupported format')\n }\n return Object.freeze({\n version: 2,\n networkInterface: requiredString(record.networkInterface, 'mobile setup networkInterface'),\n listenPort: record.listenPort as number,\n upstreamOrigin: requiredString(record.upstreamOrigin, 'mobile setup upstreamOrigin'),\n tls: Object.freeze({\n mode: 'managed',\n caCertFile: requiredString(tls.caCertFile, 'mobile setup tls.caCertFile'),\n caKeyFile: requiredString(tls.caKeyFile, 'mobile setup tls.caKeyFile'),\n certFile: requiredString(tls.certFile, 'mobile setup tls.certFile'),\n keyFile: requiredString(tls.keyFile, 'mobile setup tls.keyFile'),\n }),\n })\n}\n\nfunction privateIpv4(value: string): boolean {\n const parts = value.split('.').map(Number)\n return parts.length === 4 && parts.every(part => Number.isInteger(part) && part >= 0 && part <= 255)\n && (parts[0] === 10\n || (parts[0] === 172 && parts[1]! >= 16 && parts[1]! <= 31)\n || (parts[0] === 192 && parts[1] === 168))\n}\n\nfunction networkCidr(address: string, cidr: string): string {\n const prefix = Number(cidr.slice(cidr.lastIndexOf('/') + 1))\n const value = address.split('.').reduce((total, part) => ((total << 8) | Number(part)) >>> 0, 0)\n const mask = prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0\n const network = (value & mask) >>> 0\n return `${[24, 16, 8, 0].map(shift => (network >>> shift) & 255).join('.')}/${String(prefix)}`\n}\n\n/** List current private IPv4 candidates with their interface identity. */\nexport function availableLanNetworks(table: InterfaceTable = networkInterfaces()): LanNetwork[] {\n const candidates = Object.entries(table).flatMap(([name, entries]) => (entries ?? [])\n .filter(entry => entry.family === 'IPv4' && !entry.internal && privateIpv4(entry.address) && entry.cidr !== null)\n .map(entry => ({ name, address: entry.address, cidr: networkCidr(entry.address, entry.cidr!) })))\n return [...new Map(candidates.map(entry => [`${entry.name}\\0${entry.address}`, entry])).values()]\n}\n\nfunction likelyVirtualInterface(name: string): boolean {\n const normalized = name.toLowerCase().replaceAll(/[^a-z0-9]+/gu, ' ')\n return VIRTUAL_INTERFACE_MARKERS.some(marker => normalized.includes(marker.replaceAll('-', ' ')))\n || /^(?:br|wg)\\d*\\b/u.test(normalized)\n}\n\nasync function runRouteCommand(file: string, args: readonly string[]): Promise<string> {\n const result = await execFile(file, [...args], { encoding: 'utf8', timeout: ROUTE_COMMAND_TIMEOUT_MS })\n return result.stdout\n}\n\nfunction uniqueLines(output: string): string[] {\n return [...new Set(output.split(/\\r?\\n/gu).map(line => line.trim()).filter(Boolean))]\n}\n\n/** Return operating-system default-route interfaces in routing preference order. */\nexport async function preferredLanInterfaceNames(\n platform: NodeJS.Platform = process.platform,\n run: RouteCommand = runRouteCommand,\n): Promise<string[]> {\n try {\n if (platform === 'win32') {\n const script = [\n \"$routes = Get-NetRoute -AddressFamily IPv4 -DestinationPrefix '0.0.0.0/0' -ErrorAction Stop\",\n \"$ranked = $routes | Where-Object { $_.State -eq 'Alive' -and $_.NextHop -ne '0.0.0.0' } | ForEach-Object {\",\n ' $route = $_',\n ' $adapter = Get-NetAdapter -InterfaceIndex $route.InterfaceIndex -ErrorAction SilentlyContinue',\n ' $ip = Get-NetIPInterface -AddressFamily IPv4 -InterfaceIndex $route.InterfaceIndex -ErrorAction SilentlyContinue',\n \" if ($adapter -and $ip -and $adapter.Status -eq 'Up' -and $adapter.HardwareInterface -eq $true -and $adapter.Virtual -ne $true) {\",\n ' [pscustomobject]@{ Name = $route.InterfaceAlias; Metric = [int]$route.RouteMetric + [int]$ip.InterfaceMetric }',\n ' }',\n '}',\n '$ranked | Sort-Object Metric | Select-Object -ExpandProperty Name -Unique',\n ].join('; ')\n return uniqueLines(await run('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script]))\n }\n if (platform === 'linux') {\n const routes = uniqueLines(await run('ip', ['-o', '-4', 'route', 'show', 'default']))\n .map(line => ({\n name: /(?:^|\\s)dev\\s+(\\S+)/u.exec(line)?.[1],\n metric: Number(/(?:^|\\s)metric\\s+(\\d+)/u.exec(line)?.[1] ?? 0),\n }))\n .filter((route): route is { name: string; metric: number } => route.name !== undefined\n && !likelyVirtualInterface(route.name))\n .sort((left, right) => left.metric - right.metric)\n return [...new Set(routes.map(route => route.name))]\n }\n if (platform === 'darwin') {\n const name = /^\\s*interface:\\s*(\\S+)\\s*$/mu.exec(await run('route', ['-n', 'get', 'default']))?.[1]\n return name === undefined || likelyVirtualInterface(name) ? [] : [name]\n }\n } catch {\n // Route discovery is advisory; deterministic candidate checks below remain the fallback.\n }\n return []\n}\n\n/** Select an active LAN, optionally by address or by a previously saved interface name. */\nexport function selectLanNetwork(\n requestedAddress?: string,\n requestedInterface?: string,\n table?: InterfaceTable,\n preferredInterfaces: readonly string[] = [],\n): LanNetwork {\n const candidates = availableLanNetworks(table)\n if (requestedAddress !== undefined) {\n const match = candidates.find(candidate => candidate.address === requestedAddress)\n if (match === undefined) throw new Error(`--address ${requestedAddress} is not an active private LAN address`)\n return match\n }\n if (requestedInterface !== undefined) {\n const matches = candidates.filter(candidate => candidate.name === requestedInterface)\n if (matches.length === 1) return matches[0]!\n if (matches.length === 0) {\n throw new Error(`saved LAN interface ${JSON.stringify(requestedInterface)} is not connected`)\n }\n throw new Error(`saved LAN interface ${JSON.stringify(requestedInterface)} has more than one private IPv4 address`)\n }\n if (candidates.length === 1) return candidates[0]!\n if (candidates.length === 0) throw new Error('no active private LAN address was found; connect to Wi-Fi or Ethernet')\n for (const name of preferredInterfaces) {\n const matches = candidates.filter(candidate => candidate.name === name)\n if (matches.length === 1) return matches[0]!\n }\n const physicalCandidates = candidates.filter(candidate => !likelyVirtualInterface(candidate.name))\n if (physicalCandidates.length === 1) return physicalCandidates[0]!\n throw new Error(`more than one LAN address is active; rerun with --address and one of: ${candidates.map(entry => `${entry.name}=${entry.address}`).join(', ')}`)\n}\n\n/**\n * True when the failure came from LAN selection (the configured network is\n * gone, nothing is up, or several are) rather than from config/TLS code.\n * Callers use it to degrade (warn + stay down) instead of failing boot.\n * Pinned to the selectLanNetwork message contract — keep in sync.\n */\nexport function isNetworkSelectionError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error ?? '')\n return message.includes('is not connected')\n || message.includes('no active private LAN address was found')\n || message.includes('more than one LAN address is active')\n || message.includes('has more than one private IPv4 address')\n || message.includes('is not an active private LAN address')\n}\n\nfunction assertMatchingCa(certPem: string, keyPem: string): X509Certificate {\n const certificate = new X509Certificate(certPem)\n if (!certificate.ca || certificate.subject !== certificate.issuer\n || !certificate.verify(certificate.publicKey)) {\n throw new Error('managed TLS CA must be a self-signed CA certificate')\n }\n const privatePublic = createPublicKey(createPrivateKey(keyPem)).export({ format: 'der', type: 'spki' })\n const certificatePublic = certificate.publicKey.export({ format: 'der', type: 'spki' })\n if (!privatePublic.equals(certificatePublic)) throw new Error('managed TLS CA certificate and key do not match')\n if (Date.parse(certificate.validFrom) > Date.now() || Date.parse(certificate.validTo) <= Date.now()) {\n throw new Error('managed TLS CA certificate is not currently valid')\n }\n return certificate\n}\n\nasync function atomicWrite(file: string, contents: string | Uint8Array): Promise<void> {\n const directory = dirname(file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n const temporary = join(directory, `.${basename(file)}.${process.pid}.tmp`)\n await writeFile(temporary, contents, { mode: 0o600 })\n await rename(temporary, file)\n await restrictPrivateFile(file)\n}\n\n/** Create a long-lived CA or migrate the legacy self-signed server certificate as that CA. */\nexport async function ensureManagedCa(\n setup: ManagedSetup['tls'],\n legacy?: { readonly certFile: string; readonly keyFile: string },\n): Promise<X509Certificate> {\n let certPem: string | undefined\n let keyPem: string | undefined\n try {\n [certPem, keyPem] = await Promise.all([readFile(setup.caCertFile, 'utf8'), readFile(setup.caKeyFile, 'utf8')])\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n let migrated = false\n if (legacy !== undefined) {\n try {\n [certPem, keyPem] = await Promise.all([readFile(legacy.certFile, 'utf8'), readFile(legacy.keyFile, 'utf8')])\n assertMatchingCa(certPem, keyPem)\n migrated = true\n } catch (legacyError) {\n if ((legacyError as NodeJS.ErrnoException).code !== 'ENOENT') throw legacyError\n }\n }\n if (!migrated) {\n const now = new Date()\n const notAfter = new Date(now)\n notAfter.setFullYear(notAfter.getFullYear() + 5)\n const generated = await generate([{ name: 'commonName', value: 'DeepSeek Harness Mobile CA' }], {\n keyType: 'ec',\n curve: 'P-256',\n algorithm: 'sha256',\n notBeforeDate: new Date(now.getTime() - 5 * 60_000),\n notAfterDate: notAfter,\n extensions: [\n { name: 'basicConstraints', cA: true, critical: true },\n { name: 'keyUsage', digitalSignature: true, keyCertSign: true, cRLSign: true, critical: true },\n ],\n })\n certPem = generated.cert\n keyPem = generated.private\n }\n if (certPem === undefined || keyPem === undefined) throw new Error('managed TLS CA creation did not produce key material')\n await Promise.all([atomicWrite(setup.caCertFile, certPem), atomicWrite(setup.caKeyFile, keyPem)])\n }\n if (certPem === undefined || keyPem === undefined) throw new Error('managed TLS CA creation did not produce key material')\n await Promise.all([restrictPrivateFile(setup.caCertFile), restrictPrivateFile(setup.caKeyFile)])\n return assertMatchingCa(certPem, keyPem)\n}\n\n/** Sign and atomically install a server leaf for the interface's current address. */\nexport async function refreshManagedServerCertificate(setup: ManagedSetup, address: string): Promise<void> {\n await Promise.all([restrictPrivateFile(setup.tls.caCertFile), restrictPrivateFile(setup.tls.caKeyFile)])\n const [caCert, caKey] = await Promise.all([\n readFile(setup.tls.caCertFile, 'utf8'),\n readFile(setup.tls.caKeyFile, 'utf8'),\n ])\n assertMatchingCa(caCert, caKey)\n const now = new Date()\n const notAfter = new Date(now)\n notAfter.setDate(notAfter.getDate() + 397)\n const server = await generate([{ name: 'commonName', value: 'DeepSeek Harness Mobile' }], {\n keyType: 'ec',\n curve: 'P-256',\n algorithm: 'sha256',\n notBeforeDate: new Date(now.getTime() - 5 * 60_000),\n notAfterDate: notAfter,\n ca: { cert: caCert, key: caKey },\n extensions: [\n { name: 'basicConstraints', cA: false, critical: true },\n { name: 'keyUsage', digitalSignature: true, critical: true },\n { name: 'extKeyUsage', serverAuth: true },\n { name: 'subjectAltName', altNames: [{ type: 7, ip: address }] },\n ],\n })\n await Promise.all([\n atomicWrite(setup.tls.certFile, server.cert),\n atomicWrite(setup.tls.keyFile, server.private),\n ])\n}\n\n/** Resolve the saved interface to the ordinary gateway config consumed by the Host plugin. */\nexport async function materializeManagedSetup(\n setup: ManagedSetup,\n table?: InterfaceTable,\n): Promise<Record<string, unknown>> {\n const network = selectLanNetwork(undefined, setup.networkInterface, table)\n await refreshManagedServerCertificate(setup, network.address)\n const ca = new X509Certificate(await readFile(setup.tls.caCertFile, 'utf8'))\n return {\n publicOrigin: `https://${network.address}:${String(setup.listenPort)}`,\n listenHost: network.address,\n allowedCidrs: [network.cidr],\n instanceId: ca.fingerprint256.replaceAll(':', '').toLowerCase(),\n pairingCaFile: setup.tls.caCertFile,\n tls: {\n mode: 'provided',\n certFile: setup.tls.certFile,\n keyFile: setup.tls.keyFile,\n },\n }\n}\n","import { execFileText as execFile } from './exec-file.js'\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, join } from 'node:path'\nimport {\n ensureManagedCa,\n refreshManagedServerCertificate,\n type LanNetwork,\n type ManagedSetup,\n} from './managed-setup.js'\nimport { restrictPrivateFile } from './private-file.js'\n\nconst FIREWALL_TCP_RULE = 'DSH Mobile HTTPS'\nconst FIREWALL_UDP_RULE = 'DSH Mobile Discovery'\n\n/** Files and active address produced by one explicit LAN setup confirmation. */\nexport interface ManagedLanSetupResult {\n readonly setup: ManagedSetup\n readonly network: LanNetwork\n readonly origin: string\n readonly androidCertificate: string\n}\n\n/** Inputs required to prepare the managed LAN listener without starting it. */\nexport interface ManagedLanSetupOptions {\n readonly setupFile: string\n readonly controlFile: string\n readonly network: LanNetwork\n readonly listenPort: number\n readonly dshPort: number\n readonly configureFirewall: boolean\n}\n\nasync function runElevatedPowerShell(script: string): Promise<void> {\n const encoded = Buffer.from(script, 'utf16le').toString('base64')\n const launch = [\n \"$ErrorActionPreference = 'Stop'; $process = Start-Process -FilePath 'powershell.exe' -Verb RunAs -WindowStyle Hidden -Wait -PassThru\",\n ` -ArgumentList @('-NoProfile','-NonInteractive','-EncodedCommand','${encoded}')`,\n '; exit $process.ExitCode',\n ].join(' ')\n await execFile('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', launch], { windowsHide: true })\n}\n\n/** Install the two Windows inbound rules used by the managed LAN listener. */\nexport async function configureWindowsFirewall(port: number): Promise<void> {\n if (process.platform !== 'win32') return\n const script = [\n \"$ErrorActionPreference = 'Stop'\",\n `Get-NetFirewallRule -DisplayName '${FIREWALL_TCP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,\n `Get-NetFirewallRule -DisplayName '${FIREWALL_UDP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,\n `New-NetFirewallRule -DisplayName '${FIREWALL_TCP_RULE}' -Direction Inbound -Action Allow -Protocol TCP -LocalPort ${String(port)} -RemoteAddress LocalSubnet -Profile Any | Out-Null`,\n `New-NetFirewallRule -DisplayName '${FIREWALL_UDP_RULE}' -Direction Inbound -Action Allow -Protocol UDP -LocalPort ${String(port)} -RemoteAddress LocalSubnet -Profile Any | Out-Null`,\n ].join('; ')\n await runElevatedPowerShell(script)\n}\n\n/** Remove only the two Windows firewall rules owned by DSH Mobile. */\nexport async function removeWindowsFirewall(): Promise<void> {\n if (process.platform !== 'win32') return\n await runElevatedPowerShell([\n \"$ErrorActionPreference = 'Stop'\",\n `Get-NetFirewallRule -DisplayName '${FIREWALL_TCP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,\n `Get-NetFirewallRule -DisplayName '${FIREWALL_UDP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,\n ].join('; '))\n}\n\n/** Generate private TLS material and persist a managed setup selected by the user. */\nexport async function prepareManagedLanSetup(options: ManagedLanSetupOptions): Promise<ManagedLanSetupResult> {\n if (!isAbsolute(options.setupFile) || !isAbsolute(options.controlFile)) {\n throw new Error('managed LAN setup paths must be absolute')\n }\n if (!Number.isSafeInteger(options.listenPort) || options.listenPort < 1024 || options.listenPort > 65535) {\n throw new Error('managed LAN listenPort must be from 1024 through 65535')\n }\n if (!Number.isSafeInteger(options.dshPort) || options.dshPort < 1024 || options.dshPort > 65535) {\n throw new Error('managed LAN dshPort must be from 1024 through 65535')\n }\n const directory = dirname(options.setupFile)\n const tlsDirectory = join(directory, 'tls')\n await mkdir(tlsDirectory, { recursive: true, mode: 0o700 })\n const managedTls: ManagedSetup['tls'] = {\n mode: 'managed',\n caCertFile: join(tlsDirectory, 'ca.pem'),\n caKeyFile: join(tlsDirectory, 'ca-key.pem'),\n certFile: join(tlsDirectory, 'server-cert.pem'),\n keyFile: join(tlsDirectory, 'server-key.pem'),\n }\n const ca = await ensureManagedCa(managedTls, {\n certFile: join(tlsDirectory, 'cert.pem'),\n keyFile: join(tlsDirectory, 'key.pem'),\n })\n const setup: ManagedSetup = {\n version: 2,\n networkInterface: options.network.name,\n listenPort: options.listenPort,\n upstreamOrigin: `http://127.0.0.1:${String(options.dshPort)}`,\n tls: managedTls,\n }\n await refreshManagedServerCertificate(setup, options.network.address)\n const androidCertificate = join(tlsDirectory, 'dsh-mobile-ca.cer')\n await writeFile(androidCertificate, ca.raw, { mode: 0o600 })\n await Promise.all([\n ...Object.values(managedTls).filter(value => value !== 'managed').map(file => restrictPrivateFile(file)),\n restrictPrivateFile(androidCertificate),\n ])\n if (options.configureFirewall) await configureWindowsFirewall(options.listenPort)\n await Promise.all([\n writeFile(options.setupFile, `${JSON.stringify({\n ...setup,\n tls: Object.fromEntries(Object.entries(setup.tls)\n .map(([key, value]) => [key, typeof value === 'string' ? value.replaceAll('\\\\', '/') : value])),\n }, null, 2)}\\n`, { mode: 0o600 }),\n writeFile(options.controlFile, '{\"version\":1,\"enabled\":true}\\n', { mode: 0o600 }),\n ])\n await Promise.all([restrictPrivateFile(options.setupFile), restrictPrivateFile(options.controlFile)])\n return Object.freeze({\n setup: Object.freeze(setup),\n network: Object.freeze({ ...options.network }),\n origin: `https://${options.network.address}:${String(options.listenPort)}`,\n androidCertificate,\n })\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { boundContextSummary, createUserMessage } from '@deepseek-ai/dsh-llm/message'\ndeclare module '@deepseek-ai/dsh-llm' {\n interface MessageSourceMap {\n 'plugin:dsh-mobile': {\n kind: 'plugin:dsh-mobile'\n form: 'notice'\n summary: string\n }\n }\n}\n// Side-effect type import: activates dsh-commands' Context augmentation so\n// `ctx.commands` and its handler types resolve without a runtime dependency.\nimport type {} from '@deepseek-ai/dsh-commands'\nimport type { WebRoute } from '@deepseek-ai/dsh-host-webserver'\nimport { createRequire } from 'node:module'\nimport { X509Certificate } from 'node:crypto'\nimport { copyFile, lstat, readFile, rm } from 'node:fs/promises'\nimport { dirname, isAbsolute, join, resolve } from 'node:path'\nimport { parseControlFile, parseGatewayConfig, type PluginConfig, type ResolvedGatewayConfig } from './config.js'\nimport { collectConnectionDiagnostics } from './diagnostics.js'\nimport { buildMobileGuide, type MobileGuideState } from './mobile-guide.js'\nimport { createVpsUninstallScript, deployVps, fetchVpsHostKeys, parseVpsDeploymentInput, uninstallVps } from './vps-deploy.js'\nimport { TaskEventHub, watchTaskCompletions } from './task-events.js'\nimport {\n FollowingMobileAccessRuntime,\n JsonMobileAccessControlStore,\n MobileAccessGatewayController,\n type MobileAccessRuntime,\n} from './control.js'\nimport { MobileAccessGateway } from './gateway.js'\nimport { createMobileAccessService, type MobileAccessService } from './extensions.js'\nimport { listComputerImages, readComputerImage } from './computer-images.js'\nimport {\n HttpError,\n LOCAL_ADMIN_PREFIX,\n assertLocalAdminTrust,\n parseRequestTarget,\n readJsonObject,\n sendFailure,\n sendJson,\n} from './http-security.js'\nimport { JsonDeviceStore } from './storage.js'\nimport { BlockedUpgradePathLog, WebSocketPathStore } from './websocket-paths.js'\nimport { FunnelController, funnelExecutable } from './funnel.js'\nimport { CpolarController } from './cpolar.js'\nimport { CpolarComponentManager, type CpolarComponentStatus } from './cpolar-component.js'\nimport { CloudflaredComponentManager, type CloudflaredComponentStatus } from './cloudflared-component.js'\nimport { CloudflaredController } from './cloudflared.js'\nimport {\n CloudflaredTunnelStore,\n mergeSavedCloudflaredTunnelSettings,\n type CloudflaredTunnelStatus,\n} from './cloudflared-tunnel.js'\nimport { FrpComponentManager, type FrpComponentStatus } from './frp-component.js'\nimport { FrpConfigStore, mergeSavedFrpSettings, mergeSavedFrpTarget, type FrpConfigurationStatus } from './frp-config.js'\nimport { FrpController } from './frp.js'\nimport { OriginConfigStore, parseOriginSettings, validateOriginListenPort, type OriginConfigurationStatus, type OriginSettings } from './origin-proxy-config.js'\nimport { OriginController } from './origin-proxy.js'\nimport { PluginReleaseManager, releaseProfileDirectory } from './release-update.js'\nimport { installMobileFileLogger } from './file-logger.js'\nimport {\n configuredRemoteProvider,\n JsonRemoteProviderStore,\n RemoteProviderCoordinator,\n REMOTE_PROVIDERS,\n type RemoteProvider,\n type RemoteProviderController,\n type RemoteProviderStatus,\n} from './remote.js'\nimport { parseAuthority, parseCidr } from './network.js'\nimport {\n availableLanNetworks,\n isNetworkSelectionError,\n materializeManagedSetup,\n parseManagedSetup,\n preferredLanInterfaceNames,\n selectLanNetwork,\n type ManagedSetup,\n} from './managed-setup.js'\nimport { prepareManagedLanSetup, type ManagedLanSetupResult } from './lan-setup.js'\n\n/** Stable Cordis plugin name. */\nexport const name = 'dsh-mobile'\n\n/** The stock WebServer serves the control card; Connection authenticates the loopback DSH origin. */\nexport const inject = ['webServer', 'commands', 'connection']\n\n/** Run cleanup steps in ownership order and report every failure after all steps settle. */\nexport async function settleCleanupSteps(steps: readonly (() => void | Promise<void>)[]): Promise<void> {\n const errors: unknown[] = []\n for (const step of steps) {\n try { await step() } catch (error) { errors.push(error) }\n }\n if (errors.length === 1 && errors[0] instanceof Error) throw errors[0]\n if (errors.length > 0) throw new AggregateError(errors, 'DSH Mobile cleanup failed')\n}\n\ninterface BrowserAuthenticatedConnection {\n authenticatedUrl?: (baseUrl: string) => string\n}\n\n/**\n * Resolve the DSH launch-token URL the gateway exchanges for its upstream cookie.\n *\n * A layer that disables DSH browser authentication — dsh-lan-access with\n * `noAuth: true` replaces `authenticatedUrl` with one returning the bare origin\n * — produces a URL with no query string, so it cannot carry a launch token. That\n * is a legitimate \"this upstream needs no browser auth\" signal: report no URL and\n * the gateway proxies without a cookie instead of failing every route with\n * `upstream_unavailable`. A token-bearing URL keeps the existing exchange, and a\n * connection service that is absent or returns a malformed URL keeps failing\n * closed. Only parsing is guarded: a connection service that throws still fails\n * plugin activation loudly rather than silently proxying without authentication.\n */\nexport function upstreamAuthenticatedUrl(ctx: Context, upstreamOrigin: URL): string | undefined {\n const connection = (ctx as Context & { readonly connection?: BrowserAuthenticatedConnection }).connection\n if (typeof connection?.authenticatedUrl !== 'function') return undefined\n const authenticatedUrl = connection.authenticatedUrl(upstreamOrigin.origin)\n try {\n return new URL(authenticatedUrl).search === '' ? undefined : authenticatedUrl\n } catch {\n return undefined\n }\n}\n\nfunction installedDshVersion(): string {\n try {\n const manifest = createRequire(import.meta.url)('@deepseek-ai/dsh-host-webserver/package.json') as unknown\n if (manifest === null || typeof manifest !== 'object') return 'unknown'\n const version = (manifest as { readonly version?: unknown }).version\n return typeof version === 'string' && version !== '' ? version : 'unknown'\n } catch {\n return 'unknown'\n }\n}\n\nfunction mapAdminError(error: unknown): HttpError {\n if (error instanceof HttpError) return error\n const code = (error as NodeJS.ErrnoException).code\n if (error instanceof Error && error.message.includes('spawn UNKNOWN')) {\n return new HttpError(409, 'frp_component_launch_failed')\n }\n if (code === 'EADDRNOTAVAIL') return new HttpError(409, 'network_address_changed')\n if (code === 'EADDRINUSE') return new HttpError(409, 'listen_port_in_use')\n if (error instanceof Error && error.message.startsWith('saved LAN interface ')) {\n return new HttpError(409, 'network_interface_unavailable')\n }\n if (error instanceof Error && error.message === 'cpolar_authtoken_invalid') {\n return new HttpError(400, 'cpolar_authtoken_invalid')\n }\n if (error instanceof Error && error.message.startsWith('cpolar_')) {\n return new HttpError(409, error.message)\n }\n // Component download and hash failures carry their own stable code, so the panel can show why an\n // install failed instead of a generic server error.\n if (error instanceof Error && error.message.startsWith('cloudflared_')) {\n return new HttpError(409, error.message)\n }\n if (error instanceof Error && [\n 'frp_server_address_invalid',\n 'frp_server_port_invalid',\n 'frp_token_invalid',\n 'frp_public_origin_invalid',\n 'frp_settings_invalid',\n ].includes(error.message)) return new HttpError(400, error.message)\n if (error instanceof Error && error.message.startsWith('frp_')) {\n return new HttpError(409, error.message)\n }\n if (error instanceof Error && [\n 'origin_settings_invalid', 'origin_public_origin_invalid', 'origin_listen_host_invalid',\n 'origin_listen_port_invalid', 'origin_listen_port_reserved', 'origin_allowed_cidrs_invalid',\n ].includes(error.message)) return new HttpError(400, error.message)\n if (error instanceof Error && error.message.startsWith('origin_')) {\n return new HttpError(409, error.message)\n }\n if (error instanceof Error && error.message.startsWith('vps_')) {\n return new HttpError(409, error.message)\n }\n if (error instanceof Error && error.message === 'plugin_update_failed') {\n return new HttpError(500, error.message)\n }\n if (error instanceof Error && error.message.startsWith('plugin_update_')) {\n return new HttpError(409, error.message)\n }\n if (error instanceof Error && error.message.startsWith('lan_setup_')) {\n return new HttpError(409, error.message)\n }\n return new HttpError(500, 'internal_error')\n}\n\nconst SETUP_KEYS = new Set([\n 'version', 'publicOrigin', 'listenHost', 'listenPort', 'upstreamOrigin',\n 'publicAuthorities', 'allowedCidrs', 'instanceId', 'pairingCaFile', 'tls',\n])\n\n/** True when path names a regular file (not a directory or symlink). */\nasync function existsRegularFile(path: string): Promise<boolean> {\n try {\n const info = await lstat(path)\n return info.isFile() && !info.isSymbolicLink()\n } catch {\n return false\n }\n}\n\ntype LoadedSetup = {\n readonly kind: 'fixed'\n readonly config: PluginConfig\n} | {\n readonly kind: 'unconfigured'\n readonly config: PluginConfig\n readonly setupFile: string\n} | {\n readonly kind: 'managed'\n readonly config: PluginConfig\n readonly setup: ManagedSetup\n}\n\nfunction withoutSetupKeys(config: PluginConfig): PluginConfig {\n const merged = { ...config } as Record<string, unknown>\n for (const key of SETUP_KEYS) if (key !== 'version') delete merged[key]\n return merged as unknown as PluginConfig\n}\n\nasync function loadSetup(config: PluginConfig): Promise<LoadedSetup> {\n if (config.setupFile === undefined) return { kind: 'fixed', config }\n if (!isAbsolute(config.setupFile)) throw new Error('setupFile must be an absolute file path')\n let source: string\n try {\n source = await readFile(resolve(config.setupFile), 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return { kind: 'unconfigured', config, setupFile: resolve(config.setupFile) }\n }\n throw error\n }\n let parsed: unknown\n try { parsed = JSON.parse(source) as unknown }\n catch (error) { throw new Error('mobile setup file is not valid JSON', { cause: error }) }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('mobile setup file must be an object')\n }\n const record = parsed as Record<string, unknown>\n if (record.version === 2) {\n return { kind: 'managed', config: withoutSetupKeys(config), setup: parseManagedSetup(record) }\n }\n if (record.version !== 1 || Reflect.ownKeys(record).some(key => typeof key !== 'string' || !SETUP_KEYS.has(key))) {\n throw new Error('mobile setup file has an unsupported format')\n }\n const { version: _version, ...setup } = record\n delete setup.upstreamOrigin\n return {\n kind: 'fixed',\n config: { ...withoutSetupKeys(config), ...setup } as unknown as PluginConfig,\n }\n}\n\nfunction loopbackTemplate(loaded: LoadedSetup, webServerPort: number): ResolvedGatewayConfig {\n const base = withoutSetupKeys(loaded.config)\n const activeUpstreamOrigin = `http://127.0.0.1:${String(webServerPort)}`\n return parseGatewayConfig({\n ...base,\n ...(loaded.kind === 'managed'\n ? { upstreamOrigin: activeUpstreamOrigin }\n : { upstreamOrigin: loaded.config.upstreamOrigin ?? activeUpstreamOrigin }),\n listenHost: '127.0.0.1',\n listenPort: 0,\n publicAuthorities: ['127.0.0.1'],\n allowedCidrs: ['127.0.0.0/8'],\n tls: { mode: 'disabled' },\n })\n}\n\nasync function stableInstanceId(loaded: LoadedSetup, template: ResolvedGatewayConfig): Promise<string> {\n if (loaded.kind !== 'managed') return loaded.config.instanceId ?? template.instanceId\n const certificate = new X509Certificate(await readFile(loaded.setup.tls.caCertFile))\n return certificate.fingerprint256.replaceAll(':', '').toLowerCase()\n}\n\nexport function remoteGatewayConfig(\n template: ResolvedGatewayConfig,\n publicOrigin: string,\n stateFile: string,\n instanceId: string,\n listenPort = 0,\n): ResolvedGatewayConfig {\n const origin = new URL(publicOrigin)\n if (origin.protocol !== 'https:' || origin.username !== '' || origin.password !== ''\n || origin.pathname !== '/' || origin.search !== '' || origin.hash !== '') {\n throw new Error('remote public origin must be an HTTPS origin')\n }\n // The gateway listens on an ephemeral loopback port behind Funnel, while the\n // public authority is HTTPS on 443. Keep that external port explicit so the\n // trust policy never substitutes the private listener port into QR URLs.\n const publicAuthority = origin.port === '' ? `${origin.hostname}:443` : origin.host\n const { pairingCaFile: _pairingCaFile, ...shared } = template\n return Object.freeze({\n ...shared,\n listenHost: '127.0.0.1',\n listenPort,\n authorities: Object.freeze([parseAuthority(publicAuthority)]),\n allowedCidrs: Object.freeze([parseCidr('127.0.0.0/8')]),\n stateFile,\n instanceId,\n tls: Object.freeze({ mode: 'disabled' }),\n publicTls: true,\n discovery: false,\n })\n}\n\n/** Reuse remote HTTPS policy while binding a separately validated private HTTP origin. */\nexport function originGatewayConfig(\n template: ResolvedGatewayConfig,\n settings: OriginSettings,\n stateFile: string,\n instanceId: string,\n listenPort = settings.listenPort,\n): ResolvedGatewayConfig {\n const validated = parseOriginSettings(settings)\n // Port zero is available only to in-process tests, never to saved settings.\n if (listenPort !== 0) validateOriginListenPort(listenPort)\n return Object.freeze({\n ...remoteGatewayConfig(template, validated.publicOrigin, stateFile, instanceId, listenPort),\n listenHost: validated.listenHost,\n allowedCidrs: Object.freeze(validated.allowedCidrs.map(parseCidr)),\n })\n}\n\nfunction remoteControlPayload(\n provider: RemoteProvider,\n status: RemoteProviderStatus,\n gateway: MobileAccessGateway | undefined,\n providerStatuses: Readonly<Record<RemoteProvider, RemoteProviderStatus>>,\n cpolarComponent: CpolarComponentStatus,\n cloudflaredComponent: CloudflaredComponentStatus,\n cloudflaredConfiguration: CloudflaredTunnelStatus,\n frpComponent: FrpComponentStatus,\n frpConfiguration: FrpConfigurationStatus,\n originConfiguration: OriginConfigurationStatus,\n): Record<string, unknown> {\n return {\n provider,\n running: status.enabled,\n state: status.state,\n ...(status.origin === undefined ? {} : { origin: status.origin }),\n ...(status.backendOrigin === undefined ? {} : { backendOrigin: status.backendOrigin }),\n ...(status.loginUrl === undefined ? {} : { loginUrl: status.loginUrl }),\n ...(status.setupUrl === undefined ? {} : { setupUrl: status.setupUrl }),\n ...(status.errorCode === undefined ? {} : { errorCode: status.errorCode }),\n ...(gateway === undefined ? {} : { extensions: gateway.extensionStatus() }),\n providers: {\n tailscale: { bundled: true, running: providerStatuses.tailscale.enabled, state: providerStatuses.tailscale.state },\n cpolar: {\n bundled: false,\n running: providerStatuses.cpolar.enabled,\n state: providerStatuses.cpolar.state,\n component: cpolarComponent,\n },\n cloudflared: {\n bundled: false,\n running: providerStatuses.cloudflared.enabled,\n state: providerStatuses.cloudflared.state,\n component: cloudflaredComponent,\n configuration: cloudflaredConfiguration,\n },\n frp: {\n bundled: false,\n running: providerStatuses.frp.enabled,\n state: providerStatuses.frp.state,\n component: frpComponent,\n configuration: frpConfiguration,\n },\n origin: {\n bundled: true,\n running: providerStatuses.origin.enabled,\n state: providerStatuses.origin.state,\n configuration: originConfiguration,\n },\n },\n }\n}\n\n/** Mount the resident control route and its optional authenticated LAN gateway. */\nexport async function apply(ctx: Context, config: PluginConfig): Promise<void> {\n const dshVersion = installedDshVersion()\n const loaded = await loadSetup(config)\n const mobileAccess: MobileAccessService = createMobileAccessService(ctx)\n const template = loopbackTemplate(loaded, ctx.webServer.port)\n const upstreamLoginUrl = upstreamAuthenticatedUrl(ctx, template.upstreamOrigin)\n const instanceId = await stableInstanceId(loaded, template)\n const stateDirectory = dirname(template.stateFile)\n const logFile = await installMobileFileLogger(ctx, stateDirectory)\n const logger = ctx.logger('dsh-mobile')\n logger.info('logging initialized file=%s', logFile)\n // Completed root turns fan out to every live mobile gateway, whose phones\n // render the notification text locally. One subscription serves all paths.\n const taskEventHub = new TaskEventHub()\n const disposeTaskEvents = watchTaskCompletions(ctx, {\n onTaskCompleted: event => taskEventHub.broadcast(event),\n log(event, fields) { logger.info('task event=%s fields=%o', event, fields) },\n })\n const remoteDirectory = join(stateDirectory, 'remote')\n const configuredDshHome = process.env.DSH_HOME?.trim()\n const dshHome = configuredDshHome === undefined || configuredDshHome === ''\n ? dirname(stateDirectory)\n : resolve(configuredDshHome)\n const releaseManager = new PluginReleaseManager({\n profileDirectory: releaseProfileDirectory(ctx, dshHome, process.argv.slice(2)),\n })\n const remoteProviderStore = new JsonRemoteProviderStore(\n join(remoteDirectory, 'provider.json'),\n configuredRemoteProvider(process.env),\n )\n const initialRemoteProvider = (await remoteProviderStore.load()).provider\n const cpolarComponent = new CpolarComponentManager({ stateDirectory })\n await cpolarComponent.initialize()\n const cloudflaredComponent = new CloudflaredComponentManager({ stateDirectory })\n await cloudflaredComponent.initialize()\n const frpComponent = new FrpComponentManager({ stateDirectory })\n await frpComponent.initialize()\n const frpConfig = new FrpConfigStore(join(remoteDirectory, 'frp', 'config'))\n await frpConfig.initialize()\n const originConfig = new OriginConfigStore(join(remoteDirectory, 'origin', 'config'))\n await originConfig.initialize()\n const cloudflaredTunnel = new CloudflaredTunnelStore(join(remoteDirectory, 'cloudflared'))\n await cloudflaredTunnel.initialize()\n const unregisterBuiltin = mobileAccess.registerExtension({\n schemaVersion: 1,\n id: 'computer-images',\n name: 'Computer images',\n version: '1.0.0',\n description: 'Authenticated computer-side image browser',\n routes: [\n {\n method: 'GET', path: 'list',\n async handle(request) {\n return { status: 200, contentType: 'application/json; charset=utf-8', body: JSON.stringify(await listComputerImages(request.query.get('path'))) }\n },\n },\n {\n method: 'GET', path: 'image',\n async handle(request) {\n const image = await readComputerImage(request.query.get('path'))\n return { status: 200, contentType: image.contentType, headers: { 'content-disposition': `inline; filename*=UTF-8''${encodeURIComponent(image.name)}` }, body: image.body }\n },\n },\n ],\n })\n // One upgrade-path policy shared by the LAN gateway and every remote\n // gateway: admin approvals apply immediately, no restart required.\n const webSocketPaths = new WebSocketPathStore(join(stateDirectory, 'websocket-paths.json'))\n await webSocketPaths.load()\n const blockedUpgradePaths = new BlockedUpgradePathLog()\n let lanGateway: MobileAccessGateway | undefined\n let preparedLanSetup: ManagedLanSetupResult | undefined\n const startGateway = async (candidateConfig: PluginConfig): Promise<MobileAccessRuntime> => {\n const resolved = parseGatewayConfig({\n ...candidateConfig,\n upstreamOrigin: template.upstreamOrigin.origin,\n })\n const candidate = new MobileAccessGateway(\n resolved,\n new JsonDeviceStore(resolved.stateFile, resolved.maxDevices),\n mobileAccess,\n upstreamLoginUrl,\n webSocketPaths,\n blockedUpgradePaths,\n (source, code) => { logger.warn('%s discovery degraded while DSH remains available: %s', source, code) },\n )\n await candidate.start()\n lanGateway = candidate\n const removeTaskSink = taskEventHub.add(candidate)\n return {\n close: async () => {\n if (lanGateway === candidate) lanGateway = undefined\n removeTaskSink()\n await candidate.close()\n },\n }\n }\n const startRuntime = async (): Promise<MobileAccessRuntime> => {\n if (loaded.kind === 'unconfigured') throw new Error(preparedLanSetup === undefined ? 'lan_setup_required' : 'lan_setup_restart_required')\n if (loaded.kind === 'fixed') return startGateway(loaded.config)\n const following = new FollowingMobileAccessRuntime(async () => {\n const network = selectLanNetwork(undefined, loaded.setup.networkInterface)\n return {\n key: `${network.name}\\0${network.address}\\0${network.cidr}`,\n start: async () => startGateway({\n ...loaded.config,\n ...await materializeManagedSetup(loaded.setup),\n }),\n }\n }, (error) => {\n process.emitWarning(`DSH Mobile could not follow the current LAN address: ${error instanceof Error ? error.message : String(error)}`, {\n code: 'DSH_MOBILE_NETWORK_REFRESH',\n })\n })\n try {\n await following.initialize(2_000)\n } catch (error) {\n // A missing saved interface (Wi-Fi/Ethernet switch, docked laptop…)\n // must not take down all of DSH: stay dormant with the poller armed so\n // a returning network recovers on its own. Anything else is a real\n // failure and still fails boot loudly.\n if (!isNetworkSelectionError(error)) throw error\n const detail = error instanceof Error ? error.message : String(error)\n process.emitWarning(\n `DSH Mobile: ${detail}; mobile access stays dormant until the network returns or setup is re-run — DSH itself keeps running.`,\n { code: 'DSH_MOBILE_NETWORK_UNAVAILABLE' },\n )\n following.beginPolling(2_000)\n }\n return following\n }\n const lanControlFile = parseControlFile(config.controlFile)\n const lanControlStore = new JsonMobileAccessControlStore(lanControlFile, config.initiallyEnabled)\n if (loaded.kind === 'unconfigured' && (await lanControlStore.load()).enabled) {\n await lanControlStore.save({ version: 1, enabled: false })\n }\n const lanController = new MobileAccessGatewayController(lanControlStore, startRuntime)\n const remoteDeviceFile = join(remoteDirectory, 'devices.json')\n const legacyCpolarDeviceFile = join(remoteDirectory, 'cpolar', 'devices.json')\n if (initialRemoteProvider === 'cpolar') {\n try {\n await lstat(remoteDeviceFile)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n try { await copyFile(legacyCpolarDeviceFile, remoteDeviceFile) } catch (copyError) {\n if ((copyError as NodeJS.ErrnoException).code !== 'ENOENT') throw copyError\n }\n }\n }\n const createRemoteGateway = async (publicOrigin: string, listenPort = 0): Promise<MobileAccessGateway> => {\n const resolved = remoteGatewayConfig(\n template,\n publicOrigin,\n remoteDeviceFile,\n instanceId,\n listenPort,\n )\n const candidate = new MobileAccessGateway(\n resolved,\n new JsonDeviceStore(resolved.stateFile, resolved.maxDevices),\n mobileAccess,\n upstreamLoginUrl,\n webSocketPaths,\n blockedUpgradePaths,\n )\n await candidate.start()\n return candidate\n }\n const createOriginGateway = async (settings: OriginSettings): Promise<MobileAccessGateway> => {\n const resolved = originGatewayConfig(template, settings, remoteDeviceFile, instanceId)\n const candidate = new MobileAccessGateway(\n resolved, new JsonDeviceStore(resolved.stateFile, resolved.maxDevices), mobileAccess,\n upstreamLoginUrl, webSocketPaths, blockedUpgradePaths,\n )\n try { await candidate.start() } catch (error) {\n await candidate.close()\n throw error\n }\n return candidate\n }\n const tailscaleStore = new JsonMobileAccessControlStore(join(remoteDirectory, 'control.json'), false)\n const cpolarStore = new JsonMobileAccessControlStore(join(remoteDirectory, 'cpolar', 'control.json'), false)\n const cloudflaredStore = new JsonMobileAccessControlStore(join(remoteDirectory, 'cloudflared', 'control.json'), false)\n const frpStore = new JsonMobileAccessControlStore(join(remoteDirectory, 'frp', 'control.json'), false)\n const originStore = new JsonMobileAccessControlStore(join(remoteDirectory, 'origin', 'control.json'), false)\n const remoteControllers: Record<RemoteProvider, RemoteProviderController> = {\n tailscale: new FunnelController({\n store: tailscaleStore,\n executable: funnelExecutable(import.meta.url),\n stateDirectory: join(remoteDirectory, 'tailscale'),\n hostname: `dsh-${instanceId.slice(0, 12)}`,\n createGateway: createRemoteGateway,\n }),\n cpolar: new CpolarController({\n store: cpolarStore,\n executable: cpolarComponent.executable,\n configFile: cpolarComponent.configFile,\n createGateway: createRemoteGateway,\n }),\n cloudflared: new CloudflaredController({\n store: cloudflaredStore,\n executable: cloudflaredComponent.executable,\n tunnel: cloudflaredTunnel,\n createGateway: createRemoteGateway,\n }),\n frp: new FrpController({\n store: frpStore,\n executable: frpComponent.executable,\n config: frpConfig,\n instanceId,\n createGateway: createRemoteGateway,\n }),\n origin: new OriginController({ store: originStore, config: originConfig, createGateway: createOriginGateway }),\n }\n const remoteProviders = new RemoteProviderCoordinator(initialRemoteProvider, remoteControllers, remoteProviderStore)\n const remoteController = () => remoteProviders.controller()\n // Remote gateways rotate inside their controllers; forward through the\n // current instance so stale gateways never receive events.\n for (const provider of REMOTE_PROVIDERS) {\n const controller = remoteControllers[provider]\n taskEventHub.add({ broadcastTaskEvent: event => { controller.gateway()?.broadcastTaskEvent(event) } })\n }\n const remotePayload = (): Record<string, unknown> => remoteControlPayload(\n remoteProviders.selected,\n remoteController().status(),\n remoteController().gateway(),\n {\n tailscale: remoteControllers.tailscale.status(),\n cpolar: remoteControllers.cpolar.status(),\n cloudflared: remoteControllers.cloudflared.status(),\n frp: remoteControllers.frp.status(),\n origin: remoteControllers.origin.status(),\n },\n cpolarComponent.status(),\n cloudflaredComponent.status(),\n cloudflaredTunnel.status(),\n frpComponent.status(),\n frpConfig.status(),\n originConfig.status(),\n )\n const lanPayload = (): Record<string, unknown> => ({\n ...(loaded.kind === 'unconfigured' ? {\n configured: preparedLanSetup !== undefined,\n restartRequired: preparedLanSetup !== undefined,\n } : {}),\n running: lanController.isRunning(),\n origin: lanGateway?.address().origin,\n ...(preparedLanSetup === undefined ? {} : { pendingOrigin: preparedLanSetup.origin }),\n ...(lanGateway === undefined ? {} : { extensions: lanGateway.extensionStatus() }),\n })\n const lanSetupPayload = async (): Promise<Record<string, unknown>> => {\n if (loaded.kind !== 'unconfigured' || preparedLanSetup !== undefined) return lanPayload()\n const networks = availableLanNetworks()\n const preferred = await preferredLanInterfaceNames()\n let recommendedAddress: string | undefined\n try { recommendedAddress = selectLanNetwork(undefined, undefined, undefined, preferred).address } catch { /* user chooses when selection is ambiguous */ }\n return {\n ...lanPayload(),\n networks: networks.map(network => ({\n name: network.name,\n address: network.address,\n cidr: network.cidr,\n recommended: network.address === recommendedAddress,\n })),\n listenPort: 3443,\n windowsFirewall: process.platform === 'win32',\n }\n }\n const diagnosticsPayload = async (): Promise<Record<string, unknown>> => {\n let interfaceName: string | undefined\n let networkError: string | undefined\n if (loaded.kind === 'managed') {\n try { interfaceName = selectLanNetwork(undefined, loaded.setup.networkInterface).name }\n catch { networkError = 'network_interface_unavailable' }\n }\n const remote = remoteController().status()\n return collectConnectionDiagnostics({\n dshVersion,\n lan: {\n configured: loaded.kind !== 'unconfigured' || preparedLanSetup !== undefined,\n running: lanController.isRunning(),\n ...(lanGateway === undefined ? {} : { origin: lanGateway.address().origin, port: lanGateway.address().port }),\n ...(loaded.kind === 'managed' ? { configuredInterface: loaded.setup.networkInterface, port: loaded.setup.listenPort } : {}),\n ...(interfaceName === undefined ? {} : { interfaceName }),\n ...(networkError === undefined ? {} : { networkError }),\n },\n remote: {\n provider: remoteProviders.selected,\n running: remote.enabled,\n state: remote.state,\n ...(remote.origin === undefined ? {} : { origin: remote.origin }),\n ...(remote.errorCode === undefined ? {} : { errorCode: remote.errorCode }),\n },\n }) as unknown as Record<string, unknown>\n }\n\n const adminRoute: WebRoute = {\n kind: 'prefix',\n path: LOCAL_ADMIN_PREFIX,\n handler: async (request, response) => {\n try {\n const target = parseRequestTarget(request.url)\n assertLocalAdminTrust(request, request.method === 'POST')\n if (target.search !== '') throw new HttpError(400, 'bad_request')\n const lanControl = target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/control`\n || target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/lan/control`\n if (request.method === 'GET' && lanControl) {\n sendJson(response, 200, lanPayload(), false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/lan/setup`) {\n sendJson(response, 200, await lanSetupPayload(), false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/diagnostics`) {\n sendJson(response, 200, await diagnosticsPayload(), false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/release`) {\n sendJson(response, 200, await releaseManager.status(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/release/update`) {\n await readJsonObject(request, 4096)\n sendJson(response, 200, await releaseManager.update(), false)\n return\n }\n if (request.method === 'POST' && lanControl) {\n const body = await readJsonObject(request, 4096)\n if (typeof body.running !== 'boolean') throw new HttpError(400, 'bad_request')\n if (body.running && loaded.kind === 'unconfigured') {\n throw new HttpError(409, preparedLanSetup === undefined ? 'lan_setup_required' : 'lan_setup_restart_required')\n }\n await lanController.setRunning(body.running)\n sendJson(response, 200, lanPayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/lan/setup`) {\n const body = await readJsonObject(request, 4096)\n if (loaded.kind !== 'unconfigured') throw new HttpError(409, 'lan_setup_already_configured')\n if (preparedLanSetup !== undefined) {\n sendJson(response, 200, await lanSetupPayload(), false)\n return\n }\n if (body.confirm !== true || typeof body.address !== 'string') throw new HttpError(400, 'bad_request')\n const network = availableLanNetworks().find(candidate => candidate.address === body.address)\n if (network === undefined) throw new HttpError(409, 'lan_setup_network_unavailable')\n try {\n preparedLanSetup = await prepareManagedLanSetup({\n setupFile: loaded.setupFile,\n controlFile: lanControlFile,\n network,\n listenPort: 3443,\n dshPort: ctx.webServer.port,\n configureFirewall: body.configureFirewall !== false,\n })\n } catch (error) {\n logger.error('managed LAN setup failed: %s', error instanceof Error ? error.stack ?? error.message : String(error))\n throw new HttpError(409, 'lan_setup_failed')\n }\n sendJson(response, 200, await lanSetupPayload(), false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/control`) {\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/provider`) {\n const body = await readJsonObject(request, 4096)\n if (body.provider !== 'tailscale' && body.provider !== 'cpolar' && body.provider !== 'cloudflared'\n && body.provider !== 'frp' && body.provider !== 'origin') {\n throw new HttpError(400, 'bad_request')\n }\n await remoteProviders.select(body.provider)\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/origin/configure`) {\n const body = await readJsonObject(request, 8192)\n await remoteProviders.mutate(async () => {\n await originConfig.configure(body)\n if (remoteControllers.origin.status().enabled) await remoteControllers.origin.reconnect()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/origin/purge`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async () => {\n await remoteControllers.origin.setEnabled(false)\n await originConfig.purge()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cpolar/component/install`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async () => cpolarComponent.install())\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cpolar/configure`) {\n const body = await readJsonObject(request, 4096)\n await remoteProviders.mutate(async () => cpolarComponent.configure(body.authtoken))\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cpolar/component/purge`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async () => {\n await remoteControllers.cpolar.setEnabled(false)\n await cpolarComponent.purge()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cloudflared/component/install`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n logger.info('cloudflared component install started')\n try {\n await remoteProviders.mutate(async () => cloudflaredComponent.install())\n logger.info('cloudflared component install completed')\n } catch (error) {\n logger.error('cloudflared component install failed: %s', error instanceof Error ? error.stack ?? error.message : String(error))\n throw error\n }\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cloudflared/component/purge`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async () => {\n await remoteControllers.cloudflared.setEnabled(false)\n await cloudflaredComponent.purge()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cloudflared/tunnel`) {\n const body = await readJsonObject(request, 8192)\n await remoteProviders.mutate(async () => {\n await cloudflaredTunnel.configure(mergeSavedCloudflaredTunnelSettings(body, cloudflaredTunnel.settings()))\n // A live connector is bound to the previous hostname, port and token, so\n // the new configuration only takes effect through a restart.\n if (remoteControllers.cloudflared.status().enabled) await remoteControllers.cloudflared.reconnect()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cloudflared/tunnel/purge`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async () => {\n await remoteControllers.cloudflared.setEnabled(false)\n await cloudflaredTunnel.purge()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/component/install`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n logger.info('frpc component install started')\n try {\n await remoteProviders.mutate(async () => frpComponent.install())\n logger.info('frpc component install completed')\n } catch (error) {\n logger.error('frpc component install failed: %s', error instanceof Error ? error.stack ?? error.message : String(error))\n throw error\n }\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/configure`) {\n const body = await readJsonObject(request, 4096)\n await remoteProviders.mutate(async () => {\n await frpConfig.configure(body)\n if (remoteControllers.frp.status().enabled) await remoteControllers.frp.reconnect()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/websocket-paths`) {\n sendJson(response, 200, { paths: webSocketPaths.list() }, false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/websocket-paths/blocked`) {\n sendJson(response, 200, { blocked: blockedUpgradePaths.report() }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/websocket-paths`) {\n const body = await readJsonObject(request, 4096)\n if (!Array.isArray(body.paths)) throw new HttpError(400, 'bad_request')\n const paths = await webSocketPaths.replace(body.paths)\n logger.info('websocket upgrade paths updated count=%d paths=%o', paths.length, paths)\n sendJson(response, 200, { paths }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/vps/host-keys`) {\n const body = await readJsonObject(request, 8192)\n const serverAddress = typeof body.serverAddress === 'string' ? body.serverAddress : ''\n logger.info('vps host keys requested host=%s sshUser=%s sshPort=%d',\n serverAddress, String(body.sshUser), Number(body.sshPort))\n const hostKeys = await fetchVpsHostKeys(serverAddress, {\n sshUser: body.sshUser,\n sshPort: body.sshPort,\n ...(body.sshKeyPath === undefined || body.sshKeyPath === '' ? {} : { sshKeyPath: body.sshKeyPath }),\n }, {\n log(event, fields) { logger.info('vps host keys event=%s fields=%o', event, fields) },\n })\n for (const key of hostKeys) logger.info('vps host key host=%s type=%s fingerprint=%s', serverAddress, key.keyType, key.fingerprint)\n sendJson(response, 200, { ...remotePayload(), vpsHostKeys: hostKeys }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/vps/deploy`) {\n const body = await readJsonObject(request, 8192)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n logger.info('vps deploy requested host=%s port=%d sshUser=%s sshPort=%d keyProvided=%s fingerprints=%s',\n String(body.serverAddress), Number(body.serverPort), String(body.sshUser), Number(body.sshPort), body.sshKeyPath === undefined ? 'false' : 'true',\n Array.isArray(body.hostFingerprints) ? String(body.hostFingerprints.length) : 'none')\n const deployment = await remoteProviders.mutate(async () => {\n // Blank fields keep their saved values so a saved token can stay empty.\n const settings = mergeSavedFrpSettings(body, frpConfig.settings())\n const result = await deployVps(settings, parseVpsDeploymentInput({\n sshUser: body.sshUser,\n sshPort: body.sshPort,\n ...(body.sshKeyPath === undefined ? {} : { sshKeyPath: body.sshKeyPath }),\n hostFingerprints: body.hostFingerprints,\n }), {\n log(event, fields) { logger.info('vps deploy event=%s fields=%o', event, fields) },\n })\n await frpConfig.configure(settings)\n logger.info('vps deploy completed host=%s origin=%s checks=%d', settings.serverAddress, settings.publicOrigin, result.checks.length)\n return result\n })\n sendJson(response, 200, { ...remotePayload(), vpsDeployment: deployment }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/vps/uninstall-script`) {\n const body = await readJsonObject(request, 4096)\n const savedTarget = mergeSavedFrpTarget(body, frpConfig.settings())\n const script = createVpsUninstallScript({\n serverPort: savedTarget.serverPort,\n ...(body.certName === undefined || body.certName === '' ? {} : { certName: body.certName }),\n })\n sendJson(response, 200, { ...remotePayload(), vpsUninstallScript: script }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/vps/uninstall`) {\n const body = await readJsonObject(request, 8192)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n logger.info('vps uninstall requested host=%s sshUser=%s sshPort=%d',\n String(body.serverAddress), String(body.sshUser), Number(body.sshPort))\n const removal = await remoteProviders.mutate(async () => {\n const savedTarget = mergeSavedFrpTarget(body, frpConfig.settings())\n const result = await uninstallVps(savedTarget.serverAddress, {\n serverPort: savedTarget.serverPort,\n ...(body.certName === undefined || body.certName === '' ? {} : { certName: body.certName }),\n }, parseVpsDeploymentInput({\n sshUser: body.sshUser,\n sshPort: body.sshPort,\n ...(body.sshKeyPath === undefined ? {} : { sshKeyPath: body.sshKeyPath }),\n hostFingerprints: body.hostFingerprints,\n }), {\n log(event, fields) { logger.info('vps uninstall event=%s fields=%o', event, fields) },\n })\n logger.info('vps uninstall completed host=%s checks=%d', result.serverAddress, result.checks.length)\n return result\n })\n sendJson(response, 200, { ...remotePayload(), vpsUninstall: removal }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/component/purge`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async () => {\n await remoteControllers.frp.setEnabled(false)\n await Promise.all([frpComponent.purge(), frpConfig.purge()])\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/control`) {\n const body = await readJsonObject(request, 4096)\n const running = body.running\n if (typeof running !== 'boolean') throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async controller => controller.setEnabled(running))\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/reconnect`) {\n await readJsonObject(request, 4096)\n await remoteProviders.mutate(async controller => controller.reconnect())\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/reset`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async controller => {\n await controller.reset()\n await rm(remoteDeviceFile, { force: true })\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (target.decodedPathname.startsWith(`${LOCAL_ADMIN_PREFIX}/remote/`)) {\n const active = remoteController().gateway()\n if (active === undefined) throw new HttpError(409, 'gateway_stopped')\n await active.localAdminRoute(`${LOCAL_ADMIN_PREFIX}/remote`).handler(request, response)\n return\n }\n if (target.decodedPathname.startsWith(`${LOCAL_ADMIN_PREFIX}/lan/`)) {\n const active = lanGateway\n if (active === undefined) throw new HttpError(409, 'gateway_stopped')\n await active.localAdminRoute(`${LOCAL_ADMIN_PREFIX}/lan`).handler(request, response)\n return\n }\n const active = lanGateway\n if (active === undefined) throw new HttpError(409, 'gateway_stopped')\n await active.localAdminRoute().handler(request, response)\n } catch (error) {\n const mapped = mapAdminError(error)\n if (response.headersSent) response.destroy()\n else sendFailure(response, mapped.status, mapped.code, false)\n }\n },\n }\n\n await ctx.effect(async () => {\n const unregister = ctx.webServer.register(adminRoute)\n const disposeMobileCommand = ctx.commands.register({\n name: 'mobile',\n description: '按需求修改 DSH Mobile 的手机端界面或添加电脑端能力',\n input: { hint: '<要做什么>' },\n handler: async ({ agent, rawInput }) => {\n const task = rawInput.trim()\n if (task === '') return { kind: 'error', text: '请带上需求,例如:/mobile 把手机端改成深色主题' }\n // Collect the current customization state so the guide does not\n // overwrite earlier /mobile work blindly.\n const state: MobileGuideState = {\n directory: stateDirectory,\n hasCustomCss: await existsRegularFile(template.customCssFile),\n hasCustomJs: await existsRegularFile(template.customScriptFile),\n extensions: mobileAccess.manifest().map(entry => ({\n id: entry.id,\n name: entry.name,\n version: entry.version,\n })),\n failedExtensionCount: mobileAccess.status().failed,\n }\n const guide = buildMobileGuide(state)\n // A plugin-source message renders as a collapsed context-injection row\n // (label \"dsh-mobile\", one-line notice summary) instead of a user bubble,\n // while steering still wakes the agent with the full guide as input.\n agent.steer(createUserMessage({\n content: [{ type: 'text', text: `${guide}\\n\\n用户需求:${task}` }],\n source: {\n kind: 'plugin:dsh-mobile',\n form: 'notice',\n summary: boundContextSummary(`/mobile ${task}`),\n },\n }))\n return { kind: 'success', text: '已把需求交给 DSH 处理,改动会在手机端几秒内生效。' }\n },\n })\n try {\n await mobileAccess.startLocal(template.extensionsDir, ctx)\n await lanController.initialize()\n const stores: Record<RemoteProvider, JsonMobileAccessControlStore> = {\n tailscale: tailscaleStore,\n cpolar: cpolarStore,\n cloudflared: cloudflaredStore,\n frp: frpStore,\n origin: originStore,\n }\n await Promise.all((Object.keys(stores) as RemoteProvider[])\n .filter(provider => provider !== remoteProviders.selected)\n .map(provider => stores[provider].save({ version: 1, enabled: false })))\n for (const provider of REMOTE_PROVIDERS) await remoteControllers[provider].initialize()\n // Broadcast discovery degrades silently when its UDP port cannot be bound, which\n // otherwise leaves \"the phone cannot find this computer\" with no visible cause.\n const lanDiscovery = lanGateway?.discoveryStatus()\n if (lanDiscovery !== undefined && !lanDiscovery.broadcast) {\n logger.warn(\n 'broadcast discovery is unavailable (%s); mDNS is still published, so pair manually or free the UDP port',\n lanDiscovery.errorCode ?? 'unknown_cause',\n )\n }\n if (lanDiscovery?.mobileAssetsErrorCode !== undefined) {\n logger.warn(\n 'a bundled mobile asset is missing (%s); the phone frontend will serve without it',\n lanDiscovery.mobileAssetsErrorCode,\n )\n }\n } catch (error) {\n try {\n await settleCleanupSteps([\n unregister,\n disposeMobileCommand,\n disposeTaskEvents,\n async () => {\n const results = await Promise.allSettled(Object.values(remoteControllers).map(controller => controller.close()))\n const failures = results.filter(result => result.status === 'rejected').map(result => result.reason as unknown)\n if (failures.length > 0) throw new AggregateError(failures, 'remote provider cleanup failed')\n },\n () => lanController.close(),\n () => mobileAccess.stopLocal(),\n unregisterBuiltin,\n ])\n } catch (cleanupError) {\n throw new AggregateError([error, cleanupError], 'DSH Mobile initialization and cleanup failed')\n }\n throw error\n }\n return async () => {\n await settleCleanupSteps([\n unregister,\n disposeMobileCommand,\n disposeTaskEvents,\n async () => {\n const results = await Promise.allSettled(Object.values(remoteControllers).map(controller => controller.close()))\n const failures = results.filter(result => result.status === 'rejected').map(result => result.reason as unknown)\n if (failures.length > 0) throw new AggregateError(failures, 'remote provider cleanup failed')\n },\n () => lanController.close(),\n () => mobileAccess.stopLocal(),\n unregisterBuiltin,\n ])\n }\n }, 'dsh-mobile: independent LAN and selectable remote providers with /mobile command')\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["parseIpv4","parsePublicOrigin","execFile","regularFile","createHttpsServer","createHttpServer","requestHttp","request","FRP_VERSION","releases","inside","regularFile","sha256","defaultExtractArtifact","defaultFetchArtifact","MAX_SETTINGS_BYTES","hostname","atomicPrivateWrite","connectTls","execFile","START_TIMEOUT_MS","publicStatus","httpsRequest","RESERVED_LAN_GATEWAY_PORT","atomicPrivateWrite","releases","DOWNLOAD_PAGE","TERMS_URL","inside","sha256","regularFile","defaultFetchArtifact","MAX_LOG_BUFFER_BYTES","START_TIMEOUT_MS","publicStatus","reserveLoopbackPort","withoutProxyEnvironment","hostname","execFile","proxyRequest","tlsConnect","publicStatus","execFile"],"sources":["../src/access.ts","../src/ip.ts","../src/network.ts","../src/config.ts","../src/exec-file.ts","../src/private-file.ts","../src/control.ts","../src/local-admin-host.ts","../src/http-security.ts","../src/mobile-compat-bootstrap.ts","../src/version.ts","../src/computer-images.ts","../src/extensions.ts","../src/auth-pages.ts","../src/gateway.ts","../src/storage.ts","../src/frp-component.ts","../src/websocket-paths.ts","../src/frp-template.ts","../src/frp-config.ts","../src/frp-attach.ts","../src/frp-attach-plan.ts","../src/cert-renewal.ts","../src/managed-setup.ts","../src/frp-ingress.ts","../src/remote.ts","../src/frp.ts","../src/origin-proxy-config.ts","../src/origin-proxy.ts","../src/component-download.ts","../src/cloudflared-component.ts","../src/cloudflared.ts","../src/cloudflared-tunnel.ts","../src/task-events.ts","../src/diagnostics.ts","../src/mobile-guide.ts","../src/vps-deploy.ts","../src/funnel.ts","../src/cpolar.ts","../src/cpolar-component.ts","../src/release-update.ts","../src/file-logger.ts","../src/lan-setup.ts","../src/plugin.ts"],"sourcesContent":["import { createHash, randomBytes, timingSafeEqual } from 'node:crypto'\nimport type { DeviceSnapshot, DeviceStore, StoredDevice } from './storage.js'\n\n/** Stable error categories converted to deliberately terse HTTP responses. */\nexport class AccessError extends Error {\n constructor(readonly status: number, readonly code: string) {\n super(code)\n this.name = 'AccessError'\n }\n}\n\n/** Resource and lifetime controls for device authentication. */\nexport interface AccessControllerOptions {\n readonly pairingTtlMs: number\n readonly deviceTtlMs: number\n readonly sessionTtlMs: number\n readonly maxDevices: number\n readonly maxSessions: number\n readonly rateLimitWindowMs: number\n readonly maxPairingAttempts: number\n readonly maxRateLimitKeys: number\n readonly now?: () => number\n}\n\n/** Values issued once after pairing; only digests survive the response. */\nexport interface PairingResult {\n readonly deviceId: string\n readonly deviceToken: string\n readonly deviceExpiresAt: number\n readonly sessionToken: string\n readonly csrfToken: string\n readonly sessionExpiresAt: number\n}\n\n/** Values issued after renewal with the persistent HttpOnly device Cookie. */\nexport interface RenewalResult {\n readonly deviceId: string\n readonly sessionToken: string\n readonly csrfToken: string\n readonly sessionExpiresAt: number\n}\n\n/** Authenticated Session identity retained only inside the gateway. */\nexport interface SessionAuthorization {\n readonly sessionKey: string\n readonly deviceId: string\n readonly expiresAt: number\n}\n\n/** Result of a persistent-device reachability check without opening a Session. */\nexport interface DeviceProbeResult {\n readonly deviceId: string\n readonly deviceExpiresAt: number\n}\n\n/** Why a short-lived Session ended; only device revocation is sent to clients. */\nexport type SessionEndReason = 'logout' | 'expired' | 'evicted' | 'revoked'\n\n/** Safe device metadata returned by the loopback administration API. */\nexport interface DeviceSummary {\n readonly id: string\n readonly label: string\n readonly createdAt: number\n readonly expiresAt: number\n readonly lastSeenAt: number\n readonly revokedAt?: number\n}\n\ninterface PairingWindow {\n readonly digest: Buffer\n readonly expiresAt: number\n}\n\ninterface SessionRecord {\n readonly key: string\n readonly deviceId: string\n readonly csrfDigest: Buffer\n readonly createdAt: number\n readonly expiresAt: number\n}\n\ninterface LimitBucket {\n count: number\n resetAt: number\n}\n\n/** Fixed-window limiter whose attacker-controlled key table is itself bounded. */\nexport class BoundedRateLimiter {\n private readonly buckets = new Map<string, LimitBucket>()\n\n constructor(\n private readonly limit: number,\n private readonly windowMs: number,\n private readonly maximumKeys: number,\n ) {}\n\n /** Consume one attempt; unknown keys fail closed when the bounded table is full. */\n take(key: string, now: number): boolean {\n for (const [candidate, bucket] of this.buckets) {\n if (bucket.resetAt <= now) this.buckets.delete(candidate)\n }\n const current = this.buckets.get(key)\n if (current === undefined) {\n if (this.buckets.size >= this.maximumKeys) return false\n this.buckets.set(key, { count: 1, resetAt: now + this.windowMs })\n return true\n }\n if (current.count >= this.limit) return false\n current.count += 1\n return true\n }\n\n /** Current table size, exposed for bounded-state assertions. */\n get size(): number {\n return this.buckets.size\n }\n}\n\nfunction opaqueToken(): string {\n return randomBytes(32).toString('base64url')\n}\n\nfunction digest(value: string): Buffer {\n return createHash('sha256').update(value, 'utf8').digest()\n}\n\nfunction digestHex(value: string): string {\n return digest(value).toString('hex')\n}\n\nfunction matchesDigest(value: string, expected: Buffer): boolean {\n return timingSafeEqual(digest(value), expected)\n}\n\nfunction normalizeLabel(value: string | undefined): string {\n const label = (value ?? 'Mobile device').normalize('NFC').trim()\n if (label.length < 1 || label.length > 64 || /[\\u0000-\\u001f\\u007f]/u.test(label)) {\n throw new AccessError(400, 'invalid_request')\n }\n return label\n}\n\nfunction publicDevice(device: StoredDevice): DeviceSummary {\n return Object.freeze({\n id: device.id,\n label: device.label,\n createdAt: device.createdAt,\n expiresAt: device.expiresAt,\n lastSeenAt: device.lastSeenAt,\n ...(device.revokedAt === undefined ? {} : { revokedAt: device.revokedAt }),\n })\n}\n\n/** Pairing, persistent-device, short-Session, revocation, and CSRF state machine. */\nexport class AccessController {\n private readonly now: () => number\n private readonly pairLimiter: BoundedRateLimiter\n private devices: StoredDevice[] = []\n private pairingWindow: PairingWindow | undefined\n private readonly sessions = new Map<string, SessionRecord>()\n private readonly sessionEndedListeners = new Set<(authorization: SessionAuthorization, reason: SessionEndReason) => void>()\n private mutation: Promise<void> = Promise.resolve()\n private initialized = false\n private closing = false\n private closeTask: Promise<void> | undefined\n\n constructor(private readonly store: DeviceStore, private readonly options: AccessControllerOptions) {\n this.now = options.now ?? Date.now\n this.pairLimiter = new BoundedRateLimiter(\n options.maxPairingAttempts,\n options.rateLimitWindowMs,\n options.maxRateLimitKeys,\n )\n }\n\n /** Load and validate digest-only durable state before accepting traffic. */\n async initialize(): Promise<void> {\n if (this.initialized || this.closing) throw new Error('access controller cannot be initialized again')\n const snapshot = await this.store.load()\n // Older releases retained revoked identities. Remove those tombstones on upgrade.\n const devices = snapshot.devices.filter(device => device.revokedAt === undefined)\n if (devices.length > this.options.maxDevices) throw new Error('device state exceeds configured maxDevices')\n if (devices.length !== snapshot.devices.length) await this.store.save(this.snapshot(devices))\n this.devices = devices\n this.initialized = true\n }\n\n private requireInitialized(): void {\n if (!this.initialized || this.closing) throw new Error('access controller is not available')\n }\n\n private async exclusive<T>(operation: () => Promise<T>): Promise<T> {\n const prior = this.mutation\n let release!: () => void\n this.mutation = new Promise<void>(resolve => { release = resolve })\n await prior\n try {\n return await operation()\n } finally {\n release()\n }\n }\n\n private snapshot(devices: readonly StoredDevice[]): DeviceSnapshot {\n return Object.freeze({ version: 1, devices: Object.freeze([...devices]) })\n }\n\n private emitSessionEnded(session: SessionRecord, reason: SessionEndReason): void {\n const authorization = Object.freeze({\n sessionKey: session.key,\n deviceId: session.deviceId,\n expiresAt: session.expiresAt,\n })\n for (const listener of this.sessionEndedListeners) listener(authorization, reason)\n }\n\n private removeSession(key: string, reason: SessionEndReason): void {\n const session = this.sessions.get(key)\n if (session === undefined) return\n this.sessions.delete(key)\n this.emitSessionEnded(session, reason)\n }\n\n private pruneSessions(now: number): void {\n for (const [key, session] of this.sessions) {\n if (session.expiresAt <= now) this.removeSession(key, 'expired')\n }\n }\n\n private createSession(deviceId: string, now: number, deviceExpiresAt: number): RenewalResult {\n this.pruneSessions(now)\n if (this.sessions.size >= this.options.maxSessions) {\n const oldest = [...this.sessions.values()].sort((left, right) => left.createdAt - right.createdAt)[0]\n if (oldest !== undefined) this.removeSession(oldest.key, 'evicted')\n }\n const sessionToken = opaqueToken()\n const csrfToken = opaqueToken()\n const key = digestHex(sessionToken)\n const record: SessionRecord = Object.freeze({\n key,\n deviceId,\n csrfDigest: digest(csrfToken),\n createdAt: now,\n expiresAt: Math.min(now + this.options.sessionTtlMs, deviceExpiresAt),\n })\n this.sessions.set(key, record)\n return Object.freeze({ deviceId, sessionToken, csrfToken, sessionExpiresAt: record.expiresAt })\n }\n\n /** Open one short pairing window and return its one-time secret to a loopback caller only. */\n async openPairing(requestedTtlMs?: number): Promise<{ token: string; expiresAt: number }> {\n this.requireInitialized()\n return this.exclusive(async () => {\n const ttl = requestedTtlMs ?? this.options.pairingTtlMs\n if (!Number.isSafeInteger(ttl) || ttl < 10_000 || ttl > this.options.pairingTtlMs) {\n throw new AccessError(400, 'invalid_request')\n }\n const token = opaqueToken()\n const expiresAt = this.now() + ttl\n this.pairingWindow = Object.freeze({ digest: digest(token), expiresAt })\n return Object.freeze({ token, expiresAt })\n })\n }\n\n /** Consume the pairing window exactly once and persist only the device-token digest. */\n async pair(sourceKey: string, token: string, label?: string): Promise<PairingResult> {\n this.requireInitialized()\n const now = this.now()\n if (!this.pairLimiter.take(sourceKey, now)) throw new AccessError(429, 'rate_limited')\n if (token.length > 512) throw new AccessError(401, 'authentication_failed')\n return this.exclusive(async () => {\n const window = this.pairingWindow\n if (window === undefined || window.expiresAt <= now || !matchesDigest(token, window.digest)) {\n if (window !== undefined && window.expiresAt <= now) this.pairingWindow = undefined\n throw new AccessError(401, 'authentication_failed')\n }\n this.pairingWindow = undefined\n const active = this.devices.filter(device => device.revokedAt === undefined && device.expiresAt > now)\n if (active.length >= this.options.maxDevices) throw new AccessError(409, 'device_limit')\n\n const deviceToken = opaqueToken()\n const device: StoredDevice = Object.freeze({\n id: randomBytes(16).toString('hex'),\n label: normalizeLabel(label),\n tokenDigest: digestHex(deviceToken),\n createdAt: now,\n expiresAt: now + this.options.deviceTtlMs,\n lastSeenAt: now,\n })\n const retained = this.devices.filter(candidate => candidate.revokedAt === undefined && candidate.expiresAt > now)\n const next = [...retained, device]\n await this.store.save(this.snapshot(next))\n this.devices = next\n const session = this.createSession(device.id, now, device.expiresAt)\n return Object.freeze({\n ...session,\n deviceToken,\n deviceExpiresAt: device.expiresAt,\n })\n })\n }\n\n /** Exchange a valid persistent device credential for a new short Session. */\n async renew(deviceToken: string): Promise<RenewalResult> {\n this.requireInitialized()\n if (deviceToken.length > 512) throw new AccessError(401, 'authentication_failed')\n return this.exclusive(async () => {\n const now = this.now()\n const tokenDigest = digest(deviceToken)\n const index = this.devices.findIndex(device => timingSafeEqual(Buffer.from(device.tokenDigest, 'hex'), tokenDigest))\n const device = this.devices[index]\n if (device === undefined) {\n throw new AccessError(401, 'authentication_failed')\n }\n if (device.revokedAt !== undefined) throw new AccessError(401, 'device_revoked')\n if (device.expiresAt <= now) throw new AccessError(401, 'device_expired')\n const updated: StoredDevice = Object.freeze({ ...device, lastSeenAt: now })\n const next = [...this.devices]\n next[index] = updated\n await this.store.save(this.snapshot(next))\n this.devices = next\n return this.createSession(device.id, now, device.expiresAt)\n })\n }\n\n /** Validate a persistent device credential without consuming a Session slot. */\n async probe(deviceToken: string): Promise<DeviceProbeResult> {\n this.requireInitialized()\n if (deviceToken.length > 512) throw new AccessError(401, 'authentication_failed')\n return this.exclusive(async () => {\n const now = this.now()\n const tokenDigest = digest(deviceToken)\n const index = this.devices.findIndex(device => timingSafeEqual(Buffer.from(device.tokenDigest, 'hex'), tokenDigest))\n const device = this.devices[index]\n if (device === undefined) throw new AccessError(401, 'authentication_failed')\n if (device.revokedAt !== undefined) throw new AccessError(401, 'device_revoked')\n if (device.expiresAt <= now) throw new AccessError(401, 'device_expired')\n const updated: StoredDevice = Object.freeze({ ...device, lastSeenAt: now })\n const next = [...this.devices]\n next[index] = updated\n await this.store.save(this.snapshot(next))\n this.devices = next\n return Object.freeze({ deviceId: device.id, deviceExpiresAt: device.expiresAt })\n })\n }\n\n /** Resolve a short Session Cookie without revealing whether device or Session failed. */\n authorizeSession(sessionToken: string): SessionAuthorization {\n this.requireInitialized()\n if (sessionToken.length > 512) throw new AccessError(401, 'authentication_failed')\n const now = this.now()\n this.pruneSessions(now)\n const key = digestHex(sessionToken)\n const session = this.sessions.get(key)\n const device = session === undefined ? undefined : this.devices.find(candidate => candidate.id === session.deviceId)\n if (session === undefined || device === undefined || device.revokedAt !== undefined || device.expiresAt <= now) {\n if (session !== undefined) this.removeSession(session.key, 'expired')\n throw new AccessError(401, 'authentication_failed')\n }\n return Object.freeze({ sessionKey: key, deviceId: session.deviceId, expiresAt: session.expiresAt })\n }\n\n /** Require the Session-bound anti-CSRF value for an authenticated mutation. */\n assertCsrf(authorization: SessionAuthorization, csrfToken: string | undefined): void {\n const session = this.sessions.get(authorization.sessionKey)\n if (session === undefined || csrfToken === undefined || csrfToken.length > 512\n || !matchesDigest(csrfToken, session.csrfDigest)) {\n throw new AccessError(403, 'forbidden')\n }\n }\n\n /** End one short Session and notify the gateway to abort its attached work. */\n logout(authorization: SessionAuthorization): void {\n this.removeSession(authorization.sessionKey, 'logout')\n }\n\n /** Durably delete the device, then end every Session owned by that device. */\n async revokeDevice(deviceId: string): Promise<boolean> {\n this.requireInitialized()\n return this.exclusive(async () => {\n const next = this.devices.filter(device => device.id !== deviceId)\n if (next.length === this.devices.length) return false\n await this.store.save(this.snapshot(next))\n this.devices = next\n for (const [key, session] of this.sessions) {\n if (session.deviceId === deviceId) this.removeSession(key, 'revoked')\n }\n return true\n })\n }\n\n /** Remove every persistent credential and terminate every active Session. */\n async resetDevices(): Promise<void> {\n this.requireInitialized()\n await this.exclusive(async () => {\n await this.store.save(this.snapshot([]))\n this.devices = []\n for (const key of [...this.sessions.keys()]) this.removeSession(key, 'revoked')\n this.pairingWindow = undefined\n })\n }\n\n /** Safe metadata for the loopback administration surface. */\n listDevices(): readonly DeviceSummary[] {\n this.requireInitialized()\n return Object.freeze(this.devices.map(publicDevice))\n }\n\n /** Pairing status without exposing the one-time secret. */\n pairingStatus(): { open: boolean; expiresAt?: number } {\n this.requireInitialized()\n const window = this.pairingWindow\n if (window === undefined || window.expiresAt <= this.now()) {\n this.pairingWindow = undefined\n return Object.freeze({ open: false })\n }\n return Object.freeze({ open: true, expiresAt: window.expiresAt })\n }\n\n /** Subscribe gateway resources to Session logout, expiry, eviction, and device revocation. */\n onSessionEnded(listener: (authorization: SessionAuthorization, reason: SessionEndReason) => void): () => void {\n this.sessionEndedListeners.add(listener)\n return () => { this.sessionEndedListeners.delete(listener) }\n }\n\n /** Stop new operations, drain durable mutations, then clear volatile credentials. */\n close(): Promise<void> {\n if (this.closeTask !== undefined) return this.closeTask\n this.closing = true\n this.closeTask = this.finishClose()\n return this.closeTask\n }\n\n private async finishClose(): Promise<void> {\n await this.mutation\n this.pairingWindow = undefined\n for (const key of [...this.sessions.keys()]) this.removeSession(key, 'expired')\n this.sessionEndedListeners.clear()\n this.initialized = false\n }\n\n /** Bounded volatile-state metrics for tests and local status. */\n metrics(): { sessions: number; rateLimitKeys: number } {\n return Object.freeze({ sessions: this.sessions.size, rateLimitKeys: this.pairLimiter.size })\n }\n}\n","/**\n * Browser-safe IP literal classification.\n *\n * `node:net` cannot be imported from the browser Client face, so the pure\n * predicate lives here instead. Semantics are deliberately identical to\n * `net.isIP`: strict dotted-quad IPv4 (no leading zeros, no signs, no\n * whitespace), IPv6 with at most one `::`, at most four hex digits per group,\n * an optional dotted-quad tail in the final position, and an optional\n * non-empty zone id after a single `%`.\n *\n * This module is dependency-free and side-effect-free on purpose: it is the\n * single classification source shared by every face of the plugin — the Node\n * side (`config.ts`, `cloudflared-tunnel.ts`, `origin-proxy-config.ts`,\n * `vps-deploy.ts`, `gateway.ts`, `network.ts`, `frp-config.ts`) and the browser\n * Client face that cannot reach `node:net` — so both classify a given literal\n * identically. Callers rely on the strict reading above and never post-process\n * the literal afterwards, so changing the accept/reject boundary here changes\n * it everywhere, which is the intended contract.\n */\n\n/** Strict dotted-quad IPv4: POSIX `inet_pton` rules, so `01.1.1.1` is rejected. */\nfunction isStrictIpv4(value: string): boolean {\n const parts = value.split('.')\n if (parts.length !== 4) return false\n for (const part of parts) {\n if (!/^(?:0|[1-9][0-9]{0,2})$/u.test(part)) return false\n if (Number(part) > 255) return false\n }\n return true\n}\n\n/**\n * Count the 16-bit groups one side of an IPv6 address occupies.\n *\n * A dotted-quad tail counts as two groups and is accepted only where the caller\n * allows it — that is, only in the final label of the whole address.\n *\n * @returns the group count, or `-1` when any label is malformed.\n */\nfunction countIpv6Groups(labels: readonly string[], allowDottedQuad: boolean): number {\n let groups = 0\n for (let index = 0; index < labels.length; index += 1) {\n const label = labels[index] as string\n if (label.includes('.')) {\n if (index !== labels.length - 1 || !allowDottedQuad || !isStrictIpv4(label)) return -1\n groups += 2\n continue\n }\n if (!/^[\\da-f]{1,4}$/iu.test(label)) return -1\n groups += 1\n }\n return groups\n}\n\n/**\n * Classify an IP literal.\n *\n * @param value - candidate address; non-strings are never an IP.\n * @returns `4` for IPv4, `6` for IPv6, `0` when the value is neither.\n */\nexport function isIP(value: unknown): 0 | 4 | 6 {\n if (typeof value !== 'string') return 0\n if (isStrictIpv4(value)) return 4\n let text = value\n const zone = text.indexOf('%')\n if (zone !== -1) {\n const suffix = text.slice(zone + 1)\n if (suffix.length === 0 || suffix.includes('%')) return 0\n text = text.slice(0, zone)\n }\n if (text.length === 0) return 0\n const halves = text.split('::')\n if (halves.length > 2) return 0\n if (halves.length === 1) {\n return countIpv6Groups(text.split(':'), true) === 8 ? 6 : 0\n }\n const leftText = halves[0] as string\n const rightText = halves[1] as string\n const left = leftText === '' ? [] : leftText.split(':')\n const right = rightText === '' ? [] : rightText.split(':')\n // `::` must stand for at least one group, and a dotted-quad tail is valid only\n // in the final label of the final non-empty side — so `1.2.3.4::` is rejected.\n const leftGroups = countIpv6Groups(left, false)\n const rightGroups = countIpv6Groups(right, right.length > 0)\n if (leftGroups < 0 || rightGroups < 0) return 0\n return leftGroups + rightGroups >= 8 ? 0 : 6\n}","import { connect } from 'node:net'\nimport { isIP } from './ip.js'\n\n/** A parsed IP network used to authorize directly connected clients. */\nexport interface ParsedCidr {\n readonly bits: 32 | 128\n readonly network: bigint\n readonly prefix: number\n readonly source: string\n}\n\n/** A normalized public authority. A missing port is filled from the bound listener. */\nexport interface AuthoritySpec {\n readonly hostname: string\n readonly port?: number\n}\n\nfunction parseIpv4(address: string): bigint {\n const parts = address.split('.')\n if (parts.length !== 4) throw new Error(`invalid IPv4 address ${JSON.stringify(address)}`)\n let value = 0n\n for (const part of parts) {\n if (!/^\\d{1,3}$/u.test(part)) throw new Error(`invalid IPv4 address ${JSON.stringify(address)}`)\n const octet = Number(part)\n if (octet > 255) throw new Error(`invalid IPv4 address ${JSON.stringify(address)}`)\n value = (value << 8n) | BigInt(octet)\n }\n return value\n}\n\nfunction parseIpv6Part(part: string, address: string): number[] {\n if (part.includes('.')) {\n const ipv4 = parseIpv4(part)\n return [Number((ipv4 >> 16n) & 0xffffn), Number(ipv4 & 0xffffn)]\n }\n if (!/^[\\da-f]{1,4}$/iu.test(part)) throw new Error(`invalid IPv6 address ${JSON.stringify(address)}`)\n return [Number.parseInt(part, 16)]\n}\n\nfunction parseIpv6(address: string): bigint {\n const withoutZone = address.split('%', 1)[0] ?? address\n if (withoutZone.split('::').length > 2) throw new Error(`invalid IPv6 address ${JSON.stringify(address)}`)\n const [leftText, rightText] = withoutZone.split('::')\n const left = leftText === '' ? [] : leftText!.split(':').flatMap(part => parseIpv6Part(part, address))\n const right = rightText === undefined || rightText === ''\n ? []\n : rightText.split(':').flatMap(part => parseIpv6Part(part, address))\n const omitted = 8 - left.length - right.length\n if (rightText === undefined ? omitted !== 0 : omitted < 1) {\n throw new Error(`invalid IPv6 address ${JSON.stringify(address)}`)\n }\n const groups = [...left, ...Array.from({ length: omitted }, () => 0), ...right]\n if (groups.length !== 8) throw new Error(`invalid IPv6 address ${JSON.stringify(address)}`)\n return groups.reduce((value, group) => (value << 16n) | BigInt(group), 0n)\n}\n\nfunction mappedIpv4(address: string): string | undefined {\n const match = /^::ffff:(\\d{1,3}(?:\\.\\d{1,3}){3})$/iu.exec(address)\n return match?.[1]\n}\n\nfunction parseIp(address: string): { bits: 32 | 128; value: bigint } {\n const unwrapped = address.startsWith('[') && address.endsWith(']') ? address.slice(1, -1) : address\n const mapped = mappedIpv4(unwrapped)\n if (mapped !== undefined) return { bits: 32, value: parseIpv4(mapped) }\n const version = isIP(unwrapped.split('%', 1)[0] ?? unwrapped)\n if (version === 4) return { bits: 32, value: parseIpv4(unwrapped) }\n if (version === 6) return { bits: 128, value: parseIpv6(unwrapped) }\n throw new Error(`invalid IP address ${JSON.stringify(address)}`)\n}\n\n/** Parse and canonicalize one IPv4 or IPv6 CIDR. */\nexport function parseCidr(source: string): ParsedCidr {\n const slash = source.lastIndexOf('/')\n if (slash <= 0 || slash === source.length - 1) throw new Error(`invalid CIDR ${JSON.stringify(source)}`)\n const address = source.slice(0, slash)\n const parsed = parseIp(address)\n const prefixText = source.slice(slash + 1)\n if (!/^\\d{1,3}$/u.test(prefixText)) throw new Error(`invalid CIDR ${JSON.stringify(source)}`)\n const prefix = Number(prefixText)\n if (prefix > parsed.bits) throw new Error(`invalid CIDR ${JSON.stringify(source)}`)\n const hostBits = BigInt(parsed.bits - prefix)\n const mask = hostBits === BigInt(parsed.bits)\n ? 0n\n : ((1n << BigInt(parsed.bits)) - 1n) ^ ((1n << hostBits) - 1n)\n const network = parsed.value & mask\n if (network !== parsed.value) {\n throw new Error(`CIDR ${JSON.stringify(source)} has host bits set`)\n }\n return Object.freeze({ bits: parsed.bits, network, prefix, source })\n}\n\n/** Whether a directly connected socket address belongs to at least one allowed CIDR. */\nexport function addressAllowed(address: string | undefined, cidrs: readonly ParsedCidr[]): boolean {\n if (address === undefined) return false\n let parsed: ReturnType<typeof parseIp>\n try {\n parsed = parseIp(address)\n } catch {\n return false\n }\n return cidrs.some((cidr) => {\n if (cidr.bits !== parsed.bits) return false\n const hostBits = BigInt(cidr.bits - cidr.prefix)\n const mask = hostBits === BigInt(cidr.bits)\n ? 0n\n : ((1n << BigInt(cidr.bits)) - 1n) ^ ((1n << hostBits) - 1n)\n return (parsed.value & mask) === cidr.network\n })\n}\n\n/** Whether an IP literal is loopback and therefore eligible for HTTP-only development. */\nexport function isLoopbackAddress(address: string): boolean {\n try {\n const parsed = parseIp(address)\n if (parsed.bits === 32) return (parsed.value >> 24n) === 127n\n return parsed.value === 1n\n } catch {\n return false\n }\n}\n\n/**\n * IPv4 ranges that are never a public VPS endpoint (IANA special-purpose,\n * private, shared, loopback, link-local, documentation, benchmark, multicast,\n * and reserved space). Kept in sync with Android `RemoteHostPolicy`.\n */\nconst NON_ROUTABLE_IPV4_RANGES: ReadonlyArray<readonly [network: bigint, prefix: number]> = Object.freeze([\n [0x00000000n, 8], // \"This network\" / software scope\n [0x0a000000n, 8], // Private-Use (RFC 1918)\n [0x64400000n, 10], // Shared address space / CGNAT (RFC 6598)\n [0x7f000000n, 8], // Loopback (RFC 1122)\n [0xa9fe0000n, 16], // Link-local (RFC 3927)\n [0xac100000n, 12], // Private-Use (RFC 1918)\n [0xc0000000n, 24], // IETF protocol assignments (RFC 6890)\n [0xc0000200n, 24], // Documentation TEST-NET-1 (RFC 5737)\n [0xc01fc400n, 24], // AS112-v4 (RFC 7534)\n [0xc034c100n, 24], // AMT relay (RFC 7450)\n [0xc0586300n, 24], // 6to4 relay anycast, deprecated (RFC 7526)\n [0xc0a80000n, 16], // Private-Use (RFC 1918)\n [0xc0af3000n, 24], // Direct delegation AS112 (RFC 7535)\n [0xc6120000n, 15], // Benchmarking (RFC 2544)\n [0xc6336400n, 24], // Documentation TEST-NET-2 (RFC 5737)\n [0xcb007100n, 24], // Documentation TEST-NET-3 (RFC 5737)\n [0xe0000000n, 4], // Multicast (RFC 1112, incl. MCAST-TEST-NET)\n [0xf0000000n, 4], // Reserved for future use + broadcast (RFC 1112)\n])\n\nfunction parseStrictIpv4Octets(address: string): readonly [number, number, number, number] | undefined {\n const parts = address.split('.')\n if (parts.length !== 4) return undefined\n const octets: number[] = []\n for (const part of parts) {\n if (!/^(?:0|[1-9][0-9]{0,2})$/u.test(part)) return undefined\n const octet = Number(part)\n if (octet > 255) return undefined\n octets.push(octet)\n }\n return octets as [number, number, number, number]\n}\n\n/**\n * Whether a dotted-quad IPv4 literal is globally routable and therefore usable\n * as a public VPS / remote HTTPS endpoint. Rejects documentation addresses such\n * as 203.0.113.10 alongside private, shared, and reserved space.\n */\nexport function isGloballyRoutableIpv4(address: string): boolean {\n const octets = parseStrictIpv4Octets(address)\n if (octets === undefined) return false\n const value = (BigInt(octets[0]) << 24n) | (BigInt(octets[1]) << 16n) | (BigInt(octets[2]) << 8n) | BigInt(octets[3])\n return !NON_ROUTABLE_IPV4_RANGES.some(([network, prefix]) => {\n if (prefix === 0) return true\n const hostBits = BigInt(32 - prefix)\n const mask = ((1n << 32n) - 1n) ^ ((1n << hostBits) - 1n)\n return (value & mask) === network\n })\n}\n\n/** Parse a bare host or host:port authority without accepting URL components. */\nexport function parseAuthority(source: string): AuthoritySpec {\n if (source.trim() !== source || source.length === 0 || /[/?#@\\\\]/u.test(source)) {\n throw new Error(`invalid public authority ${JSON.stringify(source)}`)\n }\n let url: URL\n try {\n url = new URL(`https://${source}`)\n } catch {\n throw new Error(`invalid public authority ${JSON.stringify(source)}`)\n }\n if (url.username !== '' || url.password !== '' || url.pathname !== '/' || url.search !== '' || url.hash !== '') {\n throw new Error(`invalid public authority ${JSON.stringify(source)}`)\n }\n const explicitPort = /\\]:\\d+$/u.test(source) || (!source.startsWith('[') && /:\\d+$/u.test(source))\n const hostname = url.hostname.toLowerCase()\n const port = explicitPort ? Number(url.port === '' ? 443 : url.port) : undefined\n if (port !== undefined && (!Number.isInteger(port) || port < 1 || port > 65535)) {\n throw new Error(`invalid public authority ${JSON.stringify(source)}`)\n }\n return port === undefined ? Object.freeze({ hostname }) : Object.freeze({ hostname, port })\n}\n\nfunction formatHostname(hostname: string): string {\n return hostname.includes(':') && !hostname.startsWith('[') ? `[${hostname}]` : hostname\n}\n\n/** Resolve an authority against the actual listener port. */\nexport function resolveAuthority(spec: AuthoritySpec, listenerPort: number): string {\n return `${formatHostname(spec.hostname)}:${String(spec.port ?? listenerPort)}`\n}\n\n/** Exact Host/Origin/CIDR policy for the directly exposed listener. */\nexport class RequestTrustPolicy {\n readonly authorities: ReadonlySet<string>\n readonly origins: ReadonlySet<string>\n private readonly scheme: 'http' | 'https'\n\n constructor(\n specs: readonly AuthoritySpec[],\n listenerPort: number,\n readonly cidrs: readonly ParsedCidr[],\n tls: boolean,\n ) {\n this.scheme = tls ? 'https' : 'http'\n this.authorities = new Set(specs.map(spec => resolveAuthority(spec, listenerPort).toLowerCase()))\n this.origins = new Set([...this.authorities].map(\n authority => new URL(`${this.scheme}://${authority}`).origin.toLowerCase(),\n ))\n }\n\n /** Validate the exact Host header after WHATWG authority normalization. */\n acceptsHost(header: string | undefined): boolean {\n return this.canonicalHost(header) !== undefined\n }\n\n /** Return the canonical accepted Host authority, otherwise undefined. */\n canonicalHost(header: string | undefined): string | undefined {\n if (header === undefined || /[/?#@\\\\]/u.test(header)) return undefined\n let normalized: string\n try {\n const parsed = new URL(`${this.scheme}://${header}`)\n if (parsed.pathname !== '/' || parsed.username !== '' || parsed.password !== '') return undefined\n normalized = resolveAuthority({\n hostname: parsed.hostname,\n port: Number(parsed.port || (this.scheme === 'https' ? '443' : '80')),\n }, 80).toLowerCase()\n } catch {\n return undefined\n }\n return this.authorities.has(normalized) ? normalized : undefined\n }\n\n /** Validate an exact same-scheme browser Origin. */\n acceptsOrigin(header: string | undefined): boolean {\n return this.canonicalOrigin(header) !== undefined\n }\n\n /** Return the canonical accepted Origin, otherwise undefined. */\n canonicalOrigin(header: string | undefined): string | undefined {\n if (header === undefined) return undefined\n // 微信小程序 wx.connectSocket 会把允许的 Origin 与 undefined 用逗号合并。\n let normalized: string | undefined\n for (const part of header.split(',')) {\n const trimmed = part.trim()\n if (trimmed === 'undefined') continue\n let candidate: string\n try {\n const parsed = new URL(trimmed)\n if (parsed.pathname !== '/' || parsed.search !== '' || parsed.hash !== '' || parsed.username !== '' || parsed.password !== '') {\n return undefined\n }\n candidate = parsed.origin.toLowerCase()\n } catch {\n return undefined\n }\n if (!this.origins.has(candidate) || normalized !== undefined) return undefined\n normalized = candidate\n }\n return normalized\n }\n}\n\n/**\n * Open a plain TCP connection only to learn whether something is listening.\n *\n * Used by the on-demand self-check so the panel can tell \"frps is up\" from\n * \"frps is not listening\". A transparent proxy can acknowledge any connect, so\n * this result is advisory and never authorises a security decision on its own.\n */\nexport async function probeTcpReachable(host: string, port: number, timeoutMs = 1_500): Promise<boolean> {\n if (!Number.isSafeInteger(port) || port < 1 || port > 65_535) return false\n return new Promise<boolean>((resolveProbe) => {\n let finished = false\n const socket = connect({ host, port })\n const finish = (reachable: boolean): void => {\n if (finished) return\n finished = true\n clearTimeout(timer)\n socket.destroy()\n resolveProbe(reachable)\n }\n const timer = setTimeout(() => { finish(false) }, timeoutMs)\n timer.unref()\n socket.once('connect', () => { finish(true) })\n socket.once('error', () => { finish(false) })\n socket.once('close', () => { finish(false) })\n })\n}\n","import { dirname, isAbsolute, join, resolve } from 'node:path'\nimport { createHash } from 'node:crypto'\nimport { fileURLToPath } from 'node:url'\nimport z from '@deepseek-ai/schemastery'\nimport { isIP } from './ip.js'\nimport { isLoopbackAddress, parseAuthority, parseCidr, type AuthoritySpec, type ParsedCidr } from './network.js'\n\n/** TLS source accepted by the LAN listener. */\nexport interface ProvidedTlsConfig {\n readonly mode: 'provided'\n /** PEM server leaf followed by any intermediate certificate chain. */\n readonly certFile: string\n readonly keyFile: string\n /** Optional PEM intermediates appended after the chain in `certFile`; roots are rejected. */\n readonly caFile?: string\n}\n\n/** HTTP is available only for an explicitly loopback-bound listener. */\nexport interface DisabledTlsConfig {\n readonly mode: 'disabled'\n}\n\nexport type TlsConfig = ProvidedTlsConfig | DisabledTlsConfig\n\n/** Operator-facing plugin configuration. */\nexport interface PluginConfig {\n /** Optional setup JSON written by the packaged CLI. */\n setupFile?: string\n /** Preferred HTTPS origin used to derive the public authority and listener port. */\n publicOrigin?: string\n listenHost?: string\n listenPort?: number\n upstreamOrigin?: string\n publicAuthorities?: string[]\n allowedCidrs?: string[]\n stateFile: string\n /** Internal persisted on/off preference managed by the DSH plugin card. */\n controlFile: string\n /** Optional user stylesheet served to the authenticated mobile UI. */\n customCssFile?: string\n /** Optional user script that mounts authenticated mobile-only Web features. */\n customScriptFile?: string\n /** Internal dedicated mobile layout browser bundle. */\n mobileLayoutFile?: string\n /** Standalone browser compatibility bundle used before DSH boot. */\n mobileCompatibilityFile?: string\n /** Stable public discovery identifier; it is not an authentication secret. */\n instanceId?: string\n /** Managed CA certificate offered to the Android installer after fingerprint binding. */\n pairingCaFile?: string\n /** First-run state used only while the control file does not exist. */\n initiallyEnabled: boolean\n tls?: {\n mode?: 'provided' | 'disabled'\n certFile?: string\n keyFile?: string\n caFile?: string\n }\n pairingTtlMs?: number\n deviceTtlMs?: number\n sessionTtlMs?: number\n maxDevices?: number\n maxSessions?: number\n maxConnections?: number\n maxActiveRequests?: number\n maxWebSockets?: number\n maxBodyBytes?: number\n upstreamTimeoutMs?: number\n rateLimitWindowMs?: number\n maxPairingAttempts?: number\n maxRateLimitKeys?: number\n}\n\n/** Resolved, validated security and resource limits. */\nexport interface ResolvedGatewayConfig {\n readonly listenHost: string\n readonly listenPort: number\n readonly upstreamOrigin: URL\n readonly authorities: readonly AuthoritySpec[]\n readonly allowedCidrs: readonly ParsedCidr[]\n readonly stateFile: string\n /** Local extension root adjacent to the mobile-access state file. */\n readonly extensionsDir: string\n readonly customCssFile: string\n readonly customScriptFile: string\n readonly mobileLayoutFile: string\n readonly mobileCompatibilityFile: string\n readonly instanceId: string\n readonly pairingCaFile?: string\n readonly tls: TlsConfig\n /** Whether the public hop is HTTPS, even when a trusted loopback proxy terminates TLS. */\n readonly publicTls: boolean\n /** LAN discovery is disabled for private proxy listeners such as Funnel ingress. */\n readonly discovery: boolean\n readonly pairingTtlMs: number\n readonly deviceTtlMs: number\n readonly sessionTtlMs: number\n readonly maxDevices: number\n readonly maxSessions: number\n readonly maxConnections: number\n readonly maxActiveRequests: number\n readonly maxWebSockets: number\n readonly maxBodyBytes: number\n readonly upstreamTimeoutMs: number\n readonly rateLimitWindowMs: number\n readonly maxPairingAttempts: number\n readonly maxRateLimitKeys: number\n}\n\n/** Loader-facing defaults; {@link parseGatewayConfig} enforces cross-field security rules. */\nexport const Config: z<PluginConfig> = z.object({\n setupFile: z.string().hidden(),\n publicOrigin: z.string(),\n listenHost: z.string(),\n listenPort: z.natural().max(65535),\n upstreamOrigin: z.string(),\n publicAuthorities: z.array(String).default(undefined as unknown as string[]),\n allowedCidrs: z.array(String).default(undefined as unknown as string[]),\n stateFile: String,\n controlFile: z.string().hidden().required(),\n customCssFile: z.string().hidden(),\n customScriptFile: z.string().hidden(),\n mobileLayoutFile: z.string().hidden(),\n mobileCompatibilityFile: z.string().hidden(),\n instanceId: z.string().hidden(),\n pairingCaFile: z.string().hidden(),\n initiallyEnabled: z.boolean().hidden().required(),\n tls: z.object({\n mode: z.union([z.const('provided'), z.const('disabled')]),\n certFile: z.string(),\n keyFile: z.string(),\n caFile: z.string(),\n }),\n pairingTtlMs: z.natural(),\n deviceTtlMs: z.natural(),\n sessionTtlMs: z.natural(),\n maxDevices: z.natural(),\n maxSessions: z.natural(),\n maxConnections: z.natural(),\n maxActiveRequests: z.natural(),\n maxWebSockets: z.natural(),\n maxBodyBytes: z.natural(),\n upstreamTimeoutMs: z.natural(),\n rateLimitWindowMs: z.natural(),\n maxPairingAttempts: z.natural(),\n maxRateLimitKeys: z.natural(),\n})\n\nfunction integer(value: unknown, name: string, fallback: number, minimum: number, maximum: number): number {\n const resolved = value ?? fallback\n if (typeof resolved !== 'number' || !Number.isSafeInteger(resolved) || resolved < minimum || resolved > maximum) {\n throw new Error(`${name} must be an integer from ${String(minimum)} through ${String(maximum)}`)\n }\n return resolved\n}\n\nfunction stringArray(value: unknown, name: string): string[] {\n if (!Array.isArray(value) || value.length === 0 || value.some(entry => typeof entry !== 'string')) {\n throw new Error(`${name} must be a non-empty string array`)\n }\n return value as string[]\n}\n\nfunction absoluteFile(value: unknown, name: string): string {\n if (typeof value !== 'string' || value.length === 0 || !isAbsolute(value)) {\n throw new Error(`${name} must be an absolute file path`)\n }\n return resolve(value)\n}\n\n/** Resolve the hidden runtime-control file independently from gateway configuration. */\nexport function parseControlFile(value: unknown): string {\n return absoluteFile(value, 'controlFile')\n}\n\nfunction parseUpstream(value: unknown): URL {\n const source = value ?? 'http://127.0.0.1:3080'\n if (typeof source !== 'string') throw new Error('upstreamOrigin must be a string')\n let url: URL\n try {\n url = new URL(source)\n } catch {\n throw new Error('upstreamOrigin must be an HTTP loopback origin')\n }\n if (url.protocol !== 'http:' || !isLoopbackAddress(url.hostname) || url.username !== '' || url.password !== ''\n || url.pathname !== '/' || url.search !== '' || url.hash !== '' || url.port === '') {\n throw new Error('upstreamOrigin must be an HTTP loopback origin with an explicit port and no path or credentials')\n }\n return url\n}\n\nfunction parsePublicOrigin(value: unknown): { readonly authority: AuthoritySpec; readonly port: number } | undefined {\n if (value === undefined) return undefined\n if (typeof value !== 'string' || value.length === 0 || value.trim() !== value) {\n throw new Error('publicOrigin must be an HTTPS origin')\n }\n let url: URL\n try {\n url = new URL(value)\n } catch {\n throw new Error('publicOrigin must be an HTTPS origin')\n }\n if (url.protocol !== 'https:' || url.username !== '' || url.password !== ''\n || url.pathname !== '/' || url.search !== '' || url.hash !== '') {\n throw new Error('publicOrigin must be an HTTPS origin with no path or credentials')\n }\n if (url.hostname === '0.0.0.0' || url.hostname === '[::]') {\n throw new Error('publicOrigin must name a reachable host')\n }\n return Object.freeze({\n authority: parseAuthority(url.host),\n port: Number(url.port || '443'),\n })\n}\n\nfunction parseTls(value: PluginConfig['tls'], listenHost: string): TlsConfig {\n const mode = value?.mode ?? 'provided'\n if (mode === 'disabled') {\n if (!isLoopbackAddress(listenHost)) throw new Error('TLS may be disabled only on an IP loopback listener')\n return Object.freeze({ mode })\n }\n return Object.freeze({\n mode,\n certFile: absoluteFile(value?.certFile, 'tls.certFile'),\n keyFile: absoluteFile(value?.keyFile, 'tls.keyFile'),\n ...(value?.caFile === undefined ? {} : { caFile: absoluteFile(value.caFile, 'tls.caFile') }),\n })\n}\n\n/** Parse configuration and reject unsafe topology, credential, and resource combinations. */\nexport function parseGatewayConfig(raw: unknown): ResolvedGatewayConfig {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) throw new Error('mobile-access config must be an object')\n const value = raw as PluginConfig\n const publicOrigin = parsePublicOrigin(value.publicOrigin)\n if (publicOrigin !== undefined && value.listenPort !== undefined) {\n throw new Error('publicOrigin cannot be combined with listenPort')\n }\n if (publicOrigin !== undefined && value.publicAuthorities !== undefined) {\n throw new Error('publicOrigin cannot be combined with publicAuthorities')\n }\n const listenHost = value.listenHost ?? (publicOrigin === undefined ? '127.0.0.1' : '0.0.0.0')\n if (isIP(listenHost) === 0) throw new Error('listenHost must be an IP literal')\n const listenPort = publicOrigin?.port ?? integer(value.listenPort, 'listenPort', 3443, 0, 65535)\n const upstreamOrigin = parseUpstream(value.upstreamOrigin)\n const tls = parseTls(value.tls, listenHost)\n if (publicOrigin !== undefined && tls.mode !== 'provided') {\n throw new Error('publicOrigin requires TLS')\n }\n\n let authorities: AuthoritySpec[]\n if (publicOrigin !== undefined) {\n authorities = [publicOrigin.authority]\n } else {\n let authoritySources = value.publicAuthorities\n if (authoritySources === undefined || authoritySources.length === 0) {\n if (!isLoopbackAddress(listenHost)) throw new Error('publicAuthorities is required for a non-loopback listener')\n authoritySources = [listenHost]\n }\n authorities = authoritySources.map(parseAuthority)\n }\n for (const authority of authorities) {\n if (listenPort === 0 && authority.port !== undefined) {\n throw new Error('explicit public authority ports require a non-zero listenPort')\n }\n if (authority.port !== undefined && listenPort !== 0 && authority.port !== listenPort) {\n throw new Error('every explicit public authority port must equal listenPort')\n }\n }\n if (new Set(authorities.map(entry => `${entry.hostname}:${String(entry.port ?? listenPort)}`)).size !== authorities.length) {\n throw new Error('publicAuthorities must not contain duplicates')\n }\n\n const cidrSources = value.allowedCidrs\n ?? (isLoopbackAddress(listenHost) ? ['127.0.0.0/8', '::1/128'] : undefined)\n const allowedCidrs = stringArray(cidrSources, 'allowedCidrs').map(parseCidr)\n if (new Set(allowedCidrs.map(entry => `${String(entry.bits)}:${entry.network.toString(16)}:${String(entry.prefix)}`)).size !== allowedCidrs.length) {\n throw new Error('allowedCidrs must not contain duplicates')\n }\n const deviceTtlMs = integer(value.deviceTtlMs, 'deviceTtlMs', 90 * 24 * 60 * 60_000, 60_000, 366 * 24 * 60 * 60_000)\n const sessionTtlMs = integer(value.sessionTtlMs, 'sessionTtlMs', 8 * 60 * 60_000, 30_000, 24 * 60 * 60_000)\n if (sessionTtlMs > deviceTtlMs) throw new Error('sessionTtlMs must not exceed deviceTtlMs')\n\n return Object.freeze({\n listenHost,\n listenPort,\n upstreamOrigin,\n authorities: Object.freeze(authorities),\n allowedCidrs: Object.freeze(allowedCidrs),\n stateFile: absoluteFile(value.stateFile, 'stateFile'),\n extensionsDir: join(dirname(absoluteFile(value.stateFile, 'stateFile')), 'extensions'),\n customCssFile: value.customCssFile === undefined\n ? join(dirname(absoluteFile(value.stateFile, 'stateFile')), 'mobile.css')\n : absoluteFile(value.customCssFile, 'customCssFile'),\n customScriptFile: value.customScriptFile === undefined\n ? join(dirname(absoluteFile(value.stateFile, 'stateFile')), 'mobile.js')\n : absoluteFile(value.customScriptFile, 'customScriptFile'),\n mobileLayoutFile: value.mobileLayoutFile === undefined\n ? fileURLToPath(new URL('./mobile-layout.js', import.meta.url))\n : absoluteFile(value.mobileLayoutFile, 'mobileLayoutFile'),\n mobileCompatibilityFile: value.mobileCompatibilityFile === undefined\n ? fileURLToPath(new URL('./mobile-compat.js', import.meta.url))\n : absoluteFile(value.mobileCompatibilityFile, 'mobileCompatibilityFile'),\n instanceId: value.instanceId === undefined\n ? createHash('sha256').update(absoluteFile(value.stateFile, 'stateFile')).digest('hex')\n : /^[a-f\\d]{64}$/u.test(value.instanceId)\n ? value.instanceId\n : (() => { throw new Error('instanceId must be a lowercase SHA-256 value') })(),\n ...(value.pairingCaFile === undefined ? {} : { pairingCaFile: absoluteFile(value.pairingCaFile, 'pairingCaFile') }),\n tls,\n publicTls: tls.mode === 'provided',\n discovery: true,\n pairingTtlMs: integer(value.pairingTtlMs, 'pairingTtlMs', 120_000, 10_000, 600_000),\n deviceTtlMs,\n sessionTtlMs,\n maxDevices: integer(value.maxDevices, 'maxDevices', 32, 1, 256),\n maxSessions: integer(value.maxSessions, 'maxSessions', 64, 1, 1024),\n maxConnections: integer(value.maxConnections, 'maxConnections', 64, 1, 1024),\n maxActiveRequests: integer(value.maxActiveRequests, 'maxActiveRequests', 32, 1, 1024),\n maxWebSockets: integer(value.maxWebSockets, 'maxWebSockets', 16, 1, 256),\n maxBodyBytes: integer(value.maxBodyBytes, 'maxBodyBytes', 160 * 1024 * 1024, 1024, 256 * 1024 * 1024),\n upstreamTimeoutMs: integer(value.upstreamTimeoutMs, 'upstreamTimeoutMs', 30_000, 1_000, 300_000),\n rateLimitWindowMs: integer(value.rateLimitWindowMs, 'rateLimitWindowMs', 60_000, 1_000, 3_600_000),\n maxPairingAttempts: integer(value.maxPairingAttempts, 'maxPairingAttempts', 8, 1, 100),\n maxRateLimitKeys: integer(value.maxRateLimitKeys, 'maxRateLimitKeys', 256, 1, 4096),\n })\n}\n","import { execFile, type ExecFileOptions } from 'node:child_process'\n\n/** Capture both output streams even when a desktop host wraps execFile without Node's promisify metadata. */\nexport function execFileText(\n file: string,\n args: readonly string[],\n options: Omit<ExecFileOptions, 'encoding'> & { encoding?: 'utf8' } = {},\n): Promise<{ stdout: string; stderr: string }> {\n return new Promise((resolve, reject) => {\n execFile(file, [...args], { windowsHide: true, ...options, encoding: 'utf8' }, (error, stdout, stderr) => {\n if (error !== null) { reject(error); return }\n if (typeof stdout !== 'string' || typeof stderr !== 'string') {\n reject(new Error('subprocess returned invalid text output'))\n return\n }\n resolve({ stdout, stderr })\n })\n })\n}\n","import { chmod } from 'node:fs/promises'\nimport { win32 } from 'node:path'\nimport { execFileText as execFile } from './exec-file.js'\n\nlet userSidTask: Promise<string> | undefined\n\nfunction windowsSystemTool(name: 'whoami.exe' | 'icacls.exe'): string {\n const systemRoot = process.env.SystemRoot ?? process.env.WINDIR\n if (systemRoot === undefined || !win32.isAbsolute(systemRoot)) {\n throw new Error('Windows system root is unavailable')\n }\n return win32.join(systemRoot, 'System32', name)\n}\n\nasync function currentWindowsUserSid(): Promise<string> {\n userSidTask ??= execFile(windowsSystemTool('whoami.exe'), ['/user', '/fo', 'csv', '/nh'], {\n encoding: 'utf8',\n windowsHide: true,\n timeout: 10_000,\n }).then(({ stdout }) => {\n const match = /,\"(S-\\d(?:-\\d+)+)\"\\s*$/u.exec(stdout.trim())\n if (match?.[1] === undefined) throw new Error('unable to resolve the current Windows user SID')\n return match[1]\n }).catch((error: unknown) => {\n userSidTask = undefined\n throw error\n })\n return userSidTask\n}\n\n/** Restrict a sensitive regular file to the current user and Windows administrators. */\nexport async function restrictPrivateFile(file: string, mode = 0o600): Promise<void> {\n await chmod(file, mode)\n if (process.platform !== 'win32') return\n const userSid = await currentWindowsUserSid()\n await execFile(windowsSystemTool('icacls.exe'), [\n file,\n '/inheritance:r',\n '/grant:r',\n `*${userSid}:(F)`,\n '*S-1-5-18:(F)',\n '*S-1-5-32-544:(F)',\n '/remove:g',\n '*S-1-1-0',\n '*S-1-5-11',\n '*S-1-5-32-545',\n ], { encoding: 'utf8', windowsHide: true, timeout: 10_000 })\n}\n","import { randomBytes } from 'node:crypto'\nimport { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, join } from 'node:path'\nimport { restrictPrivateFile } from './private-file.js'\n\n/** Versioned durable preference for the resident mobile-access runtime. */\nexport interface MobileAccessControlState {\n readonly version: 1\n readonly enabled: boolean\n}\n\n/** Persistence seam for the runtime preference. */\nexport interface MobileAccessControlStore {\n load(): Promise<MobileAccessControlState>\n save(state: MobileAccessControlState): Promise<void>\n}\n\n/** One started gateway runtime owned by the controller. */\nexport interface MobileAccessRuntime {\n close(): Promise<void>\n}\n\n/** One address-specific runtime selected from the current LAN state. */\nexport interface MobileAccessRuntimeSelection {\n readonly key: string\n start(): Promise<MobileAccessRuntime>\n}\n\n/** Keeps one runtime aligned with a changing network selection. */\nexport class FollowingMobileAccessRuntime implements MobileAccessRuntime {\n private runtime: MobileAccessRuntime | undefined\n private key: string | undefined\n private queue: Promise<void> = Promise.resolve()\n private closed = false\n private timer: ReturnType<typeof setInterval> | undefined\n\n constructor(\n private readonly select: () => Promise<MobileAccessRuntimeSelection>,\n private readonly onRefreshError: (error: unknown) => void,\n ) {}\n\n /** Start the current selection and optionally poll for later changes. */\n async initialize(refreshIntervalMs?: number): Promise<void> {\n await this.refresh()\n if (refreshIntervalMs === undefined) return\n this.beginPolling(refreshIntervalMs)\n }\n\n /**\n * Arm the refresh poller without requiring an initial selection.\n * Boot-degraded path: the first selection already failed (e.g. the saved\n * LAN is down), but later refreshes must still get their chance so a\n * returning network recovers without a restart. Failures keep flowing to\n * onRefreshError; close() stays the single teardown.\n */\n beginPolling(refreshIntervalMs: number): void {\n if (this.timer !== undefined) return\n this.timer = setInterval(() => {\n void this.refresh().catch(this.onRefreshError)\n }, refreshIntervalMs)\n this.timer.unref()\n }\n\n /** Reconcile the active runtime with the latest selection. */\n refresh(): Promise<void> {\n return this.enqueue(async () => {\n if (this.closed) return\n const selection = await this.select()\n if (this.closed || (this.runtime !== undefined && this.key === selection.key)) return\n const previous = this.runtime\n this.runtime = undefined\n this.key = undefined\n if (previous !== undefined) await previous.close()\n this.runtime = await selection.start()\n this.key = selection.key\n })\n }\n\n /** Stop polling and close the most recently selected runtime. */\n close(): Promise<void> {\n if (this.closed) return this.queue\n this.closed = true\n if (this.timer !== undefined) clearInterval(this.timer)\n return this.enqueue(async () => {\n const runtime = this.runtime\n this.runtime = undefined\n this.key = undefined\n if (runtime !== undefined) await runtime.close()\n })\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const run = this.queue.then(operation, operation)\n this.queue = run.then(() => {}, () => {})\n return run\n }\n}\n\n/** Validate control state loaded across the filesystem boundary. */\nexport function parseMobileAccessControlState(value: unknown): MobileAccessControlState {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error('mobile-access control state must be an object')\n }\n const record = value as Record<string, unknown>\n if (record.version !== 1 || typeof record.enabled !== 'boolean'\n || Reflect.ownKeys(record).some(key => key !== 'version' && key !== 'enabled')) {\n throw new Error('mobile-access control state has an unsupported format')\n }\n return Object.freeze({ version: 1, enabled: record.enabled })\n}\n\n/** Atomic JSON store whose absent-file state comes from the installation-time default. */\nexport class JsonMobileAccessControlStore implements MobileAccessControlStore {\n constructor(private readonly file: string, private readonly initiallyEnabled: boolean) {}\n\n async load(): Promise<MobileAccessControlState> {\n let stat\n try {\n stat = await lstat(this.file)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return Object.freeze({ version: 1, enabled: this.initiallyEnabled })\n }\n throw error\n }\n if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 4096) {\n throw new Error('mobile-access control state must be a regular file no larger than 4 KiB')\n }\n await restrictPrivateFile(this.file)\n let parsed: unknown\n try {\n parsed = JSON.parse(await readFile(this.file, 'utf8')) as unknown\n } catch (error) {\n throw new Error('mobile-access control state is not valid JSON', { cause: error })\n }\n return parseMobileAccessControlState(parsed)\n }\n\n async save(state: MobileAccessControlState): Promise<void> {\n const validated = parseMobileAccessControlState(state)\n const directory = dirname(this.file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(this.file)\n if (!current.isFile() || current.isSymbolicLink()) {\n throw new Error('mobile-access control state target must remain a regular file')\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, `.${basename(this.file)}.${randomBytes(12).toString('hex')}.tmp`)\n try {\n await writeFile(temporary, `${JSON.stringify(validated)}\\n`, {\n encoding: 'utf8',\n flag: 'wx',\n mode: 0o600,\n })\n await rename(temporary, this.file)\n await restrictPrivateFile(this.file)\n } catch (error) {\n try {\n await rm(temporary, { force: true })\n } catch (cleanupError) {\n throw new AggregateError([error, cleanupError], 'control state write and temporary cleanup both failed')\n }\n throw error\n }\n }\n}\n\n/** Serialized persistent lifecycle for the gateway behind the always-loaded Cordis entry. */\nexport class MobileAccessGatewayController {\n private runtime: MobileAccessRuntime | undefined\n private initialized = false\n private closing = false\n private queue: Promise<void> = Promise.resolve()\n private closeTask: Promise<void> | undefined\n\n constructor(\n private readonly store: MobileAccessControlStore,\n private readonly startRuntime: () => Promise<MobileAccessRuntime>,\n ) {}\n\n /** Load the durable preference and start the first runtime when enabled. */\n initialize(): Promise<void> {\n return this.enqueue(async () => {\n if (this.initialized) throw new Error('mobile-access control is already initialized')\n if (this.closing) throw new Error('mobile-access control is closing')\n const state = await this.store.load()\n if (state.enabled) this.runtime = await this.startRuntime()\n this.initialized = true\n })\n }\n\n /** Return the committed in-process runtime state. */\n isRunning(): boolean {\n return this.runtime !== undefined\n }\n\n /** Start or stop the runtime and persist only a successfully committed transition. */\n setRunning(running: boolean): Promise<void> {\n if (this.closing) return Promise.reject(new Error('mobile-access control is closing'))\n return this.enqueue(async () => {\n if (!this.initialized) throw new Error('mobile-access control is not initialized')\n if (this.isRunning() === running) return\n if (running) {\n await this.enable()\n } else {\n await this.disable()\n }\n })\n }\n\n /** Stop the runtime after earlier transitions without changing the restart preference. */\n close(): Promise<void> {\n if (this.closeTask !== undefined) return this.closeTask\n this.closing = true\n this.closeTask = this.enqueue(async () => {\n const runtime = this.runtime\n if (runtime === undefined) return\n await runtime.close()\n this.runtime = undefined\n })\n return this.closeTask\n }\n\n private async enable(): Promise<void> {\n const candidate = await this.startRuntime()\n try {\n await this.store.save({ version: 1, enabled: true })\n } catch (error) {\n try {\n await candidate.close()\n } catch (rollbackError) {\n throw new AggregateError([error, rollbackError], 'enabling mobile access failed and runtime rollback also failed')\n }\n throw error\n }\n this.runtime = candidate\n }\n\n private async disable(): Promise<void> {\n const previous = this.runtime\n if (previous === undefined) return\n await previous.close()\n try {\n await this.store.save({ version: 1, enabled: false })\n } catch (error) {\n try {\n this.runtime = await this.startRuntime()\n } catch (rollbackError) {\n this.runtime = undefined\n throw new AggregateError([error, rollbackError], 'disabling mobile access failed and runtime rollback also failed')\n }\n throw error\n }\n this.runtime = undefined\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const run = this.queue.then(operation, operation)\n this.queue = run.then(() => {}, () => {})\n return run\n }\n}\n","/** Browser-safe desktop-admin Host checks. Do not import Node APIs here. */\n\nfunction unwrapBrackets(hostname: string): string {\n return hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname\n}\n\nfunction parseIpv4(hostname: string): readonly [number, number, number, number] | undefined {\n const parts = hostname.split('.')\n if (parts.length !== 4) return undefined\n const octets: number[] = []\n for (const part of parts) {\n if (!/^(?:0|[1-9]\\d{0,2})$/u.test(part)) return undefined\n const value = Number(part)\n if (value > 255) return undefined\n octets.push(value)\n }\n return octets as [number, number, number, number]\n}\n\nfunction isLoopbackIpv4(octets: readonly [number, number, number, number]): boolean {\n return octets[0] === 127\n}\n\nfunction isPrivateIpv4(octets: readonly [number, number, number, number]): boolean {\n return octets[0] === 10\n || (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31)\n || (octets[0] === 192 && octets[1] === 168)\n}\n\nfunction isLinkLocalIpv4(octets: readonly [number, number, number, number]): boolean {\n return octets[0] === 169 && octets[1] === 254\n}\n\n/**\n * Hostnames that may appear on the DSH desktop admin surface.\n * Loopback, RFC1918, and IPv4 link-local are accepted. Public IPs,\n * CGNAT, and arbitrary DNS names stay rejected so DNS rebinding\n * cannot reach `/api/mobile-access`.\n */\nexport function isLocalAdminHostname(hostname: string): boolean {\n const host = unwrapBrackets(hostname).trim().toLowerCase()\n if (host === 'localhost' || host === '::1') return true\n const octets = parseIpv4(host)\n if (octets === undefined) return false\n return isLoopbackIpv4(octets) || isPrivateIpv4(octets) || isLinkLocalIpv4(octets)\n}\n\n/**\n * Whether the current browser document should mount the desktop Mobile access\n * control. Dedicated Mobile HTTPS (the phone surface) stays native even when\n * the Host is a private LAN address.\n */\nexport function isDesktopAdminSurface(\n hostname: string,\n search = '',\n frontend?: string,\n): boolean {\n const query = search.startsWith('?') ? search.slice(1) : search\n return isLocalAdminHostname(hostname)\n && frontend !== 'dedicated'\n && !new URLSearchParams(query).has('dsh-mobile-preview')\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { addressAllowed, isLoopbackAddress, RequestTrustPolicy } from './network.js'\nimport { isLocalAdminHostname } from './local-admin-host.js'\n\nexport const DEVICE_COOKIE = 'dsh_ma_device'\nexport const SESSION_COOKIE = 'dsh_ma_session'\nexport const CSRF_COOKIE = 'dsh_ma_csrf'\nexport const CSRF_HEADER = 'x-dsh-mobile-csrf'\nexport const LOCAL_ADMIN_PREFIX = '/api/mobile-access'\nexport const AUTH_PREFIX = '/mobile-access'\nexport const WS_PATHS = new Set([\n '/api/events.mux',\n '/api/events.host',\n '/api/remote.mux',\n // DSH desktop UI's sidebar terminal (renderer-v2) upgrades through this\n // exact path with a session query string; it is a first-party DSH surface.\n '/sidebar/ws/terminal',\n])\n\n/** Terse request failure safe to expose without internal diagnostics. */\nexport class HttpError extends Error {\n constructor(readonly status: number, readonly code: string) {\n super(code)\n this.name = 'HttpError'\n }\n}\n\n/** Parsed origin-form request target with a decoded path for protected-prefix checks. */\nexport interface RequestTarget {\n readonly raw: string\n readonly pathname: string\n readonly decodedPathname: string\n readonly search: string\n}\n\n/** Parse only origin-form request targets and reject ambiguous slash encodings. */\nexport function parseRequestTarget(raw: string | undefined): RequestTarget {\n if (raw === undefined || !raw.startsWith('/') || raw.startsWith('//') || raw.includes('\\\\') || /[\\u0000-\\u001f\\u007f]/u.test(raw)) {\n throw new HttpError(400, 'bad_request')\n }\n let parsed: URL\n let decodedPathname: string\n try {\n parsed = new URL(raw, 'http://gateway.invalid')\n decodedPathname = decodeURIComponent(parsed.pathname)\n } catch {\n throw new HttpError(400, 'bad_request')\n }\n if (decodedPathname.includes('\\\\') || decodedPathname.startsWith('//') || /[\\u0000-\\u001f\\u007f]/u.test(decodedPathname)) {\n throw new HttpError(400, 'bad_request')\n }\n return Object.freeze({ raw, pathname: parsed.pathname, decodedPathname, search: parsed.search })\n}\n\n/**\n * Which framing policy a response carries.\n *\n * - `gateway`: the gateway's OWN documents (login, pairing, the CA helper) and\n * its JSON/SSE responses. Nothing may frame them at all.\n * - `proxied`: the upstream DSH GUI and its routes, forwarded through the\n * gateway. The GUI must be able to frame its OWN same-origin surfaces (the\n * sidebar's HTML/diff preview routes, the browser tab pointed at the GUI\n * itself) and the sidebar browser tab must be able to frame external http(s)\n * sites plus `blob:` PDF previews — a plain `default-src 'self'` with no\n * `frame-src` refuses all of them, which is what broke the sidebar browser\n * over remote access.\n */\nexport type FramingPolicy = 'gateway' | 'proxied'\n\n/**\n * The frame sources a proxied GUI document may embed: its own routes, blob:\n * (the PDF viewer's object URL) and external http(s) pages (the sidebar\n * browser tab). The framed document runs in the tab's existing sandbox\n * (opaque origin, no same-origin privileges), so this does not hand the\n * embedded page anything it did not already have.\n */\nconst PROXIED_FRAME_SRC = \"frame-src 'self' blob: https: http:\"\n\n/**\n * Set the gateway browser protections and non-cacheability.\n * @param response - the response to decorate.\n * @param tls - whether the connection is TLS (adds HSTS).\n * @param framing - the framing policy; defaults to the strict gateway one, so\n * every existing call site keeps refusing to be framed. Only the two PROXIED\n * forwarders pass `'proxied'`.\n */\nexport function setSecurityHeaders(response: ServerResponse, tls: boolean, framing: FramingPolicy = 'gateway'): void {\n const proxied = framing === 'proxied'\n response.setHeader('Cache-Control', 'no-store')\n // DSH emits inline boot code, revives Schemastery callbacks, and applies dynamic styles.\n // These allowances provide compatibility, not XSS isolation.\n response.setHeader('Content-Security-Policy', [\n \"default-src 'self'\",\n \"base-uri 'none'\",\n \"object-src 'none'\",\n // A proxied GUI is frameable by ITS OWN origin only (its preview routes and\n // the sidebar's page routes); every third-party origin stays refused, so\n // clickjacking protection is unchanged for the gateway's own documents.\n proxied ? \"frame-ancestors 'self'\" : \"frame-ancestors 'none'\",\n \"form-action 'self'\",\n \"script-src 'self' 'unsafe-inline' 'unsafe-eval'\",\n \"style-src 'self' 'unsafe-inline'\",\n \"img-src 'self' data: blob:\",\n \"font-src 'self' data:\",\n \"connect-src 'self'\",\n \"worker-src 'self' blob:\",\n // `frame-src` has no allow-by-default: the sidebar browser tab embeds\n // cross-origin pages, which only an explicit frame-src admits.\n ...(proxied ? [PROXIED_FRAME_SRC] : []),\n ].join('; '))\n response.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), payment=(), usb=()')\n response.setHeader('Referrer-Policy', 'no-referrer')\n response.setHeader('X-Content-Type-Options', 'nosniff')\n // The legacy twin of frame-ancestors: SAMEORIGIN keeps older engines aligned\n // with the proxied policy instead of refusing the GUI's own frames.\n response.setHeader('X-Frame-Options', proxied ? 'SAMEORIGIN' : 'DENY')\n response.setHeader('Cross-Origin-Resource-Policy', 'same-origin')\n if (tls) response.setHeader('Strict-Transport-Security', 'max-age=31536000')\n}\n\n/** Send a bounded JSON response without reflecting request or upstream data. */\nexport function sendJson(response: ServerResponse, status: number, value: unknown, tls: boolean): void {\n if (response.headersSent || response.destroyed) return\n setSecurityHeaders(response, tls)\n const body = `${JSON.stringify(value)}\\n`\n response.writeHead(status, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Content-Length': Buffer.byteLength(body),\n })\n response.end(body)\n}\n\n/** Send a generic failure containing only a stable category. */\nexport function sendFailure(response: ServerResponse, status: number, code: string, tls: boolean): void {\n sendJson(response, status, { error: code }, tls)\n}\n\n/** Read and parse one bounded JSON object. */\nexport async function readJsonObject(request: IncomingMessage, maximumBytes: number): Promise<Record<string, unknown>> {\n const contentType = request.headers['content-type']?.split(';', 1)[0]?.trim().toLowerCase()\n if (contentType !== 'application/json') throw new HttpError(415, 'unsupported_media_type')\n const declared = request.headers['content-length']\n if (declared !== undefined) {\n if (!/^\\d+$/u.test(declared) || Number(declared) > maximumBytes) throw new HttpError(413, 'payload_too_large')\n }\n const chunks: Buffer[] = []\n let total = 0\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n total += buffer.length\n if (total > maximumBytes) throw new HttpError(413, 'payload_too_large')\n chunks.push(buffer)\n }\n let parsed: unknown\n try {\n parsed = JSON.parse(Buffer.concat(chunks).toString('utf8')) as unknown\n } catch {\n throw new HttpError(400, 'bad_request')\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) throw new HttpError(400, 'bad_request')\n return parsed as Record<string, unknown>\n}\n\n/** Strict cookie parser: malformed or duplicate names invalidate the whole header. */\nexport function parseCookies(header: string | undefined): ReadonlyMap<string, string> | undefined {\n if (header === undefined) return new Map()\n if (header.length > 8192) return undefined\n const cookies = new Map<string, string>()\n for (const part of header.split(';')) {\n const equals = part.indexOf('=')\n if (equals <= 0) return undefined\n const name = part.slice(0, equals).trim()\n const value = part.slice(equals + 1).trim()\n if (!/^[!#$%&'*+\\-.^_`|~\\dA-Za-z]+$/u.test(name) || !/^[\\w\\-.~+/=]*$/u.test(value) || cookies.has(name)) {\n return undefined\n }\n cookies.set(name, value)\n }\n return cookies\n}\n\n/** Serialize a host-only Cookie with no Domain attribute. */\nexport function cookie(\n name: string,\n value: string,\n options: { tls: boolean; httpOnly: boolean; path: string; maxAgeSeconds: number },\n): string {\n const parts = [\n `${name}=${value}`,\n `Path=${options.path}`,\n `Max-Age=${String(Math.max(0, Math.floor(options.maxAgeSeconds)))}`,\n 'SameSite=Strict',\n 'Priority=High',\n ]\n if (options.tls) parts.push('Secure')\n if (options.httpOnly) parts.push('HttpOnly')\n return parts.join('; ')\n}\n\n/** Enforce direct CIDR, exact Host, and browser same-origin facts. */\nexport function assertExternalTrust(request: IncomingMessage, policy: RequestTrustPolicy, requireOrigin: boolean): void {\n if (!addressAllowed(request.socket.remoteAddress, policy.cidrs) || !policy.acceptsHost(request.headers.host)) {\n throw new HttpError(403, 'forbidden')\n }\n const origin = request.headers.origin\n if (origin !== undefined && !policy.acceptsOrigin(origin)) throw new HttpError(403, 'forbidden')\n const site = request.headers['sec-fetch-site']\n // 微信小程序(wx.request / wx.connectSocket)的 Sec-Fetch-Site 由微信自动附加\n // (same-origin / same-site / cross-site),客户端无法控制,也不代表真实跨站攻击;\n // CSRF 防护的核心是下方对 Origin 的强制校验,因此这里接受全部合法取值。\n if (site !== undefined && site !== 'same-origin' && site !== 'same-site' && site !== 'cross-site' && site !== 'none') throw new HttpError(403, 'forbidden')\n // POST 只强制 Origin 正确。\n if (requireOrigin && !policy.acceptsOrigin(origin)) throw new HttpError(403, 'forbidden')\n}\n\nfunction localAuthority(header: string | undefined): { hostname: string; authority: string } | undefined {\n if (header === undefined || /[/?#@\\\\]/u.test(header)) return undefined\n try {\n const url = new URL(`http://${header}`)\n if (url.pathname !== '/' || url.username !== '' || url.password !== '') return undefined\n return { hostname: url.hostname, authority: url.host.toLowerCase() }\n } catch {\n return undefined\n }\n}\n\n/** Protect the inner management route from non-loopback and DNS-rebinding callers. */\nexport function assertLocalAdminTrust(request: IncomingMessage, requireBrowserOrigin: boolean): void {\n if (request.socket.remoteAddress === undefined || !isLoopbackAddress(request.socket.remoteAddress)) {\n throw new HttpError(403, 'forbidden')\n }\n const host = localAuthority(request.headers.host)\n if (host === undefined || !isLocalAdminHostname(host.hostname)) {\n throw new HttpError(403, 'forbidden')\n }\n const site = request.headers['sec-fetch-site']\n if (site !== undefined && site !== 'same-origin' && site !== 'none') throw new HttpError(403, 'forbidden')\n const origin = request.headers.origin\n if (origin !== undefined) {\n try {\n const parsed = new URL(origin)\n if (parsed.host.toLowerCase() !== host.authority || (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')) {\n throw new HttpError(403, 'forbidden')\n }\n } catch (error) {\n if (error instanceof HttpError) throw error\n throw new HttpError(403, 'forbidden')\n }\n }\n // Mutating admin requests are browser-only. Fetch metadata is optional on\n // older clients, so a missing Sec-Fetch-Site must not make a missing Origin\n // acceptable; Origin is the stable CSRF signal across supported browsers.\n if (requireBrowserOrigin && origin === undefined) {\n throw new HttpError(403, 'forbidden')\n }\n if (requireBrowserOrigin && site !== undefined && site !== 'same-origin') {\n throw new HttpError(403, 'forbidden')\n }\n}\n","import { AUTH_PREFIX } from './http-security.js'\n\nexport const MOBILE_COMPAT_PATH = `${AUTH_PREFIX}/compat.js`\n\n/**\n * Read one attribute out of a tag body.\n *\n * A plain `/\\snonce\\s*=/` search can be fooled by a value that merely contains the\n * text, and cannot tell a double-quoted attribute from a single-quoted one. Walking\n * `name=value` pairs consumes each quoted value whole, so the attribute that is\n * actually present is the one returned.\n */\nfunction attributeValue(tag: string, name: string): string | undefined {\n const pattern = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)\\s*=\\s*(\"[^\"]*\"|'[^']*')/gu\n for (const match of tag.matchAll(pattern)) {\n const attribute = match[1]\n const raw = match[2]\n if (attribute === undefined || raw === undefined) continue\n if (attribute.toLowerCase() === name) return raw.slice(1, -1)\n }\n return undefined\n}\n\n/** Character ranges covered by an HTML comment, where markup is inert text. */\nfunction commentRanges(html: string): readonly (readonly [number, number])[] {\n return [...html.matchAll(/<!--[\\s\\S]*?-->/gu)].map(match => [match.index, match.index + match[0].length] as const)\n}\n\n/**\n * Load the compatibility bundle synchronously, before the first DSH script.\n *\n * Returns the HTML unchanged when there is no script to anchor to. The bundle is an\n * enhancement for WebViews without Iterator helpers, so a document this function\n * cannot place it in must still be served: throwing here took the entire dedicated\n * mobile frontend down with a 502.\n */\nexport function ensureMobileCompatibility(html: string): string {\n const comments = commentRanges(html)\n const isInert = (at: number): boolean => comments.some(([start, end]) => at >= start && at < end)\n // Markup inside a comment never executes, so anchoring to it would inject the\n // bundle into inert text and silently skip the fix.\n const scripts = [...html.matchAll(/<script\\b[^>]*>/giu)].filter(match => !isInert(match.index))\n if (scripts.length === 0) return html\n if (scripts.some(match => attributeValue(match[0], 'src') === MOBILE_COMPAT_PATH)) return html\n const first = scripts[0]\n if (first === undefined) return html\n // Keep any upstream CSP nonce; do not move execution ahead of preceding CSP meta tags.\n const nonce = attributeValue(first[0], 'nonce')?.replace(/[\"'<>\\s]/gu, '') ?? ''\n const attribute = nonce === '' ? '' : ` nonce=\"${nonce}\"`\n const script = `<script src=\"${MOBILE_COMPAT_PATH}\"${attribute}></script>`\n return `${html.slice(0, first.index)}${script}${html.slice(first.index)}`\n}\n","import { createRequire } from 'node:module'\n\ninterface PackageManifest {\n readonly version?: unknown\n}\n\nconst manifest = createRequire(import.meta.url)('../package.json') as PackageManifest\n\n/** Version of the installed DSH Mobile plugin package. */\nexport const DSH_MOBILE_VERSION = typeof manifest.version === 'string' ? manifest.version : 'unknown'\n\n/** Oldest Android App release supported by this plugin generation. */\nexport const MINIMUM_ANDROID_APP_VERSION = '0.2.2'\n\n/** Public gateway metadata format understood by the Android App. */\nexport const MOBILE_METADATA_VERSION = 1\n","import { lstat, opendir, readFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { basename, dirname, extname, isAbsolute, resolve } from 'node:path'\nimport { HttpError } from './http-security.js'\n\nconst MAX_ENTRIES = 500\nconst MAX_IMAGE_BYTES = 20 * 1024 * 1024\nconst IMAGE_TYPES: Readonly<Record<string, string>> = Object.freeze({\n '.gif': 'image/gif',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.png': 'image/png',\n '.webp': 'image/webp',\n})\n\n/** One computer-side row rendered by the authenticated mobile file sheet. */\nexport interface ComputerImageEntry {\n readonly kind: 'directory' | 'image'\n readonly name: string\n readonly path: string\n}\n\n/** A bounded computer-side directory listing containing folders and supported images. */\nexport interface ComputerImageListing {\n readonly path: string\n readonly parent?: string\n readonly entries: readonly ComputerImageEntry[]\n readonly truncated: boolean\n}\n\n/** Normalize an optional mobile-browser path without rebasing relative input. */\nexport function resolveComputerImagePath(path: string | null): string {\n if (path === null || path === '') return homedir()\n if (!isAbsolute(path) || path.includes('\\0')) throw new HttpError(400, 'bad_path')\n return resolve(path)\n}\n\n/** List folders and supported image files without following symbolic links. */\nexport async function listComputerImages(path: string | null, signal?: AbortSignal): Promise<ComputerImageListing> {\n signal?.throwIfAborted()\n const target = resolveComputerImagePath(path)\n const rows: ComputerImageEntry[] = []\n let truncated = false\n let directory\n try {\n directory = await opendir(target)\n for await (const entry of directory) {\n signal?.throwIfAborted()\n if (entry.isSymbolicLink()) continue\n const kind = entry.isDirectory() ? 'directory' : IMAGE_TYPES[extname(entry.name).toLowerCase()] === undefined ? undefined : 'image'\n if (kind === undefined) continue\n if (rows.length === MAX_ENTRIES) {\n truncated = true\n break\n }\n rows.push({ kind, name: entry.name, path: resolve(target, entry.name) })\n }\n } catch (error) {\n if (signal?.aborted) throw signal.reason\n throw new HttpError(404, 'directory_unavailable')\n } finally {\n await directory?.close().catch(() => undefined)\n }\n rows.sort((left, right) => left.kind === right.kind\n ? left.name.localeCompare(right.name)\n : left.kind === 'directory' ? -1 : 1)\n const parent = dirname(target)\n return Object.freeze({\n path: target,\n ...(parent === target ? {} : { parent }),\n entries: Object.freeze(rows),\n truncated,\n })\n}\n\n/** Read one bounded regular image file selected by an authenticated device. */\nexport async function readComputerImage(path: string | null, signal?: AbortSignal): Promise<{ body: Buffer; contentType: string; name: string }> {\n signal?.throwIfAborted()\n const target = resolveComputerImagePath(path)\n const contentType = IMAGE_TYPES[extname(target).toLowerCase()]\n if (contentType === undefined) throw new HttpError(415, 'unsupported_file_type')\n let info\n try {\n info = await lstat(target)\n } catch {\n throw new HttpError(404, 'file_unavailable')\n }\n if (!info.isFile() || info.isSymbolicLink()) throw new HttpError(404, 'file_unavailable')\n if (info.size > MAX_IMAGE_BYTES) throw new HttpError(413, 'file_too_large')\n try {\n return { body: await readFile(target, { signal }), contentType, name: basename(target) }\n } catch (error) {\n if (signal?.aborted) throw signal.reason\n throw new HttpError(404, 'file_unavailable')\n }\n}\n","import { createHash } from 'node:crypto'\nimport type { Dirent } from 'node:fs'\nimport { lstat, mkdir, opendir, readFile, realpath } from 'node:fs/promises'\nimport { basename, isAbsolute, join, relative, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { Service, type Context } from '@deepseek-ai/cordis'\nimport z from '@deepseek-ai/schemastery'\nimport { finished, type Readable } from 'node:stream'\n\n/** Maximum sizes enforced at the local-extension filesystem boundary. */\nexport const EXTENSION_LIMITS = Object.freeze({\n manifest: 64 * 1024,\n script: 1024 * 1024,\n css: 512 * 1024,\n asset: 8 * 1024 * 1024,\n assetFiles: 256,\n assetBytes: 32 * 1024 * 1024,\n assetDepth: 8,\n})\n\n/** A misbehaving host activation must not wedge the local watcher forever. */\nconst HOST_ACTIVATION_TIMEOUT_MS = 5_000\n\n/** The previous Host outlives the hidden-page refresh interval and one timed refresh. */\nconst RETIRED_GENERATION_TTL_MS = 10 * 60_000\n\n/** Extension teardown is advisory and must never stop watcher progress. */\nconst HOST_TEARDOWN_TIMEOUT_MS = 2_000\n\nasync function withActivationTimeout<T>(promise: Promise<T>, id: string, signal: AbortSignal): Promise<T> {\n let timer: NodeJS.Timeout | undefined\n let onAbort: (() => void) | undefined\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new MobileExtensionError('host_load_timeout', `extension ${id} activation timed out`, 500)), HOST_ACTIVATION_TIMEOUT_MS)\n }),\n new Promise<never>((_, reject) => {\n const abort = (): void => { reject(new MobileExtensionError('host_activation_closed', `extension ${id} activation is closed`, 409)) }\n if (signal.aborted) abort()\n else { onAbort = abort; signal.addEventListener('abort', abort, { once: true }) }\n }),\n ])\n } finally {\n if (timer !== undefined) clearTimeout(timer)\n if (onAbort !== undefined) signal.removeEventListener('abort', onAbort)\n }\n}\n\n/** A controlled business failure returned by an extension action or route. */\nexport class MobileExtensionError extends Error {\n constructor(readonly code: string, message: string, readonly status = 400) {\n super(message)\n this.name = 'MobileExtensionError'\n }\n}\n\n/** One host-side action exposed by an extension. */\ntype CallableActionInput = (value?: never, options?: never) => unknown\n\nexport interface MobileHostAction {\n /** A callable Schemastery schema or an adapter exposing parse(). */\n readonly input?: CallableActionInput | { parse(value: unknown): unknown }\n readonly run: (context: MobileActionContext, input: unknown) => unknown | Promise<unknown>\n}\n\n/** Context supplied to a host action. */\nexport interface MobileActionContext {\n readonly signal: AbortSignal\n readonly deviceId: string\n}\n\n/** Safe request values supplied to a host route. */\nexport interface MobileRouteRequest {\n readonly method: string\n readonly pathname: string\n readonly query: Readonly<URLSearchParams>\n readonly headers: Readonly<Record<string, string>>\n readonly body: Uint8Array\n readonly signal: AbortSignal\n readonly deviceId: string\n}\n\n/** Values an extension route may return; status is a final HTTP code from 200 through 599. */\nexport interface MobileRouteResponse {\n readonly status?: number\n readonly contentType?: string\n readonly headers?: Readonly<Record<string, string>>\n readonly body: string | Uint8Array | Readable\n}\n\n/** One host-side route exposed by an extension. */\nexport interface MobileHostRoute {\n readonly method: string\n readonly path: string\n readonly kind?: 'exact' | 'prefix'\n readonly handle: (request: MobileRouteRequest) => MobileRouteResponse | Promise<MobileRouteResponse>\n}\n\n/** Metadata shared by local and npm-provided extensions. */\nexport interface MobileExtensionManifest {\n readonly schemaVersion: 1\n readonly id: string\n readonly name: string\n readonly version: string\n readonly description?: string\n}\n\n/** Definition registered by a normal Cordis plugin. */\nexport interface MobileExtensionDefinition extends MobileExtensionManifest {\n readonly actions?: Readonly<Record<string, MobileHostAction>>\n readonly routes?: readonly MobileHostRoute[]\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n mobileAccess: MobileAccessService\n }\n}\n\n/** A local extension manifest read from extension.json. */\nexport interface LocalExtensionManifest extends MobileExtensionManifest {}\n\n/** Public snapshot sent to the mobile browser. */\nexport interface MobileExtensionClientEntry extends MobileExtensionManifest {\n readonly generation?: string\n readonly scriptUrl?: string\n readonly styleUrl?: string\n readonly assetsUrl?: string\n}\n\ninterface LocalAssetSnapshot {\n readonly body: Buffer\n readonly digest: string\n readonly name: string\n}\n\n/** Small status summary used by the desktop mobile-access card. */\nexport interface MobileExtensionStatus {\n readonly loaded: number\n readonly failed: number\n}\n\ninterface ActiveLocalExtension {\n readonly manifest: LocalExtensionManifest\n readonly directory: string\n readonly scriptBody?: Buffer\n readonly styleBody?: Buffer\n readonly assets: ReadonlyMap<string, LocalAssetSnapshot>\n readonly host: MobileExtensionDefinition\n readonly controller: AbortController\n readonly cleanups: readonly (() => void | Promise<void>)[]\n readonly digest: string\n}\n\ninterface RegisteredExtension {\n readonly definition: MobileExtensionDefinition\n readonly dispose: () => void\n}\n\ninterface RetiredLocalExtension {\n readonly active: ActiveLocalExtension\n readonly timer: NodeJS.Timeout\n}\n\ntype HostApi = {\n readonly manifest: LocalExtensionManifest\n readonly context: Context\n readonly schema: typeof z\n readonly signal: AbortSignal\n action(name: string, spec: MobileHostAction): void\n route(spec: MobileHostRoute): void\n effect(setup: () => void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)>): void\n}\n\ntype LocalHostModule = { readonly default?: (api: HostApi) => void | Promise<void> }\n\n/** Validate user-facing extension text without allowing control characters. */\nfunction text(value: unknown, field: string, maximum: number, required: boolean): string | undefined {\n if (value === undefined && !required) return undefined\n if (typeof value !== 'string' || (required && value.length === 0) || value.length > maximum\n || /[\\u0000-\\u001f\\u007f]/u.test(value)) throw new MobileExtensionError('invalid_manifest', `${field} is invalid`)\n return value\n}\n\n/** Validate a stable extension id. */\nexport function assertExtensionId(value: unknown): string {\n if (typeof value !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/u.test(value)) {\n throw new MobileExtensionError('invalid_manifest', 'extension id is invalid')\n }\n return value\n}\n\n/** Validate a manifest from JSON or a plugin definition. */\nexport function parseExtensionManifest(value: unknown): LocalExtensionManifest {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new MobileExtensionError('invalid_manifest', 'extension.json must be an object')\n }\n const record = value as Record<string, unknown>\n if (record.schemaVersion !== 1) throw new MobileExtensionError('invalid_manifest', 'unsupported extension schema')\n const id = assertExtensionId(record.id)\n const name = text(record.name, 'name', 120, true) as string\n const version = text(record.version, 'version', 64, true) as string\n const description = text(record.description, 'description', 500, false)\n for (const key of Reflect.ownKeys(record)) {\n if (!['schemaVersion', 'id', 'name', 'version', 'description'].includes(String(key))) {\n throw new MobileExtensionError('invalid_manifest', 'extension.json has unknown fields')\n }\n }\n return Object.freeze({ schemaVersion: 1, id, name, version, ...(description === undefined ? {} : { description }) })\n}\n\nfunction normalizeRelativePath(value: string, field: string): string {\n if (value.length === 0 || value.includes('\\0') || isAbsolute(value)) throw new MobileExtensionError('invalid_extension_path', `${field} is invalid`)\n const normalized = value.replaceAll('\\\\', '/')\n if (normalized.split('/').some(part => part === '' || part === '.' || part === '..')) {\n throw new MobileExtensionError('invalid_extension_path', `${field} escapes extension directory`)\n }\n return normalized\n}\n\nasync function regularFile(path: string, maximum: number, field: string): Promise<{ readonly path: string; readonly size: number }> {\n let info\n try { info = await lstat(path) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') throw new MobileExtensionError('invalid_extension', `${field} is missing`)\n throw error\n }\n if (!info.isFile() || info.isSymbolicLink() || info.size > maximum) {\n throw new MobileExtensionError('invalid_extension', `${field} must be a regular file within its size limit`)\n }\n return { path, size: info.size }\n}\n\nasync function containedPath(root: string, relativePath: string, maximum: number, field: string): Promise<{ readonly path: string; readonly size: number }> {\n const normalized = normalizeRelativePath(relativePath, field)\n const target = resolve(root, normalized)\n const rootReal = await realpath(root)\n const targetReal = await realpath(target)\n const relation = relative(rootReal, targetReal)\n if (relation === '' || relation.startsWith('..') || isAbsolute(relation)) throw new MobileExtensionError('invalid_extension_path', `${field} escapes extension directory`)\n return regularFile(targetReal, maximum, field)\n}\n\nasync function optionalFile(root: string, name: string, maximum: number, field: string): Promise<string | undefined> {\n try {\n return (await containedPath(root, name, maximum, field)).path\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\n if (error instanceof MobileExtensionError && error.message.includes('is missing')) return undefined\n throw error\n }\n}\n\nasync function optionalBytes(root: string, name: string, maximum: number, field: string): Promise<Buffer | undefined> {\n const path = await optionalFile(root, name, maximum, field)\n return path === undefined ? undefined : readFile(path)\n}\n\nfunction assertRealPathWithin(rootReal: string, targetReal: string, field: string): void {\n const relation = relative(rootReal, targetReal)\n if (relation === '' || relation.startsWith('..') || isAbsolute(relation)) {\n throw new MobileExtensionError('invalid_extension_path', `${field} escapes extension directory`)\n }\n}\n\nasync function realExtensionRoot(directory: string): Promise<string> {\n const root = resolve(directory)\n const info = await lstat(root)\n if (!info.isDirectory() || info.isSymbolicLink()) throw new MobileExtensionError('invalid_extension', 'extension directory must be real')\n return realpath(root)\n}\n\nasync function assetSnapshot(extensionRootReal: string): Promise<ReadonlyMap<string, LocalAssetSnapshot>> {\n const assetsPath = join(extensionRootReal, 'assets')\n let assetsInfo\n try { assetsInfo = await lstat(assetsPath) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return new Map()\n throw error\n }\n if (!assetsInfo.isDirectory() || assetsInfo.isSymbolicLink()) {\n throw new MobileExtensionError('invalid_extension', 'assets must be a real directory')\n }\n const assetsReal = await realpath(assetsPath)\n assertRealPathWithin(extensionRootReal, assetsReal, 'assets')\n const snapshots = new Map<string, LocalAssetSnapshot>()\n let totalBytes = 0\n const visit = async (directoryReal: string, prefix: string, depth: number): Promise<void> => {\n if (depth > EXTENSION_LIMITS.assetDepth) {\n throw new MobileExtensionError('invalid_extension', 'asset tree exceeds its depth limit')\n }\n assertRealPathWithin(extensionRootReal, directoryReal, 'asset directory')\n const handle = await opendir(directoryReal)\n const entries: Dirent[] = []\n try { for await (const entry of handle) entries.push(entry) }\n finally { await handle.close().catch(() => undefined) }\n entries.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0)\n for (const entry of entries) {\n const path = join(directoryReal, entry.name)\n const info = await lstat(path)\n if (info.isSymbolicLink()) throw new MobileExtensionError('invalid_extension_path', 'asset escapes extension directory')\n const targetReal = await realpath(path)\n assertRealPathWithin(extensionRootReal, targetReal, 'asset')\n const key = prefix === '' ? entry.name : `${prefix}/${entry.name}`\n if (info.isDirectory()) {\n await visit(targetReal, key, depth + 1)\n continue\n }\n if (!info.isFile() || info.size > EXTENSION_LIMITS.asset) {\n throw new MobileExtensionError('invalid_extension', 'asset must be a regular file within its size limit')\n }\n const body = await readFile(targetReal)\n totalBytes += body.byteLength\n if (snapshots.size >= EXTENSION_LIMITS.assetFiles || totalBytes > EXTENSION_LIMITS.assetBytes) {\n throw new MobileExtensionError('invalid_extension', 'asset tree exceeds its aggregate limit')\n }\n snapshots.set(key, Object.freeze({ body, digest: createHash('sha256').update(body).digest('hex'), name: entry.name }))\n }\n }\n await visit(assetsReal, '', 0)\n return snapshots\n}\n\ninterface LocalExtensionFingerprint {\n readonly manifest: LocalExtensionManifest\n readonly digest: string\n readonly scriptBody?: Buffer\n readonly styleBody?: Buffer\n readonly assets: ReadonlyMap<string, LocalAssetSnapshot>\n}\n\nasync function extensionFingerprint(directory: string): Promise<LocalExtensionFingerprint> {\n const root = await realExtensionRoot(directory)\n const manifestFile = await regularFile(join(root, 'extension.json'), EXTENSION_LIMITS.manifest, 'extension.json')\n const manifestBody = await readFile(manifestFile.path)\n const manifest = parseExtensionManifest(JSON.parse(manifestBody.toString('utf8')) as unknown)\n if (manifest.id !== basename(root)) throw new MobileExtensionError('invalid_manifest', 'extension id must match its directory name')\n const [host, script, style, assets] = await Promise.all([\n optionalBytes(root, 'host.mjs', EXTENSION_LIMITS.script, 'host.mjs'),\n optionalBytes(root, 'mobile.js', EXTENSION_LIMITS.script, 'mobile.js'),\n optionalBytes(root, 'mobile.css', EXTENSION_LIMITS.css, 'mobile.css'),\n assetSnapshot(root),\n ])\n const digest = createHash('sha256').update(`manifest:${manifestBody.byteLength}:`).update(createHash('sha256').update(manifestBody).digest())\n for (const [name, body] of [['host', host], ['script', script], ['style', style]] as const) {\n digest.update(`\\0${name}:${body?.byteLength ?? -1}:`)\n if (body !== undefined) digest.update(createHash('sha256').update(body).digest())\n }\n for (const [name, asset] of assets) {\n digest.update(`\\0asset:${Buffer.byteLength(name)}:${name}:${asset.body.byteLength}:${asset.digest}`)\n }\n return {\n manifest,\n digest: digest.digest('hex'),\n assets,\n ...(script === undefined ? {} : { scriptBody: script }),\n ...(style === undefined ? {} : { styleBody: style }),\n }\n}\n\nfunction routeKey(route: MobileHostRoute): string {\n const method = route.method.toUpperCase()\n const path = normalizeRoutePath(route.path)\n return `${method} ${route.kind ?? 'exact'} ${path}`\n}\n\nfunction normalizeRoutePath(value: string): string {\n if (typeof value !== 'string' || value.length === 0 || value.length > 256\n || value.includes('?') || value.includes('#') || value.includes('\\\\') || value.includes('\\0')\n || /[\\u0000-\\u001f\\u007f]/u.test(value)) throw new MobileExtensionError('invalid_route', 'extension route path is invalid')\n const normalizedInput = value.startsWith('/') ? value : `/${value}`\n const parts = normalizedInput.split('/')\n if (parts.some(part => part === '..' || part === '.')) throw new MobileExtensionError('invalid_route', 'extension route path is invalid')\n return normalizedInput === '/' ? '/' : normalizedInput.replace(/\\/+$/u, '')\n}\n\nfunction validateDefinition(definition: MobileExtensionDefinition): MobileExtensionDefinition {\n const manifest = parseExtensionManifest({\n schemaVersion: definition.schemaVersion,\n id: definition.id,\n name: definition.name,\n version: definition.version,\n ...(definition.description === undefined ? {} : { description: definition.description }),\n })\n const actionNames = new Set<string>()\n for (const [name, action] of Object.entries(definition.actions ?? {})) {\n if (!/^[a-z][a-z0-9-]{0,63}$/u.test(name) || action === null || typeof action !== 'object' || typeof action.run !== 'function' || actionNames.has(name)) {\n throw new MobileExtensionError('invalid_action', `invalid action ${name}`)\n }\n actionNames.add(name)\n }\n const routeNames = new Set<string>()\n const routes = (definition.routes ?? []).map(route => {\n if (route === null || typeof route !== 'object' || typeof route.handle !== 'function') throw new MobileExtensionError('invalid_route', 'invalid extension route')\n const method = route.method.toUpperCase()\n if (!['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) throw new MobileExtensionError('invalid_route', 'unsupported extension route method')\n const normalized: MobileHostRoute = { ...route, method, path: normalizeRoutePath(route.path) }\n const key = routeKey(normalized)\n if (routeNames.has(key)) throw new MobileExtensionError('duplicate_route', `duplicate route ${key}`)\n routeNames.add(key)\n return normalized\n })\n return Object.freeze({ ...manifest, ...(definition.actions === undefined ? {} : { actions: Object.freeze({ ...definition.actions }) }), ...(routes.length === 0 ? {} : { routes: Object.freeze(routes) }) })\n}\n\ninterface CombinedSignalLifetime {\n readonly signal: AbortSignal\n readonly cleanup: () => void\n}\n\nfunction combineSignalLifetime(first: AbortSignal, second: AbortSignal): CombinedSignalLifetime {\n if (first.aborted || second.aborted) {\n const aborted = new AbortController()\n aborted.abort(first.aborted ? first.reason : second.reason)\n return { signal: aborted.signal, cleanup: () => undefined }\n }\n const controller = new AbortController()\n const cleanup = (): void => {\n first.removeEventListener('abort', abortFirst)\n second.removeEventListener('abort', abortSecond)\n }\n const abortFirst = (): void => { cleanup(); controller.abort(first.reason) }\n const abortSecond = (): void => { cleanup(); controller.abort(second.reason) }\n first.addEventListener('abort', abortFirst, { once: true })\n second.addEventListener('abort', abortSecond, { once: true })\n return { signal: controller.signal, cleanup }\n}\n\n/** Validate and normalize an action input through either supported schema form. */\nfunction parseActionInput(schema: MobileHostAction['input'], input: unknown): unknown {\n if (schema === undefined) return input\n if (typeof schema === 'function') return (schema as (value: unknown) => unknown)(input)\n return schema.parse(input) ?? input\n}\n\n/** Combine two abort lifetimes without relying on AbortSignal.any in older WebViews. */\nexport function combineSignals(first: AbortSignal, second: AbortSignal): AbortSignal {\n return combineSignalLifetime(first, second).signal\n}\n\n/** Host registry and service consumed by both npm plugins and local extensions. */\nexport class MobileAccessService extends Service {\n private readonly registered = new Map<string, RegisteredExtension>()\n private readonly local = new Map<string, ActiveLocalExtension>()\n private readonly retired = new Map<string, RetiredLocalExtension>()\n private readonly failures = new Map<string, string>()\n private readonly contentListeners = new Set<() => void>()\n private contentHash = createHash('sha256').update('').digest('hex')\n private localRoot: string | undefined\n private localContext: Context | undefined\n private localTimer: NodeJS.Timeout | undefined\n private localRefreshing: Promise<void> | undefined\n private localRefreshAbort: AbortController | undefined\n private localLifecycle = 0\n private localClosed = true\n\n constructor(ctx: Context) { super(ctx, 'mobileAccess') }\n\n /** Register a normal Cordis extension and return an idempotent disposer. */\n registerExtension(definition: MobileExtensionDefinition): () => void {\n const validated = validateDefinition(definition)\n if (this.registered.has(validated.id) || this.local.has(validated.id)) throw new Error(`mobile extension id already registered: ${validated.id}`)\n const dispose = (): void => {\n const current = this.registered.get(validated.id)\n if (current?.dispose === dispose) {\n this.registered.delete(validated.id)\n this.updateContentHash()\n }\n }\n this.registered.set(validated.id, { definition: validated, dispose })\n this.updateContentHash()\n return dispose\n }\n\n /** Aggregate digest covering every registered and active local extension. */\n contentDigest(): string {\n return this.contentHash\n }\n\n /** Subscribe to committed extension generation changes. */\n onContentChanged(listener: () => void): () => void {\n this.contentListeners.add(listener)\n return () => { this.contentListeners.delete(listener) }\n }\n\n private updateContentHash(): void {\n const parts = [\n ...[...this.registered.values()].map(entry => entry.definition.id),\n ...[...this.local.values()].map(active => `${active.manifest.id}:${active.digest}`),\n ]\n const next = createHash('sha256').update(parts.sort().join('|')).digest('hex')\n if (next === this.contentHash) return\n this.contentHash = next\n for (const listener of this.contentListeners) {\n try { listener() } catch { /* One observer cannot block a committed generation. */ }\n }\n }\n\n /** Return the current client-facing manifest, deterministically sorted by id. */\n manifest(): readonly MobileExtensionClientEntry[] {\n const entries = new Map<string, MobileExtensionClientEntry>()\n for (const { definition } of this.registered.values()) entries.set(definition.id, {\n schemaVersion: 1, id: definition.id, name: definition.name, version: definition.version,\n ...(definition.description === undefined ? {} : { description: definition.description }),\n })\n for (const active of this.local.values()) entries.set(active.manifest.id, {\n ...active.manifest,\n generation: active.digest,\n ...(active.scriptBody === undefined ? {} : { scriptUrl: `/mobile-access/extensions/${active.manifest.id}/mobile.js?generation=${active.digest}` }),\n ...(active.styleBody === undefined ? {} : { styleUrl: `/mobile-access/extensions/${active.manifest.id}/mobile.css?generation=${active.digest}` }),\n assetsUrl: `/mobile-access/extensions/${active.manifest.id}/assets/`,\n })\n return [...entries.values()].sort((left, right) => left.id.localeCompare(right.id))\n }\n\n /** Return loaded and failed local extension counts without exposing host errors. */\n status(): MobileExtensionStatus {\n return Object.freeze({ loaded: this.registered.size + this.local.size, failed: this.failures.size })\n }\n\n /** Locate one active extension. */\n extension(id: string, generation?: string): MobileExtensionDefinition | ActiveLocalExtension | undefined {\n if (generation !== undefined) {\n const current = this.local.get(id)\n if (current?.digest === generation) return current\n const previous = this.retired.get(id)?.active\n return previous?.digest === generation ? previous : undefined\n }\n return this.local.get(id) ?? this.registered.get(id)?.definition\n }\n\n /** Return the active local generation signal for gateway cancellation wiring. */\n signal(id: string, generation?: string): AbortSignal | undefined {\n const extension = this.extension(id, generation)\n return extension !== undefined && 'host' in extension ? extension.controller.signal : undefined\n }\n\n /** Read a local client entry after validating that it remains inside its directory. */\n async readClientFile(id: string, kind: 'script' | 'style', signal?: AbortSignal, generation?: string): Promise<{ readonly body: Buffer; readonly digest: string }> {\n signal?.throwIfAborted()\n const selected = this.extension(id, generation)\n const active = selected !== undefined && 'host' in selected ? selected : undefined\n if (active === undefined) throw new MobileExtensionError('extension_generation_not_found', 'extension generation not found', 404)\n const snapshot = kind === 'script' ? active.scriptBody : active.styleBody\n if (snapshot === undefined) throw new MobileExtensionError('extension_asset_not_found', 'extension asset not found', 404)\n const body = Buffer.from(snapshot)\n return { body, digest: createHash('sha256').update(body).digest('hex') }\n }\n\n /** Read a generation-pinned static asset from its validated snapshot. */\n async readAsset(id: string, assetPath: string, signal?: AbortSignal, generation?: string): Promise<{ readonly body: Buffer; readonly digest: string; readonly name: string }> {\n signal?.throwIfAborted()\n const selected = this.extension(id, generation)\n const active = selected !== undefined && 'host' in selected ? selected : undefined\n if (active === undefined) throw new MobileExtensionError('extension_generation_not_found', 'extension generation not found', 404)\n const normalized = normalizeRelativePath(assetPath, 'asset')\n const asset = active.assets.get(normalized)\n if (asset === undefined) throw new MobileExtensionError('extension_asset_not_found', 'extension asset not found', 404)\n return { body: Buffer.from(asset.body), digest: asset.digest, name: asset.name }\n }\n\n /** Invoke one action after parsing its input and binding the request lifetime. */\n async invoke(id: string, actionName: string, input: unknown, context: MobileActionContext, generation?: string): Promise<unknown> {\n const extension = this.extension(id, generation)\n if (extension === undefined) throw new MobileExtensionError('extension_not_found', 'extension not found', 404)\n const definition = 'host' in extension ? extension.host : extension\n const action = definition.actions?.[actionName]\n if (action === undefined) throw new MobileExtensionError('action_not_found', 'action not found', 404)\n let parsed: unknown\n try { parsed = parseActionInput(action.input, input) } catch { throw new MobileExtensionError('invalid_action_input', 'action input is invalid', 400) }\n const lifetime = 'host' in extension ? combineSignalLifetime(extension.controller.signal, context.signal) : undefined\n const signal = lifetime?.signal ?? context.signal\n try { return await action.run({ ...context, signal }, parsed) } catch (error) {\n if (error instanceof MobileExtensionError) throw error\n throw new MobileExtensionError('extension_failed', 'extension action failed', 500)\n } finally { lifetime?.cleanup() }\n }\n\n /** Match one route and invoke it with a generation-bound abort signal. */\n async route(id: string, method: string, pathname: string, request: MobileRouteRequest, generation?: string): Promise<MobileRouteResponse> {\n const extension = this.extension(id, generation)\n if (extension === undefined) throw new MobileExtensionError('extension_not_found', 'extension not found', 404)\n const definition = 'host' in extension ? extension.host : extension\n const route = definition.routes?.find((candidate: MobileHostRoute) => {\n if (candidate.method !== method) return false\n return (candidate.kind ?? 'exact') === 'exact'\n ? candidate.path === pathname\n : pathname === candidate.path || pathname.startsWith(`${candidate.path}/`)\n })\n if (route === undefined) throw new MobileExtensionError('route_not_found', 'route not found', 404)\n const lifetime = 'host' in extension ? combineSignalLifetime(extension.controller.signal, request.signal) : undefined\n let releaseLifetime = true\n try {\n const routeRequest = lifetime === undefined ? request : { ...request, signal: lifetime.signal }\n const result = await route.handle(routeRequest)\n if (result === null || typeof result !== 'object' || typeof result.body !== 'string' && !(result.body instanceof Uint8Array) && !isReadable(result.body)) {\n throw new MobileExtensionError('invalid_route_response', 'extension returned an invalid response', 500)\n }\n if (lifetime !== undefined && isReadable(result.body)) {\n releaseLifetime = false\n releaseSignalLifetimeWhenStreamSettles(result.body, lifetime.cleanup)\n }\n return result\n } catch (error) {\n if (error instanceof MobileExtensionError) throw error\n throw new MobileExtensionError('extension_failed', 'extension route failed', 500)\n } finally { if (releaseLifetime) lifetime?.cleanup() }\n }\n\n /** Start the local directory watcher; an absent directory is intentionally inert. */\n async startLocal(root: string, context: Context): Promise<void> {\n const targetRoot = resolve(root)\n if (this.localRoot !== undefined && resolve(this.localRoot) !== targetRoot) await this.stopLocal()\n if (this.localTimer !== undefined) clearInterval(this.localTimer)\n const lifecycle = ++this.localLifecycle\n this.localRoot = targetRoot; this.localContext = context; this.localClosed = false\n await mkdir(this.localRoot, { recursive: true })\n if (this.localClosed || this.localLifecycle !== lifecycle || this.localRoot !== targetRoot || this.localContext !== context) return\n await this.refreshLocal()\n if (this.localClosed || this.localLifecycle !== lifecycle || this.localRoot !== targetRoot || this.localContext !== context) return\n this.localTimer = setInterval(() => { void this.refreshLocal() }, 2_000)\n this.localTimer.unref()\n }\n\n /** Stop the watcher and abort every local host generation. */\n async stopLocal(): Promise<void> {\n this.localClosed = true\n const lifecycle = ++this.localLifecycle\n if (this.localTimer !== undefined) clearInterval(this.localTimer)\n this.localTimer = undefined\n const refreshing = this.localRefreshing\n this.localRefreshAbort?.abort()\n const previous = [...this.local.values(), ...[...this.retired.values()].map(entry => entry.active)]\n this.local.clear()\n for (const entry of this.retired.values()) clearTimeout(entry.timer)\n this.retired.clear()\n this.failures.clear()\n this.updateContentHash()\n await Promise.allSettled([\n abortAndDisposeLocal(previous),\n ...(refreshing === undefined ? [] : [refreshing]),\n ])\n if (this.localLifecycle !== lifecycle) return\n const late = [...this.local.values(), ...[...this.retired.values()].map(entry => entry.active)]\n this.local.clear()\n for (const entry of this.retired.values()) clearTimeout(entry.timer)\n this.retired.clear()\n this.failures.clear()\n this.updateContentHash()\n await abortAndDisposeLocal(late)\n if (this.localTimer !== undefined) clearInterval(this.localTimer)\n this.localTimer = undefined\n }\n\n /** Refresh all local extensions atomically; failures keep the previous snapshot. */\n refreshLocal(): Promise<void> {\n if (this.localRefreshing !== undefined) return this.localRefreshing\n const controller = new AbortController()\n this.localRefreshAbort = controller\n const refreshing = this.stageAndCommit(controller.signal).finally(() => {\n if (this.localRefreshing === refreshing) this.localRefreshing = undefined\n if (this.localRefreshAbort === controller) this.localRefreshAbort = undefined\n })\n this.localRefreshing = refreshing\n return refreshing\n }\n\n private async stageAndCommit(signal: AbortSignal): Promise<void> {\n if (this.localClosed || signal.aborted || this.localRoot === undefined || this.localContext === undefined) return\n let names: string[] = []\n try {\n const directory = await opendir(this.localRoot)\n try { for await (const entry of directory) if (entry.isDirectory() && !entry.isSymbolicLink()) names.push(entry.name) }\n finally { await directory.close().catch(() => undefined) }\n } catch { return }\n names.sort()\n const staged: ActiveLocalExtension[] = []\n const stagedFresh: ActiveLocalExtension[] = []\n let failingName = 'local'\n try {\n for (const name of names) {\n signal.throwIfAborted()\n failingName = name\n const directory = join(this.localRoot, name)\n const fingerprint = await extensionFingerprint(directory)\n const current = this.local.get(fingerprint.manifest.id)\n const retired = this.retired.get(fingerprint.manifest.id)?.active\n const previous = current?.digest === fingerprint.digest ? current : retired?.digest === fingerprint.digest ? retired : undefined\n if (previous?.digest === fingerprint.digest) staged.push(previous)\n else {\n const fresh = await loadLocalExtension(directory, this.localContext, fingerprint, signal)\n try {\n signal.throwIfAborted()\n const confirmed = await extensionFingerprint(directory)\n if (confirmed.digest !== fingerprint.digest) {\n throw new MobileExtensionError('extension_changed_during_activation', `extension ${fingerprint.manifest.id} changed during activation`, 409)\n }\n } catch (error) {\n await abortAndDisposeLocal([fresh])\n throw error\n }\n staged.push(fresh); stagedFresh.push(fresh)\n }\n }\n if (this.localClosed || signal.aborted || this.localRoot === undefined || this.localContext === undefined) {\n await abortAndDisposeLocal(stagedFresh)\n return\n }\n const duplicate = new Set<string>()\n for (const entry of staged) {\n if (duplicate.has(entry.manifest.id) || this.registered.has(entry.manifest.id)) throw new MobileExtensionError('duplicate_extension', `duplicate extension id ${entry.manifest.id}`)\n duplicate.add(entry.manifest.id)\n }\n const previous = [...this.local.values()]\n for (const entry of staged) {\n const retired = this.retired.get(entry.manifest.id)\n if (retired?.active === entry) {\n clearTimeout(retired.timer)\n this.retired.delete(entry.manifest.id)\n }\n }\n const stagedIds = new Set(staged.map(entry => entry.manifest.id))\n const removed: ActiveLocalExtension[] = []\n for (const entry of previous) {\n if (staged.includes(entry)) continue\n if (stagedIds.has(entry.manifest.id)) {\n this.retire(entry)\n continue\n }\n removed.push(entry)\n const retired = this.retired.get(entry.manifest.id)\n if (retired !== undefined) {\n clearTimeout(retired.timer)\n this.retired.delete(entry.manifest.id)\n removed.push(retired.active)\n }\n }\n this.local.clear()\n for (const entry of staged) this.local.set(entry.manifest.id, entry)\n for (const entry of staged) this.failures.delete(entry.manifest.id)\n for (const name of names) this.failures.delete(name)\n for (const failure of this.failures.keys()) {\n if (failure !== 'local' && !names.includes(failure)) this.failures.delete(failure)\n }\n this.failures.delete('local')\n if (removed.length > 0) void abortAndDisposeLocal(removed)\n this.updateContentHash()\n } catch (error) {\n await abortAndDisposeLocal(stagedFresh)\n if (this.localClosed || signal.aborted) return\n const message = error instanceof Error ? error.message : String(error)\n this.failures.set(failingName, message)\n if (!(error instanceof MobileExtensionError)) this.ctx.logger.warn(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n private retire(active: ActiveLocalExtension): void {\n const previous = this.retired.get(active.manifest.id)\n if (previous?.active === active) return\n if (previous !== undefined) {\n clearTimeout(previous.timer)\n this.retired.delete(active.manifest.id)\n void abortAndDisposeLocal([previous.active])\n }\n const timer = setTimeout(() => {\n const current = this.retired.get(active.manifest.id)\n if (current?.active !== active) return\n this.retired.delete(active.manifest.id)\n void abortAndDisposeLocal([active])\n }, RETIRED_GENERATION_TTL_MS)\n timer.unref()\n this.retired.set(active.manifest.id, { active, timer })\n }\n}\n\nfunction isReadable(value: unknown): value is Readable {\n return value !== null && typeof value === 'object' && typeof (value as { pipe?: unknown }).pipe === 'function'\n}\n\nfunction releaseSignalLifetimeWhenStreamSettles(stream: Readable, cleanup: () => void): void {\n let stopObserving: (() => void) | undefined\n stopObserving = finished(stream, () => {\n stopObserving?.()\n cleanup()\n })\n}\n\nfunction invokeCleanups(cleanups: readonly (() => void | Promise<void>)[]): Promise<unknown>[] {\n const pending: Promise<unknown>[] = []\n for (const cleanup of [...cleanups].reverse()) {\n try { pending.push(Promise.resolve(cleanup())) } catch { /* extension teardown cannot block the owner */ }\n }\n return pending\n}\n\nasync function settleBounded(pending: readonly Promise<unknown>[], timeoutMs: number): Promise<void> {\n if (pending.length === 0) return\n let timer: NodeJS.Timeout | undefined\n await Promise.race([\n Promise.allSettled(pending),\n new Promise<void>(resolveTimeout => { timer = setTimeout(resolveTimeout, timeoutMs) }),\n ])\n if (timer !== undefined) clearTimeout(timer)\n}\n\nasync function abortAndDisposeLocal(entries: readonly ActiveLocalExtension[]): Promise<void> {\n const pending: Promise<unknown>[] = []\n for (const entry of entries) {\n entry.controller.abort()\n pending.push(...invokeCleanups(entry.cleanups))\n }\n await settleBounded(pending, HOST_TEARDOWN_TIMEOUT_MS)\n}\n\nasync function loadLocalExtension(directory: string, context: Context, known?: LocalExtensionFingerprint, parentSignal?: AbortSignal): Promise<ActiveLocalExtension> {\n const root = await realExtensionRoot(directory)\n const manifestFile = await regularFile(join(root, 'extension.json'), EXTENSION_LIMITS.manifest, 'extension.json')\n const manifest = known?.manifest ?? parseExtensionManifest(JSON.parse(await readFile(manifestFile.path, 'utf8')) as unknown)\n if (manifest.id !== basename(root)) throw new MobileExtensionError('invalid_manifest', 'extension id must match its directory name')\n const scriptBody = known === undefined\n ? await optionalFile(root, 'mobile.js', EXTENSION_LIMITS.script, 'mobile.js').then(path => path === undefined ? undefined : readFile(path))\n : known.scriptBody\n const styleBody = known === undefined\n ? await optionalFile(root, 'mobile.css', EXTENSION_LIMITS.css, 'mobile.css').then(path => path === undefined ? undefined : readFile(path))\n : known.styleBody\n const assets = known?.assets ?? await assetSnapshot(root)\n const hostFile = await optionalFile(root, 'host.mjs', EXTENSION_LIMITS.script, 'host.mjs')\n const controller = new AbortController()\n const actions: Record<string, MobileHostAction> = {}\n const routes: MobileHostRoute[] = []\n const cleanups: (() => void | Promise<void>)[] = []\n const pendingEffects: Promise<void>[] = []\n let activationOpen = true\n const onParentAbort = (): void => { controller.abort(parentSignal?.reason) }\n if (parentSignal?.aborted === true) onParentAbort()\n else parentSignal?.addEventListener('abort', onParentAbort, { once: true })\n const ensureActivationOpen = (): void => {\n if (!activationOpen || controller.signal.aborted) throw new MobileExtensionError('host_activation_closed', `extension ${manifest.id} activation is closed`, 409)\n }\n const api: HostApi = {\n manifest,\n context,\n schema: z,\n signal: controller.signal,\n action(name, spec) { ensureActivationOpen(); if (actions[name] !== undefined) throw new MobileExtensionError('duplicate_action', `duplicate action ${name}`); actions[name] = spec },\n route(spec) { ensureActivationOpen(); routes.push(spec) },\n effect(setup) {\n ensureActivationOpen()\n const result = setup()\n if (result instanceof Promise) {\n pendingEffects.push(result.then(async cleanup => {\n if (typeof cleanup !== 'function') return\n if (activationOpen) cleanups.push(cleanup)\n else await cleanup()\n }))\n } else if (typeof result === 'function') {\n if (activationOpen) cleanups.push(result)\n else void Promise.resolve(result()).catch(() => undefined)\n }\n },\n }\n try {\n const activate = async (): Promise<void> => {\n controller.signal.throwIfAborted()\n if (hostFile !== undefined) {\n const digest = createHash('sha256').update(await readFile(hostFile)).digest('hex')\n controller.signal.throwIfAborted()\n let imported: LocalHostModule\n try { imported = await import(`${pathToFileURL(hostFile).href}?dsh_generation=${digest}`) as LocalHostModule }\n catch { throw new MobileExtensionError('host_load_failed', `could not load ${manifest.id}/host.mjs`, 500) }\n controller.signal.throwIfAborted()\n if (imported.default !== undefined) await imported.default(api)\n }\n await Promise.all(pendingEffects)\n }\n await withActivationTimeout(activate(), manifest.id, controller.signal)\n const host = validateDefinition({ ...manifest, actions, routes })\n activationOpen = false\n const digest = known?.digest ?? createHash('sha256').update(manifest.id).digest('hex')\n return Object.freeze({ manifest, directory: root, ...(scriptBody === undefined ? {} : { scriptBody }), ...(styleBody === undefined ? {} : { styleBody }), assets, host, controller, cleanups: Object.freeze(cleanups), digest })\n } catch (error) {\n activationOpen = false\n controller.abort()\n const cleanupPromises = invokeCleanups(cleanups.splice(0))\n await settleBounded([...pendingEffects, ...cleanupPromises], HOST_TEARDOWN_TIMEOUT_MS)\n throw error\n } finally {\n parentSignal?.removeEventListener('abort', onParentAbort)\n }\n}\n\n/** Construct the service in a Cordis plugin without importing DSH internals. */\nexport function createMobileAccessService(ctx: Context): MobileAccessService {\n return new MobileAccessService(ctx)\n}\n","/** Browser-facing pairing and reauthentication pages with language negotiation. */\nexport type AuthPageLocale = 'zh' | 'en' | 'it'\n\ninterface AuthPageCopy {\n readonly lang: string\n readonly pairTitle: string\n readonly pairHeading: string\n readonly pairingCode: string\n readonly deviceName: string\n readonly pair: string\n readonly pairing: string\n readonly pairFailed: string\n readonly unavailable: string\n readonly reconnectTitle: string\n readonly reconnectHeading: string\n readonly restoring: string\n readonly noLongerPaired: string\n readonly openPairing: string\n}\n\nconst COPY: Record<AuthPageLocale, AuthPageCopy> = {\n en: {\n lang: 'en', pairTitle: 'Pair DSH mobile access', pairHeading: 'Pair this device',\n pairingCode: 'Pairing code', deviceName: 'Device name', pair: 'Pair', pairing: 'Pairing…',\n pairFailed: 'Pairing failed', unavailable: 'The computer is unavailable.',\n reconnectTitle: 'Reconnect DSH mobile access', reconnectHeading: 'Reconnect this device',\n restoring: 'Restoring the secure Session…', noLongerPaired: 'This device is no longer paired. Open pairing on the computer, then pair it again.',\n openPairing: 'Open pairing',\n },\n zh: {\n lang: 'zh-CN', pairTitle: '配对 DSH 移动访问', pairHeading: '配对此设备',\n pairingCode: '配对码', deviceName: '设备名称', pair: '配对', pairing: '正在配对…',\n pairFailed: '配对失败', unavailable: '电脑当前不可用。',\n reconnectTitle: '重新连接 DSH 移动访问', reconnectHeading: '重新连接此设备',\n restoring: '正在恢复安全会话…', noLongerPaired: '此设备已不在配对列表中。请在电脑端打开配对,然后重新配对。',\n openPairing: '打开配对页面',\n },\n it: {\n lang: 'it-IT', pairTitle: 'Abbina accesso mobile DSH', pairHeading: 'Abbina questo dispositivo',\n pairingCode: 'Codice di abbinamento', deviceName: 'Nome dispositivo', pair: 'Abbina', pairing: 'Abbinamento…',\n pairFailed: 'Abbinamento non riuscito', unavailable: 'Il computer non è disponibile.',\n reconnectTitle: 'Riconnetti accesso mobile DSH', reconnectHeading: 'Riconnetti questo dispositivo',\n restoring: 'Ripristino della sessione sicura…', noLongerPaired: 'Questo dispositivo non è più abbinato. Apri l’abbinamento sul computer e ripeti la procedura.',\n openPairing: 'Apri abbinamento',\n },\n}\n\n/** Choose Chinese, Italian, or English from an HTTP Accept-Language header. */\nexport function resolveAuthPageLocale(header: string | undefined): AuthPageLocale {\n const values = header?.split(',').map((value, index) => {\n const [language, ...parameters] = value.split(';')\n const quality = parameters.find(parameter => parameter.trim().toLowerCase().startsWith('q='))\n const parsedQuality = quality === undefined ? 1 : Number(quality.trim().slice(2))\n return {\n language: language?.trim().toLowerCase() ?? '',\n quality: Number.isFinite(parsedQuality) ? Math.max(0, Math.min(1, parsedQuality)) : 0,\n index,\n }\n }).filter(value => value.quality > 0).sort((left, right) => right.quality - left.quality || left.index - right.index).map(value => value.language) ?? []\n for (const value of values) {\n if (value.startsWith('zh')) return 'zh'\n if (value.startsWith('it')) return 'it'\n if (value.startsWith('en')) return 'en'\n }\n return 'en'\n}\n\n/** Render the browser pairing form without reflecting request data into HTML. */\nexport function renderPairPage(locale: AuthPageLocale): string {\n const copy = COPY[locale]\n return `<!doctype html>\n<html lang=\"${copy.lang}\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\">\n<title>${copy.pairTitle}</title>\n<main>\n <h1>${copy.pairHeading}</h1>\n <form id=\"pair-form\">\n <label>${copy.pairingCode} <input id=\"pair-token\" autocomplete=\"one-time-code\" required></label>\n <label>${copy.deviceName} <input id=\"device-label\" maxlength=\"64\" autocomplete=\"off\"></label>\n <button type=\"submit\">${copy.pair}</button>\n <output id=\"pair-status\" aria-live=\"polite\"></output>\n </form>\n</main>\n<script src=\"/mobile-access/pair.js\" defer></script>\n</html>\n`\n}\n\n/** Render the pairing form script with locale-owned status messages. */\nexport function renderPairScript(locale: AuthPageLocale): string {\n const copy = COPY[locale]\n return `(() => {\n const form = document.getElementById('pair-form')\n const token = document.getElementById('pair-token')\n const label = document.getElementById('device-label')\n const status = document.getElementById('pair-status')\n const fragment = new URLSearchParams(location.hash.slice(1))\n const supplied = fragment.get('token')\n history.replaceState(null, '', location.pathname)\n if (supplied) token.value = supplied\n form.addEventListener('submit', async (event) => {\n event.preventDefault()\n status.value = ${JSON.stringify(copy.pairing)}\n try {\n const response = await fetch('/mobile-access/auth/pair', {\n method: 'POST',\n credentials: 'same-origin',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ token: token.value, label: label.value || undefined }),\n })\n if (!response.ok) {\n status.value = ${JSON.stringify(copy.pairFailed)}\n return\n }\n location.replace('/')\n } catch {\n status.value = ${JSON.stringify(copy.unavailable)}\n }\n })\n})()\n`\n}\n\n/** Render the browser reauthentication page. */\nexport function renderLoginPage(locale: AuthPageLocale): string {\n const copy = COPY[locale]\n return `<!doctype html>\n<html lang=\"${copy.lang}\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\">\n<title>${copy.reconnectTitle}</title>\n<main>\n <h1>${copy.reconnectHeading}</h1>\n <p id=\"login-progress\" aria-live=\"polite\">${copy.restoring}</p>\n <section id=\"login-failed\" hidden>\n <p>${copy.noLongerPaired}</p>\n <a href=\"/mobile-access/pair\">${copy.openPairing}</a>\n </section>\n</main>\n<script src=\"/mobile-access/login.js\" defer></script>\n</html>\n`\n}\n\n/** Render the reauthentication script with locale-owned failure text. */\nexport function renderLoginScript(locale: AuthPageLocale): string {\n const copy = COPY[locale]\n return `(() => {\n const candidate = new URL(location.href).searchParams.get('return')\n let returnPath = '/'\n if (candidate && candidate.startsWith('/')) {\n try {\n const resolved = new URL(candidate, location.origin)\n const pathname = decodeURIComponent(resolved.pathname)\n if (resolved.origin === location.origin && pathname !== '/mobile-access'\n && !pathname.startsWith('/mobile-access/') && !pathname.includes('\\\\\\\\')) {\n returnPath = resolved.pathname + resolved.search + resolved.hash\n }\n } catch {\n // Malformed untrusted return targets keep the safe root default.\n }\n }\n fetch('/mobile-access/auth/renew', {\n method: 'POST',\n credentials: 'same-origin',\n headers: { 'content-type': 'application/json' },\n body: '{}',\n }).then((response) => {\n if (response.ok) {\n location.replace(returnPath)\n return\n }\n document.getElementById('login-progress').hidden = true\n document.getElementById('login-failed').hidden = false\n }).catch(() => {\n document.getElementById('login-progress').textContent = ${JSON.stringify(copy.unavailable)}\n })\n})()\n`\n}\n","import { createHash, X509Certificate } from 'node:crypto'\nimport { createSocket, type Socket as DatagramSocket } from 'node:dgram'\nimport { lstat, readFile, stat } from 'node:fs/promises'\nimport { hostname } from 'node:os'\nimport { extname } from 'node:path'\nimport {\n createServer as createHttpServer,\n request as requestHttp,\n type ClientRequest,\n type IncomingHttpHeaders,\n type IncomingMessage,\n type OutgoingHttpHeaders,\n type Server as HttpServer,\n type ServerResponse,\n} from 'node:http'\nimport { createServer as createHttpsServer, type Server as HttpsServer, type ServerOptions } from 'node:https'\nimport { connect, type AddressInfo, type Socket } from 'node:net'\nimport { isIP } from './ip.js'\nimport { Transform, type TransformCallback } from 'node:stream'\nimport { pipeline } from 'node:stream/promises'\nimport { promisify } from 'node:util'\nimport { createGzip, gzip } from 'node:zlib'\nimport type { WebRoute } from '@deepseek-ai/dsh-host-webserver'\nimport Bonjour from 'bonjour-service'\nimport * as QRCode from 'qrcode'\nimport {\n AccessController,\n AccessError,\n BoundedRateLimiter,\n type DeviceSummary,\n type SessionAuthorization,\n} from './access.js'\nimport type { ResolvedGatewayConfig } from './config.js'\nimport { ensureMobileCompatibility, MOBILE_COMPAT_PATH } from './mobile-compat-bootstrap.js'\nimport {\n AUTH_PREFIX,\n assertExternalTrust,\n assertLocalAdminTrust,\n cookie,\n CSRF_COOKIE,\n CSRF_HEADER,\n DEVICE_COOKIE,\n HttpError,\n LOCAL_ADMIN_PREFIX,\n parseCookies,\n parseRequestTarget,\n readJsonObject,\n sendFailure,\n sendJson,\n SESSION_COOKIE,\n setSecurityHeaders,\n WS_PATHS,\n} from './http-security.js'\nimport type { BlockedUpgradePathEntry, BlockedUpgradePathLog } from './websocket-paths.js'\nimport {\n DSH_MOBILE_VERSION,\n MINIMUM_ANDROID_APP_VERSION,\n MOBILE_METADATA_VERSION,\n} from './version.js'\nimport { addressAllowed, isLoopbackAddress, type ParsedCidr, RequestTrustPolicy } from './network.js'\nimport type { DeviceStore } from './storage.js'\nimport { listComputerImages, readComputerImage } from './computer-images.js'\nimport {\n EXTENSION_LIMITS,\n MobileExtensionError,\n type MobileAccessService,\n type MobileRouteRequest,\n type MobileRouteResponse,\n} from './extensions.js'\nimport {\n renderLoginPage,\n renderLoginScript,\n renderPairPage,\n renderPairScript,\n resolveAuthPageLocale,\n} from './auth-pages.js'\n\ntype GatewayServer = HttpServer | HttpsServer\n\ninterface ActiveRequest {\n readonly sessionKey: string\n readonly deviceId: string\n readonly expiresAt: number\n readonly abort: () => void\n readonly timer: NodeJS.Timeout\n}\n\ninterface ActiveWebSocket {\n readonly sessionKey: string\n readonly deviceId: string\n readonly client: Socket\n readonly upstream: Socket\n readonly timer: NodeJS.Timeout\n}\n\nconst MAX_CONTROL_BODY_BYTES = 16 * 1024\nconst MAX_HEADER_BYTES = 16 * 1024\nconst MOBILE_HISTORY_PAGE_MESSAGES = 10\nconst SESSION_HISTORY_PATH = '/api/session.history'\nconst DISCOVERY_QUERY = Buffer.from('DSH_MOBILE_DISCOVER_V1', 'ascii')\nconst DISCOVERY_PROTOCOL = 1\nconst DISCOVERY_INTERVAL_MS = 3_000\nconst MDNS_SERVICE_TYPE = 'dsh-mobile'\nconst MOBILE_LAYOUT_MODULE = '@deepseek-ai/dsh-client-ui-layout'\nconst MOBILE_LAYOUT_PATH = `${AUTH_PREFIX}/mobile-layout.js`\nconst MOBILE_BOOT_BATCH_PREFIX = `${AUTH_PREFIX}/mobile-boot/`\nconst MAX_MOBILE_BOOT_BATCH_BYTES = 32 * 1024 * 1024\nconst MAX_MOBILE_BOOT_ENTRY_BYTES = 8 * 1024 * 1024\n/**\n * Per-merged-batch byte budget. One upstream application batch can carry dozens\n * of client bundles whose combined size exceeds the hard batch cap; the layout\n * batch is therefore chunked into multiple merged batches, each kept safely\n * below {@link MAX_MOBILE_BOOT_BATCH_BYTES} so assembly can never reject it.\n */\nconst MOBILE_BOOT_CHUNK_BYTES = 16 * 1024 * 1024\n/** Bytes the assembly emits after each entry (`body` plus `\\n;\\n`). */\nconst MOBILE_BOOT_SEPARATOR_BYTES = 3\n/** Freshness window for cached upstream bundle byte sizes. */\nconst MOBILE_BOOT_SIZE_CACHE_TTL_MS = 30_000\n/** Marker header distinguishing size probes from ordinary proxied bundle fetches. */\nconst MOBILE_BOOT_SIZE_PROBE_HEADER = 'x-dsh-mobile-size-probe'\nconst MAX_MOBILE_BOOT_BATCHES = 8\nconst MOBILE_BOOT_UPSTREAM_ATTEMPTS = 4\nconst MOBILE_BOOT_RETRY_DELAY_MS = 150\n\nfunction upstreamPluginBundleUrl(source: string, upstreamOrigin: URL): URL | undefined {\n if ((!source.startsWith('/plugins/') && !source.startsWith('plugins/')) || source.includes('#')) return undefined\n try {\n const target = new URL(source, new URL('/', upstreamOrigin))\n if (target.origin !== upstreamOrigin.origin || !target.pathname.startsWith('/plugins/')) return undefined\n return target\n } catch {\n return undefined\n }\n}\n\nconst TRANSIENT_UPSTREAM_ERROR_CODES = new Set([\n 'EAI_AGAIN',\n 'ECONNABORTED',\n 'ECONNREFUSED',\n 'ECONNRESET',\n 'EPIPE',\n 'EHOSTUNREACH',\n 'ENETDOWN',\n 'ENETRESET',\n 'ENETUNREACH',\n 'ETIMEDOUT',\n 'ERR_STREAM_PREMATURE_CLOSE',\n])\nconst UPSTREAM_AUTH_REFRESH_MARGIN_MS = 60_000\nconst UPSTREAM_COOKIE_PAIR = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+=[\\x21-\\x3A\\x3C-\\x7E]*$/u\nconst CUSTOM_STYLE_FALLBACK = '/* Add mobile overrides in the DSH home mobile-access/mobile.css file. */\\n'\nconst CUSTOM_SCRIPT_FALLBACK = 'window.dshMobile?.register(() => undefined)\\n'\nconst EXTENSION_CHANGE_POLL_MS = 2_000\nconst EXTENSION_EVENT_HEARTBEAT_MS = 15_000\nconst MOBILE_CLIENT_MODULE = 'dsh-mobile'\nconst CONNECTION_MODULE = '@deepseek-ai/dsh-client-connection'\nconst RUNTIME_MODULE = '@deepseek-ai/dsh-client-runtime'\nconst RENDERER_MODULE = '@deepseek-ai/dsh-client-ui-renderer'\nconst SIDEBAR_MODULE = '@deepseek-ai/dsh-client-ui-sidebar'\nconst SETTINGS_MODULE = '@deepseek-ai/dsh-client-ui-settings'\nconst API_GATEWAY_MODULE = '@deepseek-ai/dsh-api-gateway'\nconst API_REMOTES_MODULE = '@deepseek-ai/dsh-api-remotes'\nconst MOBILE_LAYOUT_DEPENDENCY_PROFILES = Object.freeze([\n Object.freeze({\n slots: RUNTIME_MODULE,\n dependencies: Object.freeze([RUNTIME_MODULE, '@deepseek-ai/dsh-client-ui-theme']),\n }),\n Object.freeze({\n slots: RENDERER_MODULE,\n dependencies: Object.freeze([\n '@deepseek-ai/dsh-client-locale',\n RENDERER_MODULE,\n '@deepseek-ai/dsh-client-ui-session',\n '@deepseek-ai/dsh-client-ui-theme',\n ]),\n }),\n])\nconst MOBILE_CSRF_FETCH_BOOTSTRAP = `(()=>{const nativeFetch=window.fetch.bind(window);window.fetch=(input,init)=>{const source=input instanceof Request?input:undefined;const method=String(init?.method??source?.method??'GET').toUpperCase();if(method==='GET'||method==='HEAD')return nativeFetch(input,init);const raw=typeof input==='string'?input:input instanceof URL?input.href:source?.url;if(raw===undefined||new URL(raw,location.href).origin!==location.origin)return nativeFetch(input,init);const headers=new Headers(init?.headers??source?.headers);if(!headers.has(${JSON.stringify(CSRF_HEADER)})){const prefix=${JSON.stringify(`${CSRF_COOKIE}=`)};const token=document.cookie.split(';').map(value=>value.trim()).find(value=>value.startsWith(prefix))?.slice(prefix.length);if(token!==undefined)headers.set(${JSON.stringify(CSRF_HEADER)},token)}return nativeFetch(input,{...init,headers})};})();`\n// Paired pages use the gateway's authenticated HTTP carrier; streams retain DSH's WebSocket transport.\nconst MOBILE_AUTHENTICATED_TRANSPORT_BOOTSTRAP = `(()=>{if(window.__DSH_TRANSPORT__!==undefined)throw new Error('DSH Mobile cannot replace an existing transport override');window.__DSH_TRANSPORT__={fetch:(input,init)=>window.fetch(input,init),ownsHost:true}})();`\n/** Use this page's gateway health, rather than OS internet validation, for DSH reconnects. */\nconst MOBILE_GATEWAY_REACHABILITY_BOOTSTRAP = `(()=>{\n const view=globalThis;\n const port=view.navigator;\n const nativeFetch=typeof view.fetch==='function'?view.fetch.bind(view):undefined;\n let reachable=true;\n let checking=false;\n let replaying=false;\n let disposed=false;\n let epoch=0;\n let retryTimer;\n let activeController;\n const clearRetry=()=>{if(retryTimer!==undefined){view.clearTimeout(retryTimer);retryTimer=undefined}};\n const scheduleRetry=()=>{if(!disposed&&!reachable&&retryTimer===undefined){retryTimer=view.setTimeout(()=>{retryTimer=undefined;check()},5000)}};\n const check=()=>{\n if(checking||disposed||nativeFetch===undefined)return;\n checking=true;\n const current=++epoch;\n const controller=new AbortController();\n activeController=controller;\n const timer=view.setTimeout(()=>controller.abort(),2000);\n void nativeFetch('/mobile-access/health',{cache:'no-store',credentials:'same-origin',signal:controller.signal})\n .then(response=>response.ok,()=>false)\n .then(ok=>{\n if(disposed||current!==epoch)return;\n if(ok){\n const wasOffline=!reachable;\n reachable=true;\n clearRetry();\n if(wasOffline)view.dispatchEvent(new Event('online'));\n return;\n }\n const wasOnline=reachable;\n reachable=false;\n if(wasOnline){\n replaying=true;\n try{view.dispatchEvent(new Event('offline'))}finally{replaying=false}\n }\n scheduleRetry();\n })\n .finally(()=>{view.clearTimeout(timer);activeController=undefined;checking=false});\n };\n if(port!==undefined&&typeof Object.defineProperty==='function'){\n try{Object.defineProperty(port,'onLine',{configurable:true,get:()=>reachable})}catch{/* A frozen navigator retains its native value. */}\n }\n if(typeof view.addEventListener==='function'){\n view.addEventListener('offline',event=>{if(replaying||nativeFetch===undefined)return;event.stopImmediatePropagation();check()},true);\n view.addEventListener('online',()=>{++epoch;reachable=true;clearRetry()},true);\n view.addEventListener('pagehide',()=>{disposed=true;++epoch;clearRetry();activeController?.abort()},true);\n }\n})();`\nconst PAIR_PAGE = `<!doctype html>\n<html lang=\"en\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\">\n<title>Pair DSH mobile access</title>\n<main>\n <h1>Pair this device</h1>\n <form id=\"pair-form\">\n <label>Pairing code <input id=\"pair-token\" autocomplete=\"one-time-code\" required></label>\n <label>Device name <input id=\"device-label\" maxlength=\"64\" autocomplete=\"off\"></label>\n <button type=\"submit\">Pair</button>\n <output id=\"pair-status\"></output>\n </form>\n</main>\n<script src=\"/mobile-access/pair.js\" defer></script>\n</html>\n`\n\ninterface BootGraphEntry {\n id: string\n url: string\n rev: string\n inject?: string[]\n immediately?: boolean\n}\n\ninterface BootGraphBatch {\n phase: 'bootstrap' | 'application'\n url: string\n rev: string\n entries: string[]\n}\n\ninterface MobileBootBatchEntry {\n readonly id: string\n readonly url: string\n readonly rev: string\n}\n\ninterface MobileBootBatchPlan {\n readonly key: string\n readonly path: string\n readonly entries: readonly MobileBootBatchEntry[]\n}\n\ninterface RewrittenMobileIndex {\n readonly html: string\n /** Merged batches the gateway assembles and serves; pass-through rows are not plans. */\n readonly batches: readonly MobileBootBatchPlan[]\n}\n\n/** Parsed boot manifest with the layout entry rewired to the gateway-owned layout module. */\ninterface MobileBootPlanRef {\n readonly parsed: { rev: string; entries: BootGraphEntry[]; batches?: BootGraphBatch[] }\n readonly entries: BootGraphEntry[]\n readonly batches?: BootGraphBatch[]\n readonly slotProvider: string\n readonly assignment: string\n readonly replaceStart: number\n readonly replaceEnd: number\n readonly layoutBatch?: BootGraphBatch\n readonly planEntries?: readonly MobileBootBatchEntry[]\n}\n\ninterface SplitMobileBootBatch {\n readonly plans: readonly MobileBootBatchPlan[]\n readonly rows: readonly BootGraphBatch[]\n}\n\ninterface StoredMobileBootBatch {\n readonly plan: MobileBootBatchPlan\n body?: Buffer\n gzipBody?: Buffer\n etag?: string\n layoutMtimeMs?: number\n /** In-flight assembly shared by every concurrent requester of this batch. */\n assembly?: MobileBootBatchAssembly\n}\n\ninterface MobileBootBatchAssembly {\n readonly controller: AbortController\n readonly task: Promise<Buffer>\n}\n\nconst gzipBuffer = promisify(gzip)\n\nfunction ensureMobileViewport(html: string): string {\n const viewport = /<meta\\b(?=[^>]*\\bname\\s*=\\s*[\"']viewport[\"'])[^>]*>/iu\n const match = viewport.exec(html)\n if (match === null) {\n const head = /<head\\b[^>]*>/iu.exec(html)\n if (head?.index === undefined) return html\n const position = head.index + head[0].length\n return `${html.slice(0, position)}<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\">${html.slice(position)}`\n }\n if (/\\bviewport-fit\\s*=\\s*cover\\b/iu.test(match[0])) return html\n const content = /\\bcontent\\s*=\\s*([\"'])(.*?)\\1/iu\n const next = content.test(match[0])\n ? match[0].replace(content, (_whole, quote: string, value: string) => `content=${quote}${value},viewport-fit=cover${quote}`)\n : match[0].replace(/\\s*\\/?>$/u, ' content=\"width=device-width,initial-scale=1,viewport-fit=cover\">')\n return `${html.slice(0, match.index)}${next}${html.slice(match.index + match[0].length)}`\n}\n\nfunction orderAuthenticatedSettings(entries: BootGraphEntry[], slotsProvider: string): boolean {\n const mobile = entries.filter(entry => entry !== null && typeof entry === 'object' && entry.id === MOBILE_CLIENT_MODULE)\n const settings = entries.filter(entry => entry !== null && typeof entry === 'object' && entry.id === SETTINGS_MODULE)\n if (mobile.length === 0 || settings.length === 0) return false\n if (mobile.length !== 1 || settings.length !== 1) throw new Error('upstream DSH mobile settings graph is ambiguous')\n if (!Array.isArray(mobile[0]?.inject)\n || !mobile[0].inject.includes(CONNECTION_MODULE)\n || !mobile[0].inject.includes(SIDEBAR_MODULE)) {\n throw new Error('dsh-mobile client has unsupported dependencies')\n }\n if (!Array.isArray(settings[0]?.inject)) {\n throw new Error('upstream DSH settings module has unsupported dependencies')\n }\n const remoteSettings = !settings[0].inject.includes(CONNECTION_MODULE)\n if (remoteSettings) {\n if (!settings[0].inject.includes(API_REMOTES_MODULE) || slotsProvider !== RENDERER_MODULE) {\n throw new Error('upstream DSH settings module has unsupported dependencies')\n }\n const remotes = entries.filter(entry => entry !== null && typeof entry === 'object' && entry.id === API_REMOTES_MODULE)\n const gateway = entries.filter(entry => entry !== null && typeof entry === 'object' && entry.id === API_GATEWAY_MODULE)\n if (remotes.length !== 1 || !Array.isArray(remotes[0]?.inject) || !remotes[0].inject.includes(API_GATEWAY_MODULE)\n || gateway.length !== 1 || !Array.isArray(gateway[0]?.inject) || !gateway[0].inject.includes(CONNECTION_MODULE)) {\n throw new Error('upstream DSH settings Remote graph has unsupported dependencies')\n }\n // These package edges order factory arrival; authenticated transport trust is installed before boot.\n if (!gateway[0].inject.includes(MOBILE_CLIENT_MODULE)) gateway[0].inject = [...gateway[0].inject, MOBILE_CLIENT_MODULE]\n }\n mobile[0].inject = [CONNECTION_MODULE, slotsProvider]\n if (!settings[0].inject.includes(MOBILE_CLIENT_MODULE)) settings[0].inject = [...settings[0].inject, MOBILE_CLIENT_MODULE]\n return remoteSettings\n}\n\nfunction revisionedMobileBatchPath(entries: readonly MobileBootBatchEntry[]): { readonly key: string; readonly path: string } {\n const key = createHash('sha256')\n .update(DSH_MOBILE_VERSION)\n .update(JSON.stringify(entries))\n .digest('hex')\n return { key, path: `${MOBILE_BOOT_BATCH_PREFIX}${key}.js` }\n}\n\n/**\n * Parse one upstream boot manifest and rewire the layout entry onto the\n * gateway-owned layout module. The layout entry's URL and revision become the\n * local `mobile-layout.js` path; the dedicated frontend is authenticated in the\n * gateway, so the requested layout needs no upstream address.\n */\nexport function parseMobileBootPlan(html: string): MobileBootPlanRef {\n const assignment = /(?:window\\.__DSH_BOOT__|globalThis\\[\"__DSH_BOOT__\"\\])\\s*=\\s*/u.exec(html)\n if (assignment?.index === undefined) throw new Error('upstream DSH index has no boot manifest')\n const replaceStart = assignment.index\n const valueStart = replaceStart + assignment[0].length\n const scriptEnd = html.indexOf('</script>', valueStart)\n if (scriptEnd < 0) throw new Error('upstream DSH boot manifest script is incomplete')\n const source = html.slice(valueStart, scriptEnd).trim().replace(/;$/u, '')\n const parsed = JSON.parse(source) as { rev?: unknown; entries?: unknown; batches?: unknown }\n if (typeof parsed.rev !== 'string' || !Array.isArray(parsed.entries)) {\n throw new Error('upstream DSH boot manifest is malformed')\n }\n const entries = parsed.entries as BootGraphEntry[]\n const layout = entries.filter(entry => entry !== null && typeof entry === 'object' && entry.id === MOBILE_LAYOUT_MODULE)\n if (layout.length !== 1 || typeof layout[0]?.url !== 'string' || typeof layout[0].rev !== 'string') {\n throw new Error('upstream DSH boot manifest has no unique layout module')\n }\n if (!Array.isArray(layout[0].inject)) {\n throw new Error('upstream DSH layout module has unsupported dependencies')\n }\n const dependencyProfile = MOBILE_LAYOUT_DEPENDENCY_PROFILES.find(profile => (\n profile.dependencies.every(dependency => layout[0]?.inject?.includes(dependency))\n ))\n if (dependencyProfile === undefined) throw new Error('upstream DSH layout module has unsupported dependencies')\n layout[0].url = MOBILE_LAYOUT_PATH\n layout[0].rev = `dsh-mobile-layout-${DSH_MOBILE_VERSION}`\n const parsedBatches = parsed.batches\n if (parsedBatches === undefined) {\n return Object.freeze({\n parsed: parsed as { rev: string; entries: BootGraphEntry[] },\n entries,\n slotProvider: dependencyProfile.slots,\n assignment: assignment[0],\n replaceStart,\n replaceEnd: scriptEnd,\n })\n }\n if (!Array.isArray(parsedBatches)) throw new Error('upstream DSH boot manifest batches are malformed')\n const batches = parsedBatches as BootGraphBatch[]\n const entryById = new Map(entries.map(entry => [entry.id, entry]))\n if (entryById.size !== entries.length) throw new Error('upstream DSH boot manifest has duplicate entries')\n const layoutBatches: BootGraphBatch[] = []\n for (const batch of batches) {\n if (batch === null || typeof batch !== 'object'\n || (batch.phase !== 'bootstrap' && batch.phase !== 'application')\n || typeof batch.url !== 'string' || typeof batch.rev !== 'string'\n || !Array.isArray(batch.entries) || batch.entries.length === 0\n || batch.entries.some(id => typeof id !== 'string' || !entryById.has(id))) {\n throw new Error('upstream DSH boot manifest batches are malformed')\n }\n if (batch.entries.includes(MOBILE_LAYOUT_MODULE)) layoutBatches.push(batch)\n }\n if (layoutBatches.length !== 1 || layoutBatches[0]?.phase !== 'application') {\n throw new Error('upstream DSH boot manifest has no unique application layout batch')\n }\n const layoutBatch = layoutBatches[0]\n const planEntries = layoutBatch.entries.map((id): MobileBootBatchEntry => {\n const entry = entryById.get(id)\n if (entry === undefined || typeof entry.url !== 'string' || typeof entry.rev !== 'string') {\n throw new Error('upstream DSH boot manifest batches are malformed')\n }\n return Object.freeze({ id, url: entry.url, rev: entry.rev })\n })\n return Object.freeze({\n parsed: parsed as { rev: string; entries: BootGraphEntry[]; batches: BootGraphBatch[] },\n entries,\n batches,\n slotProvider: dependencyProfile.slots,\n assignment: assignment[0],\n replaceStart,\n replaceEnd: scriptEnd,\n layoutBatch,\n planEntries,\n })\n}\n\n/**\n * Partition one upstream application batch into merged batches plus pass-through\n * rows. Every entry whose bundle is at or above the per-entry cap keeps its own\n * upstream `/plugins` row (the gateway proxies it verbatim), because a merged\n * batch cannot carry it. The remaining entries are greedily packed into merged\n * batches under {@link MOBILE_BOOT_CHUNK_BYTES}; the layout module always merges,\n * since its body is the gateway-owned layout file, never an upstream fetch.\n */\nexport function splitMobileBootBatch(\n entries: readonly MobileBootBatchEntry[],\n sizes: ReadonlyMap<string, number>,\n passThrough: ReadonlySet<string>,\n): SplitMobileBootBatch {\n const rows: BootGraphBatch[] = []\n const plans: MobileBootBatchPlan[] = []\n let chunk: { ids: string[]; entries: MobileBootBatchEntry[]; bytes: number } | undefined\n const flush = (): void => {\n if (chunk === undefined || chunk.entries.length === 0) return\n const revision = revisionedMobileBatchPath(chunk.entries)\n rows.push({ phase: 'application', url: revision.path, rev: revision.key, entries: chunk.ids })\n plans.push(Object.freeze({ key: revision.key, path: revision.path, entries: Object.freeze(chunk.entries) }))\n chunk = undefined\n }\n for (const entry of entries) {\n const size = sizes.get(entry.url)\n const oversized = passThrough.has(entry.url)\n || (size !== undefined && size >= MAX_MOBILE_BOOT_ENTRY_BYTES)\n if (oversized && entry.id !== MOBILE_LAYOUT_MODULE) {\n // A pass-through row never joins a merged batch; it also must not split\n // the open chunk, because chunk contents are independent of row order.\n rows.push({ phase: 'application', url: entry.url, rev: entry.rev, entries: [entry.id] })\n continue\n }\n const bytes = (size ?? 0) + MOBILE_BOOT_SEPARATOR_BYTES\n if (chunk !== undefined && chunk.entries.length > 0 && chunk.bytes + bytes > MOBILE_BOOT_CHUNK_BYTES) flush()\n chunk ??= { ids: [], entries: [], bytes: 0 }\n chunk.ids.push(entry.id)\n chunk.entries.push(entry)\n chunk.bytes += bytes\n }\n flush()\n return { plans, rows }\n}\n\nfunction rewriteMobileIndexWithBatch(\n html: string,\n options: { readonly sizes?: ReadonlyMap<string, number>; readonly passThrough?: ReadonlySet<string> } = {},\n): RewrittenMobileIndex {\n const plan = parseMobileBootPlan(html)\n const remoteSettings = orderAuthenticatedSettings(plan.entries, plan.slotProvider)\n let batches: readonly MobileBootBatchPlan[] = []\n if (plan.batches !== undefined && plan.layoutBatch !== undefined && plan.planEntries !== undefined) {\n const split = splitMobileBootBatch(\n plan.planEntries,\n options.sizes ?? new Map(),\n options.passThrough ?? new Set(),\n )\n const at = plan.batches.indexOf(plan.layoutBatch)\n if (at < 0) throw new Error('upstream DSH boot manifest has no unique application layout batch')\n plan.batches.splice(at, 1, ...split.rows)\n batches = Object.freeze(split.plans)\n plan.parsed.rev = createHash('sha256').update(JSON.stringify({ entries: plan.entries, batches: plan.batches })).digest('hex').slice(0, 16)\n }\n const transportBootstrap = remoteSettings ? MOBILE_AUTHENTICATED_TRANSPORT_BOOTSTRAP : ''\n const replacement = `${MOBILE_GATEWAY_REACHABILITY_BOOTSTRAP}${transportBootstrap}${MOBILE_CSRF_FETCH_BOOTSTRAP}window.__DSH_MOBILE_FRONTEND__=\"dedicated\";${plan.assignment}${JSON.stringify(plan.parsed)};`\n return Object.freeze({\n html: ensureMobileViewport(ensureMobileCompatibility(`${html.slice(0, plan.replaceStart)}${replacement}${html.slice(plan.replaceEnd)}`)),\n batches,\n })\n}\n\n/** Replace only DSH's layout client module while retaining its complete plugin graph. */\nexport function rewriteMobileIndex(html: string): string {\n return rewriteMobileIndexWithBatch(html).html\n}\n\nclass ByteLimitTransform extends Transform {\n private total = 0\n\n constructor(private readonly maximum: number) {\n super()\n }\n\n override _transform(chunk: Buffer, encoding: BufferEncoding, callback: TransformCallback): void {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding)\n this.total += buffer.length\n if (this.total > this.maximum) {\n callback(new HttpError(413, 'payload_too_large'))\n return\n }\n callback(null, buffer)\n }\n}\n\nfunction stripIpv6Brackets(hostname: string): string {\n return hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname\n}\n\ninterface PemCertificate {\n readonly pem: string\n readonly certificate: X509Certificate\n}\n\nfunction parsePemCertificates(contents: Buffer, source: string): PemCertificate[] {\n const text = contents.toString('utf8')\n const pattern = /-----BEGIN CERTIFICATE-----[\\s\\S]*?-----END CERTIFICATE-----/gu\n const blocks = text.match(pattern) ?? []\n if (blocks.length === 0 || text.replace(pattern, '').trim() !== '') {\n throw new Error(`${source} must contain only PEM certificates`)\n }\n return blocks.map((pem) => {\n let certificate: X509Certificate\n try {\n certificate = new X509Certificate(pem)\n } catch (error) {\n throw new Error(`${source} contains an invalid certificate`, { cause: error })\n }\n return Object.freeze({ pem: `${pem}\\n`, certificate })\n })\n}\n\nfunction validateServerChain(chain: readonly PemCertificate[]): void {\n const now = Date.now()\n for (const [index, entry] of chain.entries()) {\n if (Date.parse(entry.certificate.validFrom) > now || Date.parse(entry.certificate.validTo) <= now) {\n throw new Error('TLS certificate chain contains a certificate that is not currently valid')\n }\n if (index === 0) continue\n if (entry.certificate.subject === entry.certificate.issuer\n && entry.certificate.verify(entry.certificate.publicKey)) {\n throw new Error('TLS server certificate chain must not include a self-signed root')\n }\n const child = chain[index - 1]!.certificate\n if (!entry.certificate.ca || !child.checkIssued(entry.certificate)\n || !child.verify(entry.certificate.publicKey)) {\n throw new Error('TLS server certificate chain is not an ordered leaf-to-intermediate chain')\n }\n }\n}\n\nasync function tlsOptions(config: ResolvedGatewayConfig): Promise<ServerOptions> {\n if (config.tls.mode === 'disabled') throw new Error('TLS options requested for a disabled listener')\n const [certFile, key, additionalChainFile] = await Promise.all([\n readFile(config.tls.certFile),\n readFile(config.tls.keyFile),\n config.tls.caFile === undefined ? Promise.resolve(undefined) : readFile(config.tls.caFile),\n ])\n const chain = [\n ...parsePemCertificates(certFile, 'tls.certFile'),\n ...(additionalChainFile === undefined ? [] : parsePemCertificates(additionalChainFile, 'tls.caFile')),\n ]\n validateServerChain(chain)\n const leaf = chain[0]!.certificate\n for (const authority of config.authorities) {\n const hostname = stripIpv6Brackets(authority.hostname)\n const match = isIP(hostname) === 0 ? leaf.checkHost(hostname) : leaf.checkIP(hostname)\n if (match === undefined) throw new Error(`TLS certificate does not cover configured authority ${hostname}`)\n }\n return {\n cert: chain.map(entry => entry.pem).join(''),\n key,\n requestCert: false,\n minVersion: 'TLSv1.2',\n maxHeaderSize: MAX_HEADER_BYTES,\n }\n}\n\nfunction websocketAccept(key: string): string {\n return createHash('sha1').update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`, 'ascii').digest('base64')\n}\n\nfunction headerValue(headers: IncomingHttpHeaders, name: string): string | undefined {\n const value = headers[name]\n return Array.isArray(value) ? undefined : value\n}\n\nfunction hasToken(header: string | undefined, token: string): boolean {\n return header?.split(',').some(value => value.trim().toLowerCase() === token) ?? false\n}\n\nfunction rejectUpgrade(socket: Socket, status: number, code: string): void {\n if (socket.destroyed) return\n const body = `${JSON.stringify({ error: code })}\\n`\n socket.end([\n `HTTP/1.1 ${String(status)} ${status === 401 ? 'Unauthorized' : status === 403 ? 'Forbidden' : 'Bad Request'}`,\n 'Connection: close',\n 'Cache-Control: no-store',\n 'Content-Type: application/json; charset=utf-8',\n 'Referrer-Policy: no-referrer',\n 'X-Content-Type-Options: nosniff',\n `Content-Length: ${String(Buffer.byteLength(body))}`,\n '',\n body,\n ].join('\\r\\n'))\n}\n\nfunction sanitizeRequestHeaders(\n request: IncomingMessage,\n upstream: URL,\n): OutgoingHttpHeaders {\n const headers: OutgoingHttpHeaders = {\n host: upstream.host,\n }\n if (request.headers.origin !== undefined) headers.origin = upstream.origin\n if (request.headers['sec-fetch-site'] !== undefined) headers['sec-fetch-site'] = 'same-origin'\n const allowed = [\n 'accept', 'accept-encoding', 'accept-language', 'content-encoding', 'content-length', 'content-type',\n 'if-match', 'if-modified-since', 'if-none-match', 'if-unmodified-since', 'range', 'user-agent',\n ] as const\n for (const name of allowed) {\n const value = request.headers[name]\n if (value !== undefined) headers[name] = value\n }\n return headers\n}\n\nconst BLOCKED_RESPONSE_HEADERS = new Set([\n 'alt-svc', 'cache-control', 'connection', 'content-security-policy', 'content-security-policy-report-only',\n 'cross-origin-embedder-policy', 'cross-origin-opener-policy', 'cross-origin-resource-policy', 'expires',\n 'keep-alive', 'nel', 'permissions-policy', 'pragma', 'proxy-authenticate', 'referrer-policy',\n 'report-to', 'reporting-endpoints', 'server', 'set-cookie', 'strict-transport-security', 'trailer',\n 'transfer-encoding', 'upgrade', 'via', 'x-content-type-options', 'x-frame-options', 'x-powered-by',\n])\n\nfunction sanitizeResponseHeaders(headers: IncomingHttpHeaders, upstream: URL): OutgoingHttpHeaders {\n const clean: OutgoingHttpHeaders = {}\n for (const [name, value] of Object.entries(headers)) {\n const lower = name.toLowerCase()\n if (value === undefined || BLOCKED_RESPONSE_HEADERS.has(lower) || lower.startsWith('access-control-')) continue\n if (lower === 'location' && typeof value === 'string') {\n try {\n const location = new URL(value, upstream)\n clean.location = location.origin === upstream.origin\n ? `${location.pathname}${location.search}${location.hash}`\n : value\n } catch {\n continue\n }\n continue\n }\n clean[lower] = value\n }\n return clean\n}\n\nfunction acceptsGzip(header: string | undefined): boolean {\n if (header === undefined) return false\n let wildcard: boolean | undefined\n for (const entry of header.split(',')) {\n const [rawName, ...parameters] = entry.split(';')\n const name = rawName?.trim().toLowerCase()\n if (name === undefined || name === '') continue\n let quality = 1\n for (const parameter of parameters) {\n const match = /^\\s*q\\s*=\\s*(0(?:\\.\\d+)?|1(?:\\.0+)?)\\s*$/iu.exec(parameter)\n if (match !== null) quality = Number(match[1])\n }\n if (name === 'gzip') return quality > 0\n if (name === '*') wildcard = quality > 0\n }\n return wildcard ?? false\n}\n\nfunction isCompressibleContentType(value: string | string[] | undefined): boolean {\n const contentType = Array.isArray(value) ? value[0] : value\n if (contentType === undefined) return false\n const mediaType = contentType.split(';', 1)[0]?.trim().toLowerCase() ?? ''\n return mediaType.startsWith('text/')\n || /^(?:application\\/(?:javascript|json|xml|x-javascript)|image\\/svg\\+xml)$/u.test(mediaType)\n}\n\nfunction shouldCompressResponse(request: IncomingMessage, response: IncomingMessage): boolean {\n const pathname = request.url?.split('?', 1)[0] ?? ''\n const compressibleRequest = (request.method === 'GET'\n && (pathname.startsWith('/plugins/') || pathname.startsWith('/assets/')))\n || (request.method === 'POST' && pathname === SESSION_HISTORY_PATH)\n return compressibleRequest\n && response.statusCode === 200\n && request.headers.range === undefined\n && response.headers['content-range'] === undefined\n && response.headers['content-encoding'] === undefined\n && acceptsGzip(request.headers['accept-encoding'])\n && isCompressibleContentType(response.headers['content-type'])\n}\n\nfunction revisionedStaticCacheControl(\n request: IncomingMessage,\n statusCode: number | undefined,\n): string | undefined {\n if (request.method !== 'GET' && request.method !== 'HEAD') return undefined\n // Only a delivered artifact is immutable. A rev-bearing URL is unique per\n // build, so a 200 for it can never change — but an error for the same URL is\n // not that artifact. Stamping `max-age=31536000, immutable` onto a failure\n // lets the WebView cache the rejection for a year: `rev` is a per-host-start\n // nonce, so once the host restarts the bundle URL the page already holds is\n // rejected, and the poisoned entry makes the plugin fail on every later boot\n // until the browser cache is cleared. Upstream DSH only attaches its\n // immutable directive on the success path; failures there carry no\n // cache-control at all, so the gateway keeps `no-store` from\n // setSecurityHeaders instead.\n if (statusCode !== 200) return undefined\n let target: URL\n try { target = new URL(request.url ?? '/', 'https://dsh-mobile.invalid') } catch { return undefined }\n const revision = target.searchParams.get('rev')\n const hasRevision = revision !== null && /^[a-z0-9_-]{4,128}$/iu.test(revision)\n const hashedAsset = /^\\/assets\\/.*-[a-z0-9_-]{8,}\\.[a-z0-9]+$/iu.test(target.pathname)\n if (!(target.pathname.startsWith('/plugins/') && hasRevision)\n && !(target.pathname.startsWith('/assets/') && (hasRevision || hashedAsset))) return undefined\n return 'private, max-age=31536000, immutable'\n}\n\nfunction isJsonRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction mobileHistoryRequestBody(request: IncomingMessage, body: Buffer): Buffer {\n if (request.method !== 'POST' || request.url?.split('?', 1)[0] !== SESSION_HISTORY_PATH) return body\n let parsed: unknown\n try {\n parsed = JSON.parse(body.toString('utf8'))\n } catch {\n return body\n }\n if (!isJsonRecord(parsed) || parsed.method !== 'session.history' || !isJsonRecord(parsed.payload)) return body\n const requested = parsed.payload.maxMessages\n if (typeof requested === 'number' && Number.isInteger(requested) && requested > 0 && requested <= MOBILE_HISTORY_PAGE_MESSAGES) {\n return body\n }\n return Buffer.from(JSON.stringify({\n ...parsed,\n payload: { ...parsed.payload, maxMessages: MOBILE_HISTORY_PAGE_MESSAGES },\n }))\n}\n\nfunction addVaryAcceptEncoding(headers: OutgoingHttpHeaders): void {\n const existing = headers.vary\n const rawValues: string[] = Array.isArray(existing)\n ? existing.map(value => String(value))\n : existing === undefined ? [] : [String(existing)]\n const values = rawValues.flatMap(value => value.split(',').map(part => part.trim()).filter(Boolean))\n if (!values.some(value => value.toLowerCase() === 'accept-encoding')) values.push('Accept-Encoding')\n headers.vary = values.join(', ')\n}\n\nfunction requestCookies(request: IncomingMessage): ReadonlyMap<string, string> {\n const cookies = parseCookies(request.headers.cookie)\n if (cookies === undefined) throw new HttpError(401, 'authentication_failed')\n return cookies\n}\n\nfunction mapError(error: unknown): HttpError {\n if (error instanceof HttpError) return error\n if (error instanceof AccessError) return new HttpError(error.status, error.code)\n if (error instanceof MobileExtensionError) return new HttpError(error.status, error.code)\n return new HttpError(500, 'internal_error')\n}\n\nfunction requestAbortedError(): Error {\n const error = new Error('request aborted')\n error.name = 'AbortError'\n return error\n}\n\nfunction isTransientUpstreamError(error: unknown): boolean {\n if (!(error instanceof Error)) return false\n const code = (error as NodeJS.ErrnoException).code\n return typeof code === 'string' && TRANSIENT_UPSTREAM_ERROR_CODES.has(code)\n}\n\nfunction upstreamTimeoutError(): NodeJS.ErrnoException {\n const error = new Error('upstream timeout') as NodeJS.ErrnoException\n error.code = 'ETIMEDOUT'\n return error\n}\n\nfunction waitForAbortableDelay(delayMs: number, signal: AbortSignal): Promise<void> {\n if (signal.aborted) return Promise.reject(requestAbortedError())\n return new Promise((resolve, reject) => {\n const aborted = (): void => {\n clearTimeout(timer)\n reject(requestAbortedError())\n }\n const timer = setTimeout(() => {\n signal.removeEventListener('abort', aborted)\n resolve()\n }, delayMs)\n signal.addEventListener('abort', aborted, { once: true })\n })\n}\n\nfunction waitForRequestTask<T>(task: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) return Promise.reject(requestAbortedError())\n return new Promise((resolve, reject) => {\n const aborted = (): void => {\n reject(requestAbortedError())\n }\n signal.addEventListener('abort', aborted, { once: true })\n void task.then(\n value => {\n signal.removeEventListener('abort', aborted)\n resolve(value)\n },\n error => {\n signal.removeEventListener('abort', aborted)\n reject(error)\n },\n )\n })\n}\n\nfunction discoveryDeviceName(): string {\n const value = hostname().trim().replaceAll(/[\\u0000-\\u001f\\u007f]/gu, '')\n return (value === '' ? 'DeepSeek Harness' : value).slice(0, 63)\n}\n\nfunction discoveryMdnsHost(instanceId: string): string {\n const label = hostname().toLowerCase().replaceAll(/[^a-z0-9-]/gu, '-').replaceAll(/^-+|-+$/gu, '').slice(0, 40)\n return `${label === '' ? 'dsh' : label}-${instanceId.slice(0, 8)}.local`\n}\n\nfunction discoveryBroadcastTargets(cidrs: readonly ParsedCidr[]): readonly string[] {\n const targets = new Set<string>(['255.255.255.255'])\n for (const cidr of cidrs) {\n if (cidr.bits !== 32 || cidr.prefix >= 32) continue\n const hostBits = BigInt(32 - cidr.prefix)\n const broadcast = cidr.network | ((1n << hostBits) - 1n)\n targets.add([24n, 16n, 8n, 0n].map(shift => Number((broadcast >> shift) & 0xffn)).join('.'))\n }\n return [...targets]\n}\n\n/** Keep Node's upgrade-owned socket safe after the HTTP parser removes its listener. */\nfunction guardUpgradeSocket(socket: Socket): void {\n socket.on('error', () => {\n if (!socket.destroyed) socket.destroy()\n })\n}\n\nfunction discoveryFailure(error: unknown): { readonly code: string; readonly message: string } {\n const code = error instanceof Error && typeof (error as NodeJS.ErrnoException).code === 'string'\n ? (error as NodeJS.ErrnoException).code!\n : 'unknown'\n return Object.freeze({ code, message: error instanceof Error ? error.message : String(error) })\n}\n\nfunction extensionTarget(pathname: string):\n | { readonly kind: 'manifest' }\n | { readonly kind: 'events' }\n | { readonly kind: 'script' | 'style' | 'asset'; readonly id: string; readonly path?: string }\n | { readonly kind: 'action'; readonly id: string; readonly action: string }\n | { readonly kind: 'route'; readonly id: string; readonly path: string }\n | undefined {\n const prefix = `${AUTH_PREFIX}/extensions`\n if (pathname === prefix || pathname === `${prefix}/` || pathname === `${prefix}/manifest`) return { kind: 'manifest' }\n if (pathname === `${prefix}/events`) return { kind: 'events' }\n if (!pathname.startsWith(`${prefix}/`)) return undefined\n const parts = pathname.slice(prefix.length + 1).split('/')\n const id = parts.shift()\n if (id === undefined || !/^[a-z][a-z0-9-]{0,63}$/u.test(id)) return undefined\n const leaf = parts.shift()\n if (leaf === 'mobile.js' && parts.length === 0) return { kind: 'script', id }\n if (leaf === 'mobile.css' && parts.length === 0) return { kind: 'style', id }\n if (leaf === 'assets' && parts.length > 0) return { kind: 'asset', id, path: parts.join('/') }\n if (leaf === 'actions' && parts.length === 1 && /^[a-z][a-z0-9-]{0,63}$/u.test(parts[0]!)) return { kind: 'action', id, action: parts[0]! }\n if (leaf === 'routes') return { kind: 'route', id, path: `/${parts.join('/')}`.replace(/\\/{2,}/gu, '/') }\n return undefined\n}\n\nconst EXTENSION_GENERATION_HEADER = 'x-dsh-mobile-extension-generation'\n\nfunction extensionGeneration(value: string | undefined): string | undefined {\n if (value === undefined) return undefined\n if (!/^[a-f\\d]{64}$/u.test(value)) throw new HttpError(400, 'invalid_extension_generation')\n return value\n}\n\nfunction mobileBootBatchKey(pathname: string): string | undefined {\n const match = new RegExp(`^${MOBILE_BOOT_BATCH_PREFIX.replaceAll('/', '\\\\/')}([a-f\\\\d]{64})\\\\.js$`, 'u').exec(pathname)\n return match?.[1]\n}\n\nfunction assertBoundedContentLength(request: IncomingMessage, maximum: number): void {\n const declared = request.headers['content-length']\n if (declared !== undefined && (!/^\\d+$/u.test(declared) || Number(declared) > maximum)) {\n throw new HttpError(413, 'payload_too_large')\n }\n}\n\nasync function readBoundedBody(request: IncomingMessage, maximum: number): Promise<Buffer> {\n assertBoundedContentLength(request, maximum)\n const chunks: Buffer[] = []\n let total = 0\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n total += buffer.length\n if (total > maximum) throw new HttpError(413, 'payload_too_large')\n chunks.push(buffer)\n }\n return Buffer.concat(chunks)\n}\n\nfunction extensionRequestHeaders(headers: IncomingHttpHeaders): Readonly<Record<string, string>> {\n const allowed = new Set(['accept', 'content-type', 'content-length', 'content-range', 'range', 'if-none-match', 'if-modified-since'])\n const output: Record<string, string> = {}\n for (const [name, value] of Object.entries(headers)) {\n if (!allowed.has(name) || typeof value !== 'string') continue\n output[name] = value\n }\n return Object.freeze(output)\n}\n\nfunction extensionContentType(path: string): string {\n const type = {\n '.css': 'text/css; charset=utf-8',\n '.csv': 'text/csv; charset=utf-8',\n '.gif': 'image/gif',\n '.html': 'text/html; charset=utf-8',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.js': 'text/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n }[extname(path).toLowerCase()]\n return type ?? 'application/octet-stream'\n}\n\n/** Authenticated TLS edge in front of the ordinary loopback-only DSH Web server. */\nexport class MobileAccessGateway {\n readonly access: AccessController\n private readonly listenerTlsEnabled: boolean\n private readonly tlsEnabled: boolean\n private policy: RequestTrustPolicy | undefined\n private server: GatewayServer | undefined\n private discoverySocket: DatagramSocket | undefined\n private discoveryTimer: NodeJS.Timeout | undefined\n /**\n * Why broadcast discovery is unavailable, if it is. Windows keeps separate TCP and\n * UDP port-exclusion tables, so the port the OS handed the TCP listener can be\n * refused for UDP. That degradation is survivable but must stay observable: without\n * it, \"the phone cannot find this computer\" has no diagnosable cause on the host.\n */\n private discoveryError: { readonly code: string; readonly message: string } | undefined\n private mdnsError: { readonly code: string; readonly message: string } | undefined\n /** Names of the bundled mobile assets that could not be read, if any. */\n private mobileAssetError: string | undefined\n private bonjour: Bonjour | undefined\n private pairingCaCertificate: string | undefined\n private listenerPort: number | undefined\n private readonly connectedSockets = new Set<Socket>()\n private readonly activeRequests = new Map<number, ActiveRequest>()\n private readonly activeWebSockets = new Map<number, ActiveWebSocket>()\n private readonly mobileBootBatches = new Map<string, StoredMobileBootBatch>()\n /** Cached upstream bundle byte sizes (HEAD probes), keyed by resource URL. */\n private readonly bootEntrySizeCache = new Map<string, { size: number; at: number }>()\n private readonly extensionEventListeners = new Set<(revision: number) => void>()\n private extensionEventRevision = 0\n private readonly taskEventListeners = new Set<(payload: string) => void>()\n private readonly deviceEventListeners = new Map<string, Set<(payload: string) => void>>()\n private readonly pendingDeviceRevocations = new Set<string>()\n private extensionChangeTimer: NodeJS.Timeout | undefined\n private extensionChangeTask: Promise<void> | undefined\n private legacyCustomDigest = ''\n private upstreamCookie: string | undefined\n private upstreamCookieExpiresAt = 0\n private upstreamCookieTask: Promise<string> | undefined\n private upstreamAuthRequest: ClientRequest | undefined\n private nextOperationId = 1\n private closing = false\n private started = false\n private closeTask: Promise<void> | undefined\n private readonly removeSessionListener: () => void\n private readonly removeExtensionContentListener: () => void\n private readonly renewLimiter: BoundedRateLimiter\n private readonly probeLimiter: BoundedRateLimiter\n\n constructor(\n readonly config: ResolvedGatewayConfig,\n store: DeviceStore,\n private readonly extensions?: MobileAccessService,\n private readonly upstreamAuthenticatedUrl?: string,\n private readonly extraWebSocketPaths?: { has(pathname: string): boolean },\n private readonly blockedUpgradeLog?: BlockedUpgradePathLog,\n private readonly onDiscoveryDegraded?: (source: 'broadcast' | 'mdns', code: string) => void,\n ) {\n this.listenerTlsEnabled = config.tls.mode === 'provided'\n this.tlsEnabled = config.publicTls\n this.access = new AccessController(store, {\n pairingTtlMs: config.pairingTtlMs,\n deviceTtlMs: config.deviceTtlMs,\n sessionTtlMs: config.sessionTtlMs,\n maxDevices: config.maxDevices,\n maxSessions: config.maxSessions,\n rateLimitWindowMs: config.rateLimitWindowMs,\n maxPairingAttempts: config.maxPairingAttempts,\n maxRateLimitKeys: config.maxRateLimitKeys,\n })\n this.renewLimiter = new BoundedRateLimiter(\n Math.min(100, config.maxPairingAttempts * 4),\n config.rateLimitWindowMs,\n config.maxRateLimitKeys,\n )\n // Reachability checks do not create Sessions; allow one short poll for\n // each configured device without weakening the Session-creating limit.\n this.probeLimiter = new BoundedRateLimiter(\n Math.min(1_000, config.maxDevices * 4),\n config.rateLimitWindowMs,\n config.maxRateLimitKeys,\n )\n this.removeSessionListener = this.access.onSessionEnded((authorization, reason) => {\n if (reason === 'revoked') {\n if (!this.pendingDeviceRevocations.has(authorization.deviceId)) {\n this.pendingDeviceRevocations.add(authorization.deviceId)\n this.broadcastDeviceRevoked(authorization.deviceId)\n }\n // Let the final SSE frame enter the socket before revocation closes\n // the session-owned stream and WebSockets.\n setImmediate(() => {\n this.abortSessionResources(authorization.sessionKey)\n this.pendingDeviceRevocations.delete(authorization.deviceId)\n })\n } else {\n this.abortSessionResources(authorization.sessionKey)\n }\n })\n this.removeExtensionContentListener = this.extensions?.onContentChanged(() => {\n this.broadcastExtensionChange()\n }) ?? (() => undefined)\n }\n\n /** Initialize durable state, validate TLS, and bind the externally reachable listener. */\n async start(): Promise<void> {\n if (this.started || this.server !== undefined) throw new Error('mobile-access gateway cannot be started twice')\n this.started = true\n await this.access.initialize()\n try {\n if (this.config.pairingCaFile !== undefined) {\n const certificate = new X509Certificate(await readFile(this.config.pairingCaFile))\n const fingerprint = certificate.fingerprint256.replaceAll(':', '').toLowerCase()\n if (!certificate.ca || certificate.subject !== certificate.issuer\n || !certificate.verify(certificate.publicKey) || fingerprint !== this.config.instanceId) {\n throw new Error('pairingCaFile must be the self-signed CA identified by instanceId')\n }\n this.pairingCaCertificate = certificate.raw.toString('base64')\n }\n // Both mobile assets are injected into the served index, so a missing one would\n // otherwise surface only as a 503 subresource: the page still renders and the\n // compatibility bundle silently never applies. Record it instead of failing, so an\n // enhancement cannot take the whole listener down, and expose it for diagnosis.\n const missingAssets: string[] = []\n for (const [label, file] of [\n ['compatibility', this.config.mobileCompatibilityFile],\n ['layout', this.config.mobileLayoutFile],\n ] as const) {\n if (!(await lstat(file).catch(() => undefined))?.isFile()) missingAssets.push(label)\n }\n this.mobileAssetError = missingAssets.length === 0 ? undefined : `mobile_assets_missing_${missingAssets.join('_')}`\n const handler = (request: IncomingMessage, response: ServerResponse): void => {\n void this.handleExternalRequest(request, response).catch((error: unknown) => {\n const mapped = mapError(error)\n if (response.headersSent) response.destroy()\n else sendFailure(response, mapped.status, mapped.code, this.tlsEnabled)\n })\n }\n const server = this.listenerTlsEnabled\n ? createHttpsServer(await tlsOptions(this.config), handler)\n : createHttpServer({ maxHeaderSize: MAX_HEADER_BYTES }, handler)\n this.server = server\n server.maxHeadersCount = 64\n server.maxConnections = this.config.maxConnections\n server.headersTimeout = 10_000\n server.requestTimeout = this.config.upstreamTimeoutMs\n server.keepAliveTimeout = 5_000\n server.on('connection', (socket: Socket) => {\n if (this.connectedSockets.size >= this.config.maxConnections) {\n socket.destroy()\n return\n }\n this.connectedSockets.add(socket)\n socket.on('error', () => { socket.destroy() })\n socket.once('close', () => { this.connectedSockets.delete(socket) })\n })\n server.on('connect', (_request, socket) => { socket.destroy() })\n server.on('upgrade', (request, socket, head) => {\n const client = socket as Socket\n // Node removes its normal connection error listener when it hands this\n // socket to the upgrade event. Install ours before any sync validation\n // or await so a stale TLS/WebSocket connection cannot terminate DSH.\n guardUpgradeSocket(client)\n void this.handleUpgrade(request, client, head).catch((error: unknown) => {\n const mapped = mapError(error)\n rejectUpgrade(client, mapped.status, mapped.code)\n })\n })\n server.on('clientError', (_error, socket) => { rejectUpgrade(socket as Socket, 400, 'bad_request') })\n await new Promise<void>((resolve, reject) => {\n const failed = (error: Error): void => { reject(error) }\n server.once('error', failed)\n server.listen(this.config.listenPort, this.config.listenHost, () => {\n server.off('error', failed)\n resolve()\n })\n })\n const address = server.address()\n if (address === null || typeof address === 'string') throw new Error('gateway listener has no TCP address')\n this.listenerPort = address.port\n this.policy = new RequestTrustPolicy(\n this.config.authorities,\n address.port,\n this.config.allowedCidrs,\n this.tlsEnabled,\n )\n if (this.config.discovery) await this.startDiscovery(address.port)\n await this.pollLegacyCustomChanges()\n this.extensionChangeTimer = setInterval(() => { void this.pollLegacyCustomChanges() }, EXTENSION_CHANGE_POLL_MS)\n this.extensionChangeTimer.unref()\n } catch (error) {\n await this.closeFailedStart()\n throw error\n }\n }\n\n /** Install a newly signed provided leaf for future TLS handshakes without dropping sessions. */\n async refreshProvidedTls(): Promise<void> {\n if (!this.listenerTlsEnabled || this.config.tls.mode !== 'provided') {\n throw new Error('gateway does not terminate provided TLS')\n }\n const server = this.server\n if (server === undefined || !this.started || this.closing) throw new Error('gateway is not running')\n const options = await tlsOptions(this.config)\n if (this.server !== server || this.closing) throw new Error('gateway is not running');\n (server as HttpsServer).setSecureContext(options)\n }\n\n private async startDiscovery(port: number): Promise<void> {\n const socket = createSocket('udp4')\n this.discoverySocket = socket\n const announcement = this.discoveryAnnouncement(port)\n let binding = true\n socket.on('error', error => {\n if (!binding) this.recordBroadcastFailure(error, true)\n })\n const sendAnnouncement = (targetPort: number, address: string): void => {\n if (this.closing || this.discoveryError !== undefined) return\n try {\n socket.send(announcement, targetPort, address, error => {\n if (error !== null) this.recordBroadcastFailure(error, true)\n })\n } catch (error) {\n this.recordBroadcastFailure(error, true)\n }\n }\n socket.on('message', (message, remote) => {\n if (this.closing || this.discoveryError !== undefined || !message.equals(DISCOVERY_QUERY)\n || !addressAllowed(remote.address, this.config.allowedCidrs)) return\n sendAnnouncement(remote.port, remote.address)\n })\n // The UDP socket is IPv4-only; only a literal IPv4 loopback address is bindable, never ::1.\n const bindHost = isIP(this.config.listenHost) === 4 && isLoopbackAddress(this.config.listenHost) ? this.config.listenHost : '0.0.0.0'\n // Windows keeps separate TCP and UDP port-exclusion tables, so the port the OS handed\n // the TCP listener can be unavailable for UDP; another process may also already hold\n // it. Broadcast discovery is a convenience, so a failed bind must not take the whole\n // gateway down — the mDNS publication below still advertises this origin.\n const bound = await new Promise<boolean>(resolve => {\n const onBindError = (error: NodeJS.ErrnoException): void => {\n socket.off('error', onBindError)\n this.recordBroadcastFailure(error, false)\n resolve(false)\n }\n socket.once('error', onBindError)\n try {\n socket.bind(port, bindHost, () => {\n socket.off('error', onBindError)\n binding = false\n try {\n socket.setBroadcast(true)\n resolve(true)\n } catch (error) {\n this.recordBroadcastFailure(error, true)\n resolve(false)\n }\n })\n } catch (error) {\n onBindError(error as NodeJS.ErrnoException)\n }\n })\n if (bound && this.discoveryError === undefined) {\n const announce = (): void => {\n for (const target of discoveryBroadcastTargets(this.config.allowedCidrs)) {\n sendAnnouncement(port, target)\n }\n }\n announce()\n if (this.discoveryError === undefined) {\n this.discoveryTimer = setInterval(announce, DISCOVERY_INTERVAL_MS)\n this.discoveryTimer.unref()\n }\n } else {\n this.discoverySocket = undefined\n // A socket whose bind failed was never running, so close() throws synchronously.\n try { socket.close() } catch { /* the socket never started */ }\n }\n\n const deviceName = discoveryDeviceName()\n const onMdnsError = (error: unknown): void => { this.recordMdnsFailure(error) }\n const bonjour = new Bonjour({ disableIPv6: true }, onMdnsError)\n this.bonjour = bonjour\n // bonjour-service 1.4.4 does not forward multicast-dns EventEmitter errors\n // through errorCallback. Keep a listener on its underlying emitter for its lifetime.\n const mdns = (bonjour as unknown as { server: { mdns: { on(event: 'error', listener: (error: Error) => void): void } } }).server.mdns\n mdns.on('error', onMdnsError)\n bonjour.publish({\n name: `${deviceName} (${this.config.instanceId.slice(0, 8)})`,\n type: MDNS_SERVICE_TYPE,\n protocol: 'tcp',\n port,\n host: discoveryMdnsHost(this.config.instanceId),\n disableIPv6: true,\n txt: {\n deviceName,\n origin: this.address().origin,\n instanceId: this.config.instanceId,\n protocol: String(DISCOVERY_PROTOCOL),\n },\n })\n }\n\n private reportDiscoveryDegraded(source: 'broadcast' | 'mdns', code: string): void {\n try {\n this.onDiscoveryDegraded?.(source, code)\n } catch (error) {\n process.emitWarning(`DSH Mobile could not log ${source} discovery error: ${String(error)}`, {\n code: 'DSH_MOBILE_DISCOVERY_LOG_FAILED',\n })\n }\n }\n\n private recordBroadcastFailure(error: unknown, notify: boolean): void {\n if (this.closing || this.discoveryError !== undefined) return\n this.discoveryError = discoveryFailure(error)\n if (this.discoveryTimer !== undefined) clearInterval(this.discoveryTimer)\n this.discoveryTimer = undefined\n if (notify) this.reportDiscoveryDegraded('broadcast', this.discoveryError.code)\n }\n\n private recordMdnsFailure(error: unknown): void {\n if (this.closing || this.mdnsError !== undefined) return\n this.mdnsError = discoveryFailure(error)\n this.reportDiscoveryDegraded('mdns', this.mdnsError.code)\n }\n\n private discoveryAnnouncement(port: number): Buffer {\n return Buffer.from(JSON.stringify({\n deviceName: discoveryDeviceName(),\n origin: this.address().origin,\n port,\n protocol: DISCOVERY_PROTOCOL,\n instanceId: this.config.instanceId,\n }), 'utf8')\n }\n\n private async closeFailedStart(): Promise<void> {\n if (this.extensionChangeTimer !== undefined) clearInterval(this.extensionChangeTimer)\n this.extensionChangeTimer = undefined\n this.removeExtensionContentListener()\n if (this.discoveryTimer !== undefined) clearInterval(this.discoveryTimer)\n this.discoveryTimer = undefined\n await this.closeBonjour()\n this.discoverySocket?.close()\n this.discoverySocket = undefined\n for (const socket of this.connectedSockets) socket.destroy()\n const server = this.server\n this.server = undefined\n if (server?.listening === true) {\n await new Promise<void>(resolve => { server.close(() => resolve()) })\n }\n await this.access.close()\n }\n\n private async closeBonjour(): Promise<void> {\n const bonjour = this.bonjour\n this.bonjour = undefined\n if (bonjour === undefined) return\n await new Promise<void>(resolve => {\n bonjour.unpublishAll(() => { bonjour.destroy(() => resolve()) })\n })\n }\n\n /** Actual bound address, available after start and safe for loopback status output. */\n address(): { host: string; port: number; origin: string } {\n if (this.listenerPort === undefined || this.policy === undefined) throw new Error('gateway is not listening')\n const origin = this.policy.origins.values().next().value as string | undefined\n if (origin === undefined) throw new Error('gateway has no public authority')\n return Object.freeze({ host: this.config.listenHost, port: this.listenerPort, origin })\n }\n\n /**\n * Report which discovery channels this gateway actually owns.\n *\n * Broadcast discovery degrades on its own when its UDP port cannot be bound, so\n * callers need to tell \"discovery is off\" from \"discovery was never attempted\".\n * mDNS is published independently and is unaffected by that failure.\n */\n discoveryStatus(): {\n readonly broadcast: boolean\n readonly mdns: boolean\n readonly errorCode?: string\n readonly mdnsErrorCode?: string\n readonly mobileAssetsErrorCode?: string\n } {\n return Object.freeze({\n broadcast: this.discoverySocket !== undefined && this.discoveryTimer !== undefined,\n mdns: this.bonjour !== undefined && this.mdnsError === undefined,\n ...(this.discoveryError === undefined ? {} : { errorCode: `discovery_broadcast_${this.discoveryError.code}` }),\n ...(this.mdnsError === undefined ? {} : { mdnsErrorCode: `discovery_mdns_${this.mdnsError.code}` }),\n // A bundled asset that is missing would otherwise only show up as a 503 on a\n // subresource the served page still references.\n ...(this.mobileAssetError === undefined ? {} : { mobileAssetsErrorCode: this.mobileAssetError }),\n })\n }\n\n private requirePolicy(): RequestTrustPolicy {\n if (this.policy === undefined || this.closing) throw new HttpError(503, 'unavailable')\n return this.policy\n }\n\n private authorize(request: IncomingMessage): SessionAuthorization {\n const sessionToken = requestCookies(request).get(SESSION_COOKIE)\n if (sessionToken === undefined) throw new HttpError(401, 'authentication_failed')\n return this.access.authorizeSession(sessionToken)\n }\n\n private requireCsrf(request: IncomingMessage, authorization: SessionAuthorization): void {\n const value = headerValue(request.headers, CSRF_HEADER)\n this.access.assertCsrf(authorization, value)\n }\n\n private setSessionCookies(response: ServerResponse, result: {\n sessionToken: string\n csrfToken: string\n sessionExpiresAt: number\n }, now: number): void {\n const maxAge = (result.sessionExpiresAt - now) / 1000\n response.setHeader('Set-Cookie', [\n cookie(SESSION_COOKIE, result.sessionToken, { tls: this.tlsEnabled, httpOnly: true, path: '/', maxAgeSeconds: maxAge }),\n cookie(CSRF_COOKIE, result.csrfToken, { tls: this.tlsEnabled, httpOnly: false, path: '/', maxAgeSeconds: maxAge }),\n ])\n }\n\n private async handlePair(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (typeof body.token !== 'string' || (body.label !== undefined && typeof body.label !== 'string')) {\n throw new HttpError(400, 'bad_request')\n }\n const result = await this.access.pair(request.socket.remoteAddress ?? 'unknown', body.token, body.label as string | undefined)\n const now = Date.now()\n this.setSessionCookies(response, result, now)\n const sessionCookies = response.getHeader('Set-Cookie') as string[]\n response.setHeader('Set-Cookie', [\n ...sessionCookies,\n cookie(DEVICE_COOKIE, result.deviceToken, {\n tls: this.tlsEnabled,\n httpOnly: true,\n path: '/mobile-access/auth/renew',\n maxAgeSeconds: (result.deviceExpiresAt - now) / 1000,\n }),\n ])\n sendJson(response, 201, {\n paired: true,\n deviceId: result.deviceId,\n csrfToken: result.csrfToken,\n sessionExpiresAt: result.sessionExpiresAt,\n }, this.tlsEnabled)\n }\n\n private async handleRenew(request: IncomingMessage, response: ServerResponse): Promise<void> {\n if (!this.renewLimiter.take(request.socket.remoteAddress ?? 'unknown', Date.now())) {\n throw new HttpError(429, 'rate_limited')\n }\n await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n const deviceToken = requestCookies(request).get(DEVICE_COOKIE)\n if (deviceToken === undefined) throw new HttpError(401, 'authentication_failed')\n let result\n try {\n result = await this.access.renew(deviceToken)\n } catch (error) {\n if (error instanceof AccessError && error.status === 401) {\n response.setHeader('Set-Cookie', cookie(DEVICE_COOKIE, '', {\n tls: this.tlsEnabled,\n httpOnly: true,\n path: '/mobile-access/auth/renew',\n maxAgeSeconds: 0,\n }))\n }\n throw error\n }\n this.setSessionCookies(response, result, Date.now())\n sendJson(response, 200, {\n renewed: true,\n deviceId: result.deviceId,\n csrfToken: result.csrfToken,\n sessionExpiresAt: result.sessionExpiresAt,\n }, this.tlsEnabled)\n }\n\n private async handleNativePair(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (typeof body.token !== 'string' || (body.label !== undefined && typeof body.label !== 'string')) {\n throw new HttpError(400, 'bad_request')\n }\n const result = await this.access.pair(\n request.socket.remoteAddress ?? 'unknown',\n body.token,\n body.label as string | undefined,\n )\n sendJson(response, 201, {\n instanceId: this.config.instanceId,\n deviceId: result.deviceId,\n deviceToken: result.deviceToken,\n deviceExpiresAt: result.deviceExpiresAt,\n sessionToken: result.sessionToken,\n csrfToken: result.csrfToken,\n sessionExpiresAt: result.sessionExpiresAt,\n }, this.tlsEnabled)\n }\n\n private async handleNativeRenew(request: IncomingMessage, response: ServerResponse): Promise<void> {\n if (!this.renewLimiter.take(request.socket.remoteAddress ?? 'unknown', Date.now())) {\n throw new HttpError(429, 'rate_limited')\n }\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (typeof body.deviceToken !== 'string') throw new HttpError(400, 'bad_request')\n const result = await this.access.renew(body.deviceToken)\n sendJson(response, 200, {\n instanceId: this.config.instanceId,\n deviceId: result.deviceId,\n sessionToken: result.sessionToken,\n csrfToken: result.csrfToken,\n sessionExpiresAt: result.sessionExpiresAt,\n }, this.tlsEnabled)\n }\n\n private async handleNativeProbe(request: IncomingMessage, response: ServerResponse): Promise<void> {\n if (!this.probeLimiter.take(request.socket.remoteAddress ?? 'unknown', Date.now())) {\n throw new HttpError(429, 'rate_limited')\n }\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (typeof body.deviceToken !== 'string') throw new HttpError(400, 'bad_request')\n const result = await this.access.probe(body.deviceToken)\n sendJson(response, 200, {\n instanceId: this.config.instanceId,\n deviceId: result.deviceId,\n deviceExpiresAt: result.deviceExpiresAt,\n }, this.tlsEnabled)\n }\n\n private async handleLogout(request: IncomingMessage, response: ServerResponse): Promise<void> {\n await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n const authorization = this.authorize(request)\n this.requireCsrf(request, authorization)\n this.access.logout(authorization)\n response.setHeader('Set-Cookie', [\n cookie(SESSION_COOKIE, '', { tls: this.tlsEnabled, httpOnly: true, path: '/', maxAgeSeconds: 0 }),\n cookie(CSRF_COOKIE, '', { tls: this.tlsEnabled, httpOnly: false, path: '/', maxAgeSeconds: 0 }),\n ])\n sendJson(response, 200, { loggedOut: true }, this.tlsEnabled)\n }\n\n private async handleExternalRequest(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const target = parseRequestTarget(request.url)\n const policy = this.requirePolicy()\n const isMutation = request.method !== 'GET' && request.method !== 'HEAD'\n assertExternalTrust(request, policy, isMutation)\n if (target.decodedPathname === LOCAL_ADMIN_PREFIX || target.decodedPathname.startsWith(`${LOCAL_ADMIN_PREFIX}/`)) {\n throw new HttpError(404, 'not_found')\n }\n if (request.method === 'TRACE' || request.method === 'CONNECT') throw new HttpError(405, 'method_not_allowed')\n\n if (target.search === '' && request.method === 'GET' && target.decodedPathname === `${AUTH_PREFIX}/health`) {\n sendJson(response, 200, { ok: true }, this.tlsEnabled)\n return\n }\n if (target.search === '' && request.method === 'GET' && target.decodedPathname === `${AUTH_PREFIX}/metadata`) {\n sendJson(response, 200, {\n version: MOBILE_METADATA_VERSION,\n pluginVersion: DSH_MOBILE_VERSION,\n minimumAndroidAppVersion: MINIMUM_ANDROID_APP_VERSION,\n discoveryProtocol: DISCOVERY_PROTOCOL,\n }, this.tlsEnabled)\n return\n }\n if (target.search === '' && request.method === 'GET' && target.decodedPathname === `${AUTH_PREFIX}/discovery`) {\n sendJson(response, 200, {\n deviceName: discoveryDeviceName(),\n origin: this.address().origin,\n port: this.address().port,\n protocol: DISCOVERY_PROTOCOL,\n instanceId: this.config.instanceId,\n }, this.tlsEnabled)\n return\n }\n if (target.search === '' && request.method === 'GET' && target.decodedPathname === `${AUTH_PREFIX}/ca.cer`) {\n if (this.pairingCaCertificate === undefined) throw new HttpError(404, 'not_found')\n const body = Buffer.from(this.pairingCaCertificate, 'base64')\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(200, {\n 'Content-Type': 'application/pkix-cert',\n 'Content-Length': body.length,\n 'Cache-Control': 'no-store',\n })\n response.end(body)\n return\n }\n if (target.search === '' && request.method === 'GET'\n && (target.decodedPathname === `${AUTH_PREFIX}/pair` || target.decodedPathname === `${AUTH_PREFIX}/pair.js`)) {\n if (!this.access.pairingStatus().open) throw new HttpError(404, 'not_found')\n const languageHeader = request.headers['accept-language']\n const locale = resolveAuthPageLocale(typeof languageHeader === 'string' ? languageHeader : undefined)\n setSecurityHeaders(response, this.tlsEnabled)\n response.setHeader('Vary', 'Accept-Language')\n const body = target.decodedPathname.endsWith('.js') ? renderPairScript(locale) : renderPairPage(locale)\n response.writeHead(200, {\n 'Content-Type': target.decodedPathname.endsWith('.js') ? 'text/javascript; charset=utf-8' : 'text/html; charset=utf-8',\n 'Content-Length': Buffer.byteLength(body),\n })\n response.end(body)\n return\n }\n if (request.method === 'GET'\n && (target.decodedPathname === `${AUTH_PREFIX}/login` || target.decodedPathname === `${AUTH_PREFIX}/login.js`)) {\n if (target.decodedPathname.endsWith('.js') && target.search !== '') throw new HttpError(400, 'bad_request')\n const languageHeader = request.headers['accept-language']\n const locale = resolveAuthPageLocale(typeof languageHeader === 'string' ? languageHeader : undefined)\n setSecurityHeaders(response, this.tlsEnabled)\n response.setHeader('Vary', 'Accept-Language')\n const body = target.decodedPathname.endsWith('.js') ? renderLoginScript(locale) : renderLoginPage(locale)\n response.writeHead(200, {\n 'Content-Type': target.decodedPathname.endsWith('.js') ? 'text/javascript; charset=utf-8' : 'text/html; charset=utf-8',\n 'Content-Length': Buffer.byteLength(body),\n })\n response.end(body)\n return\n }\n if (target.search === '' && request.method === 'POST' && target.decodedPathname === `${AUTH_PREFIX}/auth/pair`) {\n await this.handlePair(request, response)\n return\n }\n if (target.search === '' && request.method === 'POST' && target.decodedPathname === `${AUTH_PREFIX}/auth/renew`) {\n await this.handleRenew(request, response)\n return\n }\n if (target.search === '' && request.method === 'POST' && target.decodedPathname === `${AUTH_PREFIX}/auth/native-pair`) {\n await this.handleNativePair(request, response)\n return\n }\n if (target.search === '' && request.method === 'POST' && target.decodedPathname === `${AUTH_PREFIX}/auth/native-renew`) {\n await this.handleNativeRenew(request, response)\n return\n }\n if (target.search === '' && request.method === 'POST' && target.decodedPathname === `${AUTH_PREFIX}/auth/native-probe`) {\n await this.handleNativeProbe(request, response)\n return\n }\n if (target.search === '' && request.method === 'POST' && target.decodedPathname === `${AUTH_PREFIX}/auth/logout`) {\n await this.handleLogout(request, response)\n return\n }\n const computerImages = request.method === 'GET' && target.decodedPathname === `${AUTH_PREFIX}/computer-images`\n const computerImage = request.method === 'GET' && target.decodedPathname === `${AUTH_PREFIX}/computer-image`\n const requestedExtension = extensionTarget(target.decodedPathname)\n const requestedMobileBootBatch = mobileBootBatchKey(target.decodedPathname)\n const customAsset = request.method === 'GET'\n ? target.decodedPathname === `${AUTH_PREFIX}/custom.css`\n ? {\n file: this.config.customCssFile,\n contentType: 'text/css; charset=utf-8',\n fallback: CUSTOM_STYLE_FALLBACK,\n }\n : target.decodedPathname === `${AUTH_PREFIX}/custom.js`\n ? {\n file: this.config.customScriptFile,\n contentType: 'text/javascript; charset=utf-8',\n fallback: CUSTOM_SCRIPT_FALLBACK,\n }\n : target.decodedPathname === MOBILE_LAYOUT_PATH\n ? {\n file: this.config.mobileLayoutFile,\n contentType: 'text/javascript; charset=utf-8',\n fallback: undefined,\n }\n : target.decodedPathname === MOBILE_COMPAT_PATH\n ? {\n file: this.config.mobileCompatibilityFile,\n contentType: 'text/javascript; charset=utf-8',\n fallback: undefined,\n }\n : undefined\n : undefined\n if (customAsset === undefined && requestedMobileBootBatch === undefined && !computerImages && !computerImage\n && extensionTarget(target.decodedPathname) === undefined\n && (target.decodedPathname === AUTH_PREFIX || target.decodedPathname.startsWith(`${AUTH_PREFIX}/`))) {\n throw new HttpError(404, 'not_found')\n }\n\n if (request.method !== 'GET' && request.method !== 'HEAD' && request.method !== 'POST'\n && requestedExtension?.kind !== 'route') {\n throw new HttpError(405, 'method_not_allowed')\n }\n let authorization: SessionAuthorization\n try {\n authorization = this.authorize(request)\n } catch (error) {\n const mapped = mapError(error)\n const acceptsHtml = request.headers.accept?.split(',').some(value => value.trim().split(';', 1)[0] === 'text/html') ?? false\n const topLevel = request.method === 'GET'\n && acceptsHtml\n && (request.headers['sec-fetch-dest'] === undefined || request.headers['sec-fetch-dest'] === 'document')\n && target.decodedPathname !== '/api'\n && !target.decodedPathname.startsWith('/api/')\n if (mapped.status === 401 && topLevel) {\n const returnPath = target.raw.length <= 2048 ? target.raw : '/'\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(302, {\n Location: `${AUTH_PREFIX}/login?return=${encodeURIComponent(returnPath)}`,\n 'Content-Length': 0,\n })\n response.end()\n return\n }\n throw error\n }\n if (isMutation) this.requireCsrf(request, authorization)\n const extension = requestedExtension\n if (extension !== undefined) {\n await this.handleExtensionRequest(extension, target, request, response, authorization)\n return\n }\n if (requestedMobileBootBatch !== undefined) {\n await this.serveMobileBootBatch(requestedMobileBootBatch, request, response, authorization)\n return\n }\n if (customAsset !== undefined) {\n const operation = this.allocateRequest(authorization, response, {})\n try {\n let body: Buffer\n let mtime: Date | undefined\n try {\n body = await readFile(customAsset.file, { signal: operation.signal })\n try {\n const fileStat = await stat(customAsset.file)\n mtime = fileStat.mtime\n } catch { /* keep undefined */ }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n if (customAsset.fallback === undefined) throw new HttpError(503, 'mobile_frontend_unavailable')\n body = Buffer.from(customAsset.fallback)\n }\n if (body.byteLength > 256 * 1024) throw new HttpError(413, 'payload_too_large')\n const etag = createHash('sha256').update(body).digest('hex')\n const ifNoneMatch = headerValue(request.headers, 'if-none-match')\n if (ifNoneMatch !== undefined && ifNoneMatch === etag) {\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(304)\n response.end()\n return\n }\n setSecurityHeaders(response, this.tlsEnabled)\n const responseHeaders: Record<string, string | number> = {\n 'Content-Type': customAsset.contentType,\n 'Content-Length': body.byteLength,\n 'ETag': etag,\n }\n if (mtime !== undefined) responseHeaders['Last-Modified'] = mtime.toUTCString()\n response.writeHead(200, responseHeaders)\n response.end(body)\n return\n } finally {\n operation.release()\n }\n }\n if (computerImages) {\n const operation = this.allocateRequest(authorization, response, {})\n try {\n const query = new URL(target.raw, this.address().origin).searchParams\n sendJson(response, 200, await listComputerImages(query.get('path'), operation.signal), this.tlsEnabled)\n return\n } finally {\n operation.release()\n }\n }\n if (computerImage) {\n const operation = this.allocateRequest(authorization, response, {})\n try {\n const query = new URL(target.raw, this.address().origin).searchParams\n const image = await readComputerImage(query.get('path'), operation.signal)\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(200, {\n 'Content-Type': image.contentType,\n 'Content-Length': image.body.byteLength,\n 'Content-Disposition': `inline; filename*=UTF-8''${encodeURIComponent(image.name)}`,\n })\n response.end(image.body)\n return\n } finally {\n operation.release()\n }\n }\n const stockFrontend = new URL(target.raw, this.address().origin).searchParams.get('frontend') === 'stock'\n const acceptsHtml = request.headers.accept?.split(',').some(value => value.trim().split(';', 1)[0] === 'text/html') ?? false\n if (request.method === 'GET' && acceptsHtml && !stockFrontend) {\n await this.proxyMobileIndex(request, response, authorization)\n return\n }\n if (stockFrontend && target.decodedPathname === '/') request.url = '/'\n await this.proxyHttp(request, response, authorization)\n }\n\n private async handleExtensionRequest(\n targetInfo: NonNullable<ReturnType<typeof extensionTarget>>,\n target: ReturnType<typeof parseRequestTarget>,\n request: IncomingMessage,\n response: ServerResponse,\n authorization: SessionAuthorization,\n ): Promise<void> {\n const extensions = this.extensions\n if (extensions === undefined) throw new HttpError(404, 'not_found')\n if (targetInfo.kind === 'events') {\n if (request.method !== 'GET' || target.search !== '') throw new HttpError(request.method === 'GET' ? 400 : 405, request.method === 'GET' ? 'bad_request' : 'method_not_allowed')\n this.openExtensionEventStream(request, response, authorization)\n return\n }\n if (targetInfo.kind === 'manifest') {\n if (request.method !== 'GET' && request.method !== 'HEAD') throw new HttpError(405, 'method_not_allowed')\n const operation = this.allocateRequest(authorization, response, {})\n try {\n operation.signal.throwIfAborted()\n const customRevision = async (file: string, fallback: string): Promise<string> => {\n let source: Buffer\n try {\n source = await readFile(file, { signal: operation.signal })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n source = Buffer.from(fallback)\n }\n if (source.byteLength > 256 * 1024) throw new HttpError(413, 'payload_too_large')\n return createHash('sha256').update(source).digest('hex')\n }\n const [scriptRevision, styleRevision] = await Promise.all([\n customRevision(this.config.customScriptFile, CUSTOM_SCRIPT_FALLBACK),\n customRevision(this.config.customCssFile, CUSTOM_STYLE_FALLBACK),\n ])\n const body = Buffer.from(JSON.stringify({\n protocol: 1,\n extensions: extensions.manifest(),\n legacy: { scriptRevision, styleRevision },\n }))\n // The ETag must cover extension content, not just the manifest body, so\n // editing mobile.js/css alone invalidates the client's cached manifest.\n const etag = createHash('sha256').update(body).update(extensions.contentDigest()).digest('hex')\n if (headerValue(request.headers, 'if-none-match') === etag) {\n setSecurityHeaders(response, this.tlsEnabled); response.writeHead(304); response.end(); return\n }\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': body.byteLength, ETag: etag })\n if (request.method === 'HEAD') response.end(); else response.end(body)\n return\n } finally {\n operation.release()\n }\n }\n if (targetInfo.kind === 'script' || targetInfo.kind === 'style' || targetInfo.kind === 'asset') {\n if (request.method !== 'GET' && request.method !== 'HEAD') throw new HttpError(405, 'method_not_allowed')\n const generation = extensionGeneration(new URLSearchParams(target.search).get('generation') ?? undefined)\n const operation = this.allocateRequest(authorization, response, {})\n try {\n const file = targetInfo.kind === 'script'\n ? await extensions.readClientFile(targetInfo.id, 'script', operation.signal, generation)\n : targetInfo.kind === 'style'\n ? await extensions.readClientFile(targetInfo.id, 'style', operation.signal, generation)\n : await extensions.readAsset(targetInfo.id, targetInfo.path ?? '', operation.signal, generation)\n if (headerValue(request.headers, 'if-none-match') === file.digest) {\n setSecurityHeaders(response, this.tlsEnabled); response.writeHead(304); response.end(); return\n }\n const contentType = targetInfo.kind === 'script'\n ? 'text/javascript; charset=utf-8'\n : targetInfo.kind === 'style' ? 'text/css; charset=utf-8' : extensionContentType(targetInfo.path ?? '')\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(200, { 'Content-Type': contentType, 'Content-Length': file.body.byteLength, ETag: file.digest })\n if (request.method === 'HEAD') response.end(); else response.end(file.body)\n return\n } finally {\n operation.release()\n }\n }\n if (targetInfo.kind === 'action') {\n if (request.method !== 'POST') throw new HttpError(405, 'method_not_allowed')\n const maximum = 1024 * 1024\n assertBoundedContentLength(request, maximum)\n const generation = extensionGeneration(headerValue(request.headers, EXTENSION_GENERATION_HEADER))\n const operation = this.allocateRequest(authorization, response, {})\n const abort = new AbortController()\n response.once('close', () => { abort.abort() })\n const generationSignal = extensions.signal(targetInfo.id, generation)\n const onGenerationAbort = (): void => { abort.abort(); if (!response.destroyed) response.destroy() }\n generationSignal?.addEventListener('abort', onGenerationAbort, { once: true })\n try {\n const body = await readJsonObject(request, maximum)\n const result = await extensions.invoke(targetInfo.id, targetInfo.action, body, { signal: abort.signal, deviceId: authorization.deviceId }, generation)\n let serialized: Buffer\n try { serialized = Buffer.from(JSON.stringify(result)) } catch { throw new MobileExtensionError('extension_failed', 'extension action failed', 500) }\n if (serialized.byteLength > 4 * 1024 * 1024) throw new MobileExtensionError('extension_result_too_large', 'extension result is too large', 500)\n sendJson(response, 200, result, this.tlsEnabled)\n } finally {\n generationSignal?.removeEventListener('abort', onGenerationAbort)\n abort.abort(); operation.release()\n }\n return\n }\n if (targetInfo.kind === 'route') {\n const method = request.method ?? 'GET'\n if (!['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) throw new HttpError(405, 'method_not_allowed')\n const hasBody = method !== 'GET' && method !== 'HEAD'\n if (hasBody) assertBoundedContentLength(request, this.config.maxBodyBytes)\n const generation = extensionGeneration(headerValue(request.headers, EXTENSION_GENERATION_HEADER))\n const operation = this.allocateRequest(authorization, response, {})\n const abort = new AbortController()\n response.once('close', () => { abort.abort() })\n const generationSignal = extensions.signal(targetInfo.id, generation)\n const onGenerationAbort = (): void => { abort.abort(); if (!response.destroyed) response.destroy() }\n generationSignal?.addEventListener('abort', onGenerationAbort, { once: true })\n try {\n const body = hasBody ? await readBoundedBody(request, this.config.maxBodyBytes) : Buffer.alloc(0)\n const parsed = new URL(target.raw, this.address().origin)\n const routeRequest: MobileRouteRequest = {\n method, pathname: targetInfo.path, query: parsed.searchParams,\n headers: extensionRequestHeaders(request.headers), body, signal: abort.signal, deviceId: authorization.deviceId,\n }\n const result = await extensions.route(targetInfo.id, method, targetInfo.path, routeRequest, generation)\n await this.sendExtensionResponse(response, result, request.method === 'HEAD')\n } finally {\n generationSignal?.removeEventListener('abort', onGenerationAbort)\n abort.abort(); operation.release()\n }\n }\n }\n\n private async sendExtensionResponse(response: ServerResponse, result: MobileRouteResponse, head: boolean): Promise<void> {\n const status = result.status ?? 200\n if (!Number.isSafeInteger(status) || status < 200 || status > 599) {\n throw new MobileExtensionError('invalid_route_response', 'extension returned an invalid HTTP status', 500)\n }\n const contentType = result.contentType ?? 'application/octet-stream'\n if (contentType.length > 1024\n || !/^[\\x20-\\x7e]+$/u.test(contentType)\n || !/^[\\w!#$&+.^-]+\\/[\\w!#$&+.^-]+(?:;[\\x20-\\x7e]*)?$/u.test(contentType)) {\n throw new MobileExtensionError('invalid_route_response', 'extension returned an invalid content type', 500)\n }\n const safeHeaders: Record<string, string> = {}\n for (const [name, value] of Object.entries(result.headers ?? {})) {\n if (!/^(?:content-disposition|cache-control|etag)$/iu.test(name) || /[\\r\\n]/u.test(value)) continue\n safeHeaders[name] = value\n }\n setSecurityHeaders(response, this.tlsEnabled)\n if (typeof result.body === 'string' || result.body instanceof Uint8Array) {\n const body = typeof result.body === 'string' ? Buffer.from(result.body) : Buffer.from(result.body)\n if (body.byteLength > 4 * 1024 * 1024) throw new MobileExtensionError('extension_result_too_large', 'extension response is too large', 500)\n response.writeHead(status, { ...safeHeaders, 'Content-Type': contentType, 'Content-Length': body.byteLength })\n if (head) response.end(); else response.end(body)\n return\n }\n response.writeHead(status, { ...safeHeaders, 'Content-Type': contentType })\n if (head) { result.body.destroy(); response.end(); return }\n await pipeline(result.body, new ByteLimitTransform(4 * 1024 * 1024), response)\n }\n\n /** Exchange DSH's process-local launch token for an authority-bound cookie kept inside this gateway. */\n private async upstreamCookieHeader(): Promise<string | undefined> {\n if (this.upstreamAuthenticatedUrl === undefined) return undefined\n if (this.upstreamCookie !== undefined\n && this.upstreamCookieExpiresAt > Date.now() + UPSTREAM_AUTH_REFRESH_MARGIN_MS) {\n return this.upstreamCookie\n }\n if (this.upstreamCookieTask !== undefined) return this.upstreamCookieTask\n const task = this.exchangeUpstreamCookie()\n this.upstreamCookieTask = task\n try {\n return await task\n } finally {\n if (this.upstreamCookieTask === task) this.upstreamCookieTask = undefined\n }\n }\n\n private async exchangeUpstreamCookie(): Promise<string> {\n const authenticatedUrl = this.upstreamAuthenticatedUrl\n if (authenticatedUrl === undefined) throw new HttpError(502, 'upstream_unavailable')\n let target: URL\n try {\n target = new URL(authenticatedUrl)\n } catch {\n throw new HttpError(502, 'upstream_unavailable')\n }\n if (target.origin !== this.config.upstreamOrigin.origin || target.pathname !== '/'\n || target.hash !== '' || target.search === '') {\n throw new HttpError(502, 'upstream_unavailable')\n }\n try {\n const proxied = await new Promise<IncomingMessage>((resolve, reject) => {\n const upstreamRequest = requestHttp({\n protocol: 'http:',\n hostname: stripIpv6Brackets(this.config.upstreamOrigin.hostname),\n port: Number(this.config.upstreamOrigin.port),\n method: 'GET',\n path: `${target.pathname}${target.search}`,\n headers: {\n host: this.config.upstreamOrigin.host,\n accept: 'text/html',\n 'accept-encoding': 'identity',\n },\n agent: false,\n })\n this.upstreamAuthRequest = upstreamRequest\n upstreamRequest.setTimeout(this.config.upstreamTimeoutMs, () => {\n upstreamRequest.destroy(new Error('upstream timeout'))\n })\n upstreamRequest.once('response', resolve)\n upstreamRequest.once('error', reject)\n upstreamRequest.end()\n })\n await new Promise<void>((resolve, reject) => {\n proxied.once('end', resolve)\n proxied.once('error', reject)\n proxied.resume()\n })\n const setCookie = proxied.headers['set-cookie']?.[0]\n const pair = setCookie?.split(';', 1)[0]\n const maxAgeText = setCookie === undefined\n ? undefined\n : /(?:^|;\\s*)Max-Age=(\\d+)(?:;|$)/iu.exec(setCookie)?.[1]\n const maxAgeSeconds = maxAgeText === undefined ? Number.NaN : Number(maxAgeText)\n const expiresAt = Date.now() + maxAgeSeconds * 1000\n if (proxied.statusCode !== 303 || pair === undefined || pair.length > 4096\n || !UPSTREAM_COOKIE_PAIR.test(pair) || !Number.isSafeInteger(expiresAt)\n || maxAgeSeconds <= 0) {\n throw new HttpError(502, 'upstream_unavailable')\n }\n this.upstreamCookie = pair\n this.upstreamCookieExpiresAt = expiresAt\n return pair\n } catch (error) {\n if (error instanceof HttpError) throw error\n throw new HttpError(502, 'upstream_unavailable')\n } finally {\n this.upstreamAuthRequest?.destroy()\n this.upstreamAuthRequest = undefined\n }\n }\n\n private async proxyMobileIndex(\n request: IncomingMessage,\n response: ServerResponse,\n authorization: SessionAuthorization,\n ): Promise<void> {\n const holder: { request?: ClientRequest } = {}\n const operation = this.allocateRequest(authorization, response, holder)\n try {\n const upstreamHeaders = sanitizeRequestHeaders(request, this.config.upstreamOrigin)\n const upstreamCookie = await this.upstreamCookieHeader()\n if (upstreamCookie !== undefined) upstreamHeaders.cookie = upstreamCookie\n upstreamHeaders['accept-encoding'] = 'identity'\n const proxied = await new Promise<IncomingMessage>((resolve, reject) => {\n const upstreamRequest = requestHttp({\n protocol: 'http:',\n hostname: stripIpv6Brackets(this.config.upstreamOrigin.hostname),\n port: Number(this.config.upstreamOrigin.port),\n method: 'GET',\n path: '/',\n headers: upstreamHeaders,\n agent: false,\n })\n holder.request = upstreamRequest\n upstreamRequest.setTimeout(this.config.upstreamTimeoutMs, () => {\n upstreamRequest.destroy(new Error('upstream timeout'))\n })\n upstreamRequest.once('response', resolve)\n upstreamRequest.once('error', reject)\n upstreamRequest.end()\n })\n if ((proxied.statusCode ?? 502) !== 200) throw new HttpError(502, 'upstream_unavailable')\n const chunks: Buffer[] = []\n let bytes = 0\n for await (const chunk of proxied) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n bytes += buffer.byteLength\n if (bytes > 4 * 1024 * 1024) throw new HttpError(502, 'upstream_unavailable')\n chunks.push(buffer)\n }\n let body: Buffer\n try {\n const html = Buffer.concat(chunks).toString('utf8')\n const plan = parseMobileBootPlan(html)\n const options = plan.planEntries === undefined\n ? {}\n : await this.resolveMobileBootSizes(plan.planEntries)\n const rewritten = rewriteMobileIndexWithBatch(html, options)\n for (const batch of rewritten.batches) this.rememberMobileBootBatch(batch)\n body = Buffer.from(rewritten.html)\n } catch {\n throw new HttpError(502, 'upstream_unavailable')\n }\n const headers = sanitizeResponseHeaders(proxied.headers, this.config.upstreamOrigin)\n delete headers['content-length']\n delete headers['content-encoding']\n delete headers.etag\n // The proxied GUI document: it must be able to frame its own surfaces.\n setSecurityHeaders(response, this.tlsEnabled, 'proxied')\n response.writeHead(200, {\n ...headers,\n 'Content-Type': 'text/html; charset=utf-8',\n 'Content-Length': body.byteLength,\n })\n response.end(body)\n } catch (error) {\n holder.request?.destroy()\n if (error instanceof HttpError) throw error\n if (response.headersSent) response.destroy()\n else throw new HttpError(502, 'upstream_unavailable')\n } finally {\n operation.release()\n }\n }\n\n private rememberMobileBootBatch(plan: MobileBootBatchPlan): void {\n const existing = this.mobileBootBatches.get(plan.key)\n this.mobileBootBatches.delete(plan.key)\n this.mobileBootBatches.set(plan.key, existing ?? { plan })\n while (this.mobileBootBatches.size > MAX_MOBILE_BOOT_BATCHES) {\n const oldest = this.mobileBootBatches.keys().next().value as string | undefined\n if (oldest === undefined) break\n this.mobileBootBatches.get(oldest)?.assembly?.controller.abort()\n this.mobileBootBatches.delete(oldest)\n }\n }\n\n /**\n * Determine which layout-batch entries can share a merged boot batch and which\n * must pass through. An entry at or above the per-entry cap, or one whose size\n * could not be probed, keeps its own upstream `/plugins` row: a merged batch\n * cannot carry it, and an unknown bundle must not risk the whole batch.\n */\n private async resolveMobileBootSizes(\n planEntries: readonly MobileBootBatchEntry[],\n ): Promise<{ sizes: Map<string, number>; passThrough: Set<string> }> {\n const sizes = new Map<string, number>()\n const passThrough = new Set<string>()\n const candidates = planEntries.filter(entry => entry.id !== MOBILE_LAYOUT_MODULE\n && upstreamPluginBundleUrl(entry.url, this.config.upstreamOrigin) !== undefined)\n let cursor = 0\n const worker = async (): Promise<void> => {\n while (cursor < candidates.length) {\n const index = cursor++\n const source = candidates[index]!.url\n const size = await this.upstreamBundleSize(source)\n if (size === undefined || size >= MAX_MOBILE_BOOT_ENTRY_BYTES) passThrough.add(source)\n else sizes.set(source, size)\n }\n }\n await Promise.all(Array.from({ length: Math.min(8, candidates.length) }, worker))\n return { sizes, passThrough }\n }\n\n /**\n * Measure one upstream bundle by reading its body. The upstream serves\n * `/plugins` bundles as chunked streams without a Content-Length, so a probe\n * counts bytes; it aborts instantly past the per-entry cap. Measurements are\n * cached for a short window.\n */\n private async upstreamBundleSize(source: string): Promise<number | undefined> {\n const target = upstreamPluginBundleUrl(source, this.config.upstreamOrigin)\n if (target === undefined) return undefined\n const cached = this.bootEntrySizeCache.get(source)\n if (cached !== undefined && Date.now() - cached.at < MOBILE_BOOT_SIZE_CACHE_TTL_MS) return cached.size\n const upstreamCookie = await this.upstreamCookieHeader()\n let upstreamRequest: ClientRequest | undefined\n try {\n const proxied = await new Promise<IncomingMessage>((resolve, reject) => {\n upstreamRequest = requestHttp({\n protocol: 'http:',\n hostname: stripIpv6Brackets(this.config.upstreamOrigin.hostname),\n port: Number(this.config.upstreamOrigin.port),\n method: 'GET',\n path: `${target.pathname}${target.search}`,\n headers: {\n host: this.config.upstreamOrigin.host,\n accept: 'text/javascript',\n 'accept-encoding': 'identity',\n [MOBILE_BOOT_SIZE_PROBE_HEADER]: '1',\n ...(upstreamCookie === undefined ? {} : { cookie: upstreamCookie }),\n },\n agent: false,\n })\n upstreamRequest.setTimeout(this.config.upstreamTimeoutMs, () => {\n upstreamRequest?.destroy(upstreamTimeoutError())\n })\n upstreamRequest.once('response', resolve)\n upstreamRequest.once('error', reject)\n upstreamRequest.end()\n })\n if (proxied.statusCode !== 200) {\n proxied.resume()\n return undefined\n }\n const size = await new Promise<number | undefined>((resolve, reject) => {\n let bytes = 0\n proxied.on('data', (chunk: Buffer) => {\n bytes += chunk.length\n if (bytes > MAX_MOBILE_BOOT_ENTRY_BYTES) {\n proxied.destroy()\n resolve(undefined)\n }\n })\n proxied.once('end', () => resolve(bytes))\n proxied.once('error', reject)\n })\n if (size === undefined) return undefined\n this.bootEntrySizeCache.set(source, { size, at: Date.now() })\n return size\n } catch {\n return undefined\n } finally {\n upstreamRequest?.destroy()\n }\n }\n\n private async serveMobileBootBatch(\n key: string,\n request: IncomingMessage,\n response: ServerResponse,\n authorization: SessionAuthorization,\n ): Promise<void> {\n if (request.method !== 'GET' && request.method !== 'HEAD') throw new HttpError(405, 'method_not_allowed')\n const stored = this.mobileBootBatches.get(key)\n if (stored === undefined) throw new HttpError(404, 'not_found')\n const operation = this.allocateRequest(authorization, response, {})\n response.once('close', operation.abort)\n try {\n const layoutStat = await stat(this.config.mobileLayoutFile)\n if (stored.body === undefined || stored.etag === undefined || stored.layoutMtimeMs !== layoutStat.mtimeMs) {\n operation.signal.throwIfAborted()\n const assembly = stored.assembly ?? this.startMobileBootBatchAssembly(stored, layoutStat.mtimeMs)\n await waitForRequestTask(assembly.task, operation.signal)\n }\n const compressed = acceptsGzip(request.headers['accept-encoding'])\n // The assembly above assigns stored.body; TypeScript cannot narrow a\n // property written inside an awaited closure, so read it once here.\n const assembled = stored.body\n if (assembled === undefined) throw new HttpError(502, 'upstream_unavailable')\n const body = compressed\n ? stored.gzipBody ??= await gzipBuffer(assembled)\n : assembled\n const etag = compressed ? `${stored.etag}-gzip` : stored.etag\n const headers: OutgoingHttpHeaders = {\n 'Content-Type': 'text/javascript; charset=utf-8',\n 'Content-Length': body.byteLength,\n 'Cache-Control': 'private, no-cache',\n ETag: etag,\n }\n if (compressed) headers['Content-Encoding'] = 'gzip'\n addVaryAcceptEncoding(headers)\n if (headerValue(request.headers, 'if-none-match') === etag) {\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(304, { ETag: etag, 'Cache-Control': 'private, no-cache', Vary: String(headers.vary) })\n response.end()\n return\n }\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(200, headers)\n if (request.method === 'HEAD') response.end()\n else response.end(body)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') throw new HttpError(503, 'mobile_frontend_unavailable')\n throw error\n } finally {\n response.removeListener('close', operation.abort)\n operation.release()\n }\n }\n\n private startMobileBootBatchAssembly(stored: StoredMobileBootBatch, layoutMtimeMs: number): MobileBootBatchAssembly {\n const controller = new AbortController()\n const task = (async (): Promise<Buffer> => {\n const body = await this.assembleMobileBootBatch(stored.plan, controller.signal)\n stored.body = body\n delete stored.gzipBody\n stored.etag = createHash('sha256').update(body).digest('hex')\n stored.layoutMtimeMs = layoutMtimeMs\n return body\n })()\n const assembly = Object.freeze({ controller, task })\n stored.assembly = assembly\n void task.then(\n () => { if (stored.assembly === assembly) delete stored.assembly },\n () => { if (stored.assembly === assembly) delete stored.assembly },\n )\n return assembly\n }\n\n private async assembleMobileBootBatch(plan: MobileBootBatchPlan, signal: AbortSignal): Promise<Buffer> {\n const bodies = new Array<Buffer>(plan.entries.length)\n let cursor = 0\n const worker = async (): Promise<void> => {\n while (cursor < plan.entries.length) {\n const index = cursor++\n const entry = plan.entries[index]!\n bodies[index] = entry.id === MOBILE_LAYOUT_MODULE\n ? await readFile(this.config.mobileLayoutFile, { signal })\n : await this.readUpstreamClientBundleWithRetry(entry.url, signal)\n if (bodies[index]!.byteLength > MAX_MOBILE_BOOT_ENTRY_BYTES) throw new HttpError(502, 'upstream_unavailable')\n }\n }\n // Three workers keep the fan-out gentle: the upstream resets a fraction of\n // connections when this many entries are pulled at once, and every reset\n // used to fail the whole batch.\n await Promise.all(Array.from({ length: Math.min(3, plan.entries.length) }, worker))\n const total = bodies.reduce((bytes, body) => bytes + body.byteLength + MOBILE_BOOT_SEPARATOR_BYTES, 0)\n if (total > MAX_MOBILE_BOOT_BATCH_BYTES) throw new HttpError(502, 'upstream_unavailable')\n return Buffer.concat(bodies.flatMap(body => [body, Buffer.from('\\n;\\n')]))\n }\n\n /**\n * Read one upstream bundle, retrying transient connection failures.\n *\n * Assembling a batch fans out over every client entry, and the upstream\n * resets a fraction of those connections before sending a byte\n * (`read ECONNRESET`, recv=0) — randomly, on any entry, at any concurrency.\n * A single such reset used to fail the whole batch with 502\n * `upstream_unavailable`, surfacing in the browser as \"bundle script\n * /mobile-access/mobile-boot/<hash>.js failed to load\". Retrying recovers\n * every observed reset; a genuine upstream error still fails.\n */\n private async readUpstreamClientBundleWithRetry(source: string, signal: AbortSignal): Promise<Buffer> {\n for (let attempt = 1; attempt <= MOBILE_BOOT_UPSTREAM_ATTEMPTS; attempt++) {\n signal.throwIfAborted()\n try {\n return await this.readUpstreamClientBundle(source, signal)\n } catch (error) {\n if (signal.aborted) throw error\n if (!isTransientUpstreamError(error)) throw error\n if (attempt === MOBILE_BOOT_UPSTREAM_ATTEMPTS) throw new HttpError(502, 'upstream_unavailable')\n await waitForAbortableDelay(MOBILE_BOOT_RETRY_DELAY_MS * attempt, signal)\n }\n }\n throw new HttpError(502, 'upstream_unavailable')\n }\n\n private async readUpstreamClientBundle(source: string, signal: AbortSignal): Promise<Buffer> {\n signal.throwIfAborted()\n const target = upstreamPluginBundleUrl(source, this.config.upstreamOrigin)\n if (target === undefined) throw new HttpError(502, 'upstream_unavailable')\n let upstreamRequest: ClientRequest | undefined\n const aborted = (): void => { upstreamRequest?.destroy(new Error('request aborted')) }\n signal.addEventListener('abort', aborted, { once: true })\n try {\n const upstreamCookie = await this.upstreamCookieHeader()\n signal.throwIfAborted()\n const proxied = await new Promise<IncomingMessage>((resolve, reject) => {\n upstreamRequest = requestHttp({\n protocol: 'http:',\n hostname: stripIpv6Brackets(this.config.upstreamOrigin.hostname),\n port: Number(this.config.upstreamOrigin.port),\n method: 'GET',\n path: `${target.pathname}${target.search}`,\n headers: {\n host: this.config.upstreamOrigin.host,\n accept: 'text/javascript',\n 'accept-encoding': 'identity',\n ...(upstreamCookie === undefined ? {} : { cookie: upstreamCookie }),\n },\n agent: false,\n })\n upstreamRequest.setTimeout(this.config.upstreamTimeoutMs, () => {\n upstreamRequest?.destroy(upstreamTimeoutError())\n })\n upstreamRequest.once('response', resolve)\n upstreamRequest.once('error', reject)\n upstreamRequest.end()\n })\n if ((proxied.statusCode ?? 502) !== 200) throw new HttpError(502, 'upstream_unavailable')\n const chunks: Buffer[] = []\n let bytes = 0\n for await (const chunk of proxied) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n bytes += buffer.byteLength\n if (bytes > MAX_MOBILE_BOOT_ENTRY_BYTES) throw new HttpError(502, 'upstream_unavailable')\n chunks.push(buffer)\n }\n return Buffer.concat(chunks)\n } catch (error) {\n if (error instanceof HttpError) throw error\n if (signal.aborted || isTransientUpstreamError(error)) throw error\n throw new HttpError(502, 'upstream_unavailable')\n } finally {\n signal.removeEventListener('abort', aborted)\n upstreamRequest?.destroy()\n }\n }\n\n private allocateRequest(\n authorization: SessionAuthorization,\n response: ServerResponse,\n upstream: { request?: ClientRequest },\n ): { id: number; signal: AbortSignal; abort: () => void; release: () => void } {\n if (this.activeRequests.size >= this.config.maxActiveRequests) throw new HttpError(429, 'busy')\n const id = this.nextOperationId++\n const controller = new AbortController()\n const abort = (): void => {\n controller.abort()\n upstream.request?.destroy()\n if (!response.destroyed) response.destroy()\n }\n const timer = setTimeout(abort, Math.max(1, authorization.expiresAt - Date.now()))\n timer.unref()\n this.activeRequests.set(id, Object.freeze({ ...authorization, abort, timer }))\n return {\n id,\n signal: controller.signal,\n abort,\n release: () => {\n const entry = this.activeRequests.get(id)\n if (entry !== undefined) clearTimeout(entry.timer)\n this.activeRequests.delete(id)\n },\n }\n }\n\n /**\n * Proxy one request upstream. Pass-through client bundles (`GET /plugins`)\n * receive bounded transient retries — the upstream resets a fraction of fresh\n * connections — matching the resilience the merged-batch assembly already has.\n * A request is only retried before any byte reached the client.\n */\n private async proxyHttp(\n request: IncomingMessage,\n response: ServerResponse,\n authorization: SessionAuthorization,\n ): Promise<void> {\n const retryable = request.method === 'GET'\n && request.url?.split('?', 1)[0]?.startsWith('/plugins/') === true\n if (!retryable) {\n try {\n await this.proxyHttpOnce(request, response, authorization)\n } catch (error) {\n if (error instanceof HttpError) throw error\n if (response.headersSent) response.destroy()\n else throw new HttpError(502, 'upstream_unavailable')\n }\n return\n }\n const delay = (attempt: number): Promise<void> => (\n new Promise(resolve => setTimeout(resolve, MOBILE_BOOT_RETRY_DELAY_MS * attempt))\n )\n for (let attempt = 1; attempt <= MOBILE_BOOT_UPSTREAM_ATTEMPTS; attempt++) {\n try {\n await this.proxyHttpOnce(request, response, authorization)\n return\n } catch (error) {\n if (response.headersSent) {\n response.destroy()\n return\n }\n if (error instanceof HttpError || !isTransientUpstreamError(error)) {\n throw error instanceof HttpError ? error : new HttpError(502, 'upstream_unavailable')\n }\n if (attempt === MOBILE_BOOT_UPSTREAM_ATTEMPTS) throw new HttpError(502, 'upstream_unavailable')\n await delay(attempt)\n }\n }\n throw new HttpError(502, 'upstream_unavailable')\n }\n\n private async proxyHttpOnce(\n request: IncomingMessage,\n response: ServerResponse,\n authorization: SessionAuthorization,\n ): Promise<void> {\n const declared = request.headers['content-length']\n if (declared !== undefined && (!/^\\d+$/u.test(declared) || Number(declared) > this.config.maxBodyBytes)) {\n throw new HttpError(413, 'payload_too_large')\n }\n const holder: { request?: ClientRequest } = {}\n const operation = this.allocateRequest(authorization, response, holder)\n let bodyDone: Promise<void> | undefined\n try {\n const bufferedBody = request.method === 'POST' && request.url?.split('?', 1)[0] === SESSION_HISTORY_PATH\n ? mobileHistoryRequestBody(request, await readBoundedBody(request, this.config.maxBodyBytes))\n : undefined\n const upstreamHeaders = sanitizeRequestHeaders(request, this.config.upstreamOrigin)\n const upstreamCookie = await this.upstreamCookieHeader()\n if (upstreamCookie !== undefined) upstreamHeaders.cookie = upstreamCookie\n if (bufferedBody !== undefined) upstreamHeaders['content-length'] = String(bufferedBody.byteLength)\n const upstreamResponse = new Promise<IncomingMessage>((resolve, reject) => {\n const upstreamRequest = requestHttp({\n protocol: 'http:',\n hostname: stripIpv6Brackets(this.config.upstreamOrigin.hostname),\n port: Number(this.config.upstreamOrigin.port),\n method: request.method,\n path: request.url,\n headers: upstreamHeaders,\n agent: false,\n })\n holder.request = upstreamRequest\n upstreamRequest.setTimeout(this.config.upstreamTimeoutMs, () => {\n upstreamRequest.destroy(new Error('upstream timeout'))\n })\n upstreamRequest.once('response', resolve)\n upstreamRequest.once('error', reject)\n if (bufferedBody === undefined) {\n bodyDone = pipeline(request, new ByteLimitTransform(this.config.maxBodyBytes), upstreamRequest)\n } else {\n upstreamRequest.end(bufferedBody)\n bodyDone = Promise.resolve()\n }\n void bodyDone.catch(reject)\n })\n const proxied = await upstreamResponse\n // Proxied upstream routes (static assets, the GUI's own /sidebar routes,\n // the API): the GUI frames some of its own routes (HTML/diff previews),\n // so they follow the proxied framing policy.\n setSecurityHeaders(response, this.tlsEnabled, 'proxied')\n const headers = sanitizeResponseHeaders(proxied.headers, this.config.upstreamOrigin)\n const statusCode = proxied.statusCode ?? 502\n const cacheControl = revisionedStaticCacheControl(request, statusCode)\n if (cacheControl !== undefined) headers['cache-control'] = cacheControl\n const compressed = shouldCompressResponse(request, proxied)\n if (compressed) {\n delete headers['accept-ranges']\n delete headers['content-length']\n delete headers.etag\n headers['content-encoding'] = 'gzip'\n addVaryAcceptEncoding(headers)\n }\n response.writeHead(statusCode, headers)\n await Promise.all([\n bodyDone,\n compressed ? pipeline(proxied, createGzip(), response) : pipeline(proxied, response),\n ])\n } catch (error) {\n holder.request?.destroy()\n await bodyDone?.catch(() => undefined)\n if (error instanceof HttpError) throw error\n if (response.headersSent) response.destroy()\n // Raw transient errors reach the proxyHttp retry window; anything else\n // becomes the standard upstream-unavailable answer.\n throw error\n } finally {\n operation.release()\n }\n }\n\n private abortSessionResources(sessionKey: string): void {\n for (const request of this.activeRequests.values()) {\n if (request.sessionKey === sessionKey) request.abort()\n }\n for (const socket of this.activeWebSockets.values()) {\n if (socket.sessionKey === sessionKey) {\n socket.client.destroy()\n socket.upstream.destroy()\n }\n }\n }\n\n private broadcastExtensionChange(): void {\n if (this.closing) return\n this.extensionEventRevision += 1\n for (const listener of this.extensionEventListeners) listener(this.extensionEventRevision)\n }\n\n /** Fan a completed task to every phone holding this gateway's event stream. */\n broadcastTaskEvent(event: { readonly sessionId: string; readonly turn: number }): void {\n if (this.closing) return\n const payload = JSON.stringify({ sessionId: String(event.sessionId), turn: Number(event.turn) || 0 })\n for (const listener of this.taskEventListeners) listener(payload)\n }\n\n /** Notify only the device whose persistent credential was revoked by the Host. */\n private broadcastDeviceRevoked(deviceId: string): void {\n const listeners = this.deviceEventListeners.get(deviceId)\n if (listeners === undefined) return\n const payload = JSON.stringify({ reason: 'device_revoked' })\n for (const listener of [...listeners]) listener(payload)\n }\n\n private pollLegacyCustomChanges(): Promise<void> {\n if (this.extensionChangeTask !== undefined) return this.extensionChangeTask\n const digestFile = async (path: string, fallback: string): Promise<string> => {\n try {\n const info = await stat(path)\n if (!info.isFile() || info.size > 256 * 1024) return `invalid:${String(info.size)}:${String(info.mtimeMs)}`\n return createHash('sha256').update(await readFile(path)).digest('hex')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return createHash('sha256').update(fallback).digest('hex')\n return `error:${String((error as NodeJS.ErrnoException).code ?? 'unknown')}`\n }\n }\n const task = Promise.all([\n digestFile(this.config.customScriptFile, CUSTOM_SCRIPT_FALLBACK),\n digestFile(this.config.customCssFile, CUSTOM_STYLE_FALLBACK),\n ]).then(parts => {\n const next = createHash('sha256').update(parts.join('|')).digest('hex')\n if (this.legacyCustomDigest !== '' && next !== this.legacyCustomDigest) this.broadcastExtensionChange()\n this.legacyCustomDigest = next\n }).finally(() => {\n if (this.extensionChangeTask === task) this.extensionChangeTask = undefined\n })\n this.extensionChangeTask = task\n return task\n }\n\n private openExtensionEventStream(\n request: IncomingMessage,\n response: ServerResponse,\n authorization: SessionAuthorization,\n ): void {\n const operation = this.allocateRequest(authorization, response, {})\n let closed = false\n let heartbeat: NodeJS.Timeout | undefined\n const close = (): void => {\n if (closed) return\n closed = true\n if (heartbeat !== undefined) clearInterval(heartbeat)\n this.extensionEventListeners.delete(send)\n this.taskEventListeners.delete(sendTask)\n const deviceListeners = this.deviceEventListeners.get(authorization.deviceId)\n deviceListeners?.delete(sendDevice)\n if (deviceListeners?.size === 0) this.deviceEventListeners.delete(authorization.deviceId)\n request.removeListener('aborted', close)\n response.removeListener('close', close)\n operation.release()\n }\n const send = (revision: number): void => {\n if (closed || response.destroyed || response.writableEnded) return\n response.write(`id: ${String(revision)}\\nevent: extensions-changed\\ndata: {\\\"revision\\\":${String(revision)}}\\n\\n`)\n }\n const sendTask = (payload: string): void => {\n if (closed || response.destroyed || response.writableEnded) return\n response.write(`event: task-notify\\ndata: ${payload}\\n\\n`)\n }\n const sendDevice = (payload: string): void => {\n if (closed || response.destroyed || response.writableEnded) return\n response.write(`event: device-revoked\\ndata: ${payload}\\n\\n`)\n }\n setSecurityHeaders(response, this.tlsEnabled)\n response.writeHead(200, {\n 'Content-Type': 'text/event-stream; charset=utf-8',\n 'Cache-Control': 'no-store',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n })\n response.write('retry: 2000\\n: ready\\n\\n')\n this.extensionEventListeners.add(send)\n this.taskEventListeners.add(sendTask)\n const deviceListeners = this.deviceEventListeners.get(authorization.deviceId) ?? new Set<(payload: string) => void>()\n deviceListeners.add(sendDevice)\n this.deviceEventListeners.set(authorization.deviceId, deviceListeners)\n heartbeat = setInterval(() => {\n if (!closed && !response.destroyed && !response.writableEnded) response.write(': heartbeat\\n\\n')\n }, EXTENSION_EVENT_HEARTBEAT_MS)\n heartbeat.unref()\n request.once('aborted', close)\n response.once('close', close)\n }\n\n private async readUpgradeResponse(upstream: Socket, expectedAccept: string): Promise<{ header: string; remainder: Buffer }> {\n return new Promise((resolve, reject) => {\n let buffer = Buffer.alloc(0)\n const failed = (error: Error): void => { cleanup(); reject(error) }\n const closed = (): void => { cleanup(); reject(new Error('upstream closed during WebSocket handshake')) }\n const data = (chunk: Buffer): void => {\n buffer = Buffer.concat([buffer, chunk])\n const end = buffer.indexOf('\\r\\n\\r\\n')\n if (end < 0) {\n if (buffer.length >= MAX_HEADER_BYTES) failed(new Error('upstream WebSocket headers are too large'))\n return\n }\n if (end + 4 > MAX_HEADER_BYTES) {\n failed(new Error('upstream WebSocket headers are too large'))\n return\n }\n cleanup()\n const lines = buffer.subarray(0, end).toString('latin1').split('\\r\\n')\n if (lines.shift() !== 'HTTP/1.1 101 Switching Protocols') {\n reject(new Error('upstream refused WebSocket upgrade'))\n return\n }\n const selected = new Map<string, string>()\n for (const line of lines) {\n const colon = line.indexOf(':')\n if (colon <= 0) {\n reject(new Error('upstream returned malformed WebSocket headers'))\n return\n }\n const name = line.slice(0, colon).trim().toLowerCase()\n const value = line.slice(colon + 1).trim()\n if (selected.has(name)) {\n reject(new Error('upstream returned duplicate WebSocket headers'))\n return\n }\n selected.set(name, value)\n }\n if (selected.get('upgrade')?.toLowerCase() !== 'websocket'\n || !hasToken(selected.get('connection'), 'upgrade')\n || selected.get('sec-websocket-accept') !== expectedAccept) {\n reject(new Error('upstream returned an invalid WebSocket handshake'))\n return\n }\n const output = [\n 'HTTP/1.1 101 Switching Protocols',\n 'Upgrade: websocket',\n 'Connection: Upgrade',\n `Sec-WebSocket-Accept: ${expectedAccept}`,\n ]\n const protocol = selected.get('sec-websocket-protocol')\n const extensions = selected.get('sec-websocket-extensions')\n if (protocol !== undefined) output.push(`Sec-WebSocket-Protocol: ${protocol}`)\n if (extensions !== undefined) output.push(`Sec-WebSocket-Extensions: ${extensions}`)\n output.push('Referrer-Policy: no-referrer', 'X-Content-Type-Options: nosniff', '', '')\n resolve({ header: output.join('\\r\\n'), remainder: buffer.subarray(end + 4) })\n }\n const cleanup = (): void => {\n upstream.off('data', data)\n upstream.off('error', failed)\n upstream.off('close', closed)\n }\n upstream.on('data', data)\n upstream.once('error', failed)\n upstream.once('close', closed)\n })\n }\n\n /** Snapshot of rejected upgrade paths for the approval UI (newest first). */\n blockedUpgradePathReport(): BlockedUpgradePathEntry[] {\n return this.blockedUpgradeLog?.report() ?? []\n }\n\n private async handleUpgrade(request: IncomingMessage, client: Socket, head: Buffer): Promise<void> {\n const target = parseRequestTarget(request.url)\n const policy = this.requirePolicy()\n // Android WebView WebSockets do not consistently carry Fetch Metadata.\n // Exact Origin, direct CIDR, exact Host, and the short Session Cookie\n // remain mandatory; when Sec-Fetch-Site is present, assertExternalTrust\n // still requires it to be same-origin.\n assertExternalTrust(request, policy, false)\n if (!policy.acceptsOrigin(request.headers.origin)) throw new HttpError(403, 'forbidden')\n // Core DSH paths plus admin-approved third-party plugin paths (exact\n // pathname match; the query string rides along to the upstream plugin,\n // which owns its parsing). Everything else stays 404: paired-device\n // auth, Origin, and handshake checks below still apply to allowed paths.\n const upgradeAllowed = WS_PATHS.has(target.decodedPathname)\n || (this.extraWebSocketPaths?.has(target.decodedPathname) ?? false)\n if (!upgradeAllowed) {\n this.blockedUpgradeLog?.record(target.decodedPathname)\n throw new HttpError(404, 'not_found')\n }\n if (request.method !== 'GET' || headerValue(request.headers, 'upgrade')?.toLowerCase() !== 'websocket'\n || !hasToken(headerValue(request.headers, 'connection'), 'upgrade')) {\n throw new HttpError(400, 'bad_request')\n }\n const key = headerValue(request.headers, 'sec-websocket-key')\n if (key === undefined || headerValue(request.headers, 'sec-websocket-version') !== '13') {\n throw new HttpError(400, 'bad_request')\n }\n let decodedKey: Buffer\n try {\n decodedKey = Buffer.from(key, 'base64')\n } catch {\n throw new HttpError(400, 'bad_request')\n }\n if (decodedKey.length !== 16 || decodedKey.toString('base64') !== key) throw new HttpError(400, 'bad_request')\n const authorization = this.authorize(request)\n if (this.activeWebSockets.size >= this.config.maxWebSockets) throw new HttpError(429, 'busy')\n const upstreamCookie = await this.upstreamCookieHeader()\n if (client.destroyed) return\n\n const upstream = connect({\n host: stripIpv6Brackets(this.config.upstreamOrigin.hostname),\n port: Number(this.config.upstreamOrigin.port),\n })\n client.pause()\n const id = this.nextOperationId++\n const closeBoth = (): void => {\n client.destroy()\n upstream.destroy()\n }\n client.on('error', closeBoth)\n upstream.on('error', closeBoth)\n const timer = setTimeout(closeBoth, Math.max(1, authorization.expiresAt - Date.now()))\n timer.unref()\n const record: ActiveWebSocket = Object.freeze({ ...authorization, client, upstream, timer })\n this.activeWebSockets.set(id, record)\n const cleanup = (): void => {\n const active = this.activeWebSockets.get(id)\n if (active !== undefined) clearTimeout(active.timer)\n this.activeWebSockets.delete(id)\n }\n client.once('close', () => { upstream.destroy(); cleanup() })\n upstream.once('close', () => { client.destroy(); cleanup() })\n upstream.setTimeout(this.config.upstreamTimeoutMs, closeBoth)\n try {\n await new Promise<void>((resolve, reject) => {\n const connected = (): void => {\n upstream.off('error', failed)\n resolve()\n }\n const failed = (error: Error): void => {\n upstream.off('connect', connected)\n reject(error)\n }\n upstream.once('connect', connected)\n upstream.once('error', failed)\n })\n const requestLines = [\n `GET ${target.raw} HTTP/1.1`,\n `Host: ${this.config.upstreamOrigin.host}`,\n 'Upgrade: websocket',\n 'Connection: Upgrade',\n `Origin: ${this.config.upstreamOrigin.origin}`,\n 'Sec-Fetch-Site: same-origin',\n `Sec-WebSocket-Key: ${key}`,\n 'Sec-WebSocket-Version: 13',\n ]\n if (upstreamCookie !== undefined) requestLines.push(`Cookie: ${upstreamCookie}`)\n const protocol = headerValue(request.headers, 'sec-websocket-protocol')\n const extensions = headerValue(request.headers, 'sec-websocket-extensions')\n if (protocol !== undefined) requestLines.push(`Sec-WebSocket-Protocol: ${protocol}`)\n if (extensions !== undefined) requestLines.push(`Sec-WebSocket-Extensions: ${extensions}`)\n requestLines.push('', '')\n upstream.write(requestLines.join('\\r\\n'))\n if (head.length > 0) upstream.write(head)\n const handshake = await this.readUpgradeResponse(upstream, websocketAccept(key))\n upstream.setTimeout(0)\n client.write(handshake.header)\n if (handshake.remainder.length > 0) client.write(handshake.remainder)\n upstream.pipe(client)\n client.pipe(upstream)\n client.resume()\n } catch (error) {\n closeBoth()\n if (error instanceof HttpError) throw error\n throw new HttpError(502, 'upstream_unavailable')\n }\n }\n\n /** Loopback-only DSH WebServer route for opening pairing and managing devices. */\n localAdminRoute(prefix: string = LOCAL_ADMIN_PREFIX): WebRoute {\n return {\n kind: 'prefix',\n path: prefix,\n handler: async (request, response) => {\n try {\n const target = parseRequestTarget(request.url)\n const mutation = request.method === 'POST'\n assertLocalAdminTrust(request, mutation)\n if (target.search !== '') throw new HttpError(400, 'bad_request')\n if (request.method === 'GET' && target.decodedPathname === `${prefix}/status`) {\n sendJson(response, 200, {\n gateway: this.address(),\n pairing: this.access.pairingStatus(),\n deviceCount: this.access.listDevices().length,\n resources: {\n connections: this.connectedSockets.size,\n activeRequests: this.activeRequests.size,\n webSockets: this.activeWebSockets.size,\n },\n discovery: this.discoveryStatus(),\n }, false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${prefix}/devices`) {\n sendJson(response, 200, { devices: this.access.listDevices() }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${prefix}/pairing/open`) {\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (body.ttlMs !== undefined && typeof body.ttlMs !== 'number') throw new HttpError(400, 'bad_request')\n const opened = await this.access.openPairing(body.ttlMs as number | undefined)\n // A self-signed remote entry must never fall back to public-CA trust.\n // dsh2 is not understood by old Android apps, so they fail closed.\n const requiresRemoteCa = this.config.pairingCaFile !== undefined && !this.config.discovery\n const appKey = `dsh${requiresRemoteCa ? '2' : '1'}.${this.config.instanceId}.${opened.token}`\n const fragment = requiresRemoteCa\n ? `key=${appKey}`\n : `instance=${this.config.instanceId}&token=${opened.token}`\n const pairUrl = `${this.address().origin}/mobile-access/pair#${fragment}`\n const appPairUrl = pairUrl\n // The QR code is an enhancement; a failed render must not waste an opened window.\n let qrSvg = ''\n try {\n qrSvg = await QRCode.toString(appPairUrl, { type: 'svg', margin: 1 })\n } catch {\n // keep qrSvg empty\n }\n sendJson(response, 201, {\n ...opened,\n appKey,\n pairUrl,\n appPairUrl,\n qrSvg,\n }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${prefix}/devices/revoke`) {\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (typeof body.deviceId !== 'string' || !/^[a-f\\d]{32}$/u.test(body.deviceId)) {\n throw new HttpError(400, 'bad_request')\n }\n const revoked = await this.access.revokeDevice(body.deviceId)\n if (!revoked) throw new HttpError(404, 'not_found')\n sendJson(response, 200, { revoked: true }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${prefix}/devices/reset`) {\n const body = await readJsonObject(request, MAX_CONTROL_BODY_BYTES)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await this.access.resetDevices()\n sendJson(response, 200, { reset: true }, false)\n return\n }\n throw new HttpError(404, 'not_found')\n } catch (error) {\n const mapped = mapError(error)\n if (response.headersSent) response.destroy()\n else sendFailure(response, mapped.status, mapped.code, false)\n }\n },\n }\n }\n\n /** Close listeners and abort all accepted work before resolving teardown. */\n async close(): Promise<void> {\n if (this.closeTask !== undefined) return this.closeTask\n this.closeTask = this.performClose()\n return this.closeTask\n }\n\n private async performClose(): Promise<void> {\n this.closing = true\n if (this.extensionChangeTimer !== undefined) clearInterval(this.extensionChangeTimer)\n this.extensionChangeTimer = undefined\n this.removeExtensionContentListener()\n this.upstreamAuthRequest?.destroy()\n this.upstreamAuthRequest = undefined\n this.removeSessionListener()\n this.pendingDeviceRevocations.clear()\n const accessClose = this.access.close()\n for (const stored of this.mobileBootBatches.values()) stored.assembly?.controller.abort()\n for (const request of this.activeRequests.values()) request.abort()\n for (const websocket of this.activeWebSockets.values()) {\n websocket.client.destroy()\n websocket.upstream.destroy()\n }\n for (const socket of this.connectedSockets) socket.destroy()\n if (this.discoveryTimer !== undefined) clearInterval(this.discoveryTimer)\n this.discoveryTimer = undefined\n await this.closeBonjour()\n const discoverySocket = this.discoverySocket\n this.discoverySocket = undefined\n if (discoverySocket !== undefined) {\n await new Promise<void>(resolve => { discoverySocket.close(() => resolve()) })\n }\n const server = this.server\n this.server = undefined\n if (server !== undefined && server.listening) {\n server.closeAllConnections()\n await new Promise<void>(resolve => { server.close(() => resolve()) })\n }\n await accessClose\n this.activeRequests.clear()\n this.activeWebSockets.clear()\n this.connectedSockets.clear()\n this.policy = undefined\n this.listenerPort = undefined\n }\n\n /** Safe metadata helper for direct loopback integrations. */\n devices(): readonly DeviceSummary[] {\n return this.access.listDevices()\n }\n\n /** Status shown by the loopback mobile-access control card. */\n extensionStatus(): { readonly loaded: number; readonly failed: number } {\n return this.extensions?.status() ?? { loaded: 0, failed: 0 }\n }\n}\n","import { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, join } from 'node:path'\nimport { randomBytes } from 'node:crypto'\nimport { restrictPrivateFile } from './private-file.js'\n\n/** Persistent record containing only a digest of the long-lived device credential. */\nexport interface StoredDevice {\n readonly id: string\n readonly label: string\n readonly tokenDigest: string\n readonly createdAt: number\n readonly expiresAt: number\n readonly lastSeenAt: number\n /** Legacy tombstone accepted on load; AccessController removes it during initialization. */\n readonly revokedAt?: number\n}\n\n/** Versioned device state. Raw device and Session credentials are never members. */\nexport interface DeviceSnapshot {\n readonly version: 1\n readonly devices: readonly StoredDevice[]\n}\n\n/** Persistence seam for device-token digests and revocation metadata. */\nexport interface DeviceStore {\n load(): Promise<DeviceSnapshot>\n save(snapshot: DeviceSnapshot): Promise<void>\n}\n\nfunction assertInteger(value: unknown, name: string): asserts value is number {\n if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {\n throw new Error(`device state ${name} must be a non-negative integer`)\n }\n}\n\nfunction parseDevice(value: unknown): StoredDevice {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('device state contains an invalid device')\n const record = value as Record<string, unknown>\n if (typeof record.id !== 'string' || !/^[a-f\\d]{32}$/u.test(record.id)) throw new Error('device state contains an invalid id')\n if (typeof record.label !== 'string' || record.label.length < 1 || record.label.length > 64 || /[\\u0000-\\u001f\\u007f]/u.test(record.label)) {\n throw new Error('device state contains an invalid label')\n }\n if (typeof record.tokenDigest !== 'string' || !/^[a-f\\d]{64}$/u.test(record.tokenDigest)) {\n throw new Error('device state contains an invalid credential digest')\n }\n assertInteger(record.createdAt, 'createdAt')\n assertInteger(record.expiresAt, 'expiresAt')\n assertInteger(record.lastSeenAt, 'lastSeenAt')\n if (record.revokedAt !== undefined) assertInteger(record.revokedAt, 'revokedAt')\n if (record.expiresAt <= record.createdAt || record.lastSeenAt < record.createdAt) {\n throw new Error('device state contains inconsistent timestamps')\n }\n return Object.freeze({\n id: record.id,\n label: record.label,\n tokenDigest: record.tokenDigest,\n createdAt: record.createdAt,\n expiresAt: record.expiresAt,\n lastSeenAt: record.lastSeenAt,\n ...(record.revokedAt === undefined ? {} : { revokedAt: record.revokedAt }),\n })\n}\n\n/** Validate durable data before it can authorize a device. */\nexport function parseDeviceSnapshot(value: unknown, maximumDevices = 256): DeviceSnapshot {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('device state must be an object')\n const snapshot = value as Record<string, unknown>\n if (snapshot.version !== 1 || !Array.isArray(snapshot.devices) || snapshot.devices.length > maximumDevices) {\n throw new Error('device state has an unsupported version or device count')\n }\n const devices = snapshot.devices.map(parseDevice)\n if (new Set(devices.map(device => device.id)).size !== devices.length\n || new Set(devices.map(device => device.tokenDigest)).size !== devices.length) {\n throw new Error('device state contains duplicate device identities')\n }\n return Object.freeze({ version: 1, devices: Object.freeze(devices) })\n}\n\n/** Atomic JSON implementation with symlink refusal and owner-only file creation. */\nexport class JsonDeviceStore implements DeviceStore {\n constructor(private readonly file: string, private readonly maximumDevices = 256) {}\n\n async load(): Promise<DeviceSnapshot> {\n let stat\n try {\n stat = await lstat(this.file)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return Object.freeze({ version: 1, devices: Object.freeze([]) })\n throw error\n }\n if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1024 * 1024) {\n throw new Error('device state must be a regular file no larger than 1 MiB')\n }\n await restrictPrivateFile(this.file)\n let parsed: unknown\n try {\n parsed = JSON.parse(await readFile(this.file, 'utf8')) as unknown\n } catch (error) {\n throw new Error('device state is not valid JSON', { cause: error })\n }\n return parseDeviceSnapshot(parsed, this.maximumDevices)\n }\n\n async save(snapshot: DeviceSnapshot): Promise<void> {\n const validated = parseDeviceSnapshot(snapshot, this.maximumDevices)\n const directory = dirname(this.file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(this.file)\n if (!current.isFile() || current.isSymbolicLink()) throw new Error('device state target must remain a regular file')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, `.${basename(this.file)}.${randomBytes(12).toString('hex')}.tmp`)\n try {\n await writeFile(temporary, `${JSON.stringify(validated)}\\n`, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, this.file)\n await restrictPrivateFile(this.file)\n } catch (error) {\n try {\n await rm(temporary, { force: true })\n } catch (cleanupError) {\n throw new AggregateError([error, cleanupError], 'device state write and temporary cleanup both failed')\n }\n throw error\n }\n }\n}\n\n/** In-memory store useful for embedding and deterministic tests. */\nexport class MemoryDeviceStore implements DeviceStore {\n private snapshot: DeviceSnapshot\n\n constructor(initial: DeviceSnapshot = { version: 1, devices: [] }) {\n this.snapshot = parseDeviceSnapshot(initial)\n }\n\n async load(): Promise<DeviceSnapshot> {\n return structuredClone(this.snapshot)\n }\n\n async save(snapshot: DeviceSnapshot): Promise<void> {\n this.snapshot = structuredClone(parseDeviceSnapshot(snapshot))\n }\n\n /** Return a defensive copy for assertions or administrative export. */\n inspect(): DeviceSnapshot {\n return structuredClone(this.snapshot)\n }\n}\n","import { createHash, randomBytes } from 'node:crypto'\nimport { execFile } from 'node:child_process'\nimport {\n chmod,\n copyFile,\n lstat,\n mkdir,\n mkdtemp,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises'\nimport { basename, isAbsolute, join, relative, resolve } from 'node:path'\n\nconst FRP_VERSION = '0.70.1'\nconst MAX_ARCHIVE_ENTRIES = 128\nconst MAX_ARCHIVE_LIST_BYTES = 256 * 1024\n\ninterface FrpArtifact {\n readonly platform: NodeJS.Platform\n readonly arch: string\n readonly downloadUrl: string\n readonly downloadBytes: number\n readonly downloadSha256: string\n readonly archiveName: string\n readonly executableName: string\n}\n\nconst releases = [\n {\n platform: 'win32', arch: 'x64', archiveName: 'frp.zip', executableName: 'frpc.exe',\n downloadBytes: 13_924_309,\n downloadSha256: '531f3cd3cc41c0b4f077b54fe6b7dd83c0ff727e7f0bf412a4c78fa279165de5',\n downloadUrl: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_windows_amd64.zip`,\n },\n {\n platform: 'win32', arch: 'arm64', archiveName: 'frp.zip', executableName: 'frpc.exe',\n downloadBytes: 12_204_751,\n downloadSha256: '74d3acaf0f03ee190dd0462f9b49861dca50b0559c5488af4b36572fc951fcca',\n downloadUrl: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_windows_arm64.zip`,\n },\n {\n platform: 'linux', arch: 'x64', archiveName: 'frp.tar.gz', executableName: 'frpc',\n downloadBytes: 13_924_042,\n downloadSha256: '333da23d1b9009d7c01638e9ba38cf4600f7d37d393f854e96ee1396adefa9a6',\n downloadUrl: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_amd64.tar.gz`,\n },\n {\n platform: 'linux', arch: 'arm64', archiveName: 'frp.tar.gz', executableName: 'frpc',\n downloadBytes: 12_371_290,\n downloadSha256: '3990f396a9a490ee7f0e5f355287750ed41520064ed999eab443b5e9a78d773d',\n downloadUrl: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_arm64.tar.gz`,\n },\n {\n platform: 'darwin', arch: 'x64', archiveName: 'frp.tar.gz', executableName: 'frpc',\n downloadBytes: 13_951_979,\n downloadSha256: 'cbf69cf26e5553e914e97d37f5d4367fa30f5f531d073a889465af4719281e25',\n downloadUrl: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_darwin_amd64.tar.gz`,\n },\n {\n platform: 'darwin', arch: 'arm64', archiveName: 'frp.tar.gz', executableName: 'frpc',\n downloadBytes: 12_670_664,\n downloadSha256: 'cfa733b5a261c1647edee3c1fc4133d2542989b28f5602e81d47fc821d25c55f',\n downloadUrl: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_darwin_arm64.tar.gz`,\n },\n] as const satisfies readonly FrpArtifact[]\n\n/** Pinned official FRP release metadata for supported desktop targets. */\nexport const FRP_COMPONENT_RELEASES: Readonly<Record<string, FrpArtifact>> = Object.freeze(Object.fromEntries(\n releases.map(release => [`${release.platform}-${release.arch}`, Object.freeze(release)]),\n))\n\n/** Public, credential-free description of the managed FRP client. */\nexport interface FrpComponentStatus {\n readonly supported: boolean\n readonly installed: boolean\n readonly version: string\n readonly downloadBytes: number\n readonly installedBytes: number\n readonly sourceUrl: string\n readonly releasePage: string\n readonly storagePath: string\n readonly errorCode?: string\n}\n\ninterface FrpComponentManagerOptions {\n readonly stateDirectory: string\n readonly platform?: NodeJS.Platform\n readonly arch?: string\n readonly fetchArtifact?: (artifact: FrpArtifact, signal: AbortSignal) => Promise<Uint8Array>\n readonly extractArtifact?: (archive: string, destination: string, executableName: string) => Promise<void>\n readonly inspectExecutable?: (executable: string) => Promise<string>\n}\n\nfunction inside(parent: string, child: string): boolean {\n const candidate = relative(parent, child)\n return candidate !== '' && !candidate.startsWith('..') && !isAbsolute(candidate)\n}\n\nasync function regularFile(file: string): Promise<boolean> {\n try {\n const entry = await lstat(file)\n return entry.isFile() && !entry.isSymbolicLink()\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false\n throw error\n }\n}\n\nasync function replaceDirectory(target: string, candidate: string): Promise<void> {\n const backup = `${target}.previous-${randomBytes(12).toString('hex')}`\n let previous = false\n try {\n try {\n await rename(target, backup)\n previous = true\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n try {\n await rename(candidate, target)\n } catch (error) {\n if (previous) {\n try { await rename(backup, target) } catch (restoreError) {\n throw new AggregateError([error, restoreError], 'frp_component_replace_failed')\n }\n }\n throw error\n }\n if (previous) await rm(backup, { recursive: true, force: true })\n } finally {\n await rm(candidate, { recursive: true, force: true })\n }\n}\n\nfunction sha256(bytes: Uint8Array): string {\n return createHash('sha256').update(bytes).digest('hex')\n}\n\nasync function runCapture(file: string, args: readonly string[]): Promise<string> {\n return new Promise<string>((resolveRun, reject) => {\n execFile(file, [...args], {\n windowsHide: true,\n timeout: 120_000,\n maxBuffer: MAX_ARCHIVE_LIST_BYTES,\n encoding: 'utf8',\n }, (error, stdout) => {\n if (error === null) resolveRun(stdout)\n else reject(error)\n })\n })\n}\n\nfunction validatedArchiveEntry(rawEntry: string): readonly string[] {\n if (rawEntry.length === 0 || rawEntry.includes('\\\\') || rawEntry.includes('\\u0000')\n || rawEntry.startsWith('/') || /^[a-zA-Z]:/u.test(rawEntry)) {\n throw new Error('frp_archive_path_invalid')\n }\n const segments = rawEntry.replace(/\\/$/u, '').split('/')\n if (segments.some(segment => segment === '' || segment === '.' || segment === '..')) {\n throw new Error('frp_archive_path_invalid')\n }\n return segments\n}\n\n/** Select exactly one nested frpc executable from a safe archive listing. */\nexport function selectFrpExecutableEntry(entries: readonly string[], executableName: string): string {\n if (entries.length === 0 || entries.length > MAX_ARCHIVE_ENTRIES) throw new Error('frp_archive_entries_invalid')\n let executableEntry: string | undefined\n for (const entry of entries) {\n const segments = validatedArchiveEntry(entry)\n if (segments.length >= 2 && segments.at(-1) === executableName) {\n if (executableEntry !== undefined) throw new Error('frp_archive_executable_ambiguous')\n executableEntry = entry.replace(/\\/$/u, '')\n }\n }\n if (executableEntry === undefined) throw new Error('frp_archive_executable_missing')\n return executableEntry\n}\n\nasync function defaultExtractArtifact(archive: string, destination: string, executableName: string): Promise<void> {\n const tar = process.platform === 'win32' ? 'tar.exe' : 'tar'\n const listing = await runCapture(tar, ['-tf', archive])\n const entries = listing.split(/\\r?\\n/u).filter(entry => entry.length > 0)\n const executableEntry = selectFrpExecutableEntry(entries, executableName)\n const unpacked = join(destination, 'archive')\n await mkdir(unpacked, { recursive: true, mode: 0o700 })\n await runCapture(tar, ['-xf', archive, '-C', unpacked, executableEntry])\n const extracted = join(unpacked, ...validatedArchiveEntry(executableEntry))\n if (!await regularFile(extracted)) throw new Error('frp_archive_executable_invalid')\n await copyFile(extracted, join(destination, executableName))\n}\n\nasync function defaultFetchArtifact(artifact: FrpArtifact, signal: AbortSignal): Promise<Uint8Array> {\n const response = await fetch(artifact.downloadUrl, { redirect: 'follow', signal })\n if (!response.ok) throw new Error(`frp_download_http_${String(response.status)}`)\n const finalUrl = new URL(response.url)\n const officialHost = finalUrl.hostname === 'github.com' || finalUrl.hostname.endsWith('.githubusercontent.com')\n if (finalUrl.protocol !== 'https:' || !officialHost) throw new Error('frp_download_origin_invalid')\n const lengthHeader = response.headers.get('content-length')\n const declaredLength = lengthHeader === null ? undefined : Number(lengthHeader)\n if (declaredLength !== undefined && (!Number.isFinite(declaredLength) || declaredLength !== artifact.downloadBytes)) {\n throw new Error('frp_download_size_mismatch')\n }\n if (response.body === null) throw new Error('frp_download_empty')\n const chunks: Uint8Array[] = []\n let received = 0\n const reader = response.body.getReader()\n while (true) {\n const result = await reader.read()\n if (result.done) break\n received += result.value.byteLength\n if (received > artifact.downloadBytes) {\n await reader.cancel()\n throw new Error('frp_download_size_mismatch')\n }\n chunks.push(result.value)\n }\n if (received !== artifact.downloadBytes) throw new Error('frp_download_size_mismatch')\n const bytes = new Uint8Array(received)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return bytes\n}\n\nasync function defaultInspectExecutable(executable: string): Promise<string> {\n return (await runCapture(executable, ['--version'])).trim()\n}\n\n/** Owns the optional official frpc binary inside the DSH Mobile state directory. */\nexport class FrpComponentManager {\n readonly executable: string\n readonly componentRoot: string\n readonly componentStorage: string\n readonly logRoot: string\n private readonly stagingRoot: string\n private readonly artifact: FrpArtifact | undefined\n private readonly fetchArtifact: (artifact: FrpArtifact, signal: AbortSignal) => Promise<Uint8Array>\n private readonly extractArtifact: (archive: string, destination: string, executableName: string) => Promise<void>\n private readonly inspectExecutable: (executable: string) => Promise<string>\n private installed = false\n private installedBytes = 0\n private errorCode: string | undefined\n private queue: Promise<void> = Promise.resolve()\n\n constructor(options: FrpComponentManagerOptions) {\n const stateDirectory = resolve(options.stateDirectory)\n if (!isAbsolute(stateDirectory)) throw new Error('frp state directory must be absolute')\n const platform = options.platform ?? process.platform\n const arch = options.arch ?? process.arch\n this.artifact = FRP_COMPONENT_RELEASES[`${platform}-${arch}`]\n this.componentRoot = join(stateDirectory, 'components', 'frp')\n this.componentStorage = join(this.componentRoot, FRP_VERSION)\n this.executable = join(this.componentStorage, platform === 'win32' ? 'frpc.exe' : 'frpc')\n this.logRoot = join(stateDirectory, 'logs', 'frp')\n this.stagingRoot = join(stateDirectory, 'staging', 'frp')\n for (const child of [this.componentRoot, this.componentStorage, this.logRoot, this.stagingRoot]) {\n if (!inside(stateDirectory, child)) throw new Error('frp component path escaped its state directory')\n }\n this.fetchArtifact = options.fetchArtifact ?? defaultFetchArtifact\n this.extractArtifact = options.extractArtifact ?? defaultExtractArtifact\n this.inspectExecutable = options.inspectExecutable ?? defaultInspectExecutable\n }\n\n /** Inspect the managed executable without relying on global FRP installations. */\n async initialize(): Promise<void> {\n this.installed = await regularFile(this.executable)\n this.installedBytes = this.installed ? (await stat(this.executable)).size : 0\n if (this.installed) {\n try {\n const version = await this.inspectExecutable(this.executable)\n if (version !== FRP_VERSION) throw new Error('frp_component_version_mismatch')\n this.errorCode = undefined\n } catch {\n this.installed = false\n this.errorCode = 'frp_component_invalid'\n }\n }\n }\n\n /** Return component metadata without exposing configuration or credentials. */\n status(): FrpComponentStatus {\n return Object.freeze({\n supported: this.artifact !== undefined,\n installed: this.installed,\n version: FRP_VERSION,\n downloadBytes: this.artifact?.downloadBytes ?? 0,\n installedBytes: this.installedBytes,\n sourceUrl: this.artifact?.downloadUrl ?? 'https://github.com/fatedier/frp/releases',\n releasePage: `https://github.com/fatedier/frp/releases/tag/v${FRP_VERSION}`,\n storagePath: this.componentRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n })\n }\n\n /** Download, verify, and extract only frpc after explicit confirmation. */\n install(): Promise<FrpComponentStatus> {\n return this.enqueue(async () => {\n const artifact = this.artifact\n if (artifact === undefined) throw new Error('frp_component_unsupported')\n await mkdir(this.stagingRoot, { recursive: true, mode: 0o700 })\n const staging = await mkdtemp(join(this.stagingRoot, 'install-'))\n try {\n const controller = new AbortController()\n const timeout = setTimeout(() => { controller.abort() }, 120_000)\n timeout.unref()\n let bytes: Uint8Array\n try { bytes = await this.fetchArtifact(artifact, controller.signal) } finally { clearTimeout(timeout) }\n if (bytes.byteLength !== artifact.downloadBytes) throw new Error('frp_download_size_mismatch')\n if (sha256(bytes) !== artifact.downloadSha256) throw new Error('frp_download_hash_mismatch')\n const archive = join(staging, artifact.archiveName)\n await writeFile(archive, bytes, { flag: 'wx', mode: 0o600 })\n await this.extractArtifact(archive, staging, artifact.executableName)\n const extracted = join(staging, artifact.executableName)\n if (!await regularFile(extracted)) throw new Error('frp_executable_missing')\n await chmod(extracted, 0o700)\n const version = await this.inspectExecutable(extracted)\n if (version !== FRP_VERSION) throw new Error('frp_component_version_mismatch')\n const candidate = join(this.componentRoot, `.install-${randomBytes(12).toString('hex')}`)\n await mkdir(candidate, { recursive: true, mode: 0o700 })\n const candidateExecutable = join(candidate, artifact.executableName)\n await copyFile(extracted, candidateExecutable)\n await chmod(candidateExecutable, 0o700)\n await replaceDirectory(this.componentStorage, candidate)\n this.installed = true\n this.installedBytes = (await stat(this.executable)).size\n this.errorCode = undefined\n } finally {\n await rm(staging, { recursive: true, force: true })\n }\n })\n }\n\n /** Remove all FRP executable, staging, and log files owned by DSH Mobile. */\n purge(): Promise<FrpComponentStatus> {\n return this.enqueue(async () => {\n await Promise.all([\n rm(this.componentRoot, { recursive: true, force: true }),\n rm(this.logRoot, { recursive: true, force: true }),\n rm(this.stagingRoot, { recursive: true, force: true }),\n ])\n this.installed = false\n this.installedBytes = 0\n this.errorCode = undefined\n })\n }\n\n private enqueue(operation: () => Promise<void>): Promise<FrpComponentStatus> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task.then(() => this.status())\n }\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\n\n/** Cap the admin-approved extra WebSocket upgrade paths (exact pathnames). */\nexport const MAX_EXTRA_WEBSOCKET_PATHS = 16\nexport const MAX_WEBSOCKET_PATH_LENGTH = 256\n\nfunction fail(code: string): never {\n throw new Error(code)\n}\n\n/**\n * Validate one exact pathname for proxying. Query strings are matched at\n * upgrade time, so only the pathname is stored. Rejects anything the\n * gateway cannot match exactly.\n */\nexport function validateWebSocketPath(value: unknown): string {\n if (typeof value !== 'string') fail('websocket_path_invalid')\n const path = value as string\n if (path.length === 0 || path.length > MAX_WEBSOCKET_PATH_LENGTH) fail('websocket_path_invalid')\n if (!path.startsWith('/')) fail('websocket_path_invalid')\n // eslint-disable-next-line no-control-regex\n if (/[\\s\\u0000-\\u001f\\u007f#?]/u.test(path)) fail('websocket_path_invalid')\n if (path.includes('..')) fail('websocket_path_invalid')\n return path\n}\n\n/** Validate a whole replacement list all-or-nothing; duplicates collapse. */\nexport function normalizeWebSocketPaths(value: unknown): string[] {\n if (!Array.isArray(value)) fail('websocket_paths_invalid')\n if (value.length > MAX_EXTRA_WEBSOCKET_PATHS) fail('websocket_paths_invalid')\n const seen = new Set<string>()\n for (const entry of value) {\n const path = validateWebSocketPath(entry)\n seen.add(path)\n }\n return [...seen]\n}\n\n/** Cap remembered rejected upgrade paths offered for one-click approval. */\nexport const MAX_BLOCKED_UPGRADE_PATHS = 32\n\nexport interface BlockedUpgradePathEntry {\n readonly path: string\n readonly attempts: number\n readonly firstSeen: number\n readonly lastSeen: number\n}\n\n/**\n * In-memory log of rejected third-party upgrade paths, shared by every\n * gateway instance so the approval UI sees attempts on any listener.\n * Bounded and newest-first; a restart clears it (attempts reappear on\n * the next blocked handshake).\n */\nexport class BlockedUpgradePathLog {\n private readonly entries = new Map<string, { attempts: number; firstSeen: number; lastSeen: number }>()\n\n record(pathname: string): void {\n const now = Date.now()\n const stats = this.entries.get(pathname)\n if (stats === undefined) {\n if (this.entries.size >= MAX_BLOCKED_UPGRADE_PATHS) {\n let oldest: string | undefined\n for (const [path, entry] of this.entries) {\n if (oldest === undefined || entry.lastSeen < (this.entries.get(oldest)?.lastSeen ?? 0)) oldest = path\n }\n if (oldest !== undefined) this.entries.delete(oldest)\n }\n this.entries.set(pathname, { attempts: 1, firstSeen: now, lastSeen: now })\n return\n }\n stats.attempts += 1\n stats.lastSeen = now\n }\n\n report(): BlockedUpgradePathEntry[] {\n return [...this.entries]\n .map(([path, stats]) => ({ path, ...stats }))\n .sort((a, b) => b.lastSeen - a.lastSeen)\n }\n}\n\n/** File-backed store shared by every gateway instance (LAN and remote). */\nexport class WebSocketPathStore {\n private paths = new Set<string>()\n private loaded = false\n\n constructor(private readonly file: string) {}\n\n /** Snapshot for the upgrade check. */\n has(pathname: string): boolean {\n return this.paths.has(pathname)\n }\n\n list(): string[] {\n return [...this.paths]\n }\n\n async load(): Promise<string[]> {\n if (!this.loaded) {\n try {\n const raw = JSON.parse(await readFile(this.file, 'utf8')) as { readonly paths?: unknown }\n this.paths = new Set(normalizeWebSocketPaths(raw.paths ?? []))\n } catch {\n // Missing, corrupt, or unreadable: fall back to the empty (most\n // restrictive) list. The panel always shows the active list, so an\n // admin notices and re-adds entries instead of silently widening.\n this.paths = new Set()\n }\n this.loaded = true\n }\n return this.list()\n }\n\n /** Replace the whole list after validating; persists atomically. */\n async replace(paths: readonly string[]): Promise<string[]> {\n const next = normalizeWebSocketPaths([...paths])\n await mkdir(dirname(this.file), { recursive: true })\n await writeFile(this.file, `${JSON.stringify({ paths: next })}\\n`, 'utf8')\n this.paths = new Set(next)\n this.loaded = true\n return this.list()\n }\n}\n","/** Loopback-only HTTP vhost port used between Caddy and frps. */\nexport const FRP_VHOST_HTTP_PORT = 7080\n\n/** Caddy snippet owned entirely by DSH Mobile; the main Caddyfile only imports it. */\nexport const FRP_CADDY_SNIPPET_PATH = '/etc/caddy/dsh-mobile-dsh.caddy'\n\n/** First line of the owned snippet; also the legacy whole-file marker. */\nexport const FRP_CADDY_SNIPPET_MARKER = '# Managed by DSH Mobile - snippet, safe to delete'\n\n/** Exact line the main Caddyfile must contain (uncommented) for the site to load. */\nexport const FRP_CADDY_IMPORT_LINE = `import ${FRP_CADDY_SNIPPET_PATH}`\n\n/** Directory holding the public-IPv4 certificates installed by certbot. */\nexport const FRP_CADDY_IP_CERT_DIR = '/var/lib/caddy/dsh-mobile-certs'\n\n/**\n * Entry TLS mode for the FRP channel.\n *\n * - `public-ip-cert` (default): the VPS Caddy terminates TLS with a public-CA\n * certificate and reverse-proxies the plaintext vhost to frps.\n * - `self-signed`: no Caddy and no public certificate at all. frps publishes a\n * raw TCP proxy and the DSH gateway terminates TLS itself with a leaf signed\n * by its own pairing CA, which the Android app pins through `ca.cer`.\n */\nexport type FrpEntryTls = 'public-ip-cert' | 'self-signed'\n\n/** Optional overrides for the generated Caddy site; every field defaults to the upstream value. */\nexport interface CaddySiteOptions {\n readonly certDir?: string\n /**\n * The user's real `vhostHTTPPort`. `attach` mode must never assume the\n * upstream 7080: a wrong port silently disables the plaintext-exposure gate.\n */\n readonly vhostHttpPort?: number\n /** `self-signed` is a TCP passthrough and never produces a Caddy site. */\n readonly entryTls?: FrpEntryTls\n}\n\nfunction publicIpv4Address(value: string): boolean {\n const parts = value.split('.')\n return parts.length === 4 && parts.every(part => /^(?:0|[1-9][0-9]{0,2})$/u.test(part)\n && Number(part) <= 255)\n}\n\nfunction publicDnsHostname(value: string): boolean {\n return value.length <= 253 && value.includes('.') && !/^[0-9.]+$/u.test(value)\n && !value.includes(':') && value.split('.').every(label => label.length >= 1 && label.length <= 63\n && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label))\n}\n\nfunction parsePublicOrigin(publicOrigin: string): string {\n let url: URL\n try { url = new URL(publicOrigin) } catch { throw new Error('frp_template_input_invalid') }\n if (url.protocol !== 'https:' || url.port !== '' || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '' || (!publicIpv4Address(url.hostname) && !publicDnsHostname(url.hostname))) {\n throw new Error('frp_template_input_invalid')\n }\n return url.hostname\n}\n\nfunction resolveVhostHttpPort(options: CaddySiteOptions): number {\n const port = options.vhostHttpPort ?? FRP_VHOST_HTTP_PORT\n if (!Number.isSafeInteger(port) || port < 1 || port > 65_535) throw new Error('frp_template_input_invalid')\n return port\n}\n\n/**\n * A self-signed entry deliberately never reaches Caddy: the tunnel is a raw TCP\n * passthrough and the DSH gateway is the TLS endpoint. Refuse loudly instead of\n * emitting a Caddy site that would need the gateway's private CA on the VPS.\n */\nfunction rejectSelfSignedCaddySite(options: CaddySiteOptions): void {\n if (options.entryTls === 'self-signed') throw new Error('frp_entry_tls_invalid')\n}\n\n/**\n * Build the Caddy site for one public host (without markers or import wiring).\n *\n * The second parameter accepts either a bare certificate directory (the legacy\n * signature) or a full {@link CaddySiteOptions} object, so existing callers keep\n * working unchanged.\n */\nexport function createCaddySite(publicHost: string, options: CaddySiteOptions | string = {}): string {\n const resolved: CaddySiteOptions = typeof options === 'string' ? { certDir: options } : options\n rejectSelfSignedCaddySite(resolved)\n const certDir = resolved.certDir ?? FRP_CADDY_IP_CERT_DIR\n const vhostHttpPort = resolveVhostHttpPort(resolved)\n if (publicIpv4Address(publicHost)) {\n return [\n '{',\n ` default_sni ${publicHost}`,\n '}',\n '',\n `http://${publicHost} {`,\n ` redir https://${publicHost}{uri} permanent`,\n '}',\n '',\n `https://${publicHost} {`,\n ` tls ${certDir}/fullchain.pem ${certDir}/privkey.pem`,\n ` reverse_proxy 127.0.0.1:${String(vhostHttpPort)}`,\n '}',\n '',\n ].join('\\n')\n }\n if (!publicDnsHostname(publicHost)) throw new Error('frp_template_input_invalid')\n return [\n `${publicHost} {`,\n ` reverse_proxy 127.0.0.1:${String(vhostHttpPort)}`,\n '}',\n '',\n ].join('\\n')\n}\n\n/** Manual certbot steps for a public-IPv4 origin (Caddy cannot issue IP certificates itself). */\nexport function manualIpCertificateGuide(publicHost: string): string {\n return [\n '# Public-IPv4 manual HTTPS: Caddy cannot issue IP certificates by itself.',\n '# On the VPS (Ubuntu/Debian, port 80 reachable from the internet), run once as root:',\n '# apt-get install -y python3-venv',\n '# python3 -m venv /opt/dsh-mobile/certbot-venv',\n \"# /opt/dsh-mobile/certbot-venv/bin/pip install 'certbot==5.8.0'\",\n '# systemctl stop caddy || true',\n `# /opt/dsh-mobile/certbot-venv/bin/certbot certonly --standalone --preferred-profile shortlived --ip-address ${publicHost} --agree-tos --register-unsafely-without-email --non-interactive --keep-until-expiring`,\n '# install -d -m 0750 -o caddy -g caddy /var/lib/caddy/dsh-mobile-certs',\n `# install -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/fullchain.pem /var/lib/caddy/dsh-mobile-certs/fullchain.pem`,\n `# install -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/privkey.pem /var/lib/caddy/dsh-mobile-certs/privkey.pem`,\n '# systemctl start caddy',\n '# The site below already references those paths. Certificates last about 6 days: re-run certonly before expiry.',\n '#',\n ].join('\\n')\n}\n\n/**\n * Build the only supported frps config and Caddy snippet from validated user inputs.\n *\n * Default `options` reproduce the upstream artefact byte for byte; `attach` mode\n * passes the user's real vhost port, and `self-signed` is rejected because that\n * mode publishes a raw TCP proxy instead of an HTTP vhost.\n */\nexport function createRestrictedFrpServerTemplate(\n serverPort: number,\n token: string,\n publicOrigin: string,\n options: CaddySiteOptions = {},\n): string {\n if (!Number.isSafeInteger(serverPort) || serverPort < 1 || serverPort > 65_535\n || token.length < 16 || token.length > 512 || /[\\s\\u0000-\\u001f\\u007f]/u.test(token)) {\n throw new Error('frp_template_input_invalid')\n }\n rejectSelfSignedCaddySite(options)\n const vhostHttpPort = resolveVhostHttpPort(options)\n const certDir = options.certDir ?? FRP_CADDY_IP_CERT_DIR\n const publicHost = parsePublicOrigin(publicOrigin)\n const lines = [\n '# frps.toml — save as /etc/dsh-mobile/frps.toml, then start the frps service.',\n `bindPort = ${String(serverPort)}`,\n 'proxyBindAddr = \"127.0.0.1\"',\n `vhostHTTPPort = ${String(vhostHttpPort)}`,\n 'auth.method = \"token\"',\n `auth.token = ${JSON.stringify(token)}`,\n '',\n `# Caddy — save the site below as ${FRP_CADDY_SNIPPET_PATH},`,\n '# then make sure your Caddyfile contains exactly this line at the TOP of the file',\n '# (create the file with just this line if needed; globals must precede sites):',\n `# ${FRP_CADDY_IMPORT_LINE}`,\n '# finally run: caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy',\n '# Uninstall later removes only this snippet file and the import line; your own Caddy content is kept.',\n `${FRP_CADDY_SNIPPET_MARKER}`,\n createCaddySite(publicHost, { certDir, vhostHttpPort }).trimEnd(),\n '',\n ]\n if (publicIpv4Address(publicHost)) lines.push(manualIpCertificateGuide(publicHost), '')\n return lines.join('\\n')\n}","import { randomBytes } from 'node:crypto'\nimport { isIP } from './ip.js'\nimport { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path'\nimport { isGloballyRoutableIpv4 } from './network.js'\nimport { restrictPrivateFile } from './private-file.js'\nimport { createRestrictedFrpServerTemplate, FRP_VHOST_HTTP_PORT, type FrpEntryTls } from './frp-template.js'\n\nconst MAX_SETTINGS_BYTES = 8 * 1024\n\n/** How the restricted FRP channel is provisioned on the VPS. */\nexport type FrpMode = 'deploy' | 'attach'\n\n/** Public entry port used by the self-signed TCP passthrough (never 443 by requirement). */\nexport const FRP_DEFAULT_PUBLIC_PORT = 33_080\n\n/** Ports owned by other DSH Mobile listeners; the public entry may never reuse them. */\nexport const FRP_RESERVED_PORTS: readonly number[] = Object.freeze([3080, 3443, 3444])\n\n/** Every key `settings.json` may contain. Unknown keys stay rejected. */\nconst FRP_SETTINGS_KEYS: ReadonlySet<string> = new Set([\n 'version', 'serverAddress', 'serverPort', 'token', 'publicOrigin',\n 'mode', 'entryTls', 'vhostHttpPort', 'publicPort',\n])\n\n/** Credentials and endpoints required by the restricted FRP provider. */\nexport interface FrpSettings {\n readonly version: 1\n readonly serverAddress: string\n readonly serverPort: number\n readonly token: string\n readonly publicOrigin: string\n /**\n * `deploy` (default) installs the plugin's own frps on the VPS.\n * `attach` reuses an frps that already runs there and never touches it.\n */\n readonly mode?: FrpMode\n /** `public-ip-cert` (default) or the gateway-terminated `self-signed` passthrough. */\n readonly entryTls?: FrpEntryTls\n /** The real `vhostHTTPPort` of the user's frps; defaults to the upstream 7080. */\n readonly vhostHttpPort?: number\n /** Public entry port of the self-signed TCP proxy; defaults to 33080. */\n readonly publicPort?: number\n}\n\n/** Safe FRP configuration fields returned to the desktop UI. */\nexport interface FrpConfigurationStatus {\n readonly configured: boolean\n readonly serverAddress?: string\n readonly serverPort?: number\n readonly publicOrigin?: string\n readonly vhostHttpPort: number\n readonly storagePath: string\n readonly errorCode?: string\n /** Present only when the saved configuration leaves the upstream `deploy` default. */\n readonly mode?: FrpMode\n /** Present only when the saved configuration uses the self-signed entry. */\n readonly entryTls?: FrpEntryTls\n /** Present only when a non-default public entry port is saved. */\n readonly publicPort?: number\n}\n\nfunction hostname(value: string): boolean {\n if (value.length > 253 || !value.includes('.')) return false\n return value.split('.').every(label => label.length >= 1 && label.length <= 63\n && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label))\n}\n\n/** Validate the FRP server hostname or IP address. */\nexport function validateFrpServerAddress(value: unknown): string {\n if (typeof value !== 'string' || value !== value.trim() || value.length === 0 || value.length > 253\n || /[\\s\\u0000-\\u001f\\u007f/\\\\@?#]/u.test(value)) throw new Error('frp_server_address_invalid')\n const normalized = value.toLowerCase().replace(/\\.$/u, '')\n if (isIP(normalized) === 0 && !hostname(normalized)) throw new Error('frp_server_address_invalid')\n return normalized\n}\n\n/** Validate the FRP control port. */\nexport function validateFrpServerPort(value: unknown): number {\n if (!Number.isSafeInteger(value) || Number(value) < 1 || Number(value) > 65_535) {\n throw new Error('frp_server_port_invalid')\n }\n return Number(value)\n}\n\n/** Validate a high-entropy FRP token before durable storage. */\nexport function validateFrpToken(value: unknown): string {\n if (typeof value !== 'string' || value.length < 16 || value.length > 512\n || /[\\s\\u0000-\\u001f\\u007f]/u.test(value)) throw new Error('frp_token_invalid')\n return value\n}\n\n/** Validate the public HTTPS origin used by Caddy and Android pairing. */\nexport function validateFrpPublicOrigin(value: unknown): string {\n if (typeof value !== 'string' || value.length > 512) throw new Error('frp_public_origin_invalid')\n let url: URL\n try { url = new URL(value) } catch { throw new Error('frp_public_origin_invalid') }\n const publicHost = url.hostname\n if (url.protocol !== 'https:' || url.port !== '' || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '' || (isIP(publicHost) !== 4 && !hostname(publicHost))) {\n throw new Error('frp_public_origin_invalid')\n }\n // Documentation and other non-routable IPv4 literals (e.g. 203.0.113.10)\n // can never be a real VPS endpoint; reject them instead of deploying certs.\n if (isIP(publicHost) === 4 && !isGloballyRoutableIpv4(publicHost)) throw new Error('frp_public_origin_invalid')\n return url.origin\n}\n\n/** Validate the provisioning mode. */\nexport function validateFrpMode(value: unknown): FrpMode {\n if (value !== 'deploy' && value !== 'attach') throw new Error('frp_settings_invalid')\n return value\n}\n\n/** Validate the entry TLS mode. */\nexport function validateFrpEntryTls(value: unknown): FrpEntryTls {\n if (value !== 'public-ip-cert' && value !== 'self-signed') throw new Error('frp_entry_tls_invalid')\n return value\n}\n\nfunction validatePort(value: unknown, rejectReserved: boolean): number {\n if (!Number.isSafeInteger(value) || Number(value) < 1 || Number(value) > 65_535) {\n throw new Error('frp_settings_invalid')\n }\n const port = Number(value)\n if (rejectReserved && FRP_RESERVED_PORTS.includes(port)) throw new Error('frp_settings_invalid')\n return port\n}\n\n/** Validate the loopback vhost port of the target frps. */\nexport function validateFrpVhostHttpPort(value: unknown): number {\n return validatePort(value, false)\n}\n\n/** Validate the public entry port of the self-signed TCP proxy. */\nexport function validateFrpPublicPort(value: unknown): number {\n return validatePort(value, true)\n}\n\n/** Effective provisioning mode; an absent field keeps the upstream behaviour. */\nexport function resolveFrpMode(settings: FrpSettings): FrpMode {\n return settings.mode ?? 'deploy'\n}\n\n/** Effective entry TLS mode; an absent field keeps the upstream behaviour. */\nexport function resolveFrpEntryTls(settings: FrpSettings): FrpEntryTls {\n return settings.entryTls ?? 'public-ip-cert'\n}\n\n/** Effective loopback vhost port; an absent field keeps the upstream behaviour. */\nexport function resolveFrpVhostHttpPort(settings: FrpSettings): number {\n return settings.vhostHttpPort ?? FRP_VHOST_HTTP_PORT\n}\n\n/** Effective public entry port of the self-signed passthrough. */\nexport function resolveFrpPublicPort(settings: FrpSettings): number {\n return settings.publicPort ?? FRP_DEFAULT_PUBLIC_PORT\n}\n\n/** HTTPS origin users actually open; the self-signed TCP entry is not on 443. */\nexport function frpEntryOrigin(settings: FrpSettings): string {\n if (!isFrpSelfSignedIngress(settings)) return settings.publicOrigin\n return `https://${new URL(settings.publicOrigin).hostname}:${String(resolveFrpPublicPort(settings))}`\n}\n\n/** True when the gateway terminates TLS itself behind a raw frps TCP proxy. */\nexport function isFrpSelfSignedIngress(settings: FrpSettings): boolean {\n return resolveFrpEntryTls(settings) === 'self-signed'\n}\n\n/**\n * Parse FRP settings at the loopback request and filesystem boundaries.\n *\n * Optional keys are kept only when supplied, so a legacy `settings.json` still\n * round-trips byte for byte and every default stays the upstream behaviour.\n * Two cross-field rules are enforced here because neither can be recovered later:\n * the self-signed entry is a TCP passthrough and therefore attach-only, and\n * attach mode with a public-CA entry must state the user's real vhost port —\n * silently assuming 7080 would disable the plaintext-exposure gate.\n */\nexport function parseFrpSettings(value: unknown): FrpSettings {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('frp_settings_invalid')\n const record = value as Record<string, unknown>\n if (Reflect.ownKeys(record).some(key => !FRP_SETTINGS_KEYS.has(String(key)))) {\n throw new Error('frp_settings_invalid')\n }\n if (record.version !== undefined && record.version !== 1) throw new Error('frp_settings_invalid')\n const mode = record.mode === undefined ? undefined : validateFrpMode(record.mode)\n const entryTls = record.entryTls === undefined ? undefined : validateFrpEntryTls(record.entryTls)\n const vhostHttpPort = record.vhostHttpPort === undefined ? undefined : validateFrpVhostHttpPort(record.vhostHttpPort)\n const publicPort = record.publicPort === undefined ? undefined : validateFrpPublicPort(record.publicPort)\n const resolvedMode = mode ?? 'deploy'\n const resolvedEntryTls: FrpEntryTls = entryTls ?? 'public-ip-cert'\n if (resolvedEntryTls === 'self-signed' && resolvedMode !== 'attach') throw new Error('frp_entry_tls_invalid')\n if (resolvedMode === 'attach' && resolvedEntryTls === 'public-ip-cert' && vhostHttpPort === undefined) {\n throw new Error('frp_attach_mode_requires_vhost_port')\n }\n const publicOrigin = validateFrpPublicOrigin(record.publicOrigin)\n if (resolvedEntryTls === 'self-signed' && isIP(new URL(publicOrigin).hostname) !== 4) {\n throw new Error('frp_self_signed_requires_public_ipv4')\n }\n return Object.freeze({\n version: 1,\n serverAddress: validateFrpServerAddress(record.serverAddress),\n serverPort: validateFrpServerPort(record.serverPort),\n token: validateFrpToken(record.token),\n publicOrigin,\n ...(mode === undefined ? {} : { mode }),\n ...(entryTls === undefined ? {} : { entryTls }),\n ...(vhostHttpPort === undefined ? {} : { vhostHttpPort }),\n ...(publicPort === undefined ? {} : { publicPort }),\n })\n}\n\n/**\n * Merge a partial VPS request body with the saved configuration so a blank\n * field keeps its saved value (\"已保存时可留空\"). Every merged field is still\n * validated; with nothing saved and nothing supplied the result reports a\n * missing configuration instead of silently deploying blanks.\n */\nexport function mergeSavedFrpSettings(\n partial: Readonly<Record<string, unknown>>,\n saved: FrpSettings | undefined,\n): FrpSettings {\n const pick = <K extends 'mode' | 'entryTls' | 'vhostHttpPort' | 'publicPort'>(key: K): unknown => {\n const supplied = partial[key]\n if (supplied === '' || supplied === undefined) return saved?.[key]\n return supplied\n }\n const mode = pick('mode')\n const entryTls = pick('entryTls')\n const vhostHttpPort = pick('vhostHttpPort')\n const publicPort = pick('publicPort')\n const merged = {\n serverAddress: partial.serverAddress === '' || partial.serverAddress === undefined\n ? saved?.serverAddress : partial.serverAddress,\n serverPort: typeof partial.serverPort === 'number' && Number.isSafeInteger(partial.serverPort) && partial.serverPort >= 1\n ? partial.serverPort : saved?.serverPort,\n token: partial.token === '' || partial.token === undefined ? saved?.token : partial.token,\n publicOrigin: partial.publicOrigin === '' || partial.publicOrigin === undefined\n ? saved?.publicOrigin : partial.publicOrigin,\n ...(mode === '' || mode === undefined ? {} : { mode }),\n ...(entryTls === '' || entryTls === undefined ? {} : { entryTls }),\n ...(vhostHttpPort === '' || vhostHttpPort === undefined ? {} : { vhostHttpPort }),\n ...(publicPort === '' || publicPort === undefined ? {} : { publicPort }),\n }\n if (merged.serverAddress === undefined && merged.serverPort === undefined\n && merged.token === undefined && merged.publicOrigin === undefined) {\n throw new Error('frp_config_missing')\n }\n return parseFrpSettings(merged)\n}\n\n/** Merge a VPS target (address and control port) with the saved configuration. */\nexport function mergeSavedFrpTarget(\n partial: Readonly<Record<string, unknown>>,\n saved: FrpSettings | undefined,\n): { readonly serverAddress: string; readonly serverPort: number } {\n const serverAddress = partial.serverAddress === '' || partial.serverAddress === undefined\n ? saved?.serverAddress : partial.serverAddress\n const serverPort = typeof partial.serverPort === 'number' && Number.isSafeInteger(partial.serverPort) && partial.serverPort >= 1\n ? partial.serverPort : saved?.serverPort\n if (serverAddress === undefined || serverPort === undefined) throw new Error('frp_config_missing')\n return Object.freeze({\n serverAddress: validateFrpServerAddress(serverAddress),\n serverPort: validateFrpServerPort(serverPort),\n })\n}\n\nfunction tomlString(value: string): string {\n return JSON.stringify(value)\n}\n\n/**\n * Build the single-purpose frpc configuration for the current loopback gateway.\n *\n * The default (public-CA) entry is an HTTP vhost behind Caddy and is emitted byte\n * for byte as before. The self-signed entry cannot use a vhost at all: frps only\n * forwards raw TCP, and the gateway terminates TLS on that connection.\n */\nexport function createFrpcToml(settings: FrpSettings, localPort: number): string {\n if (!Number.isSafeInteger(localPort) || localPort < 1 || localPort > 65_535) throw new Error('frp_local_port_invalid')\n const header = [\n `serverAddr = ${tomlString(settings.serverAddress)}`,\n `serverPort = ${String(settings.serverPort)}`,\n 'auth.method = \"token\"',\n `auth.token = ${tomlString(settings.token)}`,\n 'transport.tls.enable = true',\n '',\n '[[proxies]]',\n 'name = \"dsh-mobile\"',\n ]\n if (isFrpSelfSignedIngress(settings)) {\n return [\n ...header,\n // Raw TCP passthrough: frps never terminates or inspects TLS. The DSH\n // gateway terminates it and the app pins the gateway CA instead of any\n // public certificate authority.\n 'type = \"tcp\"',\n 'localIP = \"127.0.0.1\"',\n `localPort = ${String(localPort)}`,\n `remotePort = ${String(resolveFrpPublicPort(settings))}`,\n 'transport.useEncryption = true',\n 'transport.useCompression = true',\n '',\n ].join('\\n')\n }\n const hostnameValue = new URL(settings.publicOrigin).hostname\n return [\n ...header,\n 'type = \"http\"',\n 'localIP = \"127.0.0.1\"',\n `localPort = ${String(localPort)}`,\n `customDomains = [${tomlString(hostnameValue)}]`,\n 'transport.useEncryption = true',\n 'transport.useCompression = true',\n '',\n ].join('\\n')\n}\n\n/** Build the matching restricted frps and Caddy templates for one VPS. */\nexport function createFrpServerTemplate(settings: FrpSettings): string {\n return createRestrictedFrpServerTemplate(settings.serverPort, settings.token, settings.publicOrigin, {\n vhostHttpPort: resolveFrpVhostHttpPort(settings),\n entryTls: resolveFrpEntryTls(settings),\n })\n}\n\nasync function atomicPrivateWrite(file: string, body: string): Promise<void> {\n const directory = dirname(file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(file)\n if (!current.isFile() || current.isSymbolicLink()) throw new Error('frp_config_target_invalid')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, `.${basename(file)}.${randomBytes(12).toString('hex')}.tmp`)\n try {\n await writeFile(temporary, body, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, file)\n await restrictPrivateFile(file)\n } catch (error) {\n await rm(temporary, { force: true })\n throw error\n }\n}\n\n/** Owns private FRP settings and generation-specific frpc configuration. */\nexport class FrpConfigStore {\n readonly stateRoot: string\n readonly settingsFile: string\n readonly runtimeConfigFile: string\n private settingsValue: FrpSettings | undefined\n private errorCode: string | undefined\n\n constructor(stateDirectory: string) {\n if (!isAbsolute(stateDirectory)) throw new Error('frp config state directory must be absolute')\n this.stateRoot = resolve(stateDirectory)\n this.settingsFile = join(this.stateRoot, 'settings.json')\n this.runtimeConfigFile = join(this.stateRoot, 'frpc.toml')\n }\n\n /** Load private settings while rejecting links, oversized files, and unknown fields. */\n async initialize(): Promise<void> {\n let entry\n try { entry = await lstat(this.settingsFile) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\n throw error\n }\n if (!entry.isFile() || entry.isSymbolicLink() || entry.size > MAX_SETTINGS_BYTES) {\n this.errorCode = 'frp_config_invalid'\n return\n }\n await restrictPrivateFile(this.settingsFile)\n try {\n this.settingsValue = parseFrpSettings(JSON.parse(await readFile(this.settingsFile, 'utf8')) as unknown)\n this.errorCode = undefined\n } catch {\n this.settingsValue = undefined\n this.errorCode = 'frp_config_invalid'\n }\n }\n\n /**\n * Return configuration metadata without exposing the FRP token.\n *\n * The optional keys appear only when the saved configuration departs from the\n * upstream defaults, so the pre-existing field set is unchanged for every\n * legacy configuration.\n */\n status(): FrpConfigurationStatus {\n const settings = this.settingsValue\n const mode = settings === undefined ? undefined : resolveFrpMode(settings)\n const entryTls = settings === undefined ? undefined : resolveFrpEntryTls(settings)\n return Object.freeze({\n configured: settings !== undefined,\n ...(settings === undefined ? {} : {\n serverAddress: settings.serverAddress,\n serverPort: settings.serverPort,\n publicOrigin: settings.publicOrigin,\n }),\n vhostHttpPort: settings === undefined ? FRP_VHOST_HTTP_PORT : resolveFrpVhostHttpPort(settings),\n storagePath: this.stateRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n ...(mode === undefined || mode === 'deploy' ? {} : { mode }),\n ...(entryTls === undefined || entryTls === 'public-ip-cert' ? {} : { entryTls }),\n ...(settings?.publicPort === undefined ? {} : { publicPort: settings.publicPort }),\n })\n }\n\n /** Return private settings only to the provider lifecycle. */\n settings(): FrpSettings | undefined {\n return this.settingsValue\n }\n\n /** Atomically replace private FRP settings. */\n async configure(value: unknown): Promise<FrpConfigurationStatus> {\n const settings = parseFrpSettings(value)\n await atomicPrivateWrite(this.settingsFile, `${JSON.stringify(settings)}\\n`)\n await rm(this.runtimeConfigFile, { force: true })\n this.settingsValue = settings\n this.errorCode = undefined\n return this.status()\n }\n\n /** Materialize the private generation-specific frpc configuration. */\n async writeRuntimeConfig(localPort: number): Promise<string> {\n const settings = this.settingsValue\n if (settings === undefined) throw new Error('frp_config_missing')\n await atomicPrivateWrite(this.runtimeConfigFile, createFrpcToml(settings, localPort))\n return this.runtimeConfigFile\n }\n\n /** Remove only configuration files owned by the FRP provider. */\n async purge(): Promise<FrpConfigurationStatus> {\n await rm(this.stateRoot, { recursive: true, force: true })\n this.settingsValue = undefined\n this.errorCode = undefined\n return this.status()\n }\n}\n\nexport { FRP_VHOST_HTTP_PORT as DEFAULT_VHOST_HTTP_PORT }\nexport type { FrpEntryTls } from './frp-template.js'\n","import { isIP } from './ip.js'\nimport {\n createFrpcToml,\n isFrpSelfSignedIngress,\n resolveFrpMode,\n resolveFrpPublicPort,\n resolveFrpVhostHttpPort,\n type FrpSettings,\n} from './frp-config.js'\nimport {\n createCaddySite,\n FRP_CADDY_IMPORT_LINE,\n FRP_CADDY_SNIPPET_MARKER,\n FRP_CADDY_SNIPPET_PATH,\n manualIpCertificateGuide,\n} from './frp-template.js'\n\n/** Default proof path used to confirm the public entry answered from this computer. */\nexport const FRP_ATTACH_DISCOVERY_PATH = '/mobile-access/discovery'\n\n/** Declaration printed above the VPS half of an attach template. */\nexport const FRP_ATTACH_VPS_DECLARATION = '# 本模板不安装、不改动、不重启你的 frps;只新增 Caddy 片段与证书。'\n + 'frps 侧需你自行确认 vhostHTTPPort=<N> 与 proxyBindAddr=\"127.0.0.1\"。'\n\n/** Declaration printed above the local half of an attach template. */\nexport const FRP_ATTACH_LOCAL_DECLARATION = '# 本机只写入 frpc.toml 并启动 frpc:不安装、不改动本机 frps,也不改动 DSH 自身配置。'\n\n/** Declaration for the self-signed passthrough, which keeps its CA on this computer. */\nexport const FRP_ATTACH_SELF_SIGNED_DECLARATION = '# 自签穿透档不使用 Caddy 或公开证书:'\n + 'frps 只做 TCP 透传,由本机 DSH 网关终止 TLS;服务端证书会在运行时续签,CA 到期需重新配对。'\n\n/** Optional inputs for attach artefacts; every field falls back to the validated settings. */\nexport interface FrpAttachOptions {\n readonly vhostHttpPort?: number\n readonly publicPort?: number\n readonly certDir?: string\n /** Absolute path of the plugin-written frpc.toml, used for the `frpc verify` self-check. */\n readonly configFile?: string\n /** Loopback port the gateway will listen on; only a placeholder inside the copied runbook. */\n readonly localPort?: number\n /**\n * Explicit reveal of the shared token inside previews and clipboard copies.\n *\n * Defaults to `false`: every artefact the panel produces on its own stays\n * masked, because the copied text lands in the system clipboard. Only the\n * panel's dedicated \"copy the frpc.toml with its token\" action sets this, and\n * the result never reaches a stored state, a log, or localStorage.\n */\n readonly revealToken?: boolean\n}\n\n/** Fixed placeholder that replaces the shared token in every preview or copied artefact. */\nexport const FRP_ATTACH_TOKEN_PLACEHOLDER = '***'\n\n/**\n * One-line notice printed above the masked frpc.toml preview.\n *\n * It states both facts a reader cannot verify from the text itself: the real\n * file is written with 0600 permissions, and an unmasked copy would survive in\n * the system clipboard.\n */\nexport function frpAttachTokenMaskNotice(configFile: string): string {\n return `# 预览已打码:真正的 frpc.toml 已由插件以 0600 权限写入 ${configFile};`\n + '明文 token 不打码会随复制进入系统剪贴板,故此处只显示占位符。'\n}\n\n/** One-line notice printed above an explicitly revealed frpc.toml preview. */\nexport const FRP_ATTACH_TOKEN_REVEAL_NOTICE = '# 明文 token 已按你的显式操作显示:'\n + '复制后即进入系统剪贴板并长期留存,请勿粘贴到公开位置。'\n\n/** Matches the single `auth.token` assignment of a generated frpc.toml. */\nconst FRPC_TOKEN_ASSIGNMENT = /^auth\\.token\\s*=\\s*\"(?:[^\"\\\\]|\\\\.)*\"/mu\n\n/**\n * Replace the `auth.token` value of a rendered frpc.toml with a fixed placeholder.\n *\n * Only the preview and clipboard paths call this: the file written to disk keeps\n * the real token, because frpc has to read it. The mapping never fails open — a\n * token line that cannot be masked raises instead of leaking the secret.\n */\nexport function redactFrpcTomlToken(frpcToml: string): string {\n if (!/(^|\\n)auth\\.token/u.test(frpcToml)) return frpcToml\n const masked = frpcToml.replace(FRPC_TOKEN_ASSIGNMENT, `auth.token = \"${FRP_ATTACH_TOKEN_PLACEHOLDER}\"`)\n if (!masked.includes(`auth.token = \"${FRP_ATTACH_TOKEN_PLACEHOLDER}\"`)) throw new Error('frp_token_mask_failed')\n return masked\n}\n\n/**\n * The frpc.toml exactly as the panel displays or copies it.\n *\n * `options.revealToken` is the only way to obtain the plaintext variant; the\n * default is the masked text. `createFrpcToml` itself is untouched, so the file\n * the provider writes stays byte-for-byte identical to the upstream output.\n */\nexport function createFrpAttachFrpcToml(settings: FrpSettings, options: FrpAttachOptions = {}): string {\n const frpcToml = createFrpcToml(settings, attachLocalPort(options))\n return options.revealToken === true ? frpcToml : redactFrpcTomlToken(frpcToml)\n}\n\n/** The two separated halves of the attach artefact, plus their combined text. */\nexport interface FrpAttachTemplate {\n readonly mode: 'attach'\n readonly vps: string\n readonly local: string\n readonly text: string\n}\n\nconst DEFAULT_ATTACH_CONFIG_FILE = '<DSH Mobile 私有目录>/remote/frp/config/frpc.toml'\n\nfunction attachConfigFile(options: FrpAttachOptions): string {\n const file = options.configFile\n if (file === undefined || file === '') return DEFAULT_ATTACH_CONFIG_FILE\n return file\n}\n\nfunction attachLocalPort(options: FrpAttachOptions): number {\n const port = options.localPort ?? 3443\n if (!Number.isSafeInteger(port) || port < 1 || port > 65_535) throw new Error('frp_local_port_invalid')\n return port\n}\n\n/**\n * Reject every attach request that could not be carried out safely before any\n * artefact is produced. Attach never installs frps, so it must know the user's\n * real vhost port (unless the self-signed passthrough removes the vhost entirely).\n */\nexport function validateAttachSettings(settings: FrpSettings, options: FrpAttachOptions = {}): void {\n if (resolveFrpMode(settings) !== 'attach') throw new Error('frp_settings_invalid')\n if (isFrpSelfSignedIngress(settings)) {\n if (settings.mode !== 'attach') throw new Error('frp_entry_tls_invalid')\n resolveFrpPublicPort(settings)\n return\n }\n const vhostHttpPort = options.vhostHttpPort ?? settings.vhostHttpPort\n if (vhostHttpPort === undefined) throw new Error('frp_attach_mode_requires_vhost_port')\n if (!Number.isSafeInteger(vhostHttpPort) || vhostHttpPort < 1 || vhostHttpPort > 65_535) {\n throw new Error('frp_settings_invalid')\n }\n}\n\n/** Strip the leading `# ` of a guide block so it can be pasted as runnable shell. */\nfunction guideCommands(guide: string): string[] {\n return guide\n .split('\\n')\n .map(line => line.replace(/^#\\s?/u, '').trimEnd())\n .filter(line => line !== '')\n}\n\n/** Shared VPS-side inputs of both the copied runbook and the panel plan. */\nexport interface FrpAttachVpsParts {\n readonly publicHost: string\n readonly selfSigned: boolean\n readonly vhostHttpPort?: number\n readonly publicPort?: number\n readonly snippet: string\n readonly certGuide: readonly string[]\n}\n\n/**\n * Derive every VPS-side value once so the copied runbook and the panel plan can\n * never drift apart.\n */\nexport function frpAttachVpsParts(settings: FrpSettings, options: FrpAttachOptions = {}): FrpAttachVpsParts {\n validateAttachSettings(settings, options)\n const publicHost = new URL(settings.publicOrigin).hostname\n if (isFrpSelfSignedIngress(settings)) {\n return Object.freeze({\n publicHost,\n selfSigned: true,\n publicPort: resolveFrpPublicPort(settings),\n snippet: '',\n certGuide: Object.freeze([] as string[]),\n })\n }\n const vhostHttpPort = options.vhostHttpPort ?? resolveFrpVhostHttpPort(settings)\n const certDir = options.certDir\n const site = createCaddySite(publicHost, {\n vhostHttpPort,\n ...(certDir === undefined ? {} : { certDir }),\n })\n return Object.freeze({\n publicHost,\n selfSigned: false,\n vhostHttpPort,\n snippet: `${FRP_CADDY_SNIPPET_MARKER}\\n${site.trimEnd()}\\n`,\n certGuide: Object.freeze(isIP(publicHost) === 4 ? guideCommands(manualIpCertificateGuide(publicHost)) : []),\n })\n}\n\n/**\n * Build the attach artefact for an existing frps.\n *\n * The result is deliberately split in two: the VPS half contains only what the\n * user's own server needs (Caddy snippet + import + certificate guidance, or a\n * single firewall rule for the self-signed passthrough), and the local half\n * contains the generated frpc.toml plus its `frpc verify` self-check. Neither\n * half installs, rewrites, or restarts the user's frps.\n */\nexport function createFrpAttachTemplateParts(\n settings: FrpSettings,\n options: FrpAttachOptions = {},\n): FrpAttachTemplate {\n const parts = frpAttachVpsParts(settings, options)\n const configFile = attachConfigFile(options)\n const revealed = options.revealToken === true\n const frpcToml = createFrpAttachFrpcToml(settings, options)\n const local = [\n '# ---- (b) 本机侧(在这台电脑上执行)----',\n FRP_ATTACH_LOCAL_DECLARATION,\n revealed ? FRP_ATTACH_TOKEN_REVEAL_NOTICE : frpAttachTokenMaskNotice(configFile),\n `# frpc.toml 全文(插件同样会以 0600 权限写入 ${configFile})`,\n frpcToml.trimEnd(),\n '',\n '# 启动前自检:',\n `frpc verify -c \"${configFile}\"`,\n '',\n ].join('\\n')\n\n if (parts.selfSigned) {\n const publicPort = parts.publicPort ?? resolveFrpPublicPort(settings)\n const vps = [\n '# ---- (a) VPS 侧(在你的服务器上执行,共 3 项)----',\n FRP_ATTACH_SELF_SIGNED_DECLARATION,\n '#',\n '# 1) 放行公网入口端口(就是这台电脑网关的 HTTPS 入口,TCP 透传不解密):',\n `ufw allow ${String(publicPort)}/tcp`,\n `ufw status | grep ${String(publicPort)}`,\n '# 若使用 firewalld / 云厂商安全组,请放行同样的 TCP 端口。',\n '# 前提:现有 frps 的 TCP 代理已监听公网地址;若 proxyBindAddr=127.0.0.1,放行防火墙仍无法从公网访问。',\n '# 插件不会修改你现有的 frps;不满足此前提时请改选受信任证书入口。',\n '#',\n '# 2) 确认你既有的 frps 已就绪(无需修改它的配置):',\n 'systemctl is-active frps || true',\n `ss -lnt | grep -E ':(${String(settings.serverPort)}|${String(publicPort)})\\\\b' || true`,\n '#',\n '# 3) frpc 启动后,用 -k 检查连通性;此命令不验证身份,App 配对后会固定网关 CA:',\n `curl -k -sS -o /dev/null -w '%{http_code}\\\\n' https://${parts.publicHost}:${String(publicPort)}${FRP_ATTACH_DISCOVERY_PATH}`,\n '# 期望输出 200。',\n '',\n ].join('\\n')\n return Object.freeze({\n mode: 'attach' as const,\n vps,\n local,\n text: `${vps}${local}`,\n })\n }\n\n const vps = [\n '# ---- (a) VPS 侧(在你的服务器上执行)----',\n FRP_ATTACH_VPS_DECLARATION,\n '#',\n `# 1) 保存 Caddy 片段到 ${FRP_CADDY_SNIPPET_PATH}(只新增这一个文件):`,\n `cat > ${FRP_CADDY_SNIPPET_PATH} <<'DSH_MOBILE_CADDY_SNIPPET'`,\n parts.snippet.trimEnd(),\n 'DSH_MOBILE_CADDY_SNIPPET',\n '#',\n '# 2) 确认 Caddyfile 顶部含下面这一行(全局块必须在前;没有则加到文件最上方):',\n `# ${FRP_CADDY_IMPORT_LINE}`,\n 'caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy',\n '#',\n ...(parts.certGuide.length === 0\n ? ['# 3) 域名 HTTPS:确认 DNS 指向 VPS,开放 80/443;Caddy 会自动申请和续期证书。']\n : [\n '# 3) 公网 IP 的 HTTPS 证书(Caddy 不能为 IP 自动签发,故用 certbot 签发):',\n ...parts.certGuide.map(line => `# ${line}`),\n '#',\n '# 4) 确认证书续期任务,并在续期后将新证书安装到 Caddy:',\n 'systemctl list-timers certbot.timer --all || true',\n ]),\n '#',\n '# 端到端验证(公网入口为 443,与 frps 的 vhost 端口无关):',\n `curl -sS -o /dev/null -w '%{http_code}\\\\n' https://${parts.publicHost}${FRP_ATTACH_DISCOVERY_PATH}`,\n '# 期望输出 200。',\n '',\n ].join('\\n')\n return Object.freeze({\n mode: 'attach' as const,\n vps,\n local,\n text: `${vps}${local}`,\n })\n}\n\n/** Combined attach artefact, ready for the clipboard. */\nexport function createFrpAttachTemplate(settings: FrpSettings, options: FrpAttachOptions = {}): string {\n return createFrpAttachTemplateParts(settings, options).text\n}\n","import {\n resolveFrpEntryTls,\n isFrpSelfSignedIngress,\n type FrpEntryTls,\n type FrpSettings,\n} from './frp-config.js'\nimport {\n FRP_ATTACH_DISCOVERY_PATH,\n FRP_ATTACH_LOCAL_DECLARATION,\n FRP_ATTACH_SELF_SIGNED_DECLARATION,\n FRP_ATTACH_VPS_DECLARATION,\n createFrpAttachFrpcToml,\n frpAttachVpsParts,\n type FrpAttachOptions,\n} from './frp-attach.js'\nimport { FRP_CADDY_IMPORT_LINE, FRP_CADDY_SNIPPET_PATH } from './frp-template.js'\n\n/** Stable step identifiers; the panel maps each one to a translated title. */\nexport type FrpAttachStepId =\n | 'write-snippet'\n | 'add-import'\n | 'issue-ip-cert'\n | 'enable-cert-timer'\n | 'verify-https'\n | 'open-public-port'\n | 'verify-frps'\n | 'verify-entry'\n\n/** One actionable VPS-side step; `title` is a message key, `label` the copied text. */\nexport interface FrpAttachPlanStep {\n readonly id: FrpAttachStepId\n readonly title: string\n readonly label: string\n readonly commands: readonly string[]\n readonly optional: boolean\n readonly verifyHint: string\n}\n\n/** The local half of the plan: the exact frpc.toml plus its self-check command. */\nexport interface FrpAttachPlanLocal {\n /** The frpc.toml as previewed: masked unless the caller explicitly revealed the token. */\n readonly frpcToml: string\n /** True when `frpcToml` carries the fixed placeholder instead of the shared token. */\n readonly tokenMasked: boolean\n readonly verifyCommand: string\n readonly configFile: string\n readonly declaration: string\n}\n\n/** Zero-SSH plan for attaching to an frps the user already runs. */\nexport interface FrpAttachPlan {\n readonly mode: 'attach'\n readonly entryTls: FrpEntryTls\n readonly vhostHttpPort?: number\n readonly publicPort?: number\n readonly local: FrpAttachPlanLocal\n readonly vps: readonly FrpAttachPlanStep[]\n readonly warnings: readonly string[]\n}\n\nconst DEFAULT_PLAN_CONFIG_FILE = '<DSH Mobile 私有目录>/remote/frp/config/frpc.toml'\n\nfunction planConfigFile(options: FrpAttachOptions): string {\n const file = options.configFile\n return file === undefined || file === '' ? DEFAULT_PLAN_CONFIG_FILE : file\n}\n\n/**\n * Build the copy-only attach plan. It performs no network access, no SSH, and no\n * filesystem write: the caller renders or copies the steps and the local frpc\n * configuration is written by the ordinary provider lifecycle.\n */\nexport function createFrpAttachPlan(settings: FrpSettings, options: FrpAttachOptions = {}): FrpAttachPlan {\n const parts = frpAttachVpsParts(settings, options)\n const configFile = planConfigFile(options)\n const localPort = options.localPort ?? 3443\n // The plan is a preview structure: it stays masked unless the panel's explicit\n // reveal action asked for the plaintext variant.\n const frpcToml = createFrpAttachFrpcToml(settings, { ...options, localPort })\n const tokenMasked = options.revealToken !== true\n const verifyCommand = `frpc verify -c \"${configFile}\"`\n const commonWarnings: string[] = [\n '本机探测 ≠ 公网验证:请在外部(手机流量或另一台机器)再执行一次 VPS 侧清单里的 curl 验证。',\n '插件不会安装、不会改动、不会重启你既有的 frps,也不会改动 Caddyfile 里的其他内容。',\n ]\n\n if (isFrpSelfSignedIngress(settings)) {\n const publicPort = parts.publicPort ?? 0\n const publicHost = parts.publicHost\n return Object.freeze({\n mode: 'attach' as const,\n entryTls: resolveFrpEntryTls(settings),\n publicPort,\n local: Object.freeze({\n frpcToml,\n tokenMasked,\n verifyCommand,\n configFile,\n declaration: FRP_ATTACH_LOCAL_DECLARATION,\n }),\n vps: Object.freeze([\n Object.freeze({\n id: 'open-public-port' as const,\n title: 'frpAttachStepOpenPort',\n label: `放行公网入口端口 ${String(publicPort)}/tcp`,\n commands: Object.freeze([`ufw allow ${String(publicPort)}/tcp`, `ufw status | grep ${String(publicPort)}`]),\n optional: false,\n verifyHint: `ufw status 中应出现 ${String(publicPort)}/tcp ALLOW;云厂商安全组也需放行同一 TCP 端口。`,\n }),\n Object.freeze({\n id: 'verify-frps' as const,\n title: 'frpAttachStepVerifyFrps',\n label: '确认既有 frps 已就绪(不改动它的配置)',\n commands: Object.freeze([\n 'systemctl is-active frps || true',\n `ss -lnt | grep -E ':(${String(settings.serverPort)}|${String(publicPort)})\\\\b' || true`,\n ]),\n optional: false,\n verifyHint: `frps 控制端口 ${String(settings.serverPort)} 应监听;入口 ${String(publicPort)} 的 Local Address 不应是 127.0.0.1。若仅回环监听,防火墙放行也无法公网访问。`,\n }),\n Object.freeze({\n id: 'verify-entry' as const,\n title: 'frpAttachStepVerifyEntry',\n label: 'frpc 启动后端到端验证(自签证书需 -k)',\n commands: Object.freeze([\n `curl -k -sS -o /dev/null -w '%{http_code}\\\\n' https://${publicHost}:${String(publicPort)}${FRP_ATTACH_DISCOVERY_PATH}`,\n ]),\n optional: false,\n verifyHint: '期望输出 200;curl -k 仅验证连通性,不验证服务器身份;App 配对后会固定网关 CA。返回 000 时检查端口与转发。',\n }),\n ]),\n warnings: Object.freeze([\n ...commonWarnings,\n FRP_ATTACH_SELF_SIGNED_DECLARATION,\n '现有 frps 的 TCP 代理必须已监听公网地址;若 proxyBindAddr=127.0.0.1,单靠开放防火墙端口无法连通,请改选公网证书 + Caddy 模式。插件不会修改你的 frps。',\n '手机浏览器访问自签入口会提示证书不受信任 —— 请用 App 扫码配对;App 已固定网关 CA,不需要公开证书。',\n '切勿把网关 listenHost 改成 0.0.0.0:公网入口由 frps 的 TCP 代理提供,网关只监听 127.0.0.1。',\n ]),\n })\n }\n\n const vhostHttpPort = parts.vhostHttpPort ?? 0\n const publicHost = parts.publicHost\n const certGuide = parts.certGuide\n const certificateSteps: FrpAttachPlanStep[] = certGuide.length === 0 ? [] : [\n Object.freeze({\n id: 'issue-ip-cert' as const,\n title: 'frpAttachStepIssueCert',\n label: '为公网 IP 签发受信任证书(certbot)',\n commands: Object.freeze([...certGuide]),\n optional: false,\n verifyHint: `证书应落到 /etc/letsencrypt/live/${publicHost}/ 并安装到 Caddy 证书目录。`,\n }),\n Object.freeze({\n id: 'enable-cert-timer' as const,\n title: 'frpAttachStepCertTimer',\n label: '确认 certbot 续期定时器并更新 Caddy 证书文件',\n commands: Object.freeze(['systemctl list-timers certbot.timer --all || true']),\n optional: false,\n verifyHint: '公网 IP 证书有效期较短;续期后还须将新证书安装到 Caddy 并重载。',\n }),\n ]\n return Object.freeze({\n mode: 'attach' as const,\n entryTls: resolveFrpEntryTls(settings),\n vhostHttpPort,\n local: Object.freeze({\n frpcToml,\n tokenMasked,\n verifyCommand,\n configFile,\n declaration: FRP_ATTACH_LOCAL_DECLARATION,\n }),\n vps: Object.freeze([\n Object.freeze({\n id: 'write-snippet' as const,\n title: 'frpAttachStepWriteSnippet',\n label: `新增 Caddy 片段 ${FRP_CADDY_SNIPPET_PATH}`,\n commands: Object.freeze([\n `cat > ${FRP_CADDY_SNIPPET_PATH} <<'DSH_MOBILE_CADDY_SNIPPET'`,\n parts.snippet.trimEnd(),\n 'DSH_MOBILE_CADDY_SNIPPET',\n ]),\n optional: false,\n verifyHint: `片段内容里的反代目标是 127.0.0.1:${String(vhostHttpPort)},即你 frps 的 vhostHTTPPort。`,\n }),\n Object.freeze({\n id: 'add-import' as const,\n title: 'frpAttachStepAddImport',\n label: '在 Caddyfile 顶部加入 import 行并重载',\n commands: Object.freeze([\n `# 确认 /etc/caddy/Caddyfile 顶部包含:${FRP_CADDY_IMPORT_LINE}`,\n 'caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy',\n ]),\n optional: false,\n verifyHint: 'caddy validate 通过且 reload 无报错;Caddyfile 中你自己原有的站点内容保持不变。',\n }),\n ...certificateSteps,\n Object.freeze({\n id: 'verify-https' as const,\n title: 'frpAttachStepVerifyHttps',\n label: '端到端验证公网 HTTPS 入口',\n commands: Object.freeze([\n `curl -sS -o /dev/null -w '%{http_code}\\\\n' https://${publicHost}${FRP_ATTACH_DISCOVERY_PATH}`,\n ]),\n optional: true,\n verifyHint: certGuide.length === 0\n ? '期望输出 200;证书错误时检查 DNS、80/443 端口和 Caddy 日志。'\n : '期望输出 200;证书错误时检查 certbot 证书是否安装并重载 Caddy。',\n }),\n ]),\n warnings: Object.freeze([\n ...commonWarnings,\n FRP_ATTACH_VPS_DECLARATION,\n `你 frps 的明文 vhost 必须只监听 127.0.0.1(proxyBindAddr)且 vhostHTTPPort=${String(vhostHttpPort)};`\n + '插件启动前会探测该端口,若公网可达将直接拒绝启动。',\n ]),\n })\n}\n","import { X509Certificate } from 'node:crypto'\nimport { readFile } from 'node:fs/promises'\nimport { connect as connectTls } from 'node:tls'\n\n/** Days left below which the entry certificate is reported as expiring. */\nexport const CERT_EXPIRING_DAYS = 2\n\nconst DAY_MS = 24 * 60 * 60_000\n\n/** Read-only lifetime verdict for one certificate; never mutates or renews anything. */\nexport interface CertRenewalStatus {\n readonly state: 'ok' | 'expiring' | 'expired' | 'unknown'\n /** Unix milliseconds of `notAfter`. */\n readonly notAfter?: number\n readonly daysRemaining?: number\n readonly subject?: string\n readonly issuer?: string\n readonly errorCode?: string\n}\n\n/** Evaluate one `notAfter` timestamp against the current clock. */\nexport function evaluateCertificateLifetime(notAfter: number, now: number = Date.now()): CertRenewalStatus {\n if (!Number.isFinite(notAfter) || !Number.isFinite(now)) {\n return Object.freeze({ state: 'unknown' as const, errorCode: 'frp_attach_cert_unknown' })\n }\n const daysRemaining = Math.floor((notAfter - now) / DAY_MS)\n if (notAfter <= now) return Object.freeze({ state: 'expired' as const, notAfter, daysRemaining })\n if (daysRemaining <= CERT_EXPIRING_DAYS) return Object.freeze({ state: 'expiring' as const, notAfter, daysRemaining })\n return Object.freeze({ state: 'ok' as const, notAfter, daysRemaining })\n}\n\nfunction statusFromCertificate(certificate: X509Certificate, now: number): CertRenewalStatus {\n const notAfter = Date.parse(certificate.validTo)\n const lifetime = evaluateCertificateLifetime(notAfter, now)\n return Object.freeze({\n ...lifetime,\n subject: certificate.subject,\n issuer: certificate.issuer,\n })\n}\n\n/**\n * Read one PEM certificate from disk and report only its lifetime.\n *\n * A missing or unreadable file yields `unknown` with the stable\n * `frp_attach_cert_unknown` code so the panel can explain how to re-issue it.\n */\nexport async function readCertificateRenewal(file: string, now: number = Date.now()): Promise<CertRenewalStatus> {\n try {\n return statusFromCertificate(new X509Certificate(await readFile(file)), now)\n } catch {\n return Object.freeze({ state: 'unknown' as const, errorCode: 'frp_attach_cert_unknown' })\n }\n}\n\n/**\n * Ask a live TLS endpoint for its leaf certificate and report its lifetime.\n *\n * Verification is intentionally skipped: this is a read-only lifetime probe for\n * an endpoint whose trust anchor is already pinned elsewhere, and a self-signed\n * leaf is the expected answer for the passthrough entry.\n */\nexport async function probeOriginCertificate(\n host: string,\n port: number,\n timeoutMs = 5_000,\n): Promise<CertRenewalStatus> {\n if (!Number.isSafeInteger(port) || port < 1 || port > 65_535) {\n return Object.freeze({ state: 'unknown' as const, errorCode: 'frp_attach_cert_unknown' })\n }\n return new Promise<CertRenewalStatus>((resolveProbe) => {\n let finished = false\n const socket = connectTls({ host, port, rejectUnauthorized: false, servername: undefined })\n const finish = (status: CertRenewalStatus): void => {\n if (finished) return\n finished = true\n clearTimeout(timer)\n socket.destroy()\n resolveProbe(status)\n }\n const timer = setTimeout(() => { finish(Object.freeze({ state: 'unknown' as const, errorCode: 'frp_attach_cert_unknown' })) }, timeoutMs)\n timer.unref()\n socket.once('secureConnect', () => {\n const certificate = socket.getPeerX509Certificate()\n finish(certificate === undefined\n ? Object.freeze({ state: 'unknown' as const, errorCode: 'frp_attach_cert_unknown' })\n : statusFromCertificate(certificate, Date.now()))\n })\n socket.once('error', () => { finish(Object.freeze({ state: 'unknown' as const, errorCode: 'frp_attach_cert_unknown' })) })\n socket.once('close', () => { finish(Object.freeze({ state: 'unknown' as const, errorCode: 'frp_attach_cert_unknown' })) })\n })\n}","import {\n X509Certificate,\n createPrivateKey,\n createPublicKey,\n} from 'node:crypto'\nimport { execFileText as execFile } from './exec-file.js'\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises'\nimport { networkInterfaces, type NetworkInterfaceInfo } from 'node:os'\nimport { basename, dirname, join } from 'node:path'\nimport { generate } from 'selfsigned'\nimport { restrictPrivateFile } from './private-file.js'\n\n/** One active private IPv4 address tied to a stable operating-system interface name. */\nexport interface LanNetwork {\n readonly name: string\n readonly address: string\n readonly cidr: string\n}\n\n/** Versioned setup that survives DHCP address changes on the selected interface. */\nexport interface ManagedSetup {\n readonly version: 2\n readonly networkInterface: string\n readonly listenPort: number\n /** Legacy setup snapshot retained for file compatibility; the active WebServer port wins at runtime. */\n readonly upstreamOrigin: string\n readonly tls: {\n readonly mode: 'managed'\n readonly caCertFile: string\n readonly caKeyFile: string\n readonly certFile: string\n readonly keyFile: string\n }\n}\n\ntype InterfaceTable = NodeJS.Dict<NetworkInterfaceInfo[]>\ntype RouteCommand = (file: string, args: readonly string[]) => Promise<string>\n\nconst VIRTUAL_INTERFACE_MARKERS = [\n 'bridge', 'docker', 'hyper-v', 'mihomo', 'radmin', 'tailscale', 'tap', 'tun',\n 'utun', 'vbox', 'veth', 'virtual', 'vmware', 'vpn', 'vethernet', 'wsl', 'zerotier',\n]\n\n/** Route inspection is advisory; never let a system command hold setup open indefinitely. */\nconst ROUTE_COMMAND_TIMEOUT_MS = 5_000\n\nfunction requiredString(value: unknown, name: string): string {\n if (typeof value !== 'string' || value.length === 0) throw new Error(`${name} must be a non-empty string`)\n return value\n}\n\n/** Validate the durable managed setup before it controls network and filesystem operations. */\nexport function parseManagedSetup(value: unknown): ManagedSetup {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error('mobile setup file must be an object')\n }\n const record = value as Record<string, unknown>\n if (record.version !== 2 || Reflect.ownKeys(record)\n .some(key => typeof key !== 'string' || !['version', 'networkInterface', 'listenPort', 'upstreamOrigin', 'tls'].includes(key))) {\n throw new Error('mobile setup file has an unsupported format')\n }\n if (!Number.isSafeInteger(record.listenPort) || (record.listenPort as number) < 1024\n || (record.listenPort as number) > 65535) {\n throw new Error('mobile setup listenPort must be from 1024 through 65535')\n }\n if (typeof record.tls !== 'object' || record.tls === null || Array.isArray(record.tls)) {\n throw new Error('mobile setup tls must be an object')\n }\n const tls = record.tls as Record<string, unknown>\n if (tls.mode !== 'managed' || Reflect.ownKeys(tls)\n .some(key => typeof key !== 'string' || !['mode', 'caCertFile', 'caKeyFile', 'certFile', 'keyFile'].includes(key))) {\n throw new Error('mobile setup tls has an unsupported format')\n }\n return Object.freeze({\n version: 2,\n networkInterface: requiredString(record.networkInterface, 'mobile setup networkInterface'),\n listenPort: record.listenPort as number,\n upstreamOrigin: requiredString(record.upstreamOrigin, 'mobile setup upstreamOrigin'),\n tls: Object.freeze({\n mode: 'managed',\n caCertFile: requiredString(tls.caCertFile, 'mobile setup tls.caCertFile'),\n caKeyFile: requiredString(tls.caKeyFile, 'mobile setup tls.caKeyFile'),\n certFile: requiredString(tls.certFile, 'mobile setup tls.certFile'),\n keyFile: requiredString(tls.keyFile, 'mobile setup tls.keyFile'),\n }),\n })\n}\n\nfunction privateIpv4(value: string): boolean {\n const parts = value.split('.').map(Number)\n return parts.length === 4 && parts.every(part => Number.isInteger(part) && part >= 0 && part <= 255)\n && (parts[0] === 10\n || (parts[0] === 172 && parts[1]! >= 16 && parts[1]! <= 31)\n || (parts[0] === 192 && parts[1] === 168))\n}\n\nfunction networkCidr(address: string, cidr: string): string {\n const prefix = Number(cidr.slice(cidr.lastIndexOf('/') + 1))\n const value = address.split('.').reduce((total, part) => ((total << 8) | Number(part)) >>> 0, 0)\n const mask = prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0\n const network = (value & mask) >>> 0\n return `${[24, 16, 8, 0].map(shift => (network >>> shift) & 255).join('.')}/${String(prefix)}`\n}\n\n/** List current private IPv4 candidates with their interface identity. */\nexport function availableLanNetworks(table: InterfaceTable = networkInterfaces()): LanNetwork[] {\n const candidates = Object.entries(table).flatMap(([name, entries]) => (entries ?? [])\n .filter(entry => entry.family === 'IPv4' && !entry.internal && privateIpv4(entry.address) && entry.cidr !== null)\n .map(entry => ({ name, address: entry.address, cidr: networkCidr(entry.address, entry.cidr!) })))\n return [...new Map(candidates.map(entry => [`${entry.name}\\0${entry.address}`, entry])).values()]\n}\n\nfunction likelyVirtualInterface(name: string): boolean {\n const normalized = name.toLowerCase().replaceAll(/[^a-z0-9]+/gu, ' ')\n return VIRTUAL_INTERFACE_MARKERS.some(marker => normalized.includes(marker.replaceAll('-', ' ')))\n || /^(?:br|wg)\\d*\\b/u.test(normalized)\n}\n\nasync function runRouteCommand(file: string, args: readonly string[]): Promise<string> {\n const result = await execFile(file, [...args], { encoding: 'utf8', timeout: ROUTE_COMMAND_TIMEOUT_MS })\n return result.stdout\n}\n\nfunction uniqueLines(output: string): string[] {\n return [...new Set(output.split(/\\r?\\n/gu).map(line => line.trim()).filter(Boolean))]\n}\n\n/** Return operating-system default-route interfaces in routing preference order. */\nexport async function preferredLanInterfaceNames(\n platform: NodeJS.Platform = process.platform,\n run: RouteCommand = runRouteCommand,\n): Promise<string[]> {\n try {\n if (platform === 'win32') {\n const script = [\n \"$routes = Get-NetRoute -AddressFamily IPv4 -DestinationPrefix '0.0.0.0/0' -ErrorAction Stop\",\n \"$ranked = $routes | Where-Object { $_.State -eq 'Alive' -and $_.NextHop -ne '0.0.0.0' } | ForEach-Object {\",\n ' $route = $_',\n ' $adapter = Get-NetAdapter -InterfaceIndex $route.InterfaceIndex -ErrorAction SilentlyContinue',\n ' $ip = Get-NetIPInterface -AddressFamily IPv4 -InterfaceIndex $route.InterfaceIndex -ErrorAction SilentlyContinue',\n \" if ($adapter -and $ip -and $adapter.Status -eq 'Up' -and $adapter.HardwareInterface -eq $true -and $adapter.Virtual -ne $true) {\",\n ' [pscustomobject]@{ Name = $route.InterfaceAlias; Metric = [int]$route.RouteMetric + [int]$ip.InterfaceMetric }',\n ' }',\n '}',\n '$ranked | Sort-Object Metric | Select-Object -ExpandProperty Name -Unique',\n ].join('; ')\n return uniqueLines(await run('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script]))\n }\n if (platform === 'linux') {\n const routes = uniqueLines(await run('ip', ['-o', '-4', 'route', 'show', 'default']))\n .map(line => ({\n name: /(?:^|\\s)dev\\s+(\\S+)/u.exec(line)?.[1],\n metric: Number(/(?:^|\\s)metric\\s+(\\d+)/u.exec(line)?.[1] ?? 0),\n }))\n .filter((route): route is { name: string; metric: number } => route.name !== undefined\n && !likelyVirtualInterface(route.name))\n .sort((left, right) => left.metric - right.metric)\n return [...new Set(routes.map(route => route.name))]\n }\n if (platform === 'darwin') {\n const name = /^\\s*interface:\\s*(\\S+)\\s*$/mu.exec(await run('route', ['-n', 'get', 'default']))?.[1]\n return name === undefined || likelyVirtualInterface(name) ? [] : [name]\n }\n } catch {\n // Route discovery is advisory; deterministic candidate checks below remain the fallback.\n }\n return []\n}\n\n/** Select an active LAN, optionally by address or by a previously saved interface name. */\nexport function selectLanNetwork(\n requestedAddress?: string,\n requestedInterface?: string,\n table?: InterfaceTable,\n preferredInterfaces: readonly string[] = [],\n): LanNetwork {\n const candidates = availableLanNetworks(table)\n if (requestedAddress !== undefined) {\n const match = candidates.find(candidate => candidate.address === requestedAddress)\n if (match === undefined) throw new Error(`--address ${requestedAddress} is not an active private LAN address`)\n return match\n }\n if (requestedInterface !== undefined) {\n const matches = candidates.filter(candidate => candidate.name === requestedInterface)\n if (matches.length === 1) return matches[0]!\n if (matches.length === 0) {\n throw new Error(`saved LAN interface ${JSON.stringify(requestedInterface)} is not connected`)\n }\n throw new Error(`saved LAN interface ${JSON.stringify(requestedInterface)} has more than one private IPv4 address`)\n }\n if (candidates.length === 1) return candidates[0]!\n if (candidates.length === 0) throw new Error('no active private LAN address was found; connect to Wi-Fi or Ethernet')\n for (const name of preferredInterfaces) {\n const matches = candidates.filter(candidate => candidate.name === name)\n if (matches.length === 1) return matches[0]!\n }\n const physicalCandidates = candidates.filter(candidate => !likelyVirtualInterface(candidate.name))\n if (physicalCandidates.length === 1) return physicalCandidates[0]!\n throw new Error(`more than one LAN address is active; rerun with --address and one of: ${candidates.map(entry => `${entry.name}=${entry.address}`).join(', ')}`)\n}\n\n/**\n * True when the failure came from LAN selection (the configured network is\n * gone, nothing is up, or several are) rather than from config/TLS code.\n * Callers use it to degrade (warn + stay down) instead of failing boot.\n * Pinned to the selectLanNetwork message contract — keep in sync.\n */\nexport function isNetworkSelectionError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error ?? '')\n return message.includes('is not connected')\n || message.includes('no active private LAN address was found')\n || message.includes('more than one LAN address is active')\n || message.includes('has more than one private IPv4 address')\n || message.includes('is not an active private LAN address')\n}\n\nfunction assertMatchingCa(certPem: string, keyPem: string): X509Certificate {\n const certificate = new X509Certificate(certPem)\n if (!certificate.ca || certificate.subject !== certificate.issuer\n || !certificate.verify(certificate.publicKey)) {\n throw new Error('managed TLS CA must be a self-signed CA certificate')\n }\n const privatePublic = createPublicKey(createPrivateKey(keyPem)).export({ format: 'der', type: 'spki' })\n const certificatePublic = certificate.publicKey.export({ format: 'der', type: 'spki' })\n if (!privatePublic.equals(certificatePublic)) throw new Error('managed TLS CA certificate and key do not match')\n if (Date.parse(certificate.validFrom) > Date.now() || Date.parse(certificate.validTo) <= Date.now()) {\n throw new Error('managed TLS CA certificate is not currently valid')\n }\n return certificate\n}\n\n/** Read an existing managed CA without ever generating a replacement identity. */\nexport async function readManagedCa(setup: ManagedSetup['tls']): Promise<X509Certificate> {\n const [certPem, keyPem] = await Promise.all([\n readFile(setup.caCertFile, 'utf8'),\n readFile(setup.caKeyFile, 'utf8'),\n ])\n return assertMatchingCa(certPem, keyPem)\n}\n\nasync function atomicWrite(file: string, contents: string | Uint8Array): Promise<void> {\n const directory = dirname(file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n const temporary = join(directory, `.${basename(file)}.${process.pid}.tmp`)\n await writeFile(temporary, contents, { mode: 0o600 })\n await rename(temporary, file)\n await restrictPrivateFile(file)\n}\n\n/** Create a long-lived CA or migrate the legacy self-signed server certificate as that CA. */\nexport async function ensureManagedCa(\n setup: ManagedSetup['tls'],\n legacy?: { readonly certFile: string; readonly keyFile: string },\n): Promise<X509Certificate> {\n let certPem: string | undefined\n let keyPem: string | undefined\n try {\n [certPem, keyPem] = await Promise.all([readFile(setup.caCertFile, 'utf8'), readFile(setup.caKeyFile, 'utf8')])\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n let migrated = false\n if (legacy !== undefined) {\n try {\n [certPem, keyPem] = await Promise.all([readFile(legacy.certFile, 'utf8'), readFile(legacy.keyFile, 'utf8')])\n assertMatchingCa(certPem, keyPem)\n migrated = true\n } catch (legacyError) {\n if ((legacyError as NodeJS.ErrnoException).code !== 'ENOENT') throw legacyError\n }\n }\n if (!migrated) {\n const now = new Date()\n const notAfter = new Date(now)\n notAfter.setFullYear(notAfter.getFullYear() + 5)\n const generated = await generate([{ name: 'commonName', value: 'DeepSeek Harness Mobile CA' }], {\n keyType: 'ec',\n curve: 'P-256',\n algorithm: 'sha256',\n notBeforeDate: new Date(now.getTime() - 5 * 60_000),\n notAfterDate: notAfter,\n extensions: [\n { name: 'basicConstraints', cA: true, critical: true },\n { name: 'keyUsage', digitalSignature: true, keyCertSign: true, cRLSign: true, critical: true },\n ],\n })\n certPem = generated.cert\n keyPem = generated.private\n }\n if (certPem === undefined || keyPem === undefined) throw new Error('managed TLS CA creation did not produce key material')\n await Promise.all([atomicWrite(setup.caCertFile, certPem), atomicWrite(setup.caKeyFile, keyPem)])\n }\n if (certPem === undefined || keyPem === undefined) throw new Error('managed TLS CA creation did not produce key material')\n await Promise.all([restrictPrivateFile(setup.caCertFile), restrictPrivateFile(setup.caKeyFile)])\n return assertMatchingCa(certPem, keyPem)\n}\n\n/** One host a server leaf must be valid for: an IP literal, a DNS name, or both. */\nexport interface ServerCertificateTarget {\n readonly commonName: string\n readonly ipAddresses?: readonly string[]\n readonly dnsNames?: readonly string[]\n}\n\n/** Where a freshly signed server leaf is installed. */\nexport interface ServerCertificateFiles {\n readonly certFile: string\n readonly keyFile: string\n}\n\n/** CA material a leaf is signed with. */\nexport interface ServerCertificateAuthority {\n readonly caCertFile: string\n readonly caKeyFile: string\n}\n\nfunction subjectAltNames(target: ServerCertificateTarget): { type: 2 | 7; value?: string; ip?: string }[] {\n const names: { type: 2 | 7; value?: string; ip?: string }[] = []\n for (const ip of target.ipAddresses ?? []) names.push({ type: 7, ip })\n for (const dns of target.dnsNames ?? []) names.push({ type: 2, value: dns })\n if (names.length === 0) throw new Error('managed TLS leaf needs at least one server name')\n return names\n}\n\n/**\n * Sign one server leaf with an existing self-signed CA and install it privately.\n *\n * Both the LAN listener and the self-signed FRP ingress use this one path, so a\n * certificate can never be produced by two divergent code paths. IP entries are\n * required for console addresses (`type: 7`), DNS entries (`type: 2`) cover named\n * hosts; the Android client accepts either through its pinned CA.\n */\nexport async function issueServerCertificate(\n authority: ServerCertificateAuthority,\n target: ServerCertificateTarget,\n output: ServerCertificateFiles,\n lifetimeDays = 397,\n): Promise<void> {\n const altNames = subjectAltNames(target)\n await Promise.all([restrictPrivateFile(authority.caCertFile), restrictPrivateFile(authority.caKeyFile)])\n const [caCert, caKey] = await Promise.all([\n readFile(authority.caCertFile, 'utf8'),\n readFile(authority.caKeyFile, 'utf8'),\n ])\n assertMatchingCa(caCert, caKey)\n const now = new Date()\n const notAfter = new Date(now)\n notAfter.setDate(notAfter.getDate() + lifetimeDays)\n const server = await generate([{ name: 'commonName', value: target.commonName }], {\n keyType: 'ec',\n curve: 'P-256',\n algorithm: 'sha256',\n notBeforeDate: new Date(now.getTime() - 5 * 60_000),\n notAfterDate: notAfter,\n ca: { cert: caCert, key: caKey },\n extensions: [\n { name: 'basicConstraints', cA: false, critical: true },\n { name: 'keyUsage', digitalSignature: true, critical: true },\n { name: 'extKeyUsage', serverAuth: true },\n { name: 'subjectAltName', altNames },\n ],\n })\n await Promise.all([\n atomicWrite(output.certFile, server.cert),\n atomicWrite(output.keyFile, server.private),\n ])\n}\n\n/** Sign and atomically install a server leaf for the interface's current address. */\nexport async function refreshManagedServerCertificate(setup: ManagedSetup, address: string): Promise<void> {\n await issueServerCertificate(\n { caCertFile: setup.tls.caCertFile, caKeyFile: setup.tls.caKeyFile },\n { commonName: 'DeepSeek Harness Mobile', ipAddresses: [address] },\n { certFile: setup.tls.certFile, keyFile: setup.tls.keyFile },\n )\n}\n\n/** Resolve the saved interface to the ordinary gateway config consumed by the Host plugin. */\nexport async function materializeManagedSetup(\n setup: ManagedSetup,\n table?: InterfaceTable,\n): Promise<Record<string, unknown>> {\n const network = selectLanNetwork(undefined, setup.networkInterface, table)\n await refreshManagedServerCertificate(setup, network.address)\n const ca = new X509Certificate(await readFile(setup.tls.caCertFile, 'utf8'))\n return {\n publicOrigin: `https://${network.address}:${String(setup.listenPort)}`,\n listenHost: network.address,\n allowedCidrs: [network.cidr],\n instanceId: ca.fingerprint256.replaceAll(':', '').toLowerCase(),\n pairingCaFile: setup.tls.caCertFile,\n tls: {\n mode: 'provided',\n certFile: setup.tls.certFile,\n keyFile: setup.tls.keyFile,\n },\n }\n}\n","import { randomBytes, X509Certificate } from 'node:crypto'\nimport { lstat, mkdir, readFile, rename, rmdir, unlink, writeFile } from 'node:fs/promises'\nimport { basename, dirname, join } from 'node:path'\nimport { readCertificateRenewal, type CertRenewalStatus } from './cert-renewal.js'\nimport {\n frpEntryOrigin,\n isFrpSelfSignedIngress,\n resolveFrpEntryTls,\n resolveFrpMode,\n resolveFrpPublicPort,\n resolveFrpVhostHttpPort,\n type FrpSettings,\n} from './frp-config.js'\nimport { ensureManagedCa, issueServerCertificate, readManagedCa } from './managed-setup.js'\nimport { restrictPrivateFile } from './private-file.js'\n\n/** Private files owned by the self-signed FRP ingress. */\nexport interface FrpIngressPaths {\n readonly directory: string\n readonly caCertFile: string\n readonly caKeyFile: string\n readonly certFile: string\n readonly keyFile: string\n readonly statusFile: string\n}\n\n/** Signed ingress material plus the fingerprint the app must pin. */\nexport interface FrpIngressCertificate {\n readonly paths: FrpIngressPaths\n readonly ca: X509Certificate\n readonly leaf: X509Certificate\n readonly caFingerprint: string\n readonly status: CertRenewalStatus\n}\n\n/** Everything the panel needs for the on-demand self-check. */\nexport interface FrpIngressSelfCheck {\n readonly mode: 'deploy' | 'attach'\n readonly entryTls: 'public-ip-cert' | 'self-signed'\n readonly vhostHttpPort: number\n readonly publicPort: number\n readonly publicOrigin: string\n /** Reachable HTTPS entry; includes the TCP proxy port in self-signed mode. */\n readonly entryOrigin: string\n readonly serverAddress: string\n readonly serverPort: number\n readonly caFingerprint?: string\n readonly certificate?: CertRenewalStatus\n readonly caCertificate?: CertRenewalStatus\n readonly inbound: { readonly listenHost: '127.0.0.1'; readonly allowedCidrs: readonly string[] }\n}\n\n/** Private ingress directory, always a sibling of the remote device file. */\nexport function frpIngressPaths(stateFile: string): FrpIngressPaths {\n const directory = join(dirname(stateFile), 'ingress')\n return Object.freeze({\n directory,\n caCertFile: join(directory, 'ca.pem'),\n caKeyFile: join(directory, 'ca-key.pem'),\n certFile: join(directory, 'server.pem'),\n keyFile: join(directory, 'server-key.pem'),\n // Keep the identity marker outside the certificate directory so deleting\n // that directory cannot silently create a replacement CA on next start.\n statusFile: join(dirname(stateFile), 'ingress-identity.json'),\n })\n}\n\nasync function regularFileExists(file: string): Promise<boolean> {\n try {\n const entry = await lstat(file)\n if (!entry.isFile() || entry.isSymbolicLink()) throw new Error('frp_ingress_file_invalid')\n return true\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false\n throw error\n }\n}\n\nasync function readIngressIdentity(file: string): Promise<string | undefined> {\n if (!(await regularFileExists(file))) return undefined\n try {\n const parsed: unknown = JSON.parse(await readFile(file, 'utf8'))\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) throw new Error('invalid identity')\n const record = parsed as Record<string, unknown>\n if (record.version !== 1 || typeof record.caFingerprint !== 'string'\n || !/^[a-f0-9]{64}$/u.test(record.caFingerprint)) throw new Error('invalid identity')\n return record.caFingerprint\n } catch (error) {\n throw new Error('frp_ingress_ca_invalid', { cause: error })\n }\n}\n\nasync function writeIngressIdentity(file: string, fingerprint: string): Promise<void> {\n const temporary = join(dirname(file), `.${basename(file)}.${randomBytes(12).toString('hex')}.tmp`)\n try {\n await writeFile(temporary, `${JSON.stringify({ version: 1, caFingerprint: fingerprint })}\\n`, { flag: 'wx', mode: 0o600 })\n await rename(temporary, file)\n await restrictPrivateFile(file)\n } finally {\n try { await unlink(temporary) } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n }\n}\n\nfunction coversPublicIp(certificate: X509Certificate, address: string): boolean {\n const alternatives = certificate.subjectAltName ?? ''\n return alternatives.split(',').some(entry => entry.trim() === `IP Address:${address}`)\n}\n\n/**\n * Materialize the CA and leaf the gateway terminates TLS with.\n *\n * The CA remains stable for paired phones; an expired CA must be re-paired, not\n * replaced silently. The leaf is re-signed when expiring or no longer valid for\n * the public IPv4, and the running gateway reloads it after a renewal check.\n */\nexport async function ensureFrpIngressCertificate(\n settings: FrpSettings,\n stateFile: string,\n now: number = Date.now(),\n expectedCaFingerprint?: string,\n): Promise<FrpIngressCertificate> {\n if (resolveFrpMode(settings) !== 'attach' || !isFrpSelfSignedIngress(settings)) {\n throw new Error('frp_entry_tls_invalid')\n }\n const paths = frpIngressPaths(stateFile)\n try {\n const directory = await lstat(paths.directory)\n if (!directory.isDirectory() || directory.isSymbolicLink()) throw new Error('frp_ingress_directory_invalid')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const previousFingerprint = await readIngressIdentity(paths.statusFile)\n const [caCertExists, caKeyExists, leafCertExists, leafKeyExists] = await Promise.all([\n regularFileExists(paths.caCertFile), regularFileExists(paths.caKeyFile),\n regularFileExists(paths.certFile), regularFileExists(paths.keyFile),\n ])\n if (caCertExists !== caKeyExists || (!caCertExists && (previousFingerprint !== undefined || leafCertExists || leafKeyExists))) {\n throw new Error('frp_ingress_ca_invalid')\n }\n if (caCertExists && (await readCertificateRenewal(paths.caCertFile, now)).state === 'expired') {\n throw new Error('frp_ingress_ca_expired')\n }\n if (previousFingerprint === undefined) {\n await mkdir(dirname(paths.statusFile), { recursive: true, mode: 0o700 })\n try {\n await writeFile(paths.statusFile, '{\"version\":1,\"pending\":true}\\n', { flag: 'wx', mode: 0o600 })\n await restrictPrivateFile(paths.statusFile)\n } catch (error) {\n throw new Error('frp_ingress_ca_invalid', { cause: error })\n }\n }\n const caFiles = {\n mode: 'managed',\n caCertFile: paths.caCertFile,\n caKeyFile: paths.caKeyFile,\n certFile: paths.certFile,\n keyFile: paths.keyFile,\n } as const\n let ca: X509Certificate\n try {\n ca = expectedCaFingerprint === undefined ? await ensureManagedCa(caFiles) : await readManagedCa(caFiles)\n } catch (error) {\n throw new Error('frp_ingress_ca_invalid', { cause: error })\n }\n const caFingerprint = ca.fingerprint256.replaceAll(':', '').toLowerCase()\n if (previousFingerprint !== undefined && caFingerprint !== previousFingerprint) {\n throw new Error('frp_ingress_ca_changed')\n }\n if (expectedCaFingerprint !== undefined && caFingerprint !== expectedCaFingerprint) {\n throw new Error('frp_ingress_ca_changed')\n }\n if (previousFingerprint === undefined) await writeIngressIdentity(paths.statusFile, caFingerprint)\n const publicHost = new URL(settings.publicOrigin).hostname\n let leaf: X509Certificate | undefined\n try {\n leaf = new X509Certificate(await readFile(paths.certFile))\n } catch {\n leaf = undefined\n }\n const reusable = leaf !== undefined\n && Date.parse(leaf.validTo) > now + 24 * 60 * 60_000\n && coversPublicIp(leaf, publicHost)\n && leaf.verify(ca.publicKey)\n if (!reusable) {\n await issueServerCertificate(\n { caCertFile: paths.caCertFile, caKeyFile: paths.caKeyFile },\n { commonName: 'DSH Mobile FRP ingress', ipAddresses: [publicHost] },\n { certFile: paths.certFile, keyFile: paths.keyFile },\n )\n leaf = new X509Certificate(await readFile(paths.certFile))\n }\n const signed = leaf ?? (() => { throw new Error('frp_attach_cert_unknown') })()\n return Object.freeze({\n paths,\n ca,\n leaf: signed,\n caFingerprint,\n status: await readCertificateRenewal(paths.certFile, now),\n })\n}\n\n/**\n * Build the read-only self-check payload shown in the desktop panel.\n *\n * No secret ever leaves this function: the CA is reported by fingerprint only,\n * and the token is never read.\n */\nexport async function frpIngressSelfCheck(settings: FrpSettings, stateFile: string): Promise<FrpIngressSelfCheck> {\n const paths = frpIngressPaths(stateFile)\n const base = {\n mode: resolveFrpMode(settings),\n entryTls: resolveFrpEntryTls(settings),\n vhostHttpPort: resolveFrpVhostHttpPort(settings),\n publicPort: resolveFrpPublicPort(settings),\n publicOrigin: settings.publicOrigin,\n entryOrigin: frpEntryOrigin(settings),\n serverAddress: settings.serverAddress,\n serverPort: settings.serverPort,\n inbound: { listenHost: '127.0.0.1' as const, allowedCidrs: Object.freeze(['127.0.0.0/8']) },\n }\n if (!isFrpSelfSignedIngress(settings)) {\n // The public-CA entry keeps its certificate on the VPS; nothing local to read.\n return Object.freeze(base)\n }\n let caFingerprint: string | undefined\n try {\n caFingerprint = new X509Certificate(await readFile(paths.caCertFile)).fingerprint256.replaceAll(':', '').toLowerCase()\n } catch {\n caFingerprint = undefined\n }\n return Object.freeze({\n ...base,\n ...(caFingerprint === undefined ? {} : { caFingerprint }),\n certificate: await readCertificateRenewal(paths.certFile),\n caCertificate: await readCertificateRenewal(paths.caCertFile),\n })\n}\n\n/** Remove only the five files owned by the local self-signed FRP entry. */\nexport async function purgeFrpIngressCertificates(stateFile: string): Promise<void> {\n const paths = frpIngressPaths(stateFile)\n try {\n const marker = await lstat(paths.statusFile)\n if (!marker.isFile() && !marker.isSymbolicLink()) throw new Error('frp_ingress_file_invalid')\n await unlink(paths.statusFile)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n let directory\n try { directory = await lstat(paths.directory) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\n throw error\n }\n if (directory.isSymbolicLink()) {\n await unlink(paths.directory)\n return\n }\n if (!directory.isDirectory()) throw new Error('frp_ingress_directory_invalid')\n for (const file of [paths.caCertFile, paths.caKeyFile, paths.certFile, paths.keyFile]) {\n let entry\n try { entry = await lstat(file) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') continue\n throw error\n }\n if (!entry.isFile() && !entry.isSymbolicLink()) throw new Error('frp_ingress_file_invalid')\n await unlink(file)\n }\n try { await rmdir(paths.directory) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\n throw error\n }\n}\n","import { randomBytes } from 'node:crypto'\nimport type { ChildProcess } from 'node:child_process'\nimport { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, join } from 'node:path'\nimport type { MobileAccessGateway } from './gateway.js'\nimport { restrictPrivateFile } from './private-file.js'\n\n/** Remote transports supported by the desktop plugin and Android client. */\nexport type RemoteProvider = 'tailscale' | 'cpolar' | 'cloudflared' | 'frp' | 'origin'\n\n/** Common safe status returned by every remote provider controller. */\nexport interface RemoteProviderStatus {\n readonly enabled: boolean\n readonly state: string\n readonly origin?: string\n readonly backendOrigin?: string\n readonly loginUrl?: string\n readonly setupUrl?: string\n readonly errorCode?: string\n}\n\n/** Lifecycle shared by selectable remote providers. */\nexport interface RemoteProviderController {\n initialize(): Promise<void>\n gateway(): MobileAccessGateway | undefined\n status(): RemoteProviderStatus\n setEnabled(enabled: boolean): Promise<RemoteProviderStatus>\n reconnect(): Promise<RemoteProviderStatus>\n reset(): Promise<RemoteProviderStatus>\n close(): Promise<void>\n}\n\n/** Durable selection for the single active remote transport. */\nexport interface RemoteProviderState {\n readonly version: 1\n readonly provider: RemoteProvider\n}\n\nexport const REMOTE_PROVIDERS: readonly RemoteProvider[] = Object.freeze(['tailscale', 'cpolar', 'cloudflared', 'frp', 'origin'])\n\n/** Persist only the selected remote provider. */\nexport interface RemoteProviderStore {\n save(state: RemoteProviderState): Promise<void>\n}\n\nfunction aggregateErrors(errors: readonly unknown[], message: string): Error | undefined {\n if (errors.length === 0) return undefined\n if (errors.length === 1 && errors[0] instanceof Error) return errors[0]\n return new AggregateError(errors, message)\n}\n\n/** Settle independent remote cleanup work before reporting any collected failure. */\nexport async function settleRemoteResources(\n steps: readonly (() => void | Promise<void>)[],\n message = 'remote resource cleanup failed',\n): Promise<void> {\n const results = await Promise.allSettled(steps.map(async step => step()))\n const errors = results\n .filter(result => result.status === 'rejected')\n .map(result => result.reason as unknown)\n const failure = aggregateErrors(errors, message)\n if (failure !== undefined) throw failure\n}\n\n/**\n * Serialize all provider mutations and preserve the single-provider invariant.\n * Operations read the selected controller only after reaching the front of the queue.\n */\nexport class RemoteProviderCoordinator {\n private selectedValue: RemoteProvider\n private queue: Promise<void> = Promise.resolve()\n\n constructor(\n selected: RemoteProvider,\n private readonly controllers: Readonly<Record<RemoteProvider, RemoteProviderController>>,\n private readonly store: RemoteProviderStore,\n ) {\n this.selectedValue = selected\n }\n\n /** Return the durable provider currently selected by the desktop UI. */\n get selected(): RemoteProvider {\n return this.selectedValue\n }\n\n /** Return the controller selected when this method is called. */\n controller(): RemoteProviderController {\n return this.controllers[this.selectedValue]\n }\n\n /** Run a provider-owned mutation after all earlier provider work settles. */\n mutate<T>(operation: (controller: RemoteProviderController) => Promise<T>): Promise<T> {\n return this.enqueue(() => operation(this.controller()))\n }\n\n /** Disable the previous provider, persist the new selection, and retain rollback on write failure. */\n select(provider: RemoteProvider): Promise<void> {\n return this.enqueue(async () => {\n if (provider === this.selectedValue) return\n const previous = this.controllers[this.selectedValue]\n const restore = previous.status().enabled\n if (restore) await previous.setEnabled(false)\n try {\n await this.store.save({ version: 1, provider })\n this.selectedValue = provider\n } catch (error) {\n if (restore) {\n try { await previous.setEnabled(true) }\n catch (restoreError) { throw new AggregateError([error, restoreError], 'remote provider selection rollback failed') }\n }\n throw error\n }\n })\n }\n\n private enqueue<T>(operation: () => Promise<T>): Promise<T> {\n const task = this.queue.then(\n () => this.runAndEnforce(operation),\n () => this.runAndEnforce(operation),\n )\n this.queue = task.then(() => undefined, () => undefined)\n return task\n }\n\n private async runAndEnforce<T>(operation: () => Promise<T>): Promise<T> {\n let value: T | undefined\n let operationError: unknown\n try { value = await operation() } catch (error) { operationError = error }\n const results = await Promise.allSettled(\n REMOTE_PROVIDERS\n .filter(provider => provider !== this.selectedValue)\n .map(provider => this.controllers[provider].setEnabled(false)),\n )\n const errors = [\n ...(operationError === undefined ? [] : [operationError]),\n ...results.filter(result => result.status === 'rejected').map(result => result.reason as unknown),\n ]\n const failure = aggregateErrors(errors, 'remote provider operation failed')\n if (failure !== undefined) throw failure\n return value as T\n }\n}\n\n/** Stop an owned provider process and do not report completion before its close event. */\nexport async function terminateRemoteProcess(\n child: ChildProcess,\n gracefulTimeoutMs = 1_500,\n forcedTimeoutMs = 1_500,\n): Promise<void> {\n if (child.exitCode !== null || child.signalCode !== null) return\n await new Promise<void>((resolveClose, rejectClose) => {\n let gracefulTimer: NodeJS.Timeout | undefined\n let forcedTimer: NodeJS.Timeout | undefined\n let settled = false\n const finish = (error?: Error): void => {\n if (settled) return\n settled = true\n if (gracefulTimer !== undefined) clearTimeout(gracefulTimer)\n if (forcedTimer !== undefined) clearTimeout(forcedTimer)\n child.off('close', onClose)\n if (error === undefined) resolveClose()\n else rejectClose(error)\n }\n const onClose = (): void => { finish() }\n child.once('close', onClose)\n try { child.kill('SIGTERM') } catch (error) {\n finish(error instanceof Error ? error : new Error(String(error)))\n return\n }\n if (settled) return\n gracefulTimer = setTimeout(() => {\n try {\n if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL')\n } catch (error) {\n finish(error instanceof Error ? error : new Error(String(error)))\n return\n }\n if (settled) return\n forcedTimer = setTimeout(() => {\n finish(new Error('remote_process_stop_timeout'))\n }, forcedTimeoutMs)\n forcedTimer.unref()\n }, gracefulTimeoutMs)\n gracefulTimer.unref()\n })\n}\n\n/** Validate the provider selection loaded across the filesystem boundary. */\nexport function parseRemoteProviderState(value: unknown): RemoteProviderState {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error('remote provider state must be an object')\n }\n const record = value as Record<string, unknown>\n if (record.version !== 1\n || (record.provider !== 'tailscale' && record.provider !== 'cpolar' && record.provider !== 'cloudflared'\n && record.provider !== 'frp' && record.provider !== 'origin')\n || Reflect.ownKeys(record).some(key => key !== 'version' && key !== 'provider')) {\n throw new Error('remote provider state has an unsupported format')\n }\n return Object.freeze({ version: 1, provider: record.provider })\n}\n\n/** Atomic selection store whose absent-file state uses the configured default. */\nexport class JsonRemoteProviderStore {\n constructor(private readonly file: string, private readonly defaultProvider: RemoteProvider) {}\n\n async load(): Promise<RemoteProviderState> {\n let stat\n try {\n stat = await lstat(this.file)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return Object.freeze({ version: 1, provider: this.defaultProvider })\n }\n throw error\n }\n if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 4096) {\n throw new Error('remote provider state must be a regular file no larger than 4 KiB')\n }\n await restrictPrivateFile(this.file)\n let parsed: unknown\n try { parsed = JSON.parse(await readFile(this.file, 'utf8')) as unknown } catch (error) {\n throw new Error('remote provider state is not valid JSON', { cause: error })\n }\n return parseRemoteProviderState(parsed)\n }\n\n async save(state: RemoteProviderState): Promise<void> {\n const validated = parseRemoteProviderState(state)\n const directory = dirname(this.file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(this.file)\n if (!current.isFile() || current.isSymbolicLink()) {\n throw new Error('remote provider state target must remain a regular file')\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, `.${basename(this.file)}.${randomBytes(12).toString('hex')}.tmp`)\n try {\n await writeFile(temporary, `${JSON.stringify(validated)}\\n`, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, this.file)\n await restrictPrivateFile(this.file)\n } catch (error) {\n await rm(temporary, { force: true })\n throw error\n }\n }\n}\n\n/** Resolve the first-run provider without letting environment values bypass validation. */\nexport function configuredRemoteProvider(environment: NodeJS.ProcessEnv): RemoteProvider {\n const value = environment.DSH_MOBILE_REMOTE_PROVIDER ?? 'tailscale'\n if (value !== 'tailscale' && value !== 'cpolar' && value !== 'cloudflared' && value !== 'frp' && value !== 'origin') {\n throw new Error('DSH_MOBILE_REMOTE_PROVIDER must be tailscale, cpolar, cloudflared, frp, or origin')\n }\n return value\n}\n","import { spawn, execFile, type ChildProcessWithoutNullStreams } from 'node:child_process'\nimport { lstat, rm } from 'node:fs/promises'\nimport { request as httpsRequest } from 'node:https'\nimport type { ClientRequest, IncomingMessage } from 'node:http'\nimport { connect } from 'node:net'\nimport { isAbsolute } from 'node:path'\nimport { Readable } from 'node:stream'\nimport type { MobileAccessControlStore } from './control.js'\nimport type { FrpConfigStore, FrpSettings } from './frp-config.js'\nimport { frpEntryOrigin, isFrpSelfSignedIngress, resolveFrpVhostHttpPort } from './frp-config.js'\nimport type { MobileAccessGateway } from './gateway.js'\nimport { settleRemoteResources, terminateRemoteProcess, type RemoteProviderController } from './remote.js'\n\nconst START_TIMEOUT_MS = 45_000\nconst DISCOVERY_REQUEST_TIMEOUT_MS = 5_000\nconst DISCOVERY_RETRY_MS = 1_000\nconst MAX_DISCOVERY_BYTES = 16 * 1024\nconst VHOST_PROBE_TIMEOUT_MS = 1_500\nconst INGRESS_CERT_CHECK_MS = 12 * 60 * 60_000\n\n/** Product-facing states for the restricted self-hosted FRP transport. */\nexport type FrpState = 'off' | 'unavailable' | 'starting' | 'connecting' | 'ready' | 'error'\n\n/** Safe FRP state returned only through the loopback DSH control route. */\nexport interface FrpStatus {\n readonly enabled: boolean\n readonly state: FrpState\n readonly origin?: string\n readonly errorCode?: string\n}\n\n/**\n * Explicit target of the start-up discovery self-check.\n *\n * The target is derived from the effective entry, never assumed: the public-CA\n * entry answers on the saved origin (443 behind Caddy, publicly trusted chain),\n * while the self-signed entry is a raw TCP passthrough on `publicPort` whose\n * leaf chains to the plugin's own ingress CA.\n */\nexport interface FrpDiscoveryProbeTarget {\n /** Absolute HTTPS origin to dial, including the public entry port. */\n readonly origin: string\n /**\n * PEM bundle that anchors the entry leaf in place of the system trust store.\n * Absent keeps the default chain, which is correct for a publicly trusted\n * certificate; it is never a way to skip verification.\n */\n readonly trustAnchorPem?: string\n}\n\n/** Derived self-check target plus the origin the panel is told about. */\ninterface FrpDiscoveryCheck {\n /** Origin reported once the channel is ready; the actual HTTPS entry. */\n readonly publicOrigin: string\n /** Absolute origin the probe dials, including the effective entry port. */\n readonly targetOrigin: string\n /** Present only for the self-signed entry, where it must be pinned. */\n readonly trustAnchorPem?: string\n}\n\n/** Inputs for one FRP client process and authenticated DSH gateway. */\nexport interface FrpControllerOptions {\n readonly store: MobileAccessControlStore\n readonly executable: string\n readonly config: FrpConfigStore\n /**\n * Plugin-wide installation identity (the LAN pairing CA fingerprint).\n *\n * Retained for backward compatibility and constructor validation only. It no\n * longer takes part in the start-up self-check: that check compares against the\n * identity of the gateway this controller created and advertises, because the\n * self-signed FRP ingress gateway is pinned to the ingress CA fingerprint by\n * design, and that value deliberately differs from this one.\n */\n readonly instanceId: string\n readonly createGateway: (origin: string, settings: FrpSettings) => Promise<MobileAccessGateway>\n readonly onStatus?: (status: FrpStatus) => void\n readonly verifyConfig?: (executable: string, configFile: string) => Promise<void>\n readonly launchClient?: (executable: string, configFile: string) => ChildProcessWithoutNullStreams\n readonly probeVhostExposure?: (serverAddress: string, port: number) => Promise<boolean>\n readonly probeDiscovery?: (target: FrpDiscoveryProbeTarget, expectedInstanceId: string, signal: AbortSignal) => Promise<boolean>\n /**\n * Resolve the CA the self-check must pin for the self-signed entry.\n *\n * Only the composing plugin knows where the ingress material lives, so the\n * trust anchor is injected instead of guessed here. Consulted solely for the\n * self-signed passthrough; the public-CA entry always keeps the system trust\n * store. A configured resolver that cannot provide the CA fails startup with\n * `frp_ingress_ca_invalid`; the system trust store must not mask that failure.\n */\n readonly resolveDiscoveryTrustAnchor?: (settings: FrpSettings) => Promise<string | undefined>\n /** Re-issue an expiring leaf and reload the live TLS listener without changing its CA. */\n readonly maintainIngressCertificate?: (settings: FrpSettings, gateway: MobileAccessGateway) => Promise<void>\n /** Test seam for the maintenance timer; the product uses a twelve-hour interval. */\n readonly ingressCertificateCheckMs?: number\n readonly startTimeoutMs?: number\n readonly retryIntervalMs?: number\n}\n\nfunction publicStatus(status: FrpStatus): FrpStatus {\n return Object.freeze({\n enabled: status.enabled,\n state: status.state,\n ...(status.origin === undefined ? {} : { origin: status.origin }),\n ...(status.errorCode === undefined ? {} : { errorCode: status.errorCode }),\n })\n}\n\nasync function defaultVerifyConfig(executable: string, configFile: string): Promise<void> {\n await new Promise<void>((resolveRun, reject) => {\n execFile(executable, ['verify', '-c', configFile], {\n windowsHide: true,\n timeout: 30_000,\n maxBuffer: 64 * 1024,\n }, error => {\n if (error === null) resolveRun()\n else reject(error)\n })\n })\n}\n\nfunction defaultLaunchClient(executable: string, configFile: string): ChildProcessWithoutNullStreams {\n return spawn(executable, ['-c', configFile], {\n shell: false,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n })\n}\n\nasync function defaultProbeVhostExposure(serverAddress: string, port: number): Promise<boolean> {\n return new Promise<boolean>(resolveProbe => {\n const socket = connect({ host: serverAddress, port })\n let finished = false\n let received = ''\n const finish = (exposed: boolean): void => {\n if (finished) return\n finished = true\n clearTimeout(timer)\n socket.destroy()\n resolveProbe(exposed)\n }\n const timer = setTimeout(() => { finish(false) }, VHOST_PROBE_TIMEOUT_MS)\n timer.unref()\n socket.once('connect', () => {\n // A transparent proxy/TUN can acknowledge every TCP connect even when\n // the remote port is closed. Require an actual HTTP response from the\n // FRP vhost listener before treating the plaintext port as exposed.\n socket.write('GET /dsh-mobile-exposure-probe HTTP/1.1\\r\\nHost: invalid.example\\r\\nConnection: close\\r\\n\\r\\n')\n })\n socket.on('data', chunk => {\n received = `${received}${chunk.toString('latin1')}`.slice(0, 32)\n if (/^HTTP\\/1\\.[01] [1-5][0-9]{2}/u.test(received)) finish(true)\n })\n socket.once('close', () => { finish(false) })\n socket.once('error', () => { finish(false) })\n })\n}\n\nasync function boundedResponseBytes(response: Response): Promise<Uint8Array> {\n if (response.body === null) throw new Error('frp_discovery_invalid')\n const declaredLength = Number(response.headers.get('content-length'))\n if (Number.isFinite(declaredLength) && declaredLength > MAX_DISCOVERY_BYTES) throw new Error('frp_discovery_invalid')\n const reader = response.body.getReader()\n const chunks: Uint8Array[] = []\n let received = 0\n while (true) {\n const result = await reader.read()\n if (result.done) break\n received += result.value.byteLength\n if (received > MAX_DISCOVERY_BYTES) {\n await reader.cancel()\n throw new Error('frp_discovery_invalid')\n }\n chunks.push(result.value)\n }\n const bytes = new Uint8Array(received)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return bytes\n}\n\n/** Web view over an HTTPS response so the byte cap applies to both paths alike. */\nfunction discoveryResponse(message: IncomingMessage): Response {\n const headers = new Headers()\n const declaredLength = message.headers['content-length']\n if (declaredLength !== undefined) headers.set('content-length', declaredLength)\n return new Response(Readable.toWeb(message) as ReadableStream<Uint8Array>, {\n status: message.statusCode ?? 200,\n headers,\n })\n}\n\n/**\n * Perform one discovery request against the public entry.\n *\n * `node:https` carries this request instead of the global `fetch` for a single\n * reason: only here can the trust anchor be stated. The self-signed entry\n * presents a leaf the plugin's own ingress CA signed, so the default chain\n * always fails there (`UNABLE_TO_VERIFY_LEAF_SIGNATURE`) even though the\n * transport itself is healthy. The anchor is *added* to the trust store — never\n * `rejectUnauthorized: false` — so the peer must still prove its identity, and\n * the verified name stays the host of the origin, which is the name the app\n * dials as well (Node matches IP literals against `IP Address:` SANs).\n *\n * A non-200 answer resolves `undefined` for retry; discovery requires a body.\n * transport, TLS, and timeout failures reject so the caller keeps retrying.\n * The request stays cancellable until the bounded response body has completed.\n */\nfunction requestDiscovery(\n target: FrpDiscoveryProbeTarget,\n signal: AbortSignal,\n): Promise<Uint8Array | undefined> {\n if (signal.aborted) return Promise.reject(new Error('frp_discovery_aborted'))\n const url = new URL(`${target.origin}/mobile-access/discovery`)\n if (url.protocol !== 'https:' || url.hostname === '') return Promise.reject(new Error('frp_discovery_invalid'))\n return new Promise<Uint8Array | undefined>((resolveRequest, rejectRequest) => {\n let settled = false\n let request: ClientRequest | undefined\n const release = (): void => {\n clearTimeout(timeout)\n signal.removeEventListener('abort', onAbort)\n }\n const fail = (error: Error): void => {\n if (settled) return\n settled = true\n release()\n request?.destroy()\n rejectRequest(error)\n }\n const succeed = (value: Uint8Array | undefined): void => {\n if (settled) return\n settled = true\n release()\n resolveRequest(value)\n }\n const onAbort = (): void => { fail(new Error('frp_discovery_aborted')) }\n // The request timeout spans the whole exchange, body included: it is released\n // only once the response socket is done, which is exactly what the aborted\n // fetch used to do. A timeout after the headers still tears the body read\n // down, so a stalled body keeps retrying instead of hanging forever.\n const timeout = setTimeout(() => { fail(new Error('frp_discovery_timeout')) }, DISCOVERY_REQUEST_TIMEOUT_MS)\n timeout.unref()\n signal.addEventListener('abort', onAbort, { once: true })\n request = httpsRequest({\n host: url.hostname,\n port: url.port === '' ? 443 : Number(url.port),\n path: url.pathname,\n method: 'GET',\n headers: { accept: 'application/json' },\n // Adding the anchor keeps certificate verification and the hostname check\n // switched on; it only widens what counts as a trusted issuer.\n ...(target.trustAnchorPem === undefined ? {} : { ca: target.trustAnchorPem }),\n // One fresh connection per attempt: a pooled socket must never let a stale\n // handshake decide the state of a new generation.\n agent: false,\n }, message => {\n const status = message.statusCode ?? 0\n if (status !== 200) {\n succeed(undefined)\n message.destroy()\n return\n }\n void boundedResponseBytes(discoveryResponse(message)).then(succeed, fail)\n })\n request.once('error', error => {\n fail(error instanceof Error ? error : new Error('frp_discovery_request_failed'))\n })\n request.end()\n })\n}\n\n/**\n * Read the public discovery advertisement and compare it with the expected\n * identity.\n *\n * `frp_discovery_invalid` (malformed body) and `frp_discovery_mismatch` (the\n * entry advertises another identity) stay immediate failures; transport, TLS,\n * and timeout problems reject so the caller retries until its deadline.\n */\nexport async function defaultProbeDiscovery(\n target: FrpDiscoveryProbeTarget,\n expectedInstanceId: string,\n signal: AbortSignal,\n): Promise<boolean> {\n // The byte cap, transport faults, and timeout all cover the complete body;\n // a peer that sends only headers cannot leave the controller connecting forever.\n const bytes = await requestDiscovery(target, signal)\n if (bytes === undefined) return false\n let value: unknown\n try { value = JSON.parse(new TextDecoder().decode(bytes)) as unknown } catch {\n throw new Error('frp_discovery_invalid')\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('frp_discovery_invalid')\n const actual = (value as Record<string, unknown>).instanceId\n if (typeof actual !== 'string') throw new Error('frp_discovery_invalid')\n if (actual !== expectedInstanceId) throw new Error('frp_discovery_mismatch')\n return true\n}\n\n/** Owns frpc, its generation-specific configuration, and the remote gateway. */\nexport class FrpController implements RemoteProviderController {\n private enabled = false\n private initialized = false\n private disposed = false\n private child: ChildProcessWithoutNullStreams | undefined\n private gatewayValue: MobileAccessGateway | undefined\n private generation = 0\n private latest: FrpStatus = publicStatus({ enabled: false, state: 'off' })\n private queue: Promise<void> = Promise.resolve()\n private startupAbort: AbortController | undefined\n private ingressCertificateTimer: NodeJS.Timeout | undefined\n\n constructor(private readonly options: FrpControllerOptions) {\n if (!isAbsolute(options.executable)) throw new Error('frpc executable path must be absolute')\n if (!/^[a-f0-9]{64}$/u.test(options.instanceId)) throw new Error('FRP instance ID is invalid')\n if (options.ingressCertificateCheckMs !== undefined\n && (!Number.isSafeInteger(options.ingressCertificateCheckMs)\n || options.ingressCertificateCheckMs < 1 || options.ingressCertificateCheckMs > 2_147_483_647)) {\n throw new Error('frp_ingress_check_interval_invalid')\n }\n }\n\n /** Restore the remembered FRP switch without changing LAN or other providers. */\n async initialize(): Promise<void> {\n const state = await this.options.store.load()\n this.enabled = state.enabled\n this.initialized = true\n if (this.enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n }\n\n /** Return the active FRP-backed DSH gateway. */\n gateway(): MobileAccessGateway | undefined {\n return this.gatewayValue\n }\n\n /** Return state safe for the desktop control UI. */\n status(): FrpStatus {\n return publicStatus(this.latest)\n }\n\n /** Enable or disable FRP without changing LAN or another provider. */\n async setEnabled(enabled: boolean): Promise<FrpStatus> {\n if (!this.initialized || this.disposed) throw new Error('FRP controller is unavailable')\n await this.enqueue(async () => {\n if (this.enabled === enabled && (enabled === false || this.child !== undefined)) return\n if (!enabled) await this.stop()\n this.enabled = enabled\n await this.options.store.save({ version: 1, enabled })\n if (enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Restart FRP while retaining its private server settings and devices. */\n async reconnect(): Promise<FrpStatus> {\n if (!this.initialized || this.disposed) throw new Error('FRP controller is unavailable')\n await this.enqueue(async () => {\n if (!this.enabled) {\n this.enabled = true\n await this.options.store.save({ version: 1, enabled: true })\n }\n await this.stop()\n await this.start()\n })\n return this.status()\n }\n\n /** Disable FRP without deleting its explicitly managed component or settings. */\n async reset(): Promise<FrpStatus> {\n if (!this.initialized || this.disposed) throw new Error('FRP controller is unavailable')\n await this.enqueue(async () => {\n await this.stop()\n this.enabled = false\n await this.options.store.save({ version: 1, enabled: false })\n this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Stop all FRP resources without changing the remembered switch. */\n async close(): Promise<void> {\n if (this.disposed) return\n this.disposed = true\n await this.enqueue(() => this.stop())\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task\n }\n\n private publish(status: FrpStatus): void {\n this.latest = publicStatus(status)\n try { this.options.onStatus?.(this.status()) } catch { /* UI observation cannot own runtime state. */ }\n }\n\n private async start(): Promise<void> {\n const generation = ++this.generation\n let executableEntry\n try { executableEntry = await lstat(this.options.executable) } catch {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'frp_component_missing' })\n return\n }\n if (!executableEntry.isFile() || executableEntry.isSymbolicLink()) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'frp_component_invalid' })\n return\n }\n const settings = this.options.config.settings()\n if (settings === undefined) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'frp_config_missing' })\n return\n }\n const entryOrigin = frpEntryOrigin(settings)\n this.publish({ enabled: true, state: 'starting', origin: entryOrigin })\n // The plaintext-vhost gate must probe the port the user's frps actually\n // listens on. Probing the hard-coded upstream 7080 would report \"not\n // exposed\" for a reachable vhost on any other port and let a cleartext\n // session-cookie path through.\n //\n // The self-signed entry is a raw TCP passthrough with no vhost at all, so\n // the probe does not apply: there the equivalent exposure — the public entry\n // port being reachable — is the design itself, and protection comes from the\n // gateway's own device pairing and authentication.\n if (!isFrpSelfSignedIngress(settings)) {\n let exposed: boolean\n try {\n exposed = await (this.options.probeVhostExposure ?? defaultProbeVhostExposure)(\n settings.serverAddress,\n resolveFrpVhostHttpPort(settings),\n )\n } catch {\n this.publish({ enabled: true, state: 'error', origin: entryOrigin, errorCode: 'frp_vhost_probe_failed' })\n return\n }\n if (exposed) {\n this.publish({ enabled: true, state: 'error', origin: entryOrigin, errorCode: 'frp_vhost_publicly_reachable' })\n return\n }\n }\n let gateway: MobileAccessGateway\n try { gateway = await this.options.createGateway(settings.publicOrigin, settings) } catch (error) {\n // Ingress certificate problems carry their own stable code so the panel can\n // explain how to re-issue it instead of showing a generic start failure.\n const code = error instanceof Error && error.message.startsWith('frp_') ? error.message : 'gateway_start_failed'\n this.publish({ enabled: true, state: 'error', origin: entryOrigin, errorCode: code })\n return\n }\n if (generation !== this.generation || !this.enabled) {\n await gateway.close()\n return\n }\n this.gatewayValue = gateway\n let configFile: string\n try {\n configFile = await this.options.config.writeRuntimeConfig(gateway.address().port)\n await (this.options.verifyConfig ?? defaultVerifyConfig)(this.options.executable, configFile)\n } catch {\n await this.failGeneration(generation, 'frp_config_verify_failed')\n return\n }\n if (generation !== this.generation || !this.enabled) return\n let child: ChildProcessWithoutNullStreams\n try { child = (this.options.launchClient ?? defaultLaunchClient)(this.options.executable, configFile) } catch {\n await this.failGeneration(generation, 'frp_launch_failed')\n return\n }\n this.child = child\n child.stdout.resume()\n child.stderr.resume()\n child.once('error', () => { void this.enqueue(() => this.failGeneration(generation, 'frp_launch_failed')) })\n child.once('close', code => {\n if (generation !== this.generation || this.child !== child) return\n this.child = undefined\n if (this.enabled) void this.enqueue(() => this.failGeneration(generation, code === 0 ? 'frp_stopped' : 'frp_exited'))\n })\n this.publish({ enabled: true, state: 'connecting', origin: entryOrigin })\n // The probe target is derived from the effective entry *before* the abort\n // controller exists, so reading the ingress anchor can never resurrect it\n // after a concurrent stop.\n let check: FrpDiscoveryCheck\n try { check = await this.discoveryCheck(settings) } catch (error) {\n const code = error instanceof Error && error.message.startsWith('frp_')\n ? error.message : 'frp_ingress_ca_invalid'\n await this.failGeneration(generation, code)\n return\n }\n if (generation !== this.generation || !this.enabled) return\n const controller = new AbortController()\n this.startupAbort = controller\n // The self-check must compare the public advertisement against the identity\n // *this* gateway advertises, never the plugin-wide installation identity.\n // The self-signed ingress gateway is pinned to the ingress CA fingerprint\n // (`frpIngressGatewayConfig`), which is exactly what the app pins from\n // `pairingCaFile`, so the plugin identity can never match there and the\n // channel would only ever end in `frp_start_timeout`. For the public-CA entry\n // both values are equal, so that path is unchanged. `discoveryCheck` supplies\n // the other half of the contract: the port that is actually dialled and, for\n // the self-signed entry, the CA that must anchor the handshake.\n void this.waitForDiscovery(generation, check, gateway.config.instanceId, settings, gateway, controller.signal)\n }\n\n /**\n * Derive the self-check target and trust anchor for the effective entry.\n *\n * The public-CA entry is reached on the saved origin itself: Caddy terminates\n * TLS on 443 with a publicly trusted certificate, so the probe must keep the\n * system trust store and the origin URL untouched. The self-signed entry is a\n * raw TCP passthrough on `publicPort` whose leaf chains to the ingress CA the\n * app pins from `pairingCaFile`; probing `publicOrigin` there would knock on\n * 443 — where nothing listens — with a chain the system cannot verify. Both\n * facts used to be implicit, and together they kept the channel out of `ready`\n * until `frp_start_timeout`.\n */\n private async discoveryCheck(settings: FrpSettings): Promise<FrpDiscoveryCheck> {\n if (!isFrpSelfSignedIngress(settings)) {\n return { publicOrigin: settings.publicOrigin, targetOrigin: settings.publicOrigin }\n }\n const targetOrigin = frpEntryOrigin(settings)\n const trustAnchorPem = await this.options.resolveDiscoveryTrustAnchor?.(settings)\n if (this.options.resolveDiscoveryTrustAnchor !== undefined && trustAnchorPem === undefined) {\n throw new Error('frp_ingress_ca_invalid')\n }\n return Object.freeze({\n publicOrigin: targetOrigin,\n targetOrigin,\n ...(trustAnchorPem === undefined ? {} : { trustAnchorPem }),\n })\n }\n\n private async waitForDiscovery(\n generation: number,\n check: FrpDiscoveryCheck,\n advertisedInstanceId: string,\n settings: FrpSettings,\n gateway: MobileAccessGateway,\n signal: AbortSignal,\n ): Promise<void> {\n const deadline = Date.now() + (this.options.startTimeoutMs ?? START_TIMEOUT_MS)\n const probe = this.options.probeDiscovery ?? defaultProbeDiscovery\n const target: FrpDiscoveryProbeTarget = Object.freeze({\n origin: check.targetOrigin,\n ...(check.trustAnchorPem === undefined ? {} : { trustAnchorPem: check.trustAnchorPem }),\n })\n while (!signal.aborted && Date.now() < deadline) {\n try {\n if (await probe(target, advertisedInstanceId, signal)) {\n await this.enqueue(async () => {\n if (generation !== this.generation || signal.aborted || !this.enabled) return\n this.startupAbort = undefined\n this.publish({ enabled: true, state: 'ready', origin: check.publicOrigin })\n this.scheduleIngressCertificateCheck(generation, settings, gateway)\n })\n return\n }\n } catch (error) {\n if (signal.aborted) return\n if (error instanceof Error && (error.message === 'frp_discovery_mismatch' || error.message === 'frp_discovery_invalid')) {\n await this.enqueue(() => this.failGeneration(generation, error.message))\n return\n }\n }\n await new Promise<void>(resolveWait => {\n let finished = false\n const finish = (): void => {\n if (finished) return\n finished = true\n clearTimeout(timer)\n signal.removeEventListener('abort', finish)\n resolveWait()\n }\n const timer = setTimeout(finish, this.options.retryIntervalMs ?? DISCOVERY_RETRY_MS)\n timer.unref()\n signal.addEventListener('abort', finish, { once: true })\n })\n }\n if (!signal.aborted) await this.enqueue(() => this.failGeneration(generation, 'frp_start_timeout'))\n }\n\n private scheduleIngressCertificateCheck(generation: number, settings: FrpSettings, gateway: MobileAccessGateway): void {\n const maintain = this.options.maintainIngressCertificate\n if (!isFrpSelfSignedIngress(settings) || maintain === undefined) return\n const timer = setTimeout(() => {\n if (this.ingressCertificateTimer !== timer) return\n this.ingressCertificateTimer = undefined\n void this.enqueue(async () => {\n if (generation !== this.generation || !this.enabled || this.disposed || this.gatewayValue !== gateway) return\n try {\n await maintain(settings, gateway)\n } catch (error) {\n const code = error instanceof Error && error.message.startsWith('frp_')\n ? error.message : 'frp_ingress_renewal_failed'\n await this.failGeneration(generation, code)\n return\n }\n if (generation === this.generation && this.enabled && !this.disposed && this.gatewayValue === gateway) {\n this.scheduleIngressCertificateCheck(generation, settings, gateway)\n }\n }).catch(() => {\n if (generation === this.generation && this.enabled && !this.disposed) {\n this.publish({ enabled: true, state: 'error', errorCode: 'frp_ingress_renewal_failed' })\n }\n })\n }, this.options.ingressCertificateCheckMs ?? INGRESS_CERT_CHECK_MS)\n timer.unref()\n this.ingressCertificateTimer = timer\n }\n\n private async failGeneration(generation: number, code: string): Promise<void> {\n if (generation !== this.generation) return\n await this.stopProcessAndGateway()\n if (this.enabled) this.publish({ enabled: true, state: 'error', errorCode: code })\n }\n\n private async stop(): Promise<void> {\n ++this.generation\n await this.stopProcessAndGateway()\n }\n\n private async stopProcessAndGateway(): Promise<void> {\n clearTimeout(this.ingressCertificateTimer)\n this.ingressCertificateTimer = undefined\n this.startupAbort?.abort()\n this.startupAbort = undefined\n const child = this.child\n this.child = undefined\n const gateway = this.gatewayValue\n this.gatewayValue = undefined\n await settleRemoteResources([\n () => child !== undefined && child.exitCode === null ? terminateRemoteProcess(child) : undefined,\n () => gateway?.close(),\n () => rm(this.options.config.runtimeConfigFile, { force: true }),\n ], 'FRP resource cleanup failed')\n }\n}\n","import { randomBytes } from 'node:crypto'\nimport { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { isIP } from './ip.js'\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path'\nimport { addressAllowed, isGloballyRoutableIpv4, isLoopbackAddress, parseCidr } from './network.js'\nimport { restrictPrivateFile } from './private-file.js'\n\nexport const DEFAULT_ORIGIN_LISTEN_PORT = 3444\nconst MAX_SETTINGS_BYTES = 8 * 1024\n/**\n * Ports a user-space listener can actually hold on every supported platform. Below\n * this the bind needs privileges on Linux and macOS and fails with EACCES, which\n * surfaces as a generic start failure rather than a rejected choice.\n */\nconst MIN_ORIGIN_LISTEN_PORT = 1024\n/** The LAN gateway's HTTPS port, held for as long as DSH runs. */\nconst RESERVED_LAN_GATEWAY_PORT = 3443\n/** DSH's own WebServer port; the plugin cannot function while it is taken. */\nconst RESERVED_DSH_WEB_PORT = 3080\nconst PRIVATE_NETWORKS = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'].map(parseCidr)\nconst TRUSTED_NETWORKS = [...PRIVATE_NETWORKS, parseCidr('127.0.0.0/8')]\n\n/** A private HTTP listener behind a user-managed HTTPS reverse proxy. */\nexport interface OriginSettings {\n readonly version: 1\n readonly publicOrigin: string\n readonly listenHost: string\n readonly listenPort: number\n readonly allowedCidrs: readonly string[]\n}\n\n/** Configuration metadata returned only to the local desktop control UI. */\nexport interface OriginConfigurationStatus {\n readonly configured: boolean\n readonly publicOrigin?: string\n readonly listenHost?: string\n readonly listenPort?: number\n readonly allowedCidrs?: readonly string[]\n readonly backendOrigin?: string\n readonly storagePath: string\n readonly errorCode?: string\n}\n\n/** Require a public HTTPS origin; custom external ports are supported. */\nexport function validateOriginPublicOrigin(value: unknown): string {\n if (typeof value !== 'string' || value.length > 512 || /[\\s\\u0000-\\u001f\\u007f\\\\@?#]/u.test(value)) {\n throw new Error('origin_public_origin_invalid')\n }\n let url: URL\n try { url = new URL(value) } catch { throw new Error('origin_public_origin_invalid') }\n const host = url.hostname\n const validHostname = host.length <= 253 && host.includes('.')\n && host.split('.').every(label => label.length >= 1 && label.length <= 63\n && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label))\n && !['localhost', 'local', 'lan', 'home', 'internal', 'home.arpa']\n .some(suffix => host === suffix || host.endsWith('.' + suffix))\n if (url.protocol !== 'https:' || url.pathname !== '/' || url.username !== '' || url.password !== ''\n || url.search !== '' || url.hash !== '' || (url.port !== '' && Number(url.port) < 1)\n || (isIP(host) === 4 ? !isGloballyRoutableIpv4(host) : isIP(host) !== 0 || !validHostname)) {\n throw new Error('origin_public_origin_invalid')\n }\n return url.origin\n}\n\n/** Bind only one explicit loopback or RFC1918 IPv4 interface, never all interfaces. */\nexport function validateOriginListenHost(value: unknown): string {\n if (typeof value !== 'string' || isIP(value) !== 4\n || (!isLoopbackAddress(value) && !addressAllowed(value, PRIVATE_NETWORKS))) {\n throw new Error('origin_listen_host_invalid')\n }\n return value\n}\n\nexport function validateOriginListenPort(value: unknown): number {\n if (!Number.isSafeInteger(value) || Number(value) < MIN_ORIGIN_LISTEN_PORT || Number(value) > 65_535) {\n throw new Error('origin_listen_port_invalid')\n }\n // 3443 is the LAN gateway and 3080 is DSH's own WebServer. Neither is a transient\n // conflict, so both are refused as choices rather than left to fail on bind.\n if (value === RESERVED_LAN_GATEWAY_PORT || value === RESERVED_DSH_WEB_PORT) {\n throw new Error('origin_listen_port_reserved')\n }\n return Number(value)\n}\n\n/** Authorize direct proxy socket peers, not untrusted forwarded client headers. */\nexport function validateOriginAllowedCidrs(value: unknown, listenHost: string): readonly string[] {\n const input = value === undefined && isLoopbackAddress(listenHost) ? ['127.0.0.0/8'] : value\n if (!Array.isArray(input) || input.length === 0 || input.length > 16) {\n throw new Error('origin_allowed_cidrs_invalid')\n }\n const cidrs: string[] = []\n for (const entry of input) {\n if (typeof entry !== 'string' || entry.length > 64 || isIP(entry.split('/')[0] ?? '') !== 4) {\n throw new Error('origin_allowed_cidrs_invalid')\n }\n try {\n const cidr = parseCidr(entry)\n if (cidr.bits !== 32 || !TRUSTED_NETWORKS.some(range => cidr.prefix >= range.prefix\n && addressAllowed(entry.split('/')[0], [range]))) throw new Error('untrusted CIDR')\n cidrs.push(cidr.source)\n } catch { throw new Error('origin_allowed_cidrs_invalid') }\n }\n if (!isLoopbackAddress(listenHost) && !cidrs.some(cidr => !isLoopbackAddress(cidr.split('/')[0]!))) {\n throw new Error('origin_allowed_cidrs_invalid')\n }\n return Object.freeze([...new Set(cidrs)])\n}\n\n/** Validate both saved settings and local administrative requests. */\nexport function parseOriginSettings(value: unknown): OriginSettings {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('origin_settings_invalid')\n const record = value as Record<string, unknown>\n if (Reflect.ownKeys(record).some(key => !['version', 'publicOrigin', 'listenHost', 'listenPort', 'allowedCidrs'].includes(String(key)))\n || (record.version !== undefined && record.version !== 1)) throw new Error('origin_settings_invalid')\n const listenHost = validateOriginListenHost(record.listenHost === undefined ? '127.0.0.1' : record.listenHost)\n return Object.freeze({\n version: 1,\n publicOrigin: validateOriginPublicOrigin(record.publicOrigin),\n listenHost,\n listenPort: validateOriginListenPort(record.listenPort === undefined ? DEFAULT_ORIGIN_LISTEN_PORT : record.listenPort),\n allowedCidrs: validateOriginAllowedCidrs(record.allowedCidrs, listenHost),\n })\n}\n\nasync function atomicPrivateWrite(file: string, body: string): Promise<void> {\n const directory = dirname(file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(file)\n if (!current.isFile() || current.isSymbolicLink()) throw new Error('origin_config_target_invalid')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, '.' + basename(file) + '.' + randomBytes(12).toString('hex') + '.tmp')\n try {\n await writeFile(temporary, body, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, file)\n await restrictPrivateFile(file)\n } catch (error) {\n await rm(temporary, { force: true })\n throw error\n }\n}\n\n/** Owns only origin settings; shared paired-device storage is never removed. */\nexport class OriginConfigStore {\n readonly stateRoot: string\n readonly settingsFile: string\n private settingsValue: OriginSettings | undefined\n private errorCode: string | undefined\n\n constructor(stateDirectory: string) {\n if (!isAbsolute(stateDirectory)) throw new Error('origin config state directory must be absolute')\n this.stateRoot = resolve(stateDirectory)\n this.settingsFile = join(this.stateRoot, 'settings.json')\n }\n\n async initialize(): Promise<void> {\n this.settingsValue = undefined\n this.errorCode = undefined\n let entry\n try { entry = await lstat(this.settingsFile) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\n throw error\n }\n if (!entry.isFile() || entry.isSymbolicLink() || entry.size > MAX_SETTINGS_BYTES) {\n this.errorCode = 'origin_config_invalid'\n return\n }\n await restrictPrivateFile(this.settingsFile)\n try {\n this.settingsValue = parseOriginSettings(JSON.parse(await readFile(this.settingsFile, 'utf8')) as unknown)\n } catch { this.errorCode = 'origin_config_invalid' }\n }\n\n status(): OriginConfigurationStatus {\n const settings = this.settingsValue\n return Object.freeze({\n configured: settings !== undefined,\n ...(settings === undefined ? {} : {\n publicOrigin: settings.publicOrigin,\n listenHost: settings.listenHost,\n listenPort: settings.listenPort,\n allowedCidrs: settings.allowedCidrs,\n backendOrigin: 'http://' + settings.listenHost + ':' + String(settings.listenPort),\n }),\n storagePath: this.stateRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n })\n }\n\n settings(): OriginSettings | undefined { return this.settingsValue }\n\n async configure(value: unknown): Promise<OriginConfigurationStatus> {\n const settings = parseOriginSettings(value)\n await atomicPrivateWrite(this.settingsFile, JSON.stringify(settings) + '\\n')\n this.settingsValue = settings\n this.errorCode = undefined\n return this.status()\n }\n\n async purge(): Promise<OriginConfigurationStatus> {\n await rm(this.settingsFile, { force: true })\n this.settingsValue = undefined\n this.errorCode = undefined\n return this.status()\n }\n}\n","import type { MobileAccessControlStore } from './control.js'\nimport type { MobileAccessGateway } from './gateway.js'\nimport type { OriginConfigStore, OriginSettings } from './origin-proxy-config.js'\nimport type { RemoteProviderController } from './remote.js'\n\nexport type OriginState = 'off' | 'unavailable' | 'starting' | 'ready' | 'error'\n\n/** Ready means the private listener is ready, not that public ingress was tested. */\nexport interface OriginStatus {\n readonly enabled: boolean\n readonly state: OriginState\n readonly origin?: string\n readonly backendOrigin?: string\n readonly errorCode?: string\n}\n\nexport interface OriginControllerOptions {\n readonly store: MobileAccessControlStore\n readonly config: OriginConfigStore\n readonly createGateway: (settings: OriginSettings) => Promise<MobileAccessGateway>\n readonly onStatus?: (status: OriginStatus) => void\n}\n\n/** Owns an authenticated HTTP gateway, with no tunnel process or public network probes. */\nexport class OriginController implements RemoteProviderController {\n private enabled = false\n private initialized = false\n private disposed = false\n private gatewayValue: MobileAccessGateway | undefined\n private latest: OriginStatus = Object.freeze({ enabled: false, state: 'off' })\n private queue: Promise<void> = Promise.resolve()\n\n constructor(private readonly options: OriginControllerOptions) {}\n\n async initialize(): Promise<void> {\n await this.enqueue(async () => {\n if (this.disposed) throw new Error('origin_controller_unavailable')\n if (this.initialized) return\n this.enabled = (await this.options.store.load()).enabled\n this.initialized = true\n if (this.enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n })\n }\n\n gateway(): MobileAccessGateway | undefined { return this.gatewayValue }\n status(): OriginStatus { return this.latest }\n\n async setEnabled(enabled: boolean): Promise<OriginStatus> {\n this.assertAvailable()\n await this.enqueue(async () => {\n this.assertAvailable()\n if (this.enabled === enabled && (!enabled || this.gatewayValue !== undefined)) return\n if (!enabled) await this.stop()\n await this.options.store.save({ version: 1, enabled })\n this.enabled = enabled\n if (enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n async reconnect(): Promise<OriginStatus> {\n this.assertAvailable()\n await this.enqueue(async () => {\n this.assertAvailable()\n if (!this.enabled) {\n await this.options.store.save({ version: 1, enabled: true })\n this.enabled = true\n }\n await this.stop()\n await this.start()\n })\n return this.status()\n }\n\n /** Reset the switch, retaining both settings and shared remote device pairings. */\n async reset(): Promise<OriginStatus> { return this.setEnabled(false) }\n\n /** Stop the listener without changing the durable switch. */\n async close(): Promise<void> {\n this.disposed = true\n await this.enqueue(async () => {\n await this.stop()\n this.publish({ enabled: this.enabled, state: 'off' })\n })\n }\n\n private assertAvailable(): void {\n if (!this.initialized || this.disposed) throw new Error('origin_controller_unavailable')\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task\n }\n\n private publish(status: OriginStatus): void {\n this.latest = Object.freeze({ ...status })\n try { this.options.onStatus?.(this.latest) } catch { /* Observers do not own the listener. */ }\n }\n\n private async start(): Promise<void> {\n const settings = this.options.config.settings()\n if (settings === undefined) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: this.options.config.status().errorCode ?? 'origin_config_missing' })\n return\n }\n this.publish({ enabled: true, state: 'starting', origin: settings.publicOrigin })\n let gateway: MobileAccessGateway\n try { gateway = await this.options.createGateway(settings) } catch (error) {\n const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined\n this.publish({\n enabled: true, state: 'error', origin: settings.publicOrigin,\n errorCode: code === 'EADDRINUSE' ? 'origin_listen_port_in_use'\n : code === 'EADDRNOTAVAIL' ? 'origin_listen_address_unavailable' : 'origin_gateway_start_failed',\n })\n return\n }\n this.gatewayValue = gateway\n if (this.disposed) {\n await this.stop()\n return\n }\n this.publish({\n enabled: true, state: 'ready', origin: settings.publicOrigin,\n backendOrigin: 'http://' + settings.listenHost + ':' + String(gateway.address().port),\n })\n }\n\n private async stop(): Promise<void> {\n const gateway = this.gatewayValue\n if (gateway === undefined) return\n // Retain ownership on close failure so a later stop can retry cleanup.\n await gateway.close()\n this.gatewayValue = undefined\n this.publish({ enabled: this.enabled, state: 'off' })\n }\n}\n","/**\n * Hosts a pinned GitHub release download may redirect to.\n *\n * `github.com/<owner>/<repo>/releases/download/<tag>/<asset>` does not serve the bytes itself: it\n * answers 302 with a signed URL on a release-asset host. A fetch configured with\n * `redirect: 'error'` fails that hop as `TypeError: fetch failed` (cause: `unexpected redirect`)\n * before a single byte is transferred, which is exactly how the cloudflared component could never\n * install. Following one validated hop keeps the origin restriction while allowing the shape\n * every GitHub release download uses.\n */\nconst RELEASE_ASSET_HOSTS: readonly string[] = Object.freeze([\n 'release-assets.githubusercontent.com',\n 'objects.githubusercontent.com',\n])\n\n/** Input for {@link downloadPinnedArtifact}. */\nexport interface PinnedDownloadRequest {\n /** Absolute URL of the pinned artifact. */\n readonly url: string\n /** Exact byte length the artifact must have. */\n readonly expectedBytes: number\n /** Stable error-code prefix owned by the calling component, such as `cloudflared`. */\n readonly errorPrefix: string\n readonly signal: AbortSignal\n /** Hosts a single redirect hop may target; defaults to the GitHub release asset hosts. */\n readonly redirectHosts?: readonly string[]\n}\n\n/** Follow one redirect hop after checking its scheme and host. A second hop is refused. */\nasync function followValidatedRedirect(response: Response, request: PinnedDownloadRequest): Promise<Response> {\n const location = response.headers.get('location')\n if (location === null) throw new Error(`${request.errorPrefix}_download_redirect_missing`)\n let target: URL\n try {\n target = new URL(location, request.url)\n } catch {\n throw new Error(`${request.errorPrefix}_download_redirect_invalid`)\n }\n const hosts = request.redirectHosts ?? RELEASE_ASSET_HOSTS\n if (target.protocol !== 'https:' || !hosts.includes(target.hostname)) {\n throw new Error(`${request.errorPrefix}_download_redirect_rejected`)\n }\n return fetch(target, { redirect: 'error', signal: request.signal })\n}\n\n/** Attempts per install. A 55 MB transfer through a TUN proxy can reset mid-stream: the first\n * real run in the development environment failed after 1.5 MB and succeeded on the next attempt,\n * which is exactly the confusing \"nothing happened\" the user saw. */\nconst DOWNLOAD_ATTEMPTS = 2\nconst RETRY_DELAY_MS = 750\n\n/** Whether a failure is a transport problem worth a second attempt rather than a verdict. */\nfunction isRetryable(error: unknown): boolean {\n // undici reports network faults as TypeError('fetch failed'), and a 5xx is worth one more try.\n if (error instanceof TypeError) return true\n if (error instanceof Error && /_download_http_5\\d\\d$/u.test(error.message)) return true\n return false\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise(resolve => { setTimeout(resolve, ms) })\n}\n\nasync function attemptDownload(request: PinnedDownloadRequest): Promise<Uint8Array> {\n const first = await fetch(request.url, { redirect: 'manual', signal: request.signal })\n const redirected = first.status >= 300 && first.status < 400\n const response = redirected ? await followValidatedRedirect(first, request) : first\n if (!response.ok) throw new Error(`${request.errorPrefix}_download_http_${String(response.status)}`)\n const contentLength = response.headers.get('content-length')\n if (contentLength !== null && (!/^\\d+$/u.test(contentLength) || Number(contentLength) !== request.expectedBytes)) {\n throw new Error(`${request.errorPrefix}_download_size_mismatch`)\n }\n if (response.body === null) throw new Error(`${request.errorPrefix}_download_size_mismatch`)\n const bytes = new Uint8Array(request.expectedBytes)\n const reader = response.body.getReader()\n let received = 0\n while (true) {\n const chunk = await reader.read()\n if (chunk.done) break\n if (chunk.value.byteLength > bytes.byteLength - received) {\n await reader.cancel()\n throw new Error(`${request.errorPrefix}_download_size_mismatch`)\n }\n bytes.set(chunk.value, received)\n received += chunk.value.byteLength\n }\n if (received !== request.expectedBytes) throw new Error(`${request.errorPrefix}_download_size_mismatch`)\n return bytes\n}\n\n/**\n * Download a pinned artifact, following at most one validated redirect, and check its exact length.\n * A transport failure is retried once; a length or redirect verdict is not. The caller still\n * verifies the SHA-256 before publishing anything.\n * @param request - the pinned URL, its exact byte length and the caller's error prefix.\n * @returns the artifact bytes.\n */\nexport async function downloadPinnedArtifact(request: PinnedDownloadRequest): Promise<Uint8Array> {\n let lastError: unknown\n for (let attempt = 1; attempt <= DOWNLOAD_ATTEMPTS; attempt += 1) {\n try {\n return await attemptDownload(request)\n } catch (error) {\n lastError = error\n if (attempt === DOWNLOAD_ATTEMPTS || !isRetryable(error)) throw error\n await delay(RETRY_DELAY_MS)\n }\n }\n throw lastError\n}\n","import { createHash, randomBytes } from 'node:crypto'\nimport {\n chmod,\n copyFile,\n lstat,\n mkdir,\n mkdtemp,\n readFile,\n rename,\n rm,\n writeFile,\n} from 'node:fs/promises'\nimport { isAbsolute, join, relative, resolve } from 'node:path'\nimport { downloadPinnedArtifact } from './component-download.js'\n\n/**\n * Pinned cloudflared components fetched only after an explicit user action.\n *\n * Unlike the cpolar archive each artifact IS the executable: there is nothing\n * to unpack, so the download digest and the installed digest are the same pair.\n * `--no-autoupdate` is passed at runtime as well, so the pinned bytes stay the\n * bytes that were verified.\n */\ninterface CloudflaredArtifact {\n readonly version: string\n readonly platform: NodeJS.Platform\n readonly arch: string\n readonly downloadUrl: string\n readonly downloadBytes: number\n readonly downloadSha256: string\n readonly executableName: string\n}\n\nconst CLOUDFLARED_VERSION = '2026.9.1'\n\nconst releases = [\n {\n version: CLOUDFLARED_VERSION,\n platform: 'win32',\n arch: 'x64',\n downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-windows-amd64.exe`,\n downloadBytes: 54_976_432,\n downloadSha256: '2837888cc0f5d58f15b6dc478376de90b4d3ba5241c7947455d1e0a0df429712',\n executableName: 'cloudflared.exe',\n },\n {\n version: CLOUDFLARED_VERSION,\n platform: 'linux',\n arch: 'x64',\n downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64`,\n downloadBytes: 39_838_488,\n downloadSha256: '03f1f25d1cc93b9ad6c60569d44060bc4f17ed97075760ed8cfca4b12dcd68cc',\n executableName: 'cloudflared',\n },\n {\n version: CLOUDFLARED_VERSION,\n platform: 'linux',\n arch: 'arm64',\n downloadUrl: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64`,\n downloadBytes: 37_466_252,\n downloadSha256: '3d97437c71848bd8df68041e12436b484a661d95073ea1937f01a845ce88faa3',\n executableName: 'cloudflared',\n },\n] as const satisfies readonly CloudflaredArtifact[]\n\n/** Pinned official cloudflared release metadata for supported desktop targets. */\nexport const CLOUDFLARED_COMPONENT_RELEASES: Readonly<Record<string, CloudflaredArtifact>> = Object.freeze(Object.fromEntries(\n releases.map(release => [`${release.platform}-${release.arch}`, Object.freeze(release)]),\n))\n\n/**\n * Canonical release metadata. New code should select from\n * {@link CLOUDFLARED_COMPONENT_RELEASES} by platform and architecture; this\n * alias preserves the original Windows x64 entry for existing callers.\n */\nexport const CLOUDFLARED_COMPONENT_RELEASE = CLOUDFLARED_COMPONENT_RELEASES['win32-x64'] as CloudflaredArtifact\n\nconst DOWNLOAD_PAGE = 'https://github.com/cloudflare/cloudflared/releases'\nconst TERMS_URL = 'https://www.cloudflare.com/website-terms/'\n\n/**\n * Public, credential-free description of the managed cloudflared component.\n *\n * There is deliberately no `configured` flag: a quick tunnel needs no account,\n * token, or DNS record, so installation is the only precondition.\n */\nexport interface CloudflaredComponentStatus {\n readonly supported: boolean\n readonly installed: boolean\n readonly version: string\n readonly downloadBytes: number\n readonly installedBytes: number\n readonly sourceUrl: string\n readonly downloadPage: string\n readonly termsUrl: string\n readonly storagePath: string\n readonly errorCode?: string\n}\n\ninterface CloudflaredComponentManagerOptions {\n readonly stateDirectory: string\n readonly platform?: NodeJS.Platform\n readonly arch?: string\n readonly fetchArtifact?: (url: string, signal: AbortSignal) => Promise<Uint8Array>\n}\n\nfunction inside(parent: string, child: string): boolean {\n const candidate = relative(parent, child)\n return candidate !== '' && !candidate.startsWith('..') && !isAbsolute(candidate)\n}\n\nasync function sha256(file: string): Promise<string> {\n return createHash('sha256').update(await readFile(file)).digest('hex')\n}\n\nasync function regularFile(file: string, expectedBytes?: number): Promise<boolean> {\n try {\n const stat = await lstat(file)\n return stat.isFile() && !stat.isSymbolicLink() && (expectedBytes === undefined || stat.size === expectedBytes)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false\n throw error\n }\n}\n\nasync function defaultFetchArtifact(\n url: string,\n signal: AbortSignal,\n expectedBytes: number,\n): Promise<Uint8Array> {\n return downloadPinnedArtifact({\n url,\n expectedBytes,\n errorPrefix: 'cloudflared',\n signal,\n })\n}\n\n/** Select the pinned artifact for one host, or undefined where unsupported. */\nfunction lookupRelease(platform: NodeJS.Platform, arch: string): CloudflaredArtifact | undefined {\n return CLOUDFLARED_COMPONENT_RELEASES[`${platform}-${arch}`]\n}\n\n/** Owns the optional cloudflared binary inside DSH Mobile state. */\nexport class CloudflaredComponentManager {\n readonly executable: string\n readonly componentRoot: string\n readonly componentStorage: string\n readonly stateRoot: string\n readonly logRoot: string\n private readonly stagingRoot: string\n private readonly platform: NodeJS.Platform\n private readonly arch: string\n private readonly release: CloudflaredArtifact | undefined\n private readonly fetchArtifact: (url: string, signal: AbortSignal) => Promise<Uint8Array>\n private installed = false\n private errorCode: string | undefined\n private queue: Promise<void> = Promise.resolve()\n\n constructor(options: CloudflaredComponentManagerOptions) {\n const stateDirectory = resolve(options.stateDirectory)\n if (!isAbsolute(stateDirectory)) throw new Error('cloudflared state directory must be absolute')\n this.platform = options.platform ?? process.platform\n this.arch = options.arch ?? process.arch\n this.release = lookupRelease(this.platform, this.arch)\n this.componentRoot = join(stateDirectory, 'components', 'cloudflared')\n this.componentStorage = join(this.componentRoot, this.release?.version ?? CLOUDFLARED_VERSION)\n this.executable = join(this.componentStorage, this.release?.executableName ?? 'cloudflared')\n this.stateRoot = join(stateDirectory, 'state', 'cloudflared')\n this.logRoot = join(stateDirectory, 'logs', 'cloudflared')\n this.stagingRoot = join(stateDirectory, 'staging', 'cloudflared')\n for (const child of [this.componentRoot, this.componentStorage, this.stateRoot, this.logRoot, this.stagingRoot]) {\n if (!inside(stateDirectory, child)) throw new Error('cloudflared component path escaped its state directory')\n }\n const release = this.release\n this.fetchArtifact = options.fetchArtifact\n ?? ((url, signal) => defaultFetchArtifact(url, signal, release?.downloadBytes ?? 0))\n }\n\n /** Inspect the managed binary without using any global cloudflared state. */\n async initialize(): Promise<void> {\n const release = this.release\n this.installed = release !== undefined && await regularFile(this.executable, release.downloadBytes)\n if (this.installed && release !== undefined && await sha256(this.executable) !== release.downloadSha256) {\n this.installed = false\n this.errorCode = 'cloudflared_component_invalid'\n }\n }\n\n /** Return a safe status that never includes machine-specific account data. */\n status(): CloudflaredComponentStatus {\n // Unsupported hosts still report the canonical entry so the panel can show\n // what would be installed elsewhere; `supported` carries the actual gate.\n const release = this.release ?? CLOUDFLARED_COMPONENT_RELEASE\n return Object.freeze({\n supported: this.release !== undefined,\n installed: this.installed,\n version: release.version,\n downloadBytes: release.downloadBytes,\n installedBytes: release.downloadBytes,\n sourceUrl: release.downloadUrl,\n downloadPage: DOWNLOAD_PAGE,\n termsUrl: TERMS_URL,\n storagePath: this.componentRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n })\n }\n\n /** Download, verify, and install the pinned cloudflared executable after explicit confirmation. */\n install(): Promise<CloudflaredComponentStatus> {\n return this.enqueue(async () => {\n const release = this.release\n if (release === undefined) throw new Error('cloudflared_component_unsupported')\n await mkdir(this.stagingRoot, { recursive: true, mode: 0o700 })\n const staging = await mkdtemp(join(this.stagingRoot, 'install-'))\n try {\n const controller = new AbortController()\n // The pinned artifact is tens of megabytes, so the transfer budget is\n // larger than a control handshake: a slow but working link must not\n // fail the install.\n const timeout = setTimeout(() => { controller.abort() }, 300_000)\n timeout.unref()\n let bytes: Uint8Array\n try { bytes = await this.fetchArtifact(release.downloadUrl, controller.signal) } finally { clearTimeout(timeout) }\n if (bytes.byteLength !== release.downloadBytes) {\n throw new Error('cloudflared_download_size_mismatch')\n }\n const digest = createHash('sha256').update(bytes).digest('hex')\n if (digest !== release.downloadSha256) throw new Error('cloudflared_download_hash_mismatch')\n const staged = join(staging, release.executableName)\n await writeFile(staged, bytes, { flag: 'wx', mode: 0o600 })\n await chmod(staged, 0o700)\n if (!await regularFile(staged, release.downloadBytes)\n || await sha256(staged) !== release.downloadSha256) {\n throw new Error('cloudflared_executable_hash_mismatch')\n }\n const candidate = join(this.componentRoot, `.install-${randomBytes(12).toString('hex')}`)\n await mkdir(candidate, { recursive: true, mode: 0o700 })\n await copyFile(staged, join(candidate, release.executableName))\n await chmod(join(candidate, release.executableName), 0o700)\n await rm(this.componentStorage, { recursive: true, force: true })\n await rename(candidate, this.componentStorage)\n this.installed = true\n this.errorCode = undefined\n } finally {\n await rm(staging, { recursive: true, force: true })\n }\n })\n }\n\n /** Remove every cloudflared file owned by DSH Mobile without touching global state. */\n purge(): Promise<CloudflaredComponentStatus> {\n return this.enqueue(async () => {\n await Promise.all([\n rm(this.componentRoot, { recursive: true, force: true }),\n rm(this.stateRoot, { recursive: true, force: true }),\n rm(this.logRoot, { recursive: true, force: true }),\n rm(this.stagingRoot, { recursive: true, force: true }),\n ])\n this.installed = false\n this.errorCode = undefined\n })\n }\n\n private enqueue(operation: () => Promise<void>): Promise<CloudflaredComponentStatus> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task.then(() => this.status())\n }\n}\n","import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'\nimport { lstat } from 'node:fs/promises'\nimport { createServer, type Server } from 'node:net'\nimport { isAbsolute } from 'node:path'\nimport type { CloudflaredTunnelMode, CloudflaredTunnelSettings } from './cloudflared-tunnel.js'\nimport type { MobileAccessControlStore } from './control.js'\nimport type { MobileAccessGateway } from './gateway.js'\nimport { settleRemoteResources, terminateRemoteProcess, type RemoteProviderController } from './remote.js'\n\nconst MAX_LOG_BUFFER_BYTES = 64 * 1024\n/**\n * A quick tunnel has to be allocated and registered with the Cloudflare edge\n * before the public hostname appears. That is slower than a cpolar control\n * handshake but still bounded; 60 s keeps a genuinely stalled client from\n * looking healthy for longer than a user will wait.\n */\nconst START_TIMEOUT_MS = 60_000\n/**\n * How many startup-timeout rounds a named tunnel may wait through while its\n * connector process is still alive. A rebooted PC often autostarts DSH before\n * Wi-Fi / VPN / DNS is usable; cloudflared retries the edge on its own, so\n * killing it after a single 60 s window turns a slow boot into a manual\n * reconnect loop. An exited connector (bad token, bad config) still fails fast\n * on the first round.\n */\nconst NAMED_STARTUP_ROUNDS = 5\nconst CLOUDFLARED_HOST_SUFFIX = '.trycloudflare.com'\n\n/**\n * Subdomains Cloudflare reserves for its own control plane.\n *\n * The quick-tunnel banner prints `https://api.trycloudflare.com` (the registration API) before it\n * prints the tunnel's own hostname, and that address satisfies a bare suffix check. Adopting it\n * would put the API host in the pairing QR code, where the phone gets `{\"code\":10005\",\"message\":\n * \"Method Not Allowed\"}` instead of DSH. A quick tunnel is always one random label under the\n * suffix, so the reserved single labels are refused outright.\n */\nconst RESERVED_CLOUDFLARED_LABELS: readonly string[] = Object.freeze(['api', 'www', 'update', 'login'])\n\n/** Product-facing states for the optional cloudflared remote transport. */\nexport type CloudflaredState = 'off' | 'unavailable' | 'starting' | 'connecting' | 'ready' | 'error'\n\n/** Safe cloudflared state returned only through the loopback DSH control route. */\nexport interface CloudflaredStatus {\n readonly enabled: boolean\n readonly state: CloudflaredState\n readonly origin?: string\n readonly errorCode?: string\n}\n\n/** Inputs for one cloudflared process and its authenticated DSH gateway. */\nexport interface CloudflaredControllerOptions {\n readonly store: MobileAccessControlStore\n readonly executable: string\n /** Named-tunnel configuration; quick tunnels are used when it reports quick mode. */\n readonly tunnel: { settings(): CloudflaredTunnelSettings }\n readonly createGateway: (origin: string, listenPort: number) => Promise<MobileAccessGateway>\n readonly onStatus?: (status: CloudflaredStatus) => void\n /** Liveness bound for one startup-timeout round; defaults to the product timeout. */\n readonly startupTimeoutMs?: number\n /**\n * Total startup-timeout rounds before a live named connector is given up on;\n * defaults to NAMED_STARTUP_ROUNDS. Quick tunnels always use a single round.\n */\n readonly maxStartupRounds?: number\n readonly spawnProcess?: (\n executable: string,\n args: readonly string[],\n environment: NodeJS.ProcessEnv,\n ) => ChildProcessWithoutNullStreams\n}\n\ninterface PortReservation {\n readonly port: number\n readonly release: () => Promise<void>\n}\n\nfunction publicStatus(status: CloudflaredStatus): CloudflaredStatus {\n return Object.freeze({\n enabled: status.enabled,\n state: status.state,\n ...(status.origin === undefined ? {} : { origin: status.origin }),\n ...(status.errorCode === undefined ? {} : { errorCode: status.errorCode }),\n })\n}\n\n/**\n * Whether a hostname is a quick-tunnel host: exactly one random label under the provider suffix,\n * never one of the reserved control-plane labels.\n */\nfunction isCloudflaredHost(hostname: string): boolean {\n const lower = hostname.toLowerCase()\n if (!lower.endsWith(CLOUDFLARED_HOST_SUFFIX)) return false\n const label = lower.slice(0, -CLOUDFLARED_HOST_SUFFIX.length)\n return label !== '' && !label.includes('.') && !RESERVED_CLOUDFLARED_LABELS.includes(label)\n}\n\n/**\n * Extract a validated public HTTPS origin from one cloudflared output line.\n *\n * Quick tunnels print the public hostname inside a decorated banner, so the\n * candidate is taken from the line and then re-parsed as a URL: a lookalike such\n * as `https://x.trycloudflare.com.evil.test` fails the hostname check instead of\n * being truncated into an accepted origin.\n */\nexport function parseCloudflaredOrigin(line: string): string | undefined {\n if (!line.toLowerCase().includes('trycloudflare.com')) return undefined\n const match = /https?:\\/\\/[^\\s\"'|<>]+/u.exec(line)\n // A log line that merely mentions the provider suffix carries no origin, so it\n // is ignored. Only a URL that is present but fails validation is a protocol\n // violation worth failing the generation for.\n if (match === null) return undefined\n // Banner decoration can leave trailing punctuation attached to the hostname.\n const candidate = match[0].replace(/[),.;:]+$/u, '')\n let url: URL\n try { url = new URL(candidate) } catch { throw new Error('invalid_cloudflared_origin') }\n if (url.protocol !== 'https:' || url.port !== '' || !isCloudflaredHost(url.hostname)\n || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '') throw new Error('invalid_cloudflared_origin')\n return url.origin\n}\n\n/**\n * Whether one cloudflared log line reports an established edge connection.\n *\n * A named tunnel prints no banner, so registration is the only signal that the\n * connector reached Cloudflare and the public hostname can serve traffic. Both\n * the current and the older wording are accepted; anything else is ignored so a\n * chatty log line cannot be mistaken for readiness.\n */\nexport function isCloudflaredRegistration(line: string): boolean {\n return /registered tunnel connection/iu.test(line)\n || /connection\\s+[0-9a-f][0-9a-f-]{7,}\\s+registered/iu.test(line)\n}\n\nasync function reserveLoopbackPort(requestedPort?: number): Promise<PortReservation> {\n const server: Server = createServer(socket => { socket.destroy() })\n await new Promise<void>((resolveListen, reject) => {\n server.once('error', reject)\n server.listen(requestedPort ?? 0, '127.0.0.1', () => {\n server.off('error', reject)\n resolveListen()\n })\n })\n const address = server.address()\n if (address === null || typeof address === 'string') {\n server.close()\n throw new Error('cloudflared_port_reservation_failed')\n }\n let released = false\n return {\n port: address.port,\n release: async () => {\n if (released) return\n released = true\n await new Promise<void>(resolveClose => { server.close(() => resolveClose()) })\n },\n }\n}\n\nfunction spawnCloudflaredProcess(\n executable: string,\n args: readonly string[],\n environment: NodeJS.ProcessEnv,\n): ChildProcessWithoutNullStreams {\n return spawn(executable, [...args], {\n env: environment,\n shell: false,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n })\n}\n\nfunction withoutProxyEnvironment(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const blocked = new Set(['HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'NO_PROXY'])\n return Object.fromEntries(Object.entries(environment).filter(([name]) => !blocked.has(name.toUpperCase())))\n}\n\n/**\n * Owns an installed cloudflared client and a provider-specific DSH remote gateway.\n *\n * Quick tunnels allocate a random hostname and a random forward port on every\n * start. Named tunnels instead read an account token and a stable public\n * hostname from the tunnel store, forward to the configured loopback port, and\n * learn readiness from the connector's registration line rather than a banner.\n */\nexport class CloudflaredController implements RemoteProviderController {\n private enabled = false\n private initialized = false\n private disposed = false\n private child: ChildProcessWithoutNullStreams | undefined\n private gatewayValue: MobileAccessGateway | undefined\n private reservation: PortReservation | undefined\n private generation = 0\n private buffer = ''\n private mode: CloudflaredTunnelMode = 'quick'\n private namedOrigin: string | undefined\n private latest: CloudflaredStatus = publicStatus({ enabled: false, state: 'off' })\n private queue: Promise<void> = Promise.resolve()\n private startupTimer: NodeJS.Timeout | undefined\n private startupRounds = 0\n\n constructor(private readonly options: CloudflaredControllerOptions) {\n if (!isAbsolute(options.executable)) {\n throw new Error('cloudflared executable path must be absolute')\n }\n }\n\n /** Restore the remembered cloudflared switch independently from LAN and other providers. */\n async initialize(): Promise<void> {\n const state = await this.options.store.load()\n this.enabled = state.enabled\n this.initialized = true\n if (this.enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n }\n\n /** Return the active cloudflared-backed DSH gateway. */\n gateway(): MobileAccessGateway | undefined {\n return this.gatewayValue\n }\n\n /** Return state safe for the desktop control UI. */\n status(): CloudflaredStatus {\n return publicStatus(this.latest)\n }\n\n /** Enable or disable cloudflared without changing LAN or other provider state. */\n async setEnabled(enabled: boolean): Promise<CloudflaredStatus> {\n if (!this.initialized || this.disposed) throw new Error('cloudflared controller is unavailable')\n await this.enqueue(async () => {\n if (this.enabled === enabled && (enabled === false || this.child !== undefined)) return\n if (!enabled) await this.stop()\n this.enabled = enabled\n await this.options.store.save({ version: 1, enabled })\n if (enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Restart cloudflared and allocate a fresh quick tunnel. */\n async reconnect(): Promise<CloudflaredStatus> {\n if (!this.initialized || this.disposed) throw new Error('cloudflared controller is unavailable')\n await this.enqueue(async () => {\n if (!this.enabled) {\n this.enabled = true\n await this.options.store.save({ version: 1, enabled: true })\n }\n await this.stop()\n await this.start()\n })\n return this.status()\n }\n\n /** Disable cloudflared without deleting the installed component. */\n async reset(): Promise<CloudflaredStatus> {\n if (!this.initialized || this.disposed) throw new Error('cloudflared controller is unavailable')\n await this.enqueue(async () => {\n await this.stop()\n this.enabled = false\n await this.options.store.save({ version: 1, enabled: false })\n this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Stop owned resources without changing the remembered switch. */\n async close(): Promise<void> {\n if (this.disposed) return\n this.disposed = true\n await this.enqueue(() => this.stop())\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task\n }\n\n private publish(status: CloudflaredStatus): void {\n this.latest = publicStatus(status)\n try { this.options.onStatus?.(this.status()) } catch { /* UI observation cannot own runtime state. */ }\n }\n\n private async start(): Promise<void> {\n const generation = ++this.generation\n let executableEntry\n try { executableEntry = await lstat(this.options.executable) } catch {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'cloudflared_component_missing' })\n return\n }\n if (!executableEntry.isFile() || executableEntry.isSymbolicLink()) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'cloudflared_component_invalid' })\n return\n }\n\n const settings = this.options.tunnel.settings()\n const named = settings.mode === 'named' ? settings : undefined\n this.mode = settings.mode\n this.namedOrigin = named === undefined ? undefined : `https://${named.hostname}`\n\n let reservation: PortReservation\n try {\n reservation = await reserveLoopbackPort(named?.port)\n } catch (error) {\n // A bind conflict is the user's to resolve; anything else is an internal\n // reservation failure and must not be reported as a busy port.\n const conflict = (error as NodeJS.ErrnoException | undefined)?.code === 'EADDRINUSE'\n this.publish({\n enabled: true,\n state: 'error',\n errorCode: conflict\n ? (named === undefined ? 'cloudflared_port_unavailable' : 'cloudflared_tunnel_port_unavailable')\n : 'cloudflared_port_reservation_failed',\n })\n return\n }\n this.reservation = reservation\n this.buffer = ''\n this.startupRounds = 0\n this.publish({ enabled: true, state: 'starting' })\n\n if (named !== undefined) {\n // Cloudflare already routes the public hostname to this exact port, so the\n // gateway must be listening before the connector registers; the port has to\n // survive restarts, which is why it is configuration rather than a choice.\n const origin = this.namedOrigin as string\n this.publish({ enabled: true, state: 'connecting', origin })\n await reservation.release()\n if (this.reservation === reservation) this.reservation = undefined\n let gateway: MobileAccessGateway\n try {\n gateway = await this.options.createGateway(origin, reservation.port)\n } catch (error) {\n // Only a real bind conflict is a port problem; anything else (certificate,\n // configuration, permissions) must not send the user chasing the port.\n const conflict = (error as NodeJS.ErrnoException | undefined)?.code === 'EADDRINUSE'\n this.publish({\n enabled: true,\n state: 'error',\n errorCode: conflict ? 'cloudflared_tunnel_port_unavailable' : 'gateway_start_failed',\n })\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed) {\n await gateway.close()\n return\n }\n this.gatewayValue = gateway\n }\n\n // The local DSH remote gateway speaks plain HTTP, so neither flavour needs an\n // origin TLS override. A named tunnel takes its token from the environment so\n // the credential never appears in the process command line.\n const args = named === undefined\n ? ['tunnel', '--url', `http://127.0.0.1:${String(reservation.port)}`, '--no-autoupdate']\n : ['tunnel', '--no-autoupdate', 'run']\n const environment = withoutProxyEnvironment(process.env)\n if (named !== undefined) environment.TUNNEL_TOKEN = named.token\n const child = (this.options.spawnProcess ?? spawnCloudflaredProcess)(\n this.options.executable,\n args,\n environment,\n )\n this.child = child\n child.stdout.setEncoding('utf8')\n child.stderr.setEncoding('utf8')\n child.stdout.on('data', chunk => { this.consume(generation, String(chunk)) })\n child.stderr.on('data', chunk => { this.consume(generation, String(chunk)) })\n child.once('error', () => { void this.enqueue(() => this.failGeneration(generation, 'cloudflared_launch_failed')) })\n child.once('close', code => {\n if (generation !== this.generation || this.child !== child) return\n this.child = undefined\n if (this.enabled) void this.enqueue(() => this.failGeneration(generation, code === 0 ? 'cloudflared_stopped' : 'cloudflared_exited'))\n })\n this.armStartupTimer(generation)\n }\n\n private armStartupTimer(generation: number): void {\n if (this.startupTimer !== undefined) clearTimeout(this.startupTimer)\n this.startupTimer = setTimeout(() => {\n void this.enqueue(() => this.checkStartupTimeout(generation))\n }, this.options.startupTimeoutMs ?? START_TIMEOUT_MS)\n this.startupTimer.unref()\n }\n\n /**\n * Give a live named connector more time instead of killing it: it retries the\n * edge on its own and usually registers once the rebooted network is usable.\n * A dead connector, or an exhausted budget, fails the generation as before.\n */\n private async checkStartupTimeout(generation: number): Promise<void> {\n if (generation !== this.generation || !this.enabled || this.disposed) return\n if (this.latest.state === 'ready') return\n const childAlive = this.child !== undefined && this.child.exitCode === null\n const budget = this.options.maxStartupRounds ?? NAMED_STARTUP_ROUNDS\n if (this.mode === 'named' && childAlive && this.startupRounds + 1 < budget) {\n this.startupRounds += 1\n this.armStartupTimer(generation)\n return\n }\n await this.failGeneration(generation, 'cloudflared_start_timeout')\n }\n\n private consume(generation: number, chunk: string): void {\n if (generation !== this.generation) return\n this.buffer += chunk\n if (Buffer.byteLength(this.buffer, 'utf8') > MAX_LOG_BUFFER_BYTES && !this.buffer.includes('\\n')) {\n void this.enqueue(() => this.failGeneration(generation, 'cloudflared_invalid_output'))\n return\n }\n while (true) {\n const newline = this.buffer.indexOf('\\n')\n if (newline < 0) return\n const line = this.buffer.slice(0, newline).replace(/\\r$/u, '')\n this.buffer = this.buffer.slice(newline + 1)\n if (this.mode === 'named') {\n // The public origin is configuration here, so the log is only read for the\n // registration that proves the connector reached Cloudflare.\n if (isCloudflaredRegistration(line)) void this.enqueue(() => this.confirmNamedReady(generation))\n continue\n }\n let origin: string | undefined\n try { origin = parseCloudflaredOrigin(line) } catch {\n void this.enqueue(() => this.failGeneration(generation, 'cloudflared_invalid_origin'))\n return\n }\n if (origin !== undefined) void this.enqueue(() => this.attachGateway(generation, origin))\n }\n }\n\n /** Mark a named tunnel ready once the connector has registered with the edge. */\n private async confirmNamedReady(generation: number): Promise<void> {\n if (generation !== this.generation || !this.enabled || this.disposed) return\n if (this.gatewayValue === undefined || this.latest.state === 'ready') return\n if (this.startupTimer !== undefined) clearTimeout(this.startupTimer)\n this.startupTimer = undefined\n this.publish({ enabled: true, state: 'ready', ...(this.namedOrigin === undefined ? {} : { origin: this.namedOrigin }) })\n }\n\n private async attachGateway(generation: number, origin: string): Promise<void> {\n if (generation !== this.generation || !this.enabled || this.disposed) return\n const current = this.gatewayValue\n if (current !== undefined) {\n if (current.address().origin === origin) return\n await this.rotateGateway(generation, origin, current)\n return\n }\n const reservation = this.reservation\n if (reservation === undefined) return\n this.publish({ enabled: true, state: 'connecting', origin })\n await reservation.release()\n if (this.reservation === reservation) this.reservation = undefined\n let gateway: MobileAccessGateway\n try { gateway = await this.options.createGateway(origin, reservation.port) } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed || this.child === undefined) {\n await gateway.close()\n return\n }\n this.gatewayValue = gateway\n if (this.startupTimer !== undefined) clearTimeout(this.startupTimer)\n this.startupTimer = undefined\n this.publish({ enabled: true, state: 'ready', origin })\n }\n\n /** Replace the gateway authority when a quick tunnel restarts on a new hostname. */\n private async rotateGateway(\n generation: number,\n origin: string,\n current: MobileAccessGateway,\n ): Promise<void> {\n const listenPort = current.address().port\n // A replacement must bind the same port cloudflared already forwards to.\n // Stop exposing the closing instance before releasing that port.\n if (this.gatewayValue === current) this.gatewayValue = undefined\n this.publish({ enabled: true, state: 'connecting', origin })\n try {\n await current.close()\n } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed || this.child === undefined) return\n\n let replacement: MobileAccessGateway\n try { replacement = await this.options.createGateway(origin, listenPort) } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed || this.child === undefined) {\n await replacement.close()\n return\n }\n this.gatewayValue = replacement\n this.publish({ enabled: true, state: 'ready', origin })\n }\n\n private async failGeneration(generation: number, code: string): Promise<void> {\n if (generation !== this.generation) return\n await this.stopProcessAndGateway()\n if (this.enabled) this.publish({ enabled: true, state: 'error', errorCode: code })\n }\n\n private async stop(): Promise<void> {\n ++this.generation\n await this.stopProcessAndGateway()\n }\n\n private async stopProcessAndGateway(): Promise<void> {\n if (this.startupTimer !== undefined) clearTimeout(this.startupTimer)\n this.startupTimer = undefined\n const reservation = this.reservation\n this.reservation = undefined\n const child = this.child\n this.child = undefined\n const gateway = this.gatewayValue\n this.gatewayValue = undefined\n await settleRemoteResources([\n () => reservation?.release(),\n () => child !== undefined && child.exitCode === null ? terminateRemoteProcess(child) : undefined,\n () => gateway?.close(),\n ], 'cloudflared resource cleanup failed')\n }\n}\n","import { randomBytes } from 'node:crypto'\nimport { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { isIP } from './ip.js'\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path'\nimport { restrictPrivateFile } from './private-file.js'\n\nconst MAX_CONFIG_BYTES = 8 * 1024\nconst QUICK_TUNNEL_SUFFIX = '.trycloudflare.com'\n/** Cloudflare's own tunnel-routing domain; it can never be a public hostname. */\nconst TUNNEL_ROUTING_SUFFIX = '.cfargotunnel.com'\n/**\n * The bare registrable names behind those suffixes. A suffix test alone accepts\n * them, yet Cloudflare owns both apexes outright, so neither can ever be routed to\n * a customer tunnel.\n */\nconst RESERVED_APEX_NAMES: readonly string[] = Object.freeze([\n QUICK_TUNNEL_SUFFIX.slice(1),\n TUNNEL_ROUTING_SUFFIX.slice(1),\n])\n/** Ports below this need privileges on every supported platform. */\nconst MIN_PORT = 1024\nconst MAX_PORT = 65_535\n/**\n * The LAN gateway's HTTPS port, held by the plugin for as long as DSH runs. It is\n * never available to a tunnel, so a configuration naming it must be refused up\n * front rather than failing later as a generic \"port in use\".\n */\nconst RESERVED_LAN_GATEWAY_PORT = 3443\nconst MAX_TOKEN_LENGTH = 4096\n\n/** Which tunnel flavour the cloudflared provider runs. */\nexport type CloudflaredTunnelMode = 'quick' | 'named'\n\n/**\n * Cloudflared provider configuration.\n *\n * A quick tunnel owns nothing durable: cloudflared allocates a random hostname\n * and a random forward port every start. A named tunnel instead runs with an\n * account token, and its public hostname is routed by Cloudflare to the local\n * port recorded here, so that port must stay stable across restarts.\n */\nexport type CloudflaredTunnelSettings =\n | { readonly version: 1; readonly mode: 'quick' }\n | {\n readonly version: 1\n readonly mode: 'named'\n readonly token: string\n readonly hostname: string\n readonly port: number\n }\n\n/** Configuration metadata safe to hand to the desktop panel; never the token. */\nexport interface CloudflaredTunnelStatus {\n readonly mode: CloudflaredTunnelMode\n readonly configured: boolean\n readonly hostname?: string\n readonly port?: number\n readonly storagePath: string\n readonly errorCode?: string\n}\n\nfunction hostname(value: string): boolean {\n if (value.length > 253 || !value.includes('.')) return false\n return value.split('.').every(label => label.length >= 1 && label.length <= 63\n && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label))\n}\n\n/**\n * Validate the public hostname Cloudflare routes to this tunnel.\n *\n * The name must be a real DNS name, not an IP literal or a wildcard, and it\n * cannot sit under Cloudflare's own control-plane suffixes: `trycloudflare.com`\n * belongs to quick tunnels, and a `cfargotunnel.com` name is the routing target\n * rather than a routable public address.\n */\nexport function validateCloudflaredTunnelHostname(value: unknown): string {\n if (typeof value !== 'string' || value !== value.trim() || value.length === 0 || value.length > 253) {\n throw new Error('cloudflared_tunnel_hostname_invalid')\n }\n const normalized = value.toLowerCase().replace(/\\.$/u, '')\n // An IPv4 literal is dot-separated digits and would otherwise satisfy the label\n // grammar, but Cloudflare never routes a tunnel hostname to an address.\n if (isIP(normalized) !== 0 || !hostname(normalized) || normalized.includes('*')\n || RESERVED_APEX_NAMES.includes(normalized)\n || normalized.endsWith(QUICK_TUNNEL_SUFFIX) || normalized.endsWith(TUNNEL_ROUTING_SUFFIX)) {\n throw new Error('cloudflared_tunnel_hostname_invalid')\n }\n return normalized\n}\n\n/** Validate the stable loopback port Cloudflare's ingress forwards to. */\nexport function validateCloudflaredTunnelPort(value: unknown): number {\n if (!Number.isSafeInteger(value) || Number(value) < MIN_PORT || Number(value) > MAX_PORT) {\n throw new Error('cloudflared_tunnel_port_invalid')\n }\n // The LAN gateway always owns 3443, so this is not a transient conflict and the\n // user must be told which port to avoid instead of \"the port is busy\".\n if (value === RESERVED_LAN_GATEWAY_PORT) throw new Error('cloudflared_tunnel_port_reserved')\n return Number(value)\n}\n\n/**\n * Validate a connector token before durable storage.\n *\n * A token is a base64url-encoded JSON blob that carries the account, tunnel id\n * and tunnel secret; the provider passes it through the environment rather than\n * the command line, and it is never returned to any client.\n */\nexport function validateCloudflaredTunnelToken(value: unknown): string {\n if (typeof value !== 'string' || value !== value.trim()\n || value.length < 32 || value.length > MAX_TOKEN_LENGTH\n || !/^[A-Za-z0-9_=+/-]+$/u.test(value)) {\n throw new Error('cloudflared_tunnel_token_invalid')\n }\n return value\n}\n\n/** Parse tunnel settings at the request and filesystem boundaries. */\nexport function parseCloudflaredTunnelSettings(value: unknown): CloudflaredTunnelSettings {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error('cloudflared_tunnel_settings_invalid')\n }\n const record = value as Record<string, unknown>\n const allowed = ['version', 'mode', 'token', 'hostname', 'port']\n if (Reflect.ownKeys(record).some(key => !allowed.includes(String(key)))) {\n throw new Error('cloudflared_tunnel_settings_invalid')\n }\n if (record.version !== undefined && record.version !== 1) {\n throw new Error('cloudflared_tunnel_settings_invalid')\n }\n const mode = record.mode ?? 'quick'\n if (mode === 'quick') {\n if (record.token !== undefined || record.hostname !== undefined || record.port !== undefined) {\n throw new Error('cloudflared_tunnel_settings_invalid')\n }\n return Object.freeze({ version: 1, mode: 'quick' })\n }\n if (mode !== 'named') throw new Error('cloudflared_tunnel_settings_invalid')\n return Object.freeze({\n version: 1,\n mode: 'named',\n token: validateCloudflaredTunnelToken(record.token),\n hostname: validateCloudflaredTunnelHostname(record.hostname),\n port: validateCloudflaredTunnelPort(record.port),\n })\n}\n\n/**\n * Merge a partial panel request with the saved named-tunnel settings so a blank\n * field keeps its stored value. The token in particular is write-only, so the\n * panel submits an empty string to mean \"keep the existing connector token\".\n */\nexport function mergeSavedCloudflaredTunnelSettings(\n partial: Readonly<Record<string, unknown>>,\n saved: CloudflaredTunnelSettings | undefined,\n): CloudflaredTunnelSettings {\n const requested = partial.mode ?? saved?.mode ?? 'quick'\n // The named branch below hardcodes its mode, so an unrecognized value would be\n // silently rewritten into a named tunnel. Validate here as strictly as the\n // parser does, rather than letting this layer launder it.\n if (requested !== 'quick' && requested !== 'named') {\n throw new Error('cloudflared_tunnel_settings_invalid')\n }\n if (requested === 'quick') return parseCloudflaredTunnelSettings({ version: 1, mode: 'quick' })\n const previous = saved?.mode === 'named' ? saved : undefined\n const token = partial.token === '' || partial.token === undefined ? previous?.token : partial.token\n const hostname = partial.hostname === '' || partial.hostname === undefined ? previous?.hostname : partial.hostname\n const port = partial.port === undefined || partial.port === '' ? previous?.port : partial.port\n if (token === undefined || hostname === undefined || port === undefined) {\n throw new Error('cloudflared_tunnel_config_missing')\n }\n return parseCloudflaredTunnelSettings({ version: 1, mode: 'named', token, hostname, port })\n}\n\nasync function atomicPrivateWrite(file: string, body: string): Promise<void> {\n const directory = dirname(file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(file)\n if (!current.isFile() || current.isSymbolicLink()) throw new Error('cloudflared_tunnel_target_invalid')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, `.${basename(file)}.${randomBytes(12).toString('hex')}.tmp`)\n try {\n await writeFile(temporary, body, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, file)\n await restrictPrivateFile(file)\n } catch (error) {\n await rm(temporary, { force: true })\n throw error\n }\n}\n\n/** Owns the private cloudflared tunnel configuration for one DSH installation. */\nexport class CloudflaredTunnelStore {\n readonly stateRoot: string\n readonly settingsFile: string\n private settingsValue: CloudflaredTunnelSettings = Object.freeze({ version: 1, mode: 'quick' })\n private errorCode: string | undefined\n\n constructor(stateDirectory: string) {\n if (!isAbsolute(stateDirectory)) throw new Error('cloudflared tunnel state directory must be absolute')\n this.stateRoot = resolve(stateDirectory)\n this.settingsFile = join(this.stateRoot, 'tunnel.json')\n }\n\n /** Load private settings while rejecting links, oversized files and unknown fields. */\n async initialize(): Promise<void> {\n let entry\n try { entry = await lstat(this.settingsFile) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\n throw error\n }\n if (!entry.isFile() || entry.isSymbolicLink() || entry.size > MAX_CONFIG_BYTES) {\n this.errorCode = 'cloudflared_tunnel_config_invalid'\n return\n }\n await restrictPrivateFile(this.settingsFile)\n try {\n this.settingsValue = parseCloudflaredTunnelSettings(JSON.parse(await readFile(this.settingsFile, 'utf8')) as unknown)\n this.errorCode = undefined\n } catch {\n this.settingsValue = Object.freeze({ version: 1, mode: 'quick' })\n this.errorCode = 'cloudflared_tunnel_config_invalid'\n }\n }\n\n /** Return configuration metadata without exposing the connector token. */\n status(): CloudflaredTunnelStatus {\n const settings = this.settingsValue\n return Object.freeze({\n mode: settings.mode,\n configured: settings.mode === 'named',\n ...(settings.mode === 'named' ? { hostname: settings.hostname, port: settings.port } : {}),\n storagePath: this.stateRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n })\n }\n\n /** Return private settings only to the provider lifecycle. */\n settings(): CloudflaredTunnelSettings {\n return this.settingsValue\n }\n\n /** Atomically replace the tunnel configuration. */\n async configure(value: unknown): Promise<CloudflaredTunnelStatus> {\n const settings = parseCloudflaredTunnelSettings(value)\n if (settings.mode === 'quick') await rm(this.settingsFile, { force: true })\n else await atomicPrivateWrite(this.settingsFile, `${JSON.stringify(settings)}\\n`)\n this.settingsValue = settings\n this.errorCode = undefined\n return this.status()\n }\n\n /** Forget a named tunnel and its connector token. */\n async purge(): Promise<CloudflaredTunnelStatus> {\n await rm(this.settingsFile, { force: true })\n this.settingsValue = Object.freeze({ version: 1, mode: 'quick' })\n this.errorCode = undefined\n return this.status()\n }\n}\n","/**\n * Host-side task-completion fan-out for phone notifications.\n *\n * The exact moment a run ends is known only on the Host (the phone page can\n * merely infer it from UI state), so this module watches the public\n * `session/event` bus for completed root turns and hands them to a hub that\n * fans out to every live mobile gateway. Phones render the text locally, so\n * no user content crosses this boundary — only opaque session and turn ids.\n */\n\n/** A completed root turn worth announcing on paired phones. */\nexport interface TaskCompletionEvent {\n readonly sessionId: string\n readonly turn: number\n}\n\n/** Minimal session shape read off the `session/event` bus. */\nexport interface TaskEventSession {\n readonly id: unknown\n readonly header?: { readonly parentSession?: unknown } | null | undefined\n}\n\n/**\n * Minimal turn event shape read off the `session/event` bus. Fields stay\n * unknown here and are validated at runtime below so this module never\n * depends on the harness session packages.\n */\nexport interface TaskTurnEvent {\n readonly type: string\n readonly data?: unknown\n}\n\n/** Structural slice of the Host context this module needs (keeps tests cordis-free). */\nexport interface TaskEventContext {\n on(event: 'session/event', handler: (session: TaskEventSession, event: TaskTurnEvent) => void): () => void\n}\n\nexport interface TaskEventWatcherOptions {\n /** Trailing debounce per session that merges turn-boundary bursts. */\n readonly debounceMs?: number\n readonly onTaskCompleted: (event: TaskCompletionEvent) => void\n readonly log?: (event: string, fields: Readonly<Record<string, string | number | boolean>>) => void\n}\n\nexport const TASK_EVENT_DEBOUNCE_MS = 1_000\n\nfunction turnNumber(value: unknown): number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : 0\n}\n\nfunction turnData(value: unknown): { turn: number; completed: boolean } {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return { turn: 0, completed: false }\n const record = value as Record<string, unknown>\n const reason = record.reason as { readonly kind?: unknown } | string | null | undefined\n const completed = typeof reason === 'string'\n ? reason === 'completed'\n : reason !== null && typeof reason === 'object' && (reason as { readonly kind?: unknown }).kind === 'completed'\n return { turn: turnNumber(record.turn), completed }\n}\n\n/**\n * Subscribe to completed root turns. Subagent turns are skipped so one task\n * announces once, and rapid turn boundaries collapse into a single event.\n * Returns a disposer that also drops pending debounces.\n */\nexport function watchTaskCompletions(ctx: TaskEventContext, options: TaskEventWatcherOptions): () => void {\n const debounceMs = options.debounceMs ?? TASK_EVENT_DEBOUNCE_MS\n const pending = new Map<string, ReturnType<typeof setTimeout>>()\n const disposeListener = ctx.on('session/event', (session, event) => {\n if (event.type !== 'turn/end') return\n const { turn, completed } = turnData(event.data)\n if (!completed) return\n if (session.header?.parentSession !== undefined && session.header?.parentSession !== null) return\n const sessionId = String(session.id)\n options.log?.('task-completed-observed', { sessionId, turn })\n const previous = pending.get(sessionId)\n if (previous !== undefined) clearTimeout(previous)\n pending.set(sessionId, setTimeout(() => {\n pending.delete(sessionId)\n options.log?.('task-completed-announced', { sessionId, turn })\n options.onTaskCompleted(Object.freeze({ sessionId, turn }))\n }, debounceMs))\n })\n return () => {\n disposeListener()\n for (const timer of pending.values()) clearTimeout(timer)\n pending.clear()\n }\n}\n\n/** Receives fanned-out completion events (normally a mobile gateway). */\nexport interface TaskEventSink {\n broadcastTaskEvent(event: TaskCompletionEvent): void\n}\n\n/** One subscription feeding every live gateway; gateways register on start. */\nexport class TaskEventHub {\n private readonly sinks = new Set<TaskEventSink>()\n\n /** Register a sink; returns its disposer. */\n add(sink: TaskEventSink): () => void {\n this.sinks.add(sink)\n return () => { this.sinks.delete(sink) }\n }\n\n /** Fan out to a snapshot so a failing sink cannot break its siblings. */\n broadcast(event: TaskCompletionEvent): void {\n for (const sink of [...this.sinks]) {\n try { sink.broadcastTaskEvent(event) } catch { /* A failing gateway must not break its siblings. */ }\n }\n }\n\n /** Visible for tests. */\n get size(): number {\n return this.sinks.size\n }\n}\n","import { execFileText as execFile } from './exec-file.js'\nimport { lookup } from 'node:dns/promises'\nimport type { ClientRequest, IncomingMessage } from 'node:http'\nimport { request as proxyRequest } from 'node:http'\nimport type { Socket } from 'node:net'\nimport type { TLSSocket } from 'node:tls'\nimport { connect as tlsConnect } from 'node:tls'\n\nimport type { RemoteProvider } from './remote.js'\nimport { DSH_MOBILE_VERSION, MINIMUM_ANDROID_APP_VERSION } from './version.js'\n\nexport type DiagnosticStatus = 'ok' | 'warning' | 'error' | 'info'\nexport type DiagnosticReason =\n | 'versions-current'\n | 'lan-setup-required' | 'network-unavailable' | 'network-interface' | 'network-fixed'\n | 'lan-ready' | 'lan-off'\n | 'firewall-ready' | 'firewall-missing' | 'firewall-unknown'\n | 'remote-off' | 'remote-ready' | 'remote-origin-ready' | 'remote-rate-limited' | 'remote-fake-ip' | 'remote-unreachable'\n | 'remote-needs-login' | 'remote-connecting' | 'remote-controller-error'\n | 'phone-network-unknown'\n\nexport interface DiagnosticFacts {\n readonly provider?: RemoteProvider\n readonly latencyMs?: number\n readonly interfaceName?: string\n readonly endpointSuffix?: string\n readonly controllerCode?: string\n /** The reachability answer came from the machine's configured relay egress. */\n readonly viaProxy?: boolean\n}\n\n/** One user-facing diagnostic result with stable localization data and server fallback copy. */\nexport interface DiagnosticCheck {\n readonly id: string\n readonly status: DiagnosticStatus\n readonly reason: DiagnosticReason\n readonly facts?: DiagnosticFacts\n readonly label: string\n readonly detail: string\n readonly action?: string\n}\n\n/** Runtime facts available without exposing credentials or local file paths. */\nexport interface DiagnosticSnapshot {\n readonly dshVersion: string\n readonly lan: {\n readonly configured?: boolean\n readonly running: boolean\n readonly origin?: string\n readonly configuredInterface?: string\n readonly interfaceName?: string\n readonly networkError?: string\n readonly port?: number\n }\n readonly remote: {\n readonly provider: RemoteProvider\n readonly running: boolean\n readonly state: string\n readonly origin?: string\n readonly errorCode?: string\n }\n}\n\ninterface FirewallObservation {\n readonly state: 'ready' | 'missing' | 'unknown' | 'not-applicable'\n}\n\ninterface RemoteObservation {\n readonly state: 'ready' | 'rate-limited' | 'unreachable' | 'not-applicable'\n readonly latencyMs?: number\n readonly fakeIp?: boolean\n readonly viaProxy?: boolean\n}\n\n/** Injectable probes keep diagnostics deterministic in tests. */\nexport interface DiagnosticProbes {\n readonly firewall?: (port: number | undefined) => Promise<FirewallObservation>\n readonly remote?: (origin: string | undefined) => Promise<RemoteObservation>\n}\n\n/** Sanitized diagnostic response copied by the desktop UI. */\nexport interface ConnectionDiagnostics {\n readonly version: 1\n readonly generatedAt: number\n readonly overall: 'ok' | 'attention' | 'error'\n readonly versions: {\n readonly plugin: string\n readonly dsh: string\n readonly minimumAndroidApp: string\n }\n readonly summary: string\n readonly checks: readonly DiagnosticCheck[]\n readonly report: string\n}\n\nconst REMOTE_ERROR_GUIDANCE: Readonly<Record<string, string>> = Object.freeze({\n component_missing: '重新安装完整插件包。',\n funnel_permission_required: '继续完成 Tailscale Funnel 授权。',\n funnel_https_required: '继续完成 Tailscale HTTPS 授权。',\n funnel_start_failed: '重新打开授权页并允许 Funnel。',\n funnel_start_timeout: '检查网络后点击“重新连接”。',\n tailscale_dns_missing: '确认 Tailscale 登录仍有效后重新连接。',\n sidecar_launch_failed: '重新安装完整插件包后重试。',\n sidecar_stopped: '点击“重新连接”。',\n sidecar_exited: '点击“重新连接”;仍失败时复制诊断报告。',\n control_channel_failed: '点击“重新连接”。',\n cpolar_component_missing: '先安装 cpolar 官方组件。',\n cpolar_component_invalid: '彻底移除 cpolar 组件后重新安装。',\n cpolar_config_missing: '保存 cpolar Authtoken 后重试。',\n cpolar_config_invalid: '重新保存 cpolar Authtoken。',\n cpolar_start_timeout: '检查网络后点击“重新连接”。',\n cpolar_stopped: '点击“重新连接”。',\n cpolar_exited: '点击“重新连接”;仍失败时复制诊断报告。',\n cloudflared_component_missing: '先安装 cloudflared 官方组件。',\n cloudflared_component_invalid: '彻底移除 cloudflared 组件后重新安装。',\n cloudflared_port_unavailable: '无法分配本机远程网关端口,请重试。',\n cloudflared_launch_failed: '重新安装 cloudflared 官方组件后重试。',\n cloudflared_start_timeout: '检查网络后点击“重新连接”。',\n cloudflared_stopped: '点击“重新连接”。',\n cloudflared_exited: '点击“重新连接”;仍失败时复制诊断报告。',\n cloudflared_invalid_output: 'cloudflared 返回了无法识别的状态。',\n cloudflared_invalid_origin: 'cloudflared 返回的公网地址未通过校验。',\n origin_config_missing: '先保存自有反向代理配置。',\n origin_config_invalid: '重新保存自有反向代理配置。',\n origin_listen_port_in_use: '更换 HTTP 后端端口;不要使用局域网的 3443、DSH 自身的 3080,或已被 cloudflared 隧道占用的 3444。',\n origin_listen_address_unavailable: '监听地址不属于当前电脑,请重新选择本机的私网 IPv4 地址。',\n origin_gateway_start_failed: '检查 HTTP 后端监听地址和端口后重新连接。',\n frp_component_missing: '先安装 FRP 官方组件。',\n frp_component_invalid: '彻底清理 FRP 组件后重新安装。',\n frp_config_missing: '先保存自建 FRP 连接配置。',\n frp_config_verify_failed: '检查服务器地址、端口、Token 和公开域名。',\n frp_vhost_publicly_reachable: '将 frps 的 HTTP vhost 监听限制到 127.0.0.1。',\n frp_vhost_probe_failed: '确认 VPS 地址可解析后重新连接。',\n frp_launch_failed: '重新安装 FRP 官方组件后重试。',\n frp_start_timeout: '确认 frps、Caddy 和域名解析正常后重新连接。',\n frp_discovery_mismatch: '公开域名连接到了另一台电脑,请核对 Caddy 与 frps 配置。',\n frp_discovery_invalid: '公开域名返回了非 DSH Mobile 响应。',\n frp_stopped: '点击“重新连接”。',\n frp_exited: '检查 VPS 配置后重新连接;仍失败时复制诊断报告。',\n gateway_start_failed: '确认 DSH 正在运行后重新连接。',\n})\n\nfunction check(\n id: string,\n status: DiagnosticStatus,\n reason: DiagnosticReason,\n label: string,\n detail: string,\n action?: string,\n facts?: DiagnosticFacts,\n): DiagnosticCheck {\n return Object.freeze({ id, status, reason, ...(facts === undefined ? {} : { facts: Object.freeze(facts) }), label, detail, ...(action === undefined ? {} : { action }) })\n}\n\nfunction maskLanOrigin(origin: string | undefined): string {\n if (origin === undefined) return '未分配'\n try {\n const url = new URL(origin)\n const octets = url.hostname.split('.')\n const host = octets.length === 4 ? `${octets[0]}.${octets[1]}.${octets[2]}.x` : '局域网地址'\n return `${url.protocol}//${host}${url.port === '' ? '' : `:${url.port}`}`\n } catch {\n return '地址格式无效'\n }\n}\n\nfunction remoteSuffix(origin: string | undefined): string {\n if (origin === undefined) return '未分配'\n try {\n const hostname = new URL(origin).hostname\n if (hostname.endsWith('.ts.net')) return '*.ts.net'\n if (hostname.endsWith('.trycloudflare.com')) return '*.trycloudflare.com'\n for (const suffix of ['.cpolar.cn', '.cpolar.io', '.cpolar.top', '.cpolar.com']) {\n if (hostname.endsWith(suffix)) return `*${suffix}`\n }\n return '公共 HTTPS 地址'\n } catch {\n return '地址格式无效'\n }\n}\n\nfunction defaultFirewallProbe(platform: NodeJS.Platform = process.platform): (port: number | undefined) => Promise<FirewallObservation> {\n return async (port) => {\n if (platform !== 'win32') return { state: 'not-applicable' }\n if (port === undefined) return { state: 'unknown' }\n const script = [\n \"$specs = @(@{ Name = 'DSH Mobile HTTPS'; Protocol = 'TCP' }, @{ Name = 'DSH Mobile Discovery'; Protocol = 'UDP' })\",\n '$ready = $true',\n '$specs | ForEach-Object {',\n ' $spec = $_',\n \" $rule = Get-NetFirewallRule -DisplayName $spec.Name -ErrorAction SilentlyContinue | Where-Object { $_.Enabled -eq 'True' -and $_.Direction -eq 'Inbound' -and $_.Action -eq 'Allow' } | Select-Object -First 1\",\n ' if ($null -eq $rule) { $ready = $false; return }',\n ' $filters = @($rule | Get-NetFirewallPortFilter -ErrorAction SilentlyContinue)',\n ` $matching = @($filters | Where-Object { $_.Protocol -eq $spec.Protocol -and ($_.LocalPort -eq 'Any' -or $_.LocalPort -eq '${String(port)}') })`,\n ' if ($matching.Count -eq 0) { $ready = $false }',\n '}',\n \"if ($ready) { 'ready' } else { 'missing' }\",\n ].join('; ')\n try {\n const result = await execFile('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], {\n encoding: 'utf8',\n timeout: 3_000,\n windowsHide: true,\n })\n return { state: result.stdout.trim() === 'ready' ? 'ready' : 'missing' }\n } catch {\n return { state: 'unknown' }\n }\n }\n}\n\n/** Reserve one six-second window per egress so the desktop's 15-second request can finish. */\nexport function remoteDiagnosticTimeoutMs(_origin: string): number {\n return 6_000\n}\n\ninterface RemoteHealthObservation {\n readonly status: number\n readonly latencyMs: number\n}\n\n/** One tunnel attempt through the machine's configured HTTP relay egress. */\nexport type ProxyHealthTunnel = (target: URL, proxy: URL, budgetMs: number) => Promise<RemoteHealthObservation>\n\n/** Raw observation from the direct (default) path, checked before any relay. */\nexport type DirectHealthProbe = (target: URL, budgetMs: number) => Promise<RemoteObservation>\n\nconst TUNNEL_HEADER_LIMIT_BYTES = 64 * 1024\n\nfunction proxyExcludedByNoProxy(target: URL, raw: string): boolean {\n const text = raw.trim().toLowerCase()\n if (text === '') return false\n const hostname = target.hostname.toLowerCase()\n const host = hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname\n const targetPort = target.port === '' ? (target.protocol === 'https:' ? 443 : 80) : Number(target.port)\n for (const entry of text.split(/[\\s,]+/)) {\n let value = entry\n if (value === '') continue\n if (value === '*') return true\n if (value.includes('/')) continue\n let port: number | undefined\n if (value.startsWith('[')) {\n const bracket = value.indexOf(']')\n if (bracket < 0) continue\n const tail = value.slice(bracket + 1)\n if (tail !== '') {\n if (!/^:\\d+$/u.test(tail)) continue\n port = Number(tail.slice(1))\n }\n value = value.slice(1, bracket)\n } else if ((value.match(/:/gu) ?? []).length === 1) {\n const separator = value.lastIndexOf(':')\n const suffix = value.slice(separator + 1)\n if (!/^\\d+$/u.test(suffix)) continue\n port = Number(suffix)\n value = value.slice(0, separator)\n }\n if (port !== undefined && port !== targetPort) continue\n if (value.startsWith('*.')) value = value.slice(2)\n else if (value.startsWith('.')) value = value.slice(1)\n if (value === '') continue\n if (host === value || host.endsWith(`.${value}`)) return true\n }\n return false\n}\n\n/**\n * Resolve the relay egress configured in the environment for `origin`.\n *\n * Honors `HTTPS_PROXY` (then `ALL_PROXY`, then the generic `HTTP_PROXY`) for an\n * HTTPS target, and `http_proxy`/`HTTP_PROXY`/`ALL_PROXY` for a plain one, unless\n * `NO_PROXY`/`no_proxy` covers the host. Only a plain `http://<host>:<port>`\n * relay is usable: SOCKS or a TLS-to-proxy configuration would need a different\n * tunnel, so absent that the probe keeps its direct semantics.\n */\nexport function configuredProxyFor(origin: string, env: NodeJS.ProcessEnv = process.env): URL | undefined {\n let target: URL\n try {\n target = new URL(origin)\n } catch {\n return undefined\n }\n if (proxyExcludedByNoProxy(target, env.NO_PROXY ?? env.no_proxy ?? '')) return undefined\n const isHttps = target.protocol === 'https:'\n const candidates = isHttps\n ? [env.https_proxy ?? env.HTTPS_PROXY, env.all_proxy ?? env.ALL_PROXY, env.http_proxy ?? env.HTTP_PROXY]\n : [env.http_proxy ?? env.HTTP_PROXY, env.all_proxy ?? env.ALL_PROXY]\n for (const candidate of candidates) {\n if (typeof candidate !== 'string' || candidate.trim() === '') continue\n try {\n const proxy = new URL(candidate.trim())\n if (proxy.protocol !== 'http:' || proxy.hostname === '') continue\n return proxy\n } catch {\n continue\n }\n }\n return undefined\n}\n\n/** One reachability probe through an HTTP relay: CONNECT, TLS, then a status line. */\nexport async function relayProbeThroughProxy(target: URL, proxy: URL, budgetMs = remoteDiagnosticTimeoutMs(target.origin)): Promise<RemoteHealthObservation> {\n const started = performance.now()\n return new Promise<RemoteHealthObservation>((resolve, reject) => {\n let settled = false\n let hopRequest: ClientRequest | undefined\n let rawSocket: Socket | undefined\n let secureSocket: TLSSocket | undefined\n let timer: NodeJS.Timeout | undefined\n const finish = (outcome: { readonly value?: RemoteHealthObservation; readonly error?: Error }): void => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n secureSocket?.destroy()\n rawSocket?.end()\n rawSocket?.destroy()\n hopRequest?.destroy()\n if (outcome.error !== undefined) reject(outcome.error)\n else resolve(outcome.value ?? { status: 0, latencyMs: 0 })\n }\n timer = setTimeout(() => { finish({ error: new Error('proxy_tunnel_timeout') }) }, Math.max(1, budgetMs))\n timer.unref()\n // The CONNECT target must always carry an explicit port: a bare hostname is\n // resolved by the relay against its own default (port 80), which answers\n // plaintext and breaks the TLS handshake with a wrong-version error.\n const hopPort = target.port === '' ? (target.protocol === 'https:' ? 443 : 80) : Number(target.port)\n const hopPath = `${target.hostname}:${hopPort}`\n const headers: Record<string, string> = { host: hopPath, 'proxy-connection': 'keep-alive' }\n if (proxy.username !== '') {\n headers['proxy-authorization'] = `Basic ${Buffer.from(\n `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`,\n 'utf8',\n ).toString('base64')}`\n }\n hopRequest = proxyRequest({\n host: proxy.hostname,\n port: proxy.port === '' ? 80 : Number(proxy.port),\n method: 'CONNECT',\n path: hopPath,\n headers,\n agent: false,\n })\n hopRequest.once('connect', (response: IncomingMessage, socket: Socket) => {\n if (settled) { socket.destroy(); return }\n rawSocket = socket\n if (response.statusCode !== 200) {\n finish({ error: new Error(`proxy_tunnel_rejected_${response.statusCode ?? 'unknown'}`) })\n return\n }\n const secure = tlsConnect({\n socket,\n servername: target.hostname,\n rejectUnauthorized: true,\n })\n secureSocket = secure\n secure.once('secureConnect', () => {\n if (settled) return\n secure.write([\n `GET ${target.pathname}${target.search} HTTP/1.1`,\n `Host: ${target.host}`,\n 'User-Agent: dsh-mobile-diagnostics',\n 'Accept-Encoding: identity',\n 'Connection: close',\n '',\n '',\n ].join('\\r\\n'))\n })\n let buffered = Buffer.alloc(0)\n secure.on('data', (chunk: Buffer) => {\n buffered = Buffer.concat([buffered, chunk])\n if (buffered.byteLength > TUNNEL_HEADER_LIMIT_BYTES) {\n finish({ error: new Error('proxy_tunnel_oversized_response') })\n return\n }\n const end = buffered.indexOf('\\r\\n\\r\\n')\n if (end < 0) return\n const statusLine = /^HTTP\\/\\d(?:\\.\\d)?\\s+(\\d{3})/u.exec(buffered.subarray(0, end).toString('latin1'))?.[1]\n const status = Number.parseInt(statusLine ?? '', 10)\n if (!Number.isSafeInteger(status) || status < 100) {\n finish({ error: new Error('proxy_tunnel_invalid_response') })\n return\n }\n finish({ value: { status, latencyMs: Math.max(0, Math.round(performance.now() - started)) } })\n })\n secure.once('error', (error: Error) => { finish({ error }) })\n secure.once('close', () => { finish({ error: new Error('proxy_tunnel_closed') }) })\n })\n hopRequest.once('error', (error: Error) => { finish({ error }) })\n hopRequest.end()\n })\n}\n\n/** Bounded supplementary DNS context for a failed direct attempt. */\nasync function unreachableWithDnsContext(hostname: string, budgetMs: number): Promise<RemoteObservation> {\n let fakeIp = false\n let timer: NodeJS.Timeout | undefined\n try {\n const addresses = await Promise.race([\n lookup(hostname, { all: true }),\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => { reject(new Error('dns_context_timeout')) }, Math.max(1, budgetMs))\n timer.unref()\n }),\n ])\n fakeIp = addresses.some(({ address }) => {\n const [first, second] = address.split('.').map(Number)\n return first === 198 && (second === 18 || second === 19)\n })\n } catch {\n // DNS lookup is supplementary; the failed HTTPS probe remains authoritative.\n } finally {\n clearTimeout(timer)\n }\n return { state: 'unreachable', ...(fakeIp ? { fakeIp: true } : {}) }\n}\n\nasync function fetchRemoteHealth(target: URL, budgetMs: number): Promise<RemoteObservation> {\n const started = performance.now()\n try {\n const response = await fetch(target, {\n cache: 'no-store',\n redirect: 'error',\n signal: AbortSignal.timeout(Math.max(1, budgetMs - 500)),\n })\n const latencyMs = Math.max(0, Math.round(performance.now() - started))\n if (response.status === 429) return { state: 'rate-limited', latencyMs }\n return response.ok ? { state: 'ready', latencyMs } : { state: 'unreachable', latencyMs }\n } catch {\n return unreachableWithDnsContext(target.hostname, Math.max(1, budgetMs - (performance.now() - started)))\n }\n}\n\n/** Explicit test seams: environment, the direct attempt, and the relay attempt. */\nexport interface RemoteHealthProbeOptions {\n readonly env?: NodeJS.ProcessEnv\n readonly direct?: DirectHealthProbe\n readonly tunnel?: ProxyHealthTunnel\n}\n\n/**\n * Answer \"is the public endpoint reachable\" from the machine's own egress.\n *\n * A direct attempt stays authoritative first. When it fails and the environment\n * configures an HTTP relay that `NO_PROXY` does not exclude, one tunnelled\n * attempt runs: a self-managed Funnel origin otherwise resolves to this\n * machine's tailnet address, where nothing serves the funnel port locally, so\n * the direct measurement reports an outage while the relay's egress — the same\n * public path a phone takes — still succeeds. When both fail, the original\n * direct observation (including its Fake-IP context) stands unchanged.\n */\nexport async function probeRemoteHealth(\n origin: string | undefined,\n options: RemoteHealthProbeOptions = {},\n): Promise<RemoteObservation> {\n if (origin === undefined) return { state: 'not-applicable' }\n let target: URL\n try {\n target = new URL('/mobile-access/health', origin)\n } catch {\n return { state: 'not-applicable' }\n }\n const budgetMs = remoteDiagnosticTimeoutMs(origin)\n const direct = options.direct ?? fetchRemoteHealth\n const observation = await direct(target, budgetMs)\n if (observation.state !== 'unreachable') return observation\n const relay = configuredProxyFor(origin, options.env ?? process.env)\n if (relay === undefined) return observation\n const tunnel = options.tunnel ?? relayProbeThroughProxy\n try {\n const { status, latencyMs } = await tunnel(target, relay, budgetMs)\n if (status === 429) return { state: 'rate-limited', latencyMs, viaProxy: true }\n return status >= 200 && status < 300\n ? { state: 'ready', latencyMs, viaProxy: true }\n : observation\n } catch {\n return observation\n }\n}\n\nasync function defaultRemoteProbe(origin: string | undefined): Promise<RemoteObservation> {\n return probeRemoteHealth(origin)\n}\n\nfunction reportLine(entry: DiagnosticCheck): string {\n return `[${entry.status.toUpperCase()}] ${entry.label}: ${entry.detail}${entry.action === undefined ? '' : ` ${entry.action}`}`\n}\n\n/** Run bounded read-only checks and return a report safe to paste into an issue. */\nexport async function collectConnectionDiagnostics(\n snapshot: DiagnosticSnapshot,\n probes: DiagnosticProbes = {},\n): Promise<ConnectionDiagnostics> {\n const checks: DiagnosticCheck[] = []\n const remoteProbe = snapshot.remote.provider !== 'origin' && snapshot.remote.running && snapshot.remote.state === 'ready' && snapshot.remote.origin !== undefined\n ? (probes.remote ?? defaultRemoteProbe)(snapshot.remote.origin)\n : Promise.resolve<RemoteObservation>({ state: 'not-applicable' })\n const [firewall, remoteObservation] = await Promise.all([\n (probes.firewall ?? defaultFirewallProbe())(snapshot.lan.port),\n remoteProbe,\n ])\n checks.push(check(\n 'versions',\n 'ok',\n 'versions-current',\n '版本兼容',\n `插件 ${DSH_MOBILE_VERSION},DSH ${snapshot.dshVersion},Android App 最低 ${MINIMUM_ANDROID_APP_VERSION}。`,\n ))\n\n if (snapshot.lan.configured === false) {\n checks.push(check('network', 'error', 'lan-setup-required', '局域网配置', '尚未完成局域网初始化。', '返回局域网页选择网卡并完成配置。'))\n } else if (snapshot.lan.networkError !== undefined) {\n checks.push(check('network', 'error', 'network-unavailable', '局域网网卡', '已保存的网卡当前不可用。', '重新运行 dsh-mobile setup。'))\n } else if (snapshot.lan.configuredInterface !== undefined) {\n const interfaceName = snapshot.lan.interfaceName ?? snapshot.lan.configuredInterface\n checks.push(check(\n 'network',\n 'ok',\n 'network-interface',\n '局域网网卡',\n `正在跟随 ${interfaceName}。`,\n undefined,\n { interfaceName },\n ))\n } else {\n checks.push(check('network', 'info', 'network-fixed', '局域网网卡', '当前使用固定网络配置。'))\n }\n\n if (snapshot.lan.running && snapshot.lan.origin !== undefined) {\n const endpointSuffix = maskLanOrigin(snapshot.lan.origin)\n checks.push(check('lan', 'ok', 'lan-ready', '局域网网关', `已监听 ${endpointSuffix},配对入口可用。`, undefined, { endpointSuffix }))\n } else {\n checks.push(check('lan', 'info', 'lan-off', '局域网网关', '当前未开启。', '需要手机直连时开启局域网访问。'))\n }\n\n if (firewall.state === 'ready') {\n checks.push(check('firewall', 'ok', 'firewall-ready', 'Windows 防火墙', '局域网 TCP 与发现规则已启用。'))\n } else if (firewall.state === 'missing') {\n checks.push(check('firewall', 'warning', 'firewall-missing', 'Windows 防火墙', '未找到完整的局域网放行规则。', '以管理员身份重新运行 dsh-mobile setup。'))\n } else if (firewall.state === 'unknown') {\n checks.push(check('firewall', 'info', 'firewall-unknown', 'Windows 防火墙', '系统未允许插件读取防火墙状态。', '若手机找不到电脑,以管理员身份重新运行 setup。'))\n }\n\n if (!snapshot.remote.running || snapshot.remote.state === 'off') {\n checks.push(check('remote', 'info', 'remote-off', '远程通道', '当前未启用。', undefined, { provider: snapshot.remote.provider }))\n } else if (snapshot.remote.provider === 'origin' && snapshot.remote.state === 'ready') {\n checks.push(check('remote', 'info', 'remote-origin-ready', '远程通道',\n '私网 HTTP 后端已就绪;未检测公网 HTTPS、证书或 WebSocket 连通性。',\n '在手机上经反向代理验证连接;代理须保留原始 Host(含端口)并支持 WebSocket。',\n { provider: 'origin' }))\n } else if (snapshot.remote.state === 'ready' && snapshot.remote.origin !== undefined) {\n const endpointSuffix = remoteSuffix(snapshot.remote.origin)\n const facts = {\n provider: snapshot.remote.provider,\n endpointSuffix,\n ...(remoteObservation.latencyMs === undefined ? {} : { latencyMs: remoteObservation.latencyMs }),\n ...(remoteObservation.viaProxy === true ? { viaProxy: true as const } : {}),\n }\n if (remoteObservation.state === 'ready') {\n checks.push(check(\n 'remote',\n remoteObservation.viaProxy === true ? 'warning' : 'ok',\n 'remote-ready',\n '远程通道',\n remoteObservation.viaProxy === true\n ? `电脑经代理可访问 ${endpointSuffix},但尚未验证手机能否连接。`\n : `${snapshot.remote.provider} 公共地址 ${endpointSuffix} 可达,往返约 ${String(remoteObservation.latencyMs ?? 0)} ms。`,\n remoteObservation.viaProxy === true ? '请在手机上测试远程连接。' : undefined,\n facts,\n ))\n } else if (remoteObservation.state === 'rate-limited') {\n checks.push(check(\n 'remote', 'warning', 'remote-rate-limited', '远程通道',\n remoteObservation.viaProxy === true\n ? '电脑经代理访问时观察到限流,尚未验证手机能否连接。'\n : '公共地址可达,但本次检查观察到服务限流。',\n '稍后重试;旧会话会按需加载以减少流量。', facts,\n ))\n } else if (snapshot.remote.provider === 'tailscale' && remoteObservation.fakeIp === true) {\n checks.push(check(\n 'remote',\n 'error',\n 'remote-fake-ip',\n '远程通道',\n 'Tailscale 地址被当前 VPN 或 DNS 代理接管,但 TLS 链路未建立。',\n '切换 VPN 节点或代理模式;仍失败时改用 cpolar。',\n facts,\n ))\n } else {\n checks.push(check('remote', 'error', 'remote-unreachable', '远程通道', '提供方显示已就绪,但公共地址暂不可达。', '点击“重新连接”;仍失败时检查提供方状态。', facts))\n }\n } else if (snapshot.remote.state === 'starting' || snapshot.remote.state === 'connecting' || snapshot.remote.state === 'needs-login') {\n const needsLogin = snapshot.remote.state === 'needs-login'\n checks.push(check(\n 'remote',\n 'warning',\n needsLogin ? 'remote-needs-login' : 'remote-connecting',\n '远程通道',\n needsLogin ? '等待完成 Tailscale 登录。' : '仍在建立连接。',\n needsLogin ? '返回远程页继续登录。' : '等待片刻后重新检查。',\n { provider: snapshot.remote.provider },\n ))\n } else {\n const controllerCode = snapshot.remote.errorCode ?? snapshot.remote.state\n checks.push(check(\n 'remote',\n 'error',\n 'remote-controller-error',\n '远程通道',\n `连接未建立(${controllerCode})。`,\n REMOTE_ERROR_GUIDANCE[controllerCode] ?? '返回远程页点击“重新连接”。',\n { provider: snapshot.remote.provider, controllerCode },\n ))\n }\n\n checks.push(check(\n 'phone-network',\n 'info',\n 'phone-network-unknown',\n '手机网络',\n '电脑无法判断路由器是否隔离了手机。',\n '局域网仍失败时,确认手机与电脑在同一网络,并关闭访客网络或 AP 隔离。',\n ))\n\n const overall = checks.some(entry => entry.status === 'error')\n ? 'error'\n : checks.some(entry => entry.status === 'warning') ? 'attention' : 'ok'\n const summary = overall === 'ok' ? '连接基础检查正常。' : overall === 'attention' ? '发现需要留意的项目。' : '发现会影响连接的问题。'\n const report = [\n 'DSH Mobile 诊断报告',\n `生成时间: ${new Date().toISOString()}`,\n `版本: plugin=${DSH_MOBILE_VERSION}; dsh=${snapshot.dshVersion}; min-app=${MINIMUM_ANDROID_APP_VERSION}`,\n `LAN: ${snapshot.lan.running ? 'on' : 'off'}; endpoint=${maskLanOrigin(snapshot.lan.origin)}`,\n `Remote: provider=${snapshot.remote.provider}; state=${snapshot.remote.state}; endpoint=${remoteSuffix(snapshot.remote.origin)}`,\n ...checks.map(reportLine),\n ].join('\\n')\n return Object.freeze({\n version: 1,\n generatedAt: Date.now(),\n overall,\n versions: Object.freeze({ plugin: DSH_MOBILE_VERSION, dsh: snapshot.dshVersion, minimumAndroidApp: MINIMUM_ANDROID_APP_VERSION }),\n summary,\n checks: Object.freeze(checks),\n report,\n })\n}\n","/**\n * Instructions handed to the DSH agent when the user runs `/mobile <task>`.\n * The agent edits files under the DSH home; this text is what tells it the\n * layout of the mobile-access customization surface so it does not guess.\n *\n * A snapshot of the current customization state (whether mobile.css /\n * mobile.js exist and which extensions are installed) is injected at the\n * top of the guide so the agent does not overwrite earlier work blindly.\n */\n\n/** One installed extension as seen by the agent. */\nexport interface MobileGuideExtensionState {\n readonly id: string\n readonly name: string\n readonly version: string\n}\n\n/** Customization facts collected before steering the agent. */\nexport interface MobileGuideState {\n /** Absolute path of the mobile-access directory the agent edits. */\n readonly directory: string\n /** True when the user's mobile.css exists (a custom style override). */\n readonly hasCustomCss: boolean\n /** True when the user's mobile.js exists (a custom script override). */\n readonly hasCustomJs: boolean\n /** Extensions currently installed under extensions/. */\n readonly extensions: readonly MobileGuideExtensionState[]\n /** Extensions whose host failed to activate. */\n readonly failedExtensionCount: number\n}\n\n/** Compose the guide with the current customization state injected. */\nexport function buildMobileGuide(state: MobileGuideState): string {\n const styleLine = state.hasCustomCss ? '存在(当前生效的自定义样式)' : '不存在(使用内置默认样式)'\n const scriptLine = state.hasCustomJs ? '存在(当前生效的自定义脚本)' : '不存在(无自定义脚本)'\n const extensionLines = state.extensions.length === 0\n ? '(无)'\n : state.extensions.map(entry => `- ${entry.id}(${entry.name} v${entry.version})`).join('\\n')\n const failureLine = state.failedExtensionCount > 0\n ? `注意:${state.failedExtensionCount} 个扩展的电脑端 host 激活失败,如改动相关扩展请先检查其 host.mjs 与 extension.json。`\n : ''\n const currentState = `## 手机端当前状态(改名前必读,避免覆盖已有定制)\n\n- 定制目录:${state.directory}(所有改动只允许在这里进行)\n- mobile.css:${styleLine}\n- mobile.js:${scriptLine}\n- 已安装扩展:\n${extensionLines}\n${failureLine}\n\n“恢复默认”操作说明:当用户要求恢复默认 / 还原初始外观时,删除 mobile.css 与 mobile.js 两个文件(删除后手机端自动回到内置默认外观,无需创建占位文件),并按需删除 extensions/ 下的扩展目录。\\n\\n`\n\n return `${currentState}${MOBILE_CUSTOMIZATION_GUIDE_BODY}`\n}\n\n/**\n * Static body of the customization guide. Kept separate from the injected\n * state snapshot so the two concerns stay easy to edit independently.\n */\nconst MOBILE_CUSTOMIZATION_GUIDE_BODY = `你在为用户定制 DSH Mobile 的手机端。DSH Mobile 是一个把电脑上的 DeepSeek Harness 带到手机浏览器的插件,手机端界面和能力都来自本机文件。\n\n所有改动只允许在 $DSH_HOME/mobile-access/ 目录内进行,绝不修改 DeepSeek Harness 的源码或其他目录。$DSH_HOME 是 DeepSeek Harness 的配置目录(通常为 ~/.dsh),先确认它的实际路径再操作。\n\n手机端的能力分两层,按用户需求选择改动目标:\n\n1. 界面与交互 —— 只改外观和交互,不需要碰电脑的文件或程序:\n - $DSH_HOME/mobile-access/mobile.css:手机端样式\n - $DSH_HOME/mobile-access/mobile.js:手机端脚本,用 window.dshMobile.register(({ root }) => { ... }) 把内容挂载到 root,返回清理函数\n - 保存后手机端几秒内自动应用,无需重启\n\n2. 电脑端能力 —— 手机需要读电脑文件、执行命令或访问硬件时,创建扩展:\n - 目录:$DSH_HOME/mobile-access/extensions/<id>/,id 用小写字母数字和连字符(如 media-remote)\n - extension.json:{\"schemaVersion\":1,\"id\":\"<id>\",\"name\":\"显示名\",\"version\":\"0.1.0\",\"description\":\"说明\"}\n - host.mjs:电脑端 Node.js 代码(可信本地代码,可读写文件、执行命令)。导出默认函数 (api) => { ... },用 api.action('名称', { input, run }) 注册动作、api.route({ method, path, handle }) 注册路由、api.effect(fn) 注册清理。input 可以直接使用 api.schema.object(...) 等 Schemastery schema,也可以传入带 parse(value) 的适配器;两种形式都会在动作执行前校验并规范化输入。\n - mobile.js:手机端脚本,用 window.dshMobile.define({ apiVersion:1, id:'<id>', activate(api) { ... } }),activate 返回清理函数\n - mobile.css:手机端样式(可选)\n - assets/:手机端静态资源(可选)\n - mobile.js 里用 api.host.invoke('动作名', 输入) 调 host.mjs 的 action(请求会按 application/json 发送),api.host.fetch('/路由路径') 调 route,api.host.assetUrl('相对路径') 生成与当前版本绑定的资源地址\n - 也可以先用命令生成模板:dsh plugin --profile web exec dsh-mobile extension create <id> --name \"<名称>\",再在模板上改\n\n安全约束:\n- host.mjs 拥有电脑用户的完整权限,绝不能放入不可信代码,也不要让手机端无条件执行任意命令\n- 所有改动只限 $DSH_HOME/mobile-access/,不要动 DeepSeek Harness 源码\n\n完成前请自检:\n- 改动涉及 mobile.js 或扩展的 mobile.js / host.mjs 时,先做语法检查再保存(如 node --check <file>),确保没有语法错误\n- 创建或修改扩展后,确认 extension.json 的 schemaVersion 为 1、id 与目录名一致、且 id 只含小写字母数字和连字符\n- 扩展的 host.mjs 若在完成前无法激活,先修正而不是留下损坏的扩展\n- 完成后检查自己实际写入了哪些文件,向用户简要说明改了什么、手机端会有什么变化`\n","import { createHash, randomBytes } from 'node:crypto'\nimport { spawn } from 'node:child_process'\nimport { lstat, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'\nimport { isIP } from './ip.js'\nimport { tmpdir } from 'node:os'\nimport { isAbsolute, join, resolve } from 'node:path'\nimport { validateFrpPublicOrigin, validateFrpServerAddress, validateFrpServerPort, validateFrpToken, type FrpSettings } from './frp-config.js'\nimport {\n FRP_CADDY_IMPORT_LINE,\n FRP_CADDY_SNIPPET_MARKER,\n FRP_CADDY_SNIPPET_PATH,\n createCaddySite,\n} from './frp-template.js'\nimport { isGloballyRoutableIpv4 } from './network.js'\n\nconst FRP_VERSION = '0.70.1'\nconst SSH_TIMEOUT_MS = 300_000\nconst MAX_OUTPUT_BYTES = 96 * 1024\n\nconst LINUX_ARTIFACTS = Object.freeze({\n x64: Object.freeze({\n directory: `frp_${FRP_VERSION}_linux_amd64`,\n url: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_amd64.tar.gz`,\n sha256: '333da23d1b9009d7c01638e9ba38cf4600f7d37d393f854e96ee1396adefa9a6',\n }),\n arm64: Object.freeze({\n directory: `frp_${FRP_VERSION}_linux_arm64`,\n url: `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_arm64.tar.gz`,\n sha256: '3990f396a9a490ee7f0e5f355287750ed41520064ed999eab443b5e9a78d773d',\n }),\n})\n\nexport interface VpsDeploymentInput {\n readonly sshUser: string\n readonly sshPort: number\n readonly sshKeyPath?: string\n /**\n * User-confirmed SHA256 host-key fingerprints (`SHA256:…`, one per server key).\n * The deployment aborts unless every key the server currently presents is confirmed.\n */\n readonly hostFingerprints: readonly string[]\n}\n\n/** One SSH server host key with its OpenSSH-style SHA256 fingerprint. */\nexport interface VpsHostKey {\n readonly keyType: string\n readonly fingerprint: string\n}\n\nexport interface VpsDeploymentCheck {\n readonly id: string\n readonly status: 'ok' | 'warning' | 'error'\n readonly detail: string\n}\n\nexport interface VpsDeploymentResult {\n readonly version: 1\n readonly deployed: boolean\n readonly serverAddress: string\n readonly publicOrigin: string\n readonly checks: readonly VpsDeploymentCheck[]\n}\n\nexport interface VpsDeploymentOptions {\n readonly runSsh?: (input: VpsDeploymentInput, serverAddress: string, script: string) => Promise<{ stdout: string; stderr: string }>\n readonly runKeyscan?: (input: { readonly sshUser: unknown; readonly sshPort: unknown }, serverAddress: string) => Promise<string>\n readonly runSshFetch?: (input: VpsSshFetchInput, serverAddress: string) => Promise<string>\n readonly runRemoteScript?: (input: VpsDeploymentInput, serverAddress: string, script: string) => Promise<{ stdout: string; stderr: string }>\n readonly log?: (event: string, fields: Readonly<Record<string, string | number | boolean>>) => void\n}\n\nexport class VpsSshError extends Error {\n constructor(message: string, readonly stdout: string, readonly stderr: string, options?: ErrorOptions) {\n super(message, options)\n }\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replaceAll(\"'\", \"'\\\\''\")}'`\n}\n\n/**\n * Reject loopback, private, and other non-routable IPv4 literals as VPS SSH\n * targets. A self-hosted deployment always addresses a public server; the\n * shared frpc settings stay permissive so local loopback test rigs keep working.\n */\nfunction assertPublicSshTarget(serverAddress: string): void {\n if (isIP(serverAddress) === 4 && !isGloballyRoutableIpv4(serverAddress)) {\n throw new Error('vps_server_not_public')\n }\n}\n\n/**\n * Validate a VPS address for every operation that opens a network connection\n * to it (scan, deploy, cleanup). IPv6 is unsupported by the SSH flow and\n * loopback/private targets are never valid VPS endpoints.\n */\nfunction validateVpsServerTarget(serverAddress: string): string {\n const address = validateFrpServerAddress(serverAddress)\n if (isIP(address) !== 0 && address.includes(':')) throw new Error('vps_ipv6_ssh_not_supported')\n assertPublicSshTarget(address)\n return address\n}\n\nfunction validSshUser(value: unknown): string {\n if (typeof value !== 'string' || value.length < 1 || value.length > 64 || !/^[a-z_][a-z0-9_.-]*[$]?$/iu.test(value)) {\n throw new Error('vps_ssh_user_invalid')\n }\n return value\n}\n\nfunction validSshPort(value: unknown): number {\n if (!Number.isSafeInteger(value) || Number(value) < 1 || Number(value) > 65_535) throw new Error('vps_ssh_port_invalid')\n return Number(value)\n}\n\nfunction validSshKeyPath(value: unknown): string | undefined {\n if (value === undefined || value === '') return undefined\n if (typeof value !== 'string' || !isAbsolute(value) || value.length > 4_096 || /[\\u0000-\\u001f\\u007f]/u.test(value)) {\n throw new Error('vps_ssh_key_invalid')\n }\n return resolve(value)\n}\n\nexport function parseVpsDeploymentInput(value: unknown): VpsDeploymentInput {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('vps_deploy_input_invalid')\n const record = value as Record<string, unknown>\n if (Reflect.ownKeys(record).some(key => !['sshUser', 'sshPort', 'sshKeyPath', 'hostFingerprints'].includes(String(key)))) {\n throw new Error('vps_deploy_input_invalid')\n }\n const sshKeyPath = validSshKeyPath(record.sshKeyPath)\n return Object.freeze({\n sshUser: validSshUser(record.sshUser),\n sshPort: validSshPort(record.sshPort),\n ...(sshKeyPath === undefined ? {} : { sshKeyPath }),\n hostFingerprints: Object.freeze(parseVpsHostFingerprints(record.hostFingerprints)),\n })\n}\n\n/** Validate user-confirmed SHA256 host-key fingerprints (`SHA256:…`). */\nexport function parseVpsHostFingerprints(value: unknown): string[] {\n if (!Array.isArray(value) || value.length < 1 || value.length > 8) throw new Error('vps_host_key_unconfirmed')\n const fingerprints: string[] = []\n for (const entry of value) {\n if (typeof entry !== 'string' || !/^SHA256:[A-Za-z0-9+/]{40,60}={0,2}$/u.test(entry) || entry.length > 96) {\n throw new Error('vps_host_key_unconfirmed')\n }\n fingerprints.push(entry)\n }\n return [...new Set(fingerprints)]\n}\n\nconst SUPPORTED_HOST_KEY_TYPES = new Set([\n 'ssh-rsa',\n 'ecdsa-sha2-nistp256',\n 'ecdsa-sha2-nistp384',\n 'ecdsa-sha2-nistp521',\n 'ssh-ed25519',\n])\n\n/** Format a raw host public key the way OpenSSH displays it (`SHA256:…` without padding). */\nexport function fingerprintHostPublicKey(keyType: string, base64Key: string): string {\n if (!SUPPORTED_HOST_KEY_TYPES.has(keyType)) throw new Error('vps_host_key_unsupported_type')\n if (!/^[A-Za-z0-9+/]+={0,2}$/u.test(base64Key) || base64Key.length < 24 || base64Key.length > 1_024) {\n throw new Error('vps_host_key_invalid')\n }\n const raw = Buffer.from(base64Key, 'base64')\n if (raw.length < 16 || raw.length > 768) throw new Error('vps_host_key_invalid')\n return `SHA256:${createHash('sha256').update(raw).digest('base64').replace(/=+$/u, '')}`\n}\n\nfunction parseKeyscanOutput(output: string): Array<{ keyType: string; base64Key: string }> {\n const keys: Array<{ keyType: string; base64Key: string }> = []\n for (const line of output.split(/\\r?\\n/u)) {\n const trimmed = line.trim()\n if (trimmed === '' || trimmed.startsWith('#')) continue\n // The host field is optional: ssh-cat fallback lines carry only type and key.\n // It is discarded anyway; pinned known_hosts lines are rebuilt from the\n // validated server address, so accepting host-less lines is safe.\n const match = /^(?:\\S+\\s+)?(ssh-rsa|ecdsa-sha2-nistp\\d+|ssh-ed25519)\\s+([A-Za-z0-9+/]+={0,2})(\\s|$)/u.exec(trimmed)\n if (match?.[1] === undefined || match[2] === undefined) throw new Error('vps_host_key_invalid')\n keys.push({ keyType: match[1], base64Key: match[2] })\n }\n return keys\n}\n\n/** Base SSH options shared by long deployment sessions. Keepalives survive NAT\n * middleboxes during minute-long apt/pip phases; host identity stays pinned. */\nfunction sshSessionOptions(knownHostsFile: string): string[] {\n return [\n '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=15',\n '-o', 'ServerAliveInterval=15', '-o', 'ServerAliveCountMax=8',\n '-o', 'StrictHostKeyChecking=yes', `-o UserKnownHostsFile=${knownHostsFile}`,\n ]\n}\n\n/** Lenient scan probe: garbage fails the whole fetch, comment-only output falls back. */\nfunction tryParseKeyscanOutput(output: string): Array<{ keyType: string; base64Key: string }> | undefined {\n try {\n return parseKeyscanOutput(output)\n } catch {\n return undefined\n }\n}\n\nasync function gitBundledKeyscan(): Promise<string | undefined> {\n if (process.platform !== 'win32') return undefined\n const programFiles = process.env['ProgramFiles'] ?? 'C:\\\\Program Files'\n const candidate = join(programFiles, 'Git', 'usr', 'bin', 'ssh-keyscan.exe')\n try {\n const entry = await lstat(candidate)\n if (!entry.isFile()) return undefined\n } catch {\n return undefined\n }\n return candidate\n}\n\nasync function defaultRunKeyscan(input: { readonly sshUser: unknown; readonly sshPort: unknown }, serverAddress: string): Promise<string> {\n const keyscan = process.platform === 'win32' ? 'ssh-keyscan.exe' : 'ssh-keyscan'\n const args = ['-T', '10', '-p', String(input.sshPort), '-t', 'rsa,ecdsa,ed25519', serverAddress]\n const first = await runProcess(keyscan, args, undefined, 30_000).catch(() => undefined)\n if (first !== undefined && tryParseKeyscanOutput(first.stdout)?.length) return first.stdout\n // Old keyscan binaries fail KEX against modern servers (non-zero exit, no\n // keys); retry with the Git for Windows copy when present before giving up.\n // An empty result lets the caller fall back to an authenticated read.\n const bundled = await gitBundledKeyscan()\n if (bundled !== undefined) {\n const second = await runProcess(bundled, args, undefined, 30_000).catch(() => undefined)\n if (second !== undefined && tryParseKeyscanOutput(second.stdout)?.length) return second.stdout\n }\n return first?.stdout ?? ''\n}\n\nexport interface VpsSshFetchInput {\n readonly sshUser: unknown\n readonly sshPort: unknown\n readonly sshKeyPath?: unknown\n}\n\n/**\n * Read the server's public host keys over an authenticated connection with a\n * throwaway known_hosts file. Fallback for keyscan binaries that cannot\n * negotiate with modern servers; output feeds the same confirm-and-pin pipeline.\n */\nasync function defaultRunSshFetch(input: VpsSshFetchInput, serverAddress: string): Promise<string> {\n const ssh = process.platform === 'win32' ? 'ssh.exe' : 'ssh'\n const sshUser = validSshUser(input.sshUser)\n const sshPort = validSshPort(input.sshPort)\n const sshKeyPath = validSshKeyPath(input.sshKeyPath)\n const nullDevice = process.platform === 'win32' ? 'NUL' : '/dev/null'\n const args = [\n '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=15',\n '-o', 'StrictHostKeyChecking=no', `-o UserKnownHostsFile=${nullDevice}`,\n ...(sshKeyPath === undefined ? [] : ['-i', sshKeyPath]),\n '-p', String(sshPort), `${sshUser}@${serverAddress}`, 'cat /etc/ssh/ssh_host_*_key.pub',\n ]\n const { stdout } = await runProcess(ssh, args, undefined, 30_000).catch((error: unknown) => {\n throw new VpsSshError('vps_host_key_unavailable', '', error instanceof Error ? error.message : String(error))\n })\n const lines: string[] = []\n for (const line of stdout.split(/\\r?\\n/u)) {\n const match = /^(ssh-rsa|ecdsa-sha2-nistp\\d+|ssh-ed25519)\\s+([A-Za-z0-9+/]+={0,2})(\\s|$)/u.exec(line.trim())\n if (match?.[1] !== undefined && match[2] !== undefined) lines.push(`${serverAddress} ${match[1]} ${match[2]}`)\n }\n return lines.length === 0 ? stdout : `${lines.join('\\n')}\\n`\n}\n\n/**\n * Scan host keys with keyscan first, then fall back to an authenticated read\n * when the local keyscan binary cannot negotiate with the server. Both paths\n * feed the same confirm-and-pin pipeline, so a fallback never weakens the\n * user-confirmation gate.\n */\nasync function scanHostKeys(\n input: VpsSshFetchInput,\n serverAddress: string,\n options: VpsDeploymentOptions,\n): Promise<string> {\n const runKeyscan = options.runKeyscan ?? defaultRunKeyscan\n // A rejecting keyscan (old binary failing KEX, missing binary, DNS error)\n // is equivalent to an empty scan: fall through to the authenticated read.\n const scanned = await runKeyscan({ sshUser: input.sshUser, sshPort: input.sshPort }, serverAddress).catch(() => '')\n if (tryParseKeyscanOutput(scanned)?.length) return scanned\n options.log?.('host-keys-keyscan-empty', { serverAddress })\n const runSshFetch = options.runSshFetch ?? defaultRunSshFetch\n const fetched = await runSshFetch(input, serverAddress)\n if (tryParseKeyscanOutput(fetched)?.length) {\n options.log?.('host-keys-ssh-fallback', { serverAddress })\n return fetched\n }\n throw new Error('vps_host_key_unavailable')\n}\n\n/**\n * Fetch the server's current host keys and return them with OpenSSH-style\n * fingerprints for the user to confirm out of band (for example against the\n * VPS console) before any destructive or authenticated deployment step.\n */\nexport async function fetchVpsHostKeys(\n serverAddress: string,\n input: VpsSshFetchInput,\n options: VpsDeploymentOptions = {},\n): Promise<readonly VpsHostKey[]> {\n const address = validateVpsServerTarget(serverAddress)\n const sshUser = validSshUser(input.sshUser)\n const sshPort = validSshPort(input.sshPort)\n const output = await scanHostKeys({ sshUser, sshPort, sshKeyPath: input.sshKeyPath }, address, options)\n const keys = parseKeyscanOutput(output)\n if (keys.length === 0) throw new Error('vps_host_key_unavailable')\n const seen = new Set<string>()\n const hostKeys: VpsHostKey[] = []\n for (const key of keys) {\n const fingerprint = fingerprintHostPublicKey(key.keyType, key.base64Key)\n if (seen.has(fingerprint)) continue\n seen.add(fingerprint)\n hostKeys.push(Object.freeze({ keyType: key.keyType, fingerprint }))\n }\n options.log?.('host-keys-fetched', { serverAddress: address, keyTypes: hostKeys.length })\n return Object.freeze(hostKeys)\n}\n\n/**\n * Verify that every host key the server currently presents was confirmed by the\n * user, then return a pinned known_hosts body. Fails closed on rotation,\n * replacement, or unexpected extra keys.\n */\nexport function buildPinnedKnownHosts(\n serverAddress: string,\n sshPort: number,\n keyscanOutput: string,\n confirmedFingerprints: readonly string[],\n): string {\n const address = validateFrpServerAddress(serverAddress)\n const port = validSshPort(sshPort)\n const confirmed = new Set(parseVpsHostFingerprints([...confirmedFingerprints]))\n const keys = parseKeyscanOutput(keyscanOutput)\n if (keys.length === 0) throw new Error('vps_host_key_unavailable')\n const host = port === 22 ? address : `[${address}]:${port}`\n const lines: string[] = []\n for (const key of keys) {\n const fingerprint = fingerprintHostPublicKey(key.keyType, key.base64Key)\n if (!confirmed.has(fingerprint)) throw new Error('vps_host_key_mismatch')\n lines.push(`${host} ${key.keyType} ${key.base64Key}`)\n }\n return `${lines.join('\\n')}\\n`\n}\n\nfunction safeOutput(value: string, token: string): string {\n const redacted = token === '' ? value : value.replaceAll(token, '<redacted>')\n return redacted.replace(/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f]/gu, '').slice(0, 8_192).trim()\n}\n\nfunction parseChecks(stdout: string, stderr: string, token: string): readonly VpsDeploymentCheck[] {\n const checks: VpsDeploymentCheck[] = []\n for (const line of stdout.split(/\\r?\\n/u)) {\n const match = /^DSH_MOBILE_CHECK\\s+([a-z0-9_-]+)\\s+(ok|warning|error)\\s+(.+)$/iu.exec(line)\n if (match !== null) checks.push(Object.freeze({ id: match[1]!, status: match[2]!.toLowerCase() as VpsDeploymentCheck['status'], detail: safeOutput(match[3]!, token) }))\n }\n if (checks.length === 0 && stderr.trim() !== '') {\n checks.push(Object.freeze({ id: 'remote-command', status: 'error', detail: safeOutput(stderr, token) || 'VPS 返回了未分类错误。' }))\n }\n return Object.freeze(checks)\n}\n\n/**\n * One-line failure detail for transport-level failures: prefer the failed\n * remote check, otherwise use the last stderr line (a `set -eu` abort has no\n * check line) instead of dumping the whole transcript into the UI.\n */\nfunction failureDetail(stdout: string, stderr: string, token: string): string {\n const parsed = parseChecks(`${stdout}\\n${stderr}`, '', token)\n const failed = parsed.find(check => check.status === 'error')\n if (failed !== undefined) return failed.detail\n for (const stream of [stderr, stdout]) {\n const lines = stream.split(/\\r?\\n/u).map(line => line.trim()).filter(line => line !== '')\n const last = lines[lines.length - 1]\n if (last !== undefined) return safeOutput(last, token)\n }\n return ''\n}\n\nfunction deploymentScript(settings: FrpSettings): string {\n const amd64 = LINUX_ARTIFACTS.x64\n const arm64 = LINUX_ARTIFACTS.arm64\n const config = [\n 'bindAddr = \"0.0.0.0\"',\n `bindPort = ${String(settings.serverPort)}`,\n 'proxyBindAddr = \"127.0.0.1\"',\n 'vhostHTTPPort = 7080',\n 'auth.method = \"token\"',\n `auth.token = ${JSON.stringify(settings.token)}`,\n '',\n ].join('\\n')\n const publicHost = new URL(settings.publicOrigin).hostname\n const publicIp = isGloballyRoutableIpv4(publicHost)\n // The site content is shared with the manual template; only the wiring\n // (snippet file + one import line) differs.\n const caddySite = createCaddySite(publicHost)\n const caddySnippet = `${FRP_CADDY_SNIPPET_MARKER}\\n${caddySite.trimEnd()}\\n`\n const ipCertificateSetup = publicIp ? `\nexport DEBIAN_FRONTEND=noninteractive\napt-get install -y python3-venv\nif [ ! -x /opt/dsh-mobile/certbot-venv/bin/certbot ]; then\n python3 -m venv /opt/dsh-mobile/certbot-venv\n /opt/dsh-mobile/certbot-venv/bin/pip install --disable-pip-version-check 'certbot==5.8.0'\nfi\nsystemctl stop caddy.service || true\nif ! /opt/dsh-mobile/certbot-venv/bin/certbot certonly --standalone --preferred-profile shortlived --ip-address ${publicHost} --agree-tos --register-unsafely-without-email --non-interactive --keep-until-expiring; then\n systemctl start caddy.service || true\n fail \"公网 IP HTTPS 证书申请失败;请确认 80/tcp 可从公网访问。\"\nfi\ninstall -d -m 0750 -o caddy -g caddy /var/lib/caddy/dsh-mobile-certs\ninstall -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/fullchain.pem /var/lib/caddy/dsh-mobile-certs/fullchain.pem\ninstall -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/privkey.pem /var/lib/caddy/dsh-mobile-certs/privkey.pem\ncat > /usr/local/sbin/dsh-mobile-cert-renew <<'DSH_MOBILE_CERT_RENEW'\n#!/bin/sh\nset -eu\nsystemctl stop caddy.service\ntrap 'systemctl start caddy.service' EXIT\n/opt/dsh-mobile/certbot-venv/bin/certbot renew --cert-name ${publicHost} --preferred-profile shortlived --non-interactive\ninstall -d -m 0750 -o caddy -g caddy /var/lib/caddy/dsh-mobile-certs\ninstall -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/fullchain.pem /var/lib/caddy/dsh-mobile-certs/fullchain.pem\ninstall -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/privkey.pem /var/lib/caddy/dsh-mobile-certs/privkey.pem\nDSH_MOBILE_CERT_RENEW\nchmod 0755 /usr/local/sbin/dsh-mobile-cert-renew\ncat > /etc/systemd/system/dsh-mobile-cert-renew.service <<'DSH_MOBILE_CERT_SERVICE'\n[Unit]\nDescription=Renew DSH Mobile public IP TLS certificate\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=oneshot\nExecStart=/usr/local/sbin/dsh-mobile-cert-renew\nDSH_MOBILE_CERT_SERVICE\ncat > /etc/systemd/system/dsh-mobile-cert-renew.timer <<'DSH_MOBILE_CERT_TIMER'\n[Unit]\nDescription=Daily DSH Mobile public IP TLS certificate renewal check\n\n[Timer]\nOnCalendar=daily\nRandomizedDelaySec=2h\nPersistent=true\nUnit=dsh-mobile-cert-renew.service\n\n[Install]\nWantedBy=timers.target\nDSH_MOBILE_CERT_TIMER\ncheck certificate ok \"Let's Encrypt 公网 IP 证书已安装并启用每日自动续期。\"\n` : ''\n return `#!/bin/sh\nset -eu\numask 077\n\nfail() { echo \"DSH_MOBILE_CHECK remote-command error $1\" >&2; exit 1; }\ncheck() { echo \"DSH_MOBILE_CHECK $1 $2 $3\"; }\n\n# Serialize concurrent deploys: two writers racing sed -i on the Caddyfile\n# can duplicate the import line and break validation. Uninstall takes the\n# same lock, so deploy and cleanup also exclude each other.\nif command -v flock >/dev/null 2>&1; then\n exec 9>/tmp/dsh-mobile-deploy.lock\n flock -n 9 || fail \"已有部署或清理正在进行,请稍后再试。\"\nfi\n\n[ \"$(id -u)\" = \"0\" ] || fail \"请使用 root SSH 账号。\"\ncommand -v systemctl >/dev/null 2>&1 || fail \"VPS 不支持 systemd。\"\ncommand -v tar >/dev/null 2>&1 || fail \"VPS 缺少 tar。\"\ncommand -v curl >/dev/null 2>&1 || fail \"VPS 缺少 curl。\"\ncommand -v sha256sum >/dev/null 2>&1 || fail \"VPS 缺少 sha256sum。\"\ncommand -v useradd >/dev/null 2>&1 || fail \"VPS 缺少 useradd。\"\n\nif [ -r /etc/os-release ]; then . /etc/os-release; else fail \"无法识别 VPS 系统。\"; fi\ncase \"\\${ID:-}\" in\n debian|ubuntu) ;;\n *) fail \"首版 VPS 部署只支持 Debian/Ubuntu。\" ;;\nesac\ncheck os ok \"\\${PRETTY_NAME:-Debian/Ubuntu}\"\n\nif command -v ss >/dev/null 2>&1 && ss -ltnH | awk '{print $4}' | grep -Eq '(^|:)${String(settings.serverPort)}$'; then\n systemctl is-active --quiet dsh-mobile-frps.service || fail \"端口 ${String(settings.serverPort)} 已被占用。\"\nfi\n\nif ! command -v caddy >/dev/null 2>&1; then\n export DEBIAN_FRONTEND=noninteractive\n # A previous interrupted run may have left these files unreadable because\n # the deployment uses umask 077. APT reads repositories as the _apt user.\n chmod 0644 /usr/share/keyrings/caddy-stable-archive-keyring.gpg 2>/dev/null || true\n chmod 0644 /etc/apt/sources.list.d/caddy-stable.list 2>/dev/null || true\n apt-get update\n apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl gnupg\n curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor --yes -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg\n curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' -o /etc/apt/sources.list.d/caddy-stable.list\n chmod 0644 /usr/share/keyrings/caddy-stable-archive-keyring.gpg /etc/apt/sources.list.d/caddy-stable.list\n apt-get update\n apt-get install -y caddy\nfi\ncheck caddy ok \"Caddy 已安装。\"\n\n# The site lives in our own snippet file; the main Caddyfile only gains one\n# import line, so existing user content is never rewritten or merged.\ncaddy_import='${FRP_CADDY_IMPORT_LINE}'\ninstall -d -m 0755 /etc/caddy\ncaddyfile_ready=false\nif [ ! -e /etc/caddy/Caddyfile ]; then\n printf '%s\\n' \"$caddy_import\" > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\nelif grep -Eq '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile; then\n # The snippet may carry global options (IP mode default_sni), which must\n # precede all site blocks after import inlining: rebuild the file with a\n # single import on top. Removal uses grep -v with the exact gate pattern\n # above (not sed -i, whose in-place delete proved unreliable here), and the\n # result is verified to carry exactly one import before replacing the file.\n {\n printf '%s\\n' \"$caddy_import\"\n grep -Ev '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile || true\n } > /etc/caddy/Caddyfile.dsh-new\n [ \"$(grep -Ec '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile.dsh-new)\" = 1 ] \\\n || fail \"Caddyfile import 整理失败,未做任何修改。\"\n cat /etc/caddy/Caddyfile.dsh-new > /etc/caddy/Caddyfile\n rm -f /etc/caddy/Caddyfile.dsh-new\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\nelif [ ! -s /etc/caddy/Caddyfile ]; then\n printf '%s\\n' \"$caddy_import\" > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\nelif grep -q '^# DSH Mobile removed its site' /etc/caddy/Caddyfile; then\n # Leftover placeholder from our own uninstall: drop only that line, then\n # ensure the import exists exactly once (same grep -v + count discipline).\n grep -Ev '^# DSH Mobile removed its site.*$' /etc/caddy/Caddyfile > /etc/caddy/Caddyfile.dsh-new || true\n grep -Eq '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile.dsh-new \\\n || printf '%s\\n' \"$caddy_import\" >> /etc/caddy/Caddyfile.dsh-new\n [ \"$(grep -Ec '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile.dsh-new)\" = 1 ] \\\n || fail \"Caddyfile import 整理失败,未做任何修改。\"\n cat /etc/caddy/Caddyfile.dsh-new > /etc/caddy/Caddyfile\n rm -f /etc/caddy/Caddyfile.dsh-new\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\nelse\n caddy_hash=\"$(sha256sum /etc/caddy/Caddyfile | awk '{print $1}')\"\n if [ \"$caddy_hash\" = '66177d46fa761acb07208065db9b0274cb1b12c02ac43b9bfc9857b698b1ccfe' ]; then\n printf '%s\\n' \"$caddy_import\" > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\n elif grep -q '^# Managed by DSH Mobile$' /etc/caddy/Caddyfile; then\n # Legacy whole-file layout: the entire file is ours by construction.\n printf '%s\\n' \"$caddy_import\" > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\n elif grep -q '^:80[[:space:]]*{' /etc/caddy/Caddyfile \\\n && grep -q 'root [*] /usr/share/caddy' /etc/caddy/Caddyfile \\\n && grep -q '^[[:space:]]*file_server[[:space:]]*$' /etc/caddy/Caddyfile; then\n printf '%s\\n' \"$caddy_import\" > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n caddyfile_ready=true\n fi\nfi\nif [ \"$caddyfile_ready\" != true ]; then\n fail \"已有 Caddyfile,请先备份,然后加一行 ${FRP_CADDY_IMPORT_LINE},或手动合并站点。\"\nfi\n\n${ipCertificateSetup}\n\narch=\"$(uname -m)\"\ncase \"$arch\" in\n x86_64|amd64) url=${shellQuote(amd64.url)}; expected=${shellQuote(amd64.sha256)}; directory=${shellQuote(amd64.directory)} ;;\n aarch64|arm64) url=${shellQuote(arm64.url)}; expected=${shellQuote(arm64.sha256)}; directory=${shellQuote(arm64.directory)} ;;\n *) fail \"只支持 Linux x86_64 和 arm64。\" ;;\nesac\n\ntmp=\"$(mktemp -d /tmp/dsh-mobile-frp.XXXXXX)\"\ncleanup() { rm -rf \"$tmp\"; [ -z \"\\${DSH_MOBILE_FRP_ARCHIVE:-}\" ] || rm -f \"$DSH_MOBILE_FRP_ARCHIVE\"; }\ntrap cleanup EXIT HUP INT TERM\narchive=\"$tmp/frp.tar.gz\"\nif [ -n \"\\${DSH_MOBILE_FRP_ARCHIVE:-}\" ]; then\n [ -f \"$DSH_MOBILE_FRP_ARCHIVE\" ] || fail \"上传的 frps 安装包不存在。\"\n cp \"$DSH_MOBILE_FRP_ARCHIVE\" \"$archive\"\nelse\n curl --fail --location --proto '=https' --tlsv1.2 --output \"$archive\" \"$url\"\nfi\nactual=\"$(sha256sum \"$archive\" | awk '{print $1}')\"\n[ \"$actual\" = \"$expected\" ] || fail \"frps 下载校验失败。\"\ntar -xzf \"$archive\" -C \"$tmp\" \"$directory/frps\"\n\ninstall -d -m 0755 /usr/local/libexec/dsh-mobile/frp/${FRP_VERSION}\ninstall -m 0755 \"$tmp/$directory/frps\" /usr/local/libexec/dsh-mobile/frp/${FRP_VERSION}/frps\n# The account must exist before anything references its group below.\ndsh_mobile_created=false\nif ! id -u dsh-mobile >/dev/null 2>&1; then\n useradd --system --home-dir /nonexistent --shell /usr/sbin/nologin --no-create-home dsh-mobile\n dsh_mobile_created=true\nfi\ninstall -d -m 0750 -o root -g dsh-mobile /etc/dsh-mobile\nif [ \"$dsh_mobile_created\" = true ]; then\n # Ownership record: uninstall removes the account only when this deployment created it.\n touch /etc/dsh-mobile/.owns-account\n check account ok \"已创建 dsh-mobile 系统用户。\"\nelse\n check account ok \"复用已有的 dsh-mobile 系统用户(卸载时将保留)。\"\nfi\ncat > /etc/dsh-mobile/frps.toml <<'DSH_MOBILE_FRPS_CONFIG'\n${config}DSH_MOBILE_FRPS_CONFIG\nchown root:dsh-mobile /etc/dsh-mobile/frps.toml\nchmod 0640 /etc/dsh-mobile/frps.toml\n\ncat > /etc/systemd/system/dsh-mobile-frps.service <<'DSH_MOBILE_FRPS_UNIT'\n[Unit]\nDescription=DSH Mobile self-hosted FRP server\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nUser=dsh-mobile\nGroup=dsh-mobile\nExecStart=/usr/local/libexec/dsh-mobile/frp/${FRP_VERSION}/frps -c /etc/dsh-mobile/frps.toml\nRestart=on-failure\nRestartSec=5s\nNoNewPrivileges=true\nPrivateTmp=true\nProtectHome=true\nProtectSystem=strict\n\n[Install]\nWantedBy=multi-user.target\nDSH_MOBILE_FRPS_UNIT\n\ncat > ${FRP_CADDY_SNIPPET_PATH} <<'DSH_MOBILE_CADDY_SNIPPET'\n${caddySnippet}DSH_MOBILE_CADDY_SNIPPET\nchmod 0644 ${FRP_CADDY_SNIPPET_PATH}\ncaddy validate --config /etc/caddy/Caddyfile\nsystemctl daemon-reload\n# Restart (not just start) so a redeploy over a running previous generation\n# actually picks up the new frps token and config instead of keeping the old\n# process alive with stale credentials.\nsystemctl enable dsh-mobile-frps.service\nsystemctl restart dsh-mobile-frps.service\nsystemctl enable --now caddy.service\n${publicIp ? 'systemctl enable --now dsh-mobile-cert-renew.timer' : ''}\nsystemctl reload caddy.service || systemctl restart caddy.service\n\nif command -v ufw >/dev/null 2>&1 && ufw status | grep -q '^Status: active'; then\n ufw allow ${String(settings.serverPort)}/tcp comment 'DSH Mobile FRP control' >/dev/null\n ufw allow 80/tcp comment 'DSH Mobile HTTPS redirect' >/dev/null\n ufw allow 443/tcp comment 'DSH Mobile HTTPS' >/dev/null\n check firewall ok \"UFW 已放行 FRP 控制端口和 HTTPS。\"\nelse\n check firewall warning \"未修改系统防火墙;请确认 ${String(settings.serverPort)}/tcp、80/tcp、443/tcp 已放行。\"\nfi\n\nsystemctl is-active --quiet dsh-mobile-frps.service || fail \"frps 服务启动失败。\"\nsystemctl is-active --quiet caddy.service || fail \"Caddy 服务启动失败。\"\ncheck frps ok \"frps ${FRP_VERSION} 已启动,7080 仅绑定回环地址。\"\ncheck caddy ok \"Caddy 已加载 ${publicHost}。\"\necho DSH_MOBILE_DEPLOYMENT_OK\n`\n}\n\nasync function runProcess(command: string, args: readonly string[], stdin?: string, timeoutMs = SSH_TIMEOUT_MS): Promise<{ stdout: string; stderr: string }> {\n return new Promise((resolveRun, rejectRun) => {\n const child = spawn(command, args, { windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'] })\n let stdout = ''\n let stderr = ''\n const append = (current: string, chunk: Buffer): string => `${current}${chunk.toString('utf8')}`.slice(-MAX_OUTPUT_BYTES)\n const timer = setTimeout(() => { child.kill(); rejectRun(new VpsSshError('vps_ssh_timeout', stdout, stderr)) }, timeoutMs)\n timer.unref()\n child.stdout.on('data', chunk => { stdout = append(stdout, Buffer.from(chunk)) })\n child.stderr.on('data', chunk => { stderr = append(stderr, Buffer.from(chunk)) })\n child.once('error', error => { clearTimeout(timer); rejectRun(new VpsSshError('vps_ssh_unavailable', stdout, stderr, { cause: error })) })\n child.once('close', code => {\n clearTimeout(timer)\n if (code !== 0) rejectRun(new VpsSshError(stderr.includes('Permission denied') ? 'vps_ssh_auth_failed' : 'vps_deploy_failed', stdout, stderr))\n else resolveRun({ stdout, stderr })\n })\n child.stdin.end(stdin, 'utf8')\n })\n}\n\nasync function downloadArtifact(artifact: (typeof LINUX_ARTIFACTS)[keyof typeof LINUX_ARTIFACTS], file: string): Promise<number> {\n const curl = process.platform === 'win32' ? 'curl.exe' : 'curl'\n await runProcess(curl, [\n ...(process.platform === 'win32' ? ['--ipv4'] : []),\n '--fail', '--location', '--silent', '--show-error',\n '--connect-timeout', '15', '--max-time', '180',\n '--proto', '=https', '--tlsv1.2', '--output', file, artifact.url,\n ], undefined, 200_000)\n const bytes = await readFile(file)\n if (createHash('sha256').update(bytes).digest('hex') !== artifact.sha256) throw new Error('vps_download_hash_mismatch')\n return bytes.byteLength\n}\n\nasync function defaultRunSsh(\n input: VpsDeploymentInput,\n serverAddress: string,\n script: string,\n knownHostsFile: string,\n log?: VpsDeploymentOptions['log'],\n): Promise<{ stdout: string; stderr: string }> {\n const ssh = process.platform === 'win32' ? 'ssh.exe' : 'ssh'\n const scp = process.platform === 'win32' ? 'scp.exe' : 'scp'\n // The server identity is pinned to the user-confirmed fingerprints captured\n // before deployment. Unknown or rotated keys abort instead of being accepted.\n const common = sshSessionOptions(knownHostsFile)\n if (input.sshKeyPath !== undefined) common.push('-i', input.sshKeyPath)\n const target = `${input.sshUser}@${serverAddress}`\n const probe = await runProcess(ssh, [...common, '-p', String(input.sshPort), target, 'uname -m'])\n const architecture = probe.stdout.trim()\n const artifact = architecture === 'x86_64' || architecture === 'amd64'\n ? LINUX_ARTIFACTS.x64\n : architecture === 'aarch64' || architecture === 'arm64' ? LINUX_ARTIFACTS.arm64 : undefined\n if (artifact === undefined) throw new Error('vps_arch_unsupported')\n log?.('architecture', { architecture })\n const localDirectory = await mkdtemp(join(tmpdir(), 'dsh-mobile-frp-'))\n const localArchive = join(localDirectory, 'frp.tar.gz')\n const remoteArchive = `/tmp/dsh-mobile-frp-${randomBytes(12).toString('hex')}.tar.gz`\n try {\n log?.('download-start', { source: 'local', architecture })\n const bytes = await downloadArtifact(artifact, localArchive)\n log?.('download-complete', { source: 'local', bytes })\n const scpResult = await runProcess(scp, [...common, '-P', String(input.sshPort), localArchive, `${target}:${remoteArchive}`])\n log?.('upload-complete', { bytes, stderrBytes: Buffer.byteLength(scpResult.stderr) })\n const remoteCommand = input.sshUser === 'root'\n ? `env DSH_MOBILE_FRP_ARCHIVE=${shellQuote(remoteArchive)} sh -s`\n : `sudo -n env DSH_MOBILE_FRP_ARCHIVE=${shellQuote(remoteArchive)} sh -s`\n return await runProcess(ssh, [...common, '-p', String(input.sshPort), target, remoteCommand], script)\n } finally {\n await rm(localDirectory, { recursive: true, force: true })\n }\n}\n\nexport async function deployVps(settings: FrpSettings, input: VpsDeploymentInput, options: VpsDeploymentOptions = {}): Promise<VpsDeploymentResult> {\n const serverPort = validateFrpServerPort(settings.serverPort)\n const token = validateFrpToken(settings.token)\n const publicOrigin = validateFrpPublicOrigin(settings.publicOrigin)\n const serverAddress = validateVpsServerTarget(settings.serverAddress)\n const parsedInput = parseVpsDeploymentInput(input)\n if (parsedInput.sshKeyPath !== undefined) {\n const entry = await lstat(parsedInput.sshKeyPath).catch(() => undefined)\n if (entry === undefined || !entry.isFile() || entry.isSymbolicLink()) throw new Error('vps_ssh_key_invalid')\n }\n // Pin the server identity before any authenticated connection: re-scan the\n // host keys now and require every presented key to be user-confirmed, so a\n // rotation between the UI preview and this deployment aborts safely.\n const keyscanOutput = await scanHostKeys(\n { sshUser: parsedInput.sshUser, sshPort: parsedInput.sshPort, sshKeyPath: parsedInput.sshKeyPath },\n serverAddress,\n options,\n )\n const knownHostsBody = buildPinnedKnownHosts(serverAddress, parsedInput.sshPort, keyscanOutput, parsedInput.hostFingerprints)\n options.log?.('host-keys-verified', { serverAddress })\n const runSsh = options.runSsh ?? (async (sshInput, host, scriptBody) => {\n const workDirectory = await mkdtemp(join(tmpdir(), 'dsh-mobile-known-hosts-'))\n try {\n const knownHostsFile = join(workDirectory, 'known_hosts')\n await writeFile(knownHostsFile, knownHostsBody, { encoding: 'utf8', mode: 0o600 })\n return await defaultRunSsh(sshInput, host, scriptBody, knownHostsFile, options.log)\n } finally {\n await rm(workDirectory, { recursive: true, force: true })\n }\n })\n options.log?.('validated', { serverAddress, serverPort, publicOrigin, sshUser: parsedInput.sshUser, sshPort: parsedInput.sshPort, keyProvided: parsedInput.sshKeyPath !== undefined })\n let result: { stdout: string; stderr: string }\n try {\n options.log?.('ssh-start', { serverAddress, sshPort: parsedInput.sshPort })\n result = await runSsh(parsedInput, serverAddress, deploymentScript({ ...settings, serverAddress, serverPort, token, publicOrigin }))\n options.log?.('ssh-complete', { stdoutBytes: Buffer.byteLength(result.stdout), stderrBytes: Buffer.byteLength(result.stderr) })\n } catch (error) {\n if (error instanceof VpsSshError) {\n // Prefer the failed remote check over raw output: the script reports\n // failures through DSH_MOBILE_CHECK lines on either stream, and the raw\n // concatenation would leak protocol framing into the UI.\n const detail = failureDetail(error.stdout, error.stderr, token)\n options.log?.('ssh-failed', { code: error.message, detail: detail || 'no remote output' })\n throw new Error(detail === '' ? error.message : `${error.message}:${detail}`, { cause: error })\n }\n options.log?.('ssh-failed', { code: error instanceof Error ? error.message : 'unknown' })\n throw error\n }\n const checks = parseChecks(result.stdout, result.stderr, token)\n for (const check of checks) options.log?.('remote-check', { id: check.id, status: check.status, detail: check.detail })\n if (!result.stdout.includes('DSH_MOBILE_DEPLOYMENT_OK')) {\n if (checks.length === 0) throw new Error('vps_deploy_failed')\n throw new Error(`vps_deploy_failed:${checks.map(check => check.detail).join(' ')}`)\n }\n return Object.freeze({ version: 1, deployed: true, serverAddress, publicOrigin, checks })\n}\n\nexport function vpsDeploymentScriptForTesting(settings: FrpSettings): string {\n return deploymentScript(settings)\n}\n\nexport interface VpsUninstallInput {\n readonly serverPort: unknown\n /** Let's Encrypt certificate name to delete (public-IPv4 mode); omit for domain mode. */\n readonly certName?: unknown\n}\n\nexport interface VpsUninstallResult {\n readonly version: 1\n readonly removed: boolean\n readonly serverAddress: string\n readonly checks: readonly VpsDeploymentCheck[]\n}\n\nfunction validCertName(value: unknown): string | undefined {\n if (value === undefined || value === '') return undefined\n if (typeof value !== 'string' || value.length > 253 || !/^[a-z0-9.-]+$/u.test(value)) throw new Error('vps_cert_name_invalid')\n return value.toLowerCase()\n}\n\n/**\n * Build a reviewable uninstall script that removes only DSH Mobile-owned\n * server artifacts: its systemd units, config, binaries, venv, renew helper,\n * managed Caddy site, owned UFW rules, and optionally its IP certificate.\n * Existing non-DSH-Mobile Caddy content and firewall rules are never touched.\n */\nexport function createVpsUninstallScript(input: VpsUninstallInput): string {\n const serverPort = validateFrpServerPort(input.serverPort)\n const certName = validCertName(input.certName)\n const certCleanup = certName === undefined ? '' : `\nif [ -x /opt/dsh-mobile/certbot-venv/bin/certbot ]; then\n /opt/dsh-mobile/certbot-venv/bin/certbot delete --cert-name ${shellQuote(certName)} --non-interactive || true\nfi\n`\n return `#!/bin/sh\n# DSH Mobile VPS uninstall. Review before running: only files, services, and\n# firewall rules created by the DSH Mobile deployment are removed.\nset -eu\numask 077\n\nfail() { echo \"DSH_MOBILE_CHECK remote-command error $1\" >&2; exit 1; }\ncheck() { echo \"DSH_MOBILE_CHECK $1 $2 $3\"; }\n\n# Same lock as the deploy script: cleanup and deployment exclude each other\n# so their Caddyfile surgeries never interleave.\nif command -v flock >/dev/null 2>&1; then\n exec 9>/tmp/dsh-mobile-deploy.lock\n flock -n 9 || fail \"已有部署或清理正在进行,请稍后再试。\"\nfi\n\n[ \"$(id -u)\" = \"0\" ] || fail \"请使用 root SSH 账号。\"\ncommand -v systemctl >/dev/null 2>&1 || fail \"VPS 不支持 systemd。\"\n\n# Ownership is decided before deleting anything: only an account created by a\n# DSH Mobile deployment (marker written at useradd time) may be removed below.\nowns_account=false\nif [ -f /etc/dsh-mobile/.owns-account ]; then owns_account=true; fi\n\nsystemctl disable --now dsh-mobile-cert-renew.timer >/dev/null 2>&1 || true\nsystemctl disable --now dsh-mobile-frps.service >/dev/null 2>&1 || true\nrm -f /etc/systemd/system/dsh-mobile-frps.service\nrm -f /etc/systemd/system/dsh-mobile-cert-renew.service\nrm -f /etc/systemd/system/dsh-mobile-cert-renew.timer\nsystemctl daemon-reload\ncheck services ok \"已停止并删除 dsh-mobile-frps 服务与证书续期定时器。\"\n${certCleanup}\nrm -rf /etc/dsh-mobile\nrm -rf /usr/local/libexec/dsh-mobile\nrm -rf /opt/dsh-mobile/certbot-venv\nrm -f /usr/local/sbin/dsh-mobile-cert-renew\nrm -rf /var/lib/caddy/dsh-mobile-certs\nrm -f ${FRP_CADDY_SNIPPET_PATH}\ncheck files ok \"已删除 DSH Mobile 配置、二进制与证书文件。\"\n\nif grep -Eq '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile 2>/dev/null; then\n # Rebuild without our import line (grep -v with the gate pattern, verified\n # to remove every copy), keeping all user content byte-identical otherwise.\n grep -Ev '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile > /etc/caddy/Caddyfile.dsh-new || true\n if grep -Eq '^[[:space:]]*import[[:space:]]+/etc/caddy/dsh-mobile-dsh\\.caddy([[:space:]]|$)' /etc/caddy/Caddyfile.dsh-new; then\n rm -f /etc/caddy/Caddyfile.dsh-new\n fail \"Caddyfile import 移除失败,未做任何修改。\"\n fi\n cat /etc/caddy/Caddyfile.dsh-new > /etc/caddy/Caddyfile\n rm -f /etc/caddy/Caddyfile.dsh-new\n if [ ! -s /etc/caddy/Caddyfile ]; then\n printf '# DSH Mobile removed its site; the remaining Caddyfile was empty.\\n' > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n fi\n if command -v caddy >/dev/null 2>&1; then\n caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy.service || systemctl restart caddy.service || true\n fi\n check caddy ok \"已移除 DSH Mobile 站点引入;其余 Caddy 配置保持原样。\"\nelif [ -f /etc/caddy/Caddyfile ] && grep -q '^# Managed by DSH Mobile$' /etc/caddy/Caddyfile; then\n # Legacy whole-file layout (pre-snippet releases): the entire file is ours.\n printf '# DSH Mobile removed its site. Restore your own Caddyfile or reinstall the Caddy defaults.\\\\n' > /etc/caddy/Caddyfile\n chmod 0644 /etc/caddy/Caddyfile\n if command -v caddy >/dev/null 2>&1; then\n caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy.service || systemctl restart caddy.service || true\n fi\n check caddy ok \"已清空旧版 DSH Mobile 管理的 Caddy 站点;请按需恢复自己的配置。\"\nelse\n check caddy ok \"Caddyfile 非 DSH Mobile 管理,保持原样。\"\nfi\n\nif command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q '^Status: active'; then\n for rule in $(ufw status numbered 2>/dev/null | grep 'DSH Mobile' | sed -E 's/^\\\\[ *([0-9]+)\\\\].*/\\\\1/' | sort -rn); do\n yes | ufw delete \"$rule\" >/dev/null 2>&1 || true\n done\n check firewall ok \"已删除带 DSH Mobile 标记的 UFW 规则(FRP 控制端口 ${String(serverPort)}、80、443)。\"\nelse\n check firewall ok \"UFW 未启用或无需调整。\"\nfi\n\nif [ \"$owns_account\" = true ]; then\n if id dsh-mobile >/dev/null 2>&1; then\n if pgrep -u dsh-mobile >/dev/null 2>&1; then\n fail \"dsh-mobile 用户仍有运行中的进程,已保留该用户;请先停止相关进程后重试。\"\n fi\n userdel dsh-mobile || fail \"删除 dsh-mobile 系统用户失败。\"\n check account ok \"已删除本次部署创建的 dsh-mobile 系统用户。\"\n else\n check account ok \"dsh-mobile 系统用户已不存在,无需删除。\"\n fi\nelse\n check account ok \"dsh-mobile 系统用户非本次部署创建,已保留。\"\nfi\n\necho DSH_MOBILE_UNINSTALL_OK\n`\n}\n\nasync function runRemoteScript(\n input: VpsDeploymentInput,\n serverAddress: string,\n script: string,\n environment: Readonly<Record<string, string>>,\n knownHostsFile: string,\n log?: VpsDeploymentOptions['log'],\n): Promise<{ stdout: string; stderr: string }> {\n const ssh = process.platform === 'win32' ? 'ssh.exe' : 'ssh'\n const parsedInput = parseVpsDeploymentInput(input)\n const common = sshSessionOptions(knownHostsFile)\n if (parsedInput.sshKeyPath !== undefined) common.push('-i', parsedInput.sshKeyPath)\n const target = `${parsedInput.sshUser}@${serverAddress}`\n const remoteCommand = parsedInput.sshUser === 'root' ? 'sh -s' : 'sudo -n sh -s'\n const envPrefix = Object.entries(environment).map(([key, value]) => `${key}=${shellQuote(value)}`).join(' ')\n log?.('uninstall-ssh-start', { serverAddress })\n return await runProcess(ssh, [...common, '-p', String(parsedInput.sshPort), target, `${envPrefix} ${remoteCommand}`.trim()], script)\n}\n\n/** Remove DSH Mobile-owned server artifacts over a pinned SSH connection. */\nexport async function uninstallVps(\n serverAddress: string,\n uninstall: VpsUninstallInput,\n input: VpsDeploymentInput,\n options: VpsDeploymentOptions = {},\n): Promise<VpsUninstallResult> {\n const address = validateVpsServerTarget(serverAddress)\n const parsedInput = parseVpsDeploymentInput(input)\n // Identity is verified with a fresh scan immediately before this destructive\n // action: every presented key must still be user-confirmed.\n const keyscanOutput = await scanHostKeys(\n { sshUser: parsedInput.sshUser, sshPort: parsedInput.sshPort, sshKeyPath: parsedInput.sshKeyPath },\n address,\n options,\n )\n const knownHostsBody = buildPinnedKnownHosts(address, parsedInput.sshPort, keyscanOutput, parsedInput.hostFingerprints)\n options.log?.('host-keys-verified', { serverAddress: address })\n const script = createVpsUninstallScript(uninstall)\n options.log?.('uninstall-start', { serverAddress: address })\n const runRemote = options.runRemoteScript ?? (async (sshInput, host, scriptBody) => {\n const workDirectory = await mkdtemp(join(tmpdir(), 'dsh-mobile-known-hosts-'))\n try {\n const knownHostsFile = join(workDirectory, 'known_hosts')\n await writeFile(knownHostsFile, knownHostsBody, { encoding: 'utf8', mode: 0o600 })\n return await runRemoteScript(sshInput, host, scriptBody, {}, knownHostsFile, options.log)\n } finally {\n await rm(workDirectory, { recursive: true, force: true })\n }\n })\n let result: { stdout: string; stderr: string }\n try {\n result = await runRemote(parsedInput, address, script)\n } catch (error) {\n if (error instanceof VpsSshError) {\n // runProcess labels transport failures as deploy errors; relabel them\n // and prefer the failed remote check over raw output (see deployVps).\n const detail = failureDetail(error.stdout, error.stderr, '')\n const code = error.message === 'vps_deploy_failed' ? 'vps_uninstall_failed' : error.message\n options.log?.('uninstall-failed', { code, detail: detail || 'no remote output' })\n throw new Error(detail === '' ? code : `${code}:${detail}`, { cause: error })\n }\n options.log?.('uninstall-failed', { code: error instanceof Error ? error.message : 'unknown' })\n throw error\n }\n const checks = parseChecks(result.stdout, result.stderr, '')\n for (const check of checks) options.log?.('remote-check', { id: check.id, status: check.status, detail: check.detail })\n if (!result.stdout.includes('DSH_MOBILE_UNINSTALL_OK')) {\n if (checks.length === 0) throw new Error('vps_uninstall_failed')\n throw new Error(`vps_uninstall_failed:${checks.map(check => check.detail).join(' ')}`)\n }\n return Object.freeze({ version: 1, removed: true, serverAddress: address, checks })\n}\n","import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'\nimport { lstat, rm } from 'node:fs/promises'\nimport { isAbsolute, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { MobileAccessControlStore } from './control.js'\nimport type { MobileAccessGateway } from './gateway.js'\nimport { settleRemoteResources, terminateRemoteProcess, type RemoteProviderController } from './remote.js'\n\nconst MAX_PROTOCOL_LINE_BYTES = 16 * 1024\nconst FUNNEL_START_TIMEOUT_MS = 45_000\n\n/** Product-facing states for the independent Tailscale Funnel transport. */\nexport type FunnelState = 'off' | 'unavailable' | 'starting' | 'needs-login' | 'connecting' | 'ready' | 'error'\n\n/** Safe state returned only through the loopback DSH control route. */\nexport interface FunnelStatus {\n readonly enabled: boolean\n readonly state: FunnelState\n readonly origin?: string\n readonly loginUrl?: string\n readonly setupUrl?: string\n readonly errorCode?: string\n}\n\ninterface FunnelEvent {\n readonly version: 1\n readonly type: 'login' | 'ready' | 'serving' | 'error'\n readonly url?: string\n readonly origin?: string\n readonly code?: string\n}\n\n/** Construction inputs for one Funnel lifecycle independent from the LAN gateway. */\nexport interface FunnelControllerOptions {\n readonly store: MobileAccessControlStore\n readonly executable: string\n readonly stateDirectory: string\n readonly hostname: string\n readonly createGateway: (origin: string) => Promise<MobileAccessGateway>\n readonly onStatus?: (status: FunnelStatus) => void\n}\n\nfunction publicStatus(status: FunnelStatus): FunnelStatus {\n return Object.freeze({\n enabled: status.enabled,\n state: status.state,\n ...(status.origin === undefined ? {} : { origin: status.origin }),\n ...(status.loginUrl === undefined ? {} : { loginUrl: status.loginUrl }),\n ...(status.setupUrl === undefined ? {} : { setupUrl: status.setupUrl }),\n ...(status.errorCode === undefined ? {} : { errorCode: status.errorCode }),\n })\n}\n\nconst FUNNEL_SETUP_URLS = new Set([\n 'https://tailscale.com/s/no-funnel',\n 'https://tailscale.com/s/https',\n])\n\nfunction parseSetupUrl(value: unknown): string | undefined {\n if (value === undefined) return undefined\n if (typeof value !== 'string' || value.length > 2048) throw new Error('invalid_sidecar_protocol')\n const url = new URL(value)\n const normalized = url.toString().replace(/\\/$/u, '')\n const officialInteractive = url.protocol === 'https:' && url.hostname === 'login.tailscale.com'\n && url.port === '' && url.username === '' && url.password === ''\n if (!FUNNEL_SETUP_URLS.has(normalized) && !officialInteractive) throw new Error('invalid_sidecar_protocol')\n return officialInteractive ? url.toString() : normalized\n}\n\nfunction parseOrigin(value: unknown): string {\n if (typeof value !== 'string' || value.length > 512) throw new Error('invalid_funnel_origin')\n let url: URL\n try { url = new URL(value) } catch { throw new Error('invalid_funnel_origin') }\n if (url.protocol !== 'https:' || !url.hostname.endsWith('.ts.net') || url.port !== ''\n || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '') throw new Error('invalid_funnel_origin')\n return url.origin\n}\n\n/** Parse one sidecar protocol line while restricting every browser-opened URL. */\nexport function parseFunnelEvent(line: string): FunnelEvent {\n if (Buffer.byteLength(line, 'utf8') === 0 || Buffer.byteLength(line, 'utf8') > MAX_PROTOCOL_LINE_BYTES) {\n throw new Error('invalid_sidecar_protocol')\n }\n let value: unknown\n try { value = JSON.parse(line) as unknown } catch { throw new Error('invalid_sidecar_protocol') }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('invalid_sidecar_protocol')\n const record = value as Record<string, unknown>\n if (record.version !== 1 || typeof record.type !== 'string') throw new Error('invalid_sidecar_protocol')\n if (record.type === 'login') {\n if (typeof record.url !== 'string' || record.url.length > 2048) throw new Error('invalid_sidecar_protocol')\n const url = new URL(record.url)\n if (url.protocol !== 'https:' || url.hostname !== 'login.tailscale.com') throw new Error('invalid_sidecar_protocol')\n return Object.freeze({ version: 1, type: 'login', url: url.toString() })\n }\n if (record.type === 'ready' || record.type === 'serving') {\n return Object.freeze({ version: 1, type: record.type, origin: parseOrigin(record.origin) })\n }\n if (record.type === 'error') {\n if (typeof record.code !== 'string' || !/^[a-z][a-z0-9_]{0,63}$/u.test(record.code)) {\n throw new Error('invalid_sidecar_protocol')\n }\n const setupUrl = parseSetupUrl(record.url)\n return Object.freeze({ version: 1, type: 'error', code: record.code, ...(setupUrl === undefined ? {} : { url: setupUrl }) })\n }\n throw new Error('invalid_sidecar_protocol')\n}\n\nfunction withoutProvisioningSecrets(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const blocked = new Set(['TS_AUTHKEY', 'TAILSCALE_AUTHKEY', 'TS_OAUTH_CLIENT_SECRET'])\n return Object.fromEntries(Object.entries(environment).filter(([name]) => !blocked.has(name.toUpperCase())))\n}\n\n/** Owns the source-built tsnet sidecar, remote gateway, and persisted remote switch. */\nexport class FunnelController implements RemoteProviderController {\n private enabled = false\n private initialized = false\n private disposed = false\n private child: ChildProcessWithoutNullStreams | undefined\n private gatewayValue: MobileAccessGateway | undefined\n private generation = 0\n private buffer = ''\n private latest: FunnelStatus = publicStatus({ enabled: false, state: 'off' })\n private queue: Promise<void> = Promise.resolve()\n private startTimer: NodeJS.Timeout | undefined\n\n constructor(private readonly options: FunnelControllerOptions) {\n if (!isAbsolute(options.executable) || !isAbsolute(options.stateDirectory)) {\n throw new Error('Funnel paths must be absolute')\n }\n }\n\n /** Restore the remote switch without coupling it to LAN availability. */\n async initialize(): Promise<void> {\n const state = await this.options.store.load()\n this.enabled = state.enabled\n this.initialized = true\n if (this.enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n }\n\n /** Return the currently attached authenticated remote gateway. */\n gateway(): MobileAccessGateway | undefined {\n return this.gatewayValue\n }\n\n /** Return state safe for the local desktop control UI. */\n status(): FunnelStatus {\n return publicStatus(this.latest)\n }\n\n /** Enable or disable Funnel without changing the LAN listener. */\n async setEnabled(enabled: boolean): Promise<FunnelStatus> {\n if (!this.initialized || this.disposed) throw new Error('Funnel controller is unavailable')\n await this.enqueue(async () => {\n if (this.enabled === enabled && (enabled === false || this.child !== undefined)) return\n if (!enabled) await this.stop()\n this.enabled = enabled\n await this.options.store.save({ version: 1, enabled })\n if (enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Restart a failed or interrupted Funnel session while retaining sign-in state. */\n async reconnect(): Promise<FunnelStatus> {\n if (!this.initialized || this.disposed) throw new Error('Funnel controller is unavailable')\n await this.enqueue(async () => {\n if (!this.enabled) {\n this.enabled = true\n await this.options.store.save({ version: 1, enabled: true })\n }\n await this.stop()\n await this.start()\n })\n return this.status()\n }\n\n /** Disable Funnel and remove only its private Tailscale node state. */\n async reset(): Promise<FunnelStatus> {\n if (!this.initialized || this.disposed) throw new Error('Funnel controller is unavailable')\n await this.enqueue(async () => {\n await this.stop()\n this.enabled = false\n await this.options.store.save({ version: 1, enabled: false })\n await rm(resolve(this.options.stateDirectory), { recursive: true, force: true })\n this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Stop all remote resources without modifying the remembered switch. */\n async close(): Promise<void> {\n if (this.disposed) return\n this.disposed = true\n await this.enqueue(() => this.stop())\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task\n }\n\n private publish(status: FunnelStatus): void {\n this.latest = publicStatus(status)\n try { this.options.onStatus?.(this.status()) } catch { /* UI observation cannot own runtime state. */ }\n }\n\n private async start(): Promise<void> {\n const generation = ++this.generation\n let entry\n try { entry = await lstat(this.options.executable) } catch {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'component_missing' })\n return\n }\n if (!entry.isFile() || entry.isSymbolicLink()) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'component_invalid' })\n return\n }\n this.buffer = ''\n this.publish({ enabled: true, state: 'starting' })\n const child = spawn(this.options.executable, [\n '--state-dir', resolve(this.options.stateDirectory),\n '--hostname', this.options.hostname,\n ], {\n env: withoutProvisioningSecrets(process.env),\n shell: false,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n })\n this.child = child\n this.clearStartTimer()\n this.startTimer = setTimeout(() => {\n void this.enqueue(() => this.failGeneration(generation, 'funnel_start_timeout'))\n }, FUNNEL_START_TIMEOUT_MS)\n this.startTimer.unref()\n child.stderr.resume()\n child.stdout.setEncoding('utf8')\n child.stdout.on('data', chunk => { this.consume(generation, String(chunk)) })\n child.once('error', () => {\n void this.enqueue(() => this.failGeneration(generation, 'sidecar_launch_failed'))\n })\n child.once('close', code => {\n if (generation !== this.generation || this.child !== child) return\n this.child = undefined\n if (this.enabled) {\n void this.enqueue(() => this.failGeneration(generation, code === 0 ? 'sidecar_stopped' : 'sidecar_exited'))\n }\n })\n }\n\n private consume(generation: number, chunk: string): void {\n if (generation !== this.generation) return\n this.buffer += chunk\n if (Buffer.byteLength(this.buffer, 'utf8') > MAX_PROTOCOL_LINE_BYTES && !this.buffer.includes('\\n')) {\n void this.enqueue(() => this.failGeneration(generation, 'invalid_sidecar_protocol'))\n return\n }\n while (true) {\n const newline = this.buffer.indexOf('\\n')\n if (newline < 0) return\n const line = this.buffer.slice(0, newline).replace(/\\r$/u, '')\n this.buffer = this.buffer.slice(newline + 1)\n let event: FunnelEvent\n try { event = parseFunnelEvent(line) } catch {\n void this.enqueue(() => this.failGeneration(generation, 'invalid_sidecar_protocol'))\n return\n }\n void this.enqueue(() => this.handleEvent(generation, event))\n }\n }\n\n private async handleEvent(generation: number, event: FunnelEvent): Promise<void> {\n if (generation !== this.generation || !this.enabled) return\n this.clearStartTimer()\n if (event.type === 'login') {\n this.publish({ enabled: true, state: 'needs-login', loginUrl: event.url! })\n return\n }\n if (event.type === 'error') {\n await this.failGeneration(generation, event.code ?? 'funnel_failed', event.url)\n return\n }\n const origin = parseOrigin(event.origin)\n if (event.type === 'ready') {\n let gateway: MobileAccessGateway\n try {\n await this.gatewayValue?.close()\n this.gatewayValue = undefined\n gateway = await this.options.createGateway(origin)\n } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled) {\n await gateway.close()\n return\n }\n this.gatewayValue = gateway\n const address = gateway.address()\n const child = this.child\n if (child === undefined) {\n await this.failGeneration(generation, 'sidecar_stopped')\n return\n }\n child.stdin.write(\n `${JSON.stringify({ version: 1, type: 'serve', target: `http://${address.host}:${String(address.port)}` })}\\n`,\n error => {\n if (error !== null && error !== undefined) {\n void this.enqueue(() => this.failGeneration(generation, 'control_channel_failed'))\n }\n },\n )\n this.publish({ enabled: true, state: 'connecting', origin })\n return\n }\n this.publish({ enabled: true, state: 'ready', origin })\n }\n\n private async failGeneration(generation: number, code: string, setupUrl?: string): Promise<void> {\n if (generation !== this.generation) return\n await this.stopProcessAndGateway()\n if (this.enabled) this.publish({ enabled: true, state: 'error', errorCode: code, ...(setupUrl === undefined ? {} : { setupUrl }) })\n }\n\n private async stop(): Promise<void> {\n ++this.generation\n await this.stopProcessAndGateway()\n }\n\n private async stopProcessAndGateway(): Promise<void> {\n this.clearStartTimer()\n const child = this.child\n this.child = undefined\n const gateway = this.gatewayValue\n this.gatewayValue = undefined\n await settleRemoteResources([\n async () => {\n child?.stdin.end()\n if (child !== undefined && child.exitCode === null) await terminateRemoteProcess(child)\n },\n () => gateway?.close(),\n ], 'Funnel resource cleanup failed')\n }\n\n private clearStartTimer(): void {\n if (this.startTimer === undefined) return\n clearTimeout(this.startTimer)\n this.startTimer = undefined\n }\n}\n\n/** Locate the current platform's bundled Funnel executable, with one local development override. */\nexport function funnelExecutable(importMetaUrl: string, environment: NodeJS.ProcessEnv = process.env): string {\n const override = environment.DSH_MOBILE_FUNNEL_SIDECAR\n if (override !== undefined) {\n if (!isAbsolute(override)) throw new Error('DSH_MOBILE_FUNNEL_SIDECAR must be an absolute path')\n return resolve(override)\n }\n const suffix = process.platform === 'win32' ? '.exe' : ''\n const file = `dsh-mobile-funnel-${process.platform}-${process.arch}${suffix}`\n return resolve(fileURLToPath(new URL(`../bin/${file}`, importMetaUrl)))\n}\n","import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'\nimport { lstat } from 'node:fs/promises'\nimport { createServer, type Server } from 'node:net'\nimport { isAbsolute, resolve } from 'node:path'\nimport type { MobileAccessControlStore } from './control.js'\nimport type { MobileAccessGateway } from './gateway.js'\nimport { settleRemoteResources, terminateRemoteProcess, type RemoteProviderController } from './remote.js'\n\nconst MAX_LOG_BUFFER_BYTES = 64 * 1024\nconst START_TIMEOUT_MS = 45_000\nconst CPOLAR_HOST_SUFFIXES = Object.freeze(['.cpolar.cn', '.cpolar.io', '.cpolar.top', '.cpolar.com'])\n\n/** Product-facing states for the optional cpolar remote transport. */\nexport type CpolarState = 'off' | 'unavailable' | 'starting' | 'connecting' | 'ready' | 'error'\n\n/** Safe cpolar state returned only through the loopback DSH control route. */\nexport interface CpolarStatus {\n readonly enabled: boolean\n readonly state: CpolarState\n readonly origin?: string\n readonly errorCode?: string\n}\n\n/** Inputs for one cpolar process and its authenticated DSH gateway. */\nexport interface CpolarControllerOptions {\n readonly store: MobileAccessControlStore\n readonly executable: string\n readonly configFile: string\n readonly region?: string\n readonly createGateway: (origin: string, listenPort: number) => Promise<MobileAccessGateway>\n readonly onStatus?: (status: CpolarStatus) => void\n readonly spawnProcess?: (\n executable: string,\n args: readonly string[],\n environment: NodeJS.ProcessEnv,\n ) => ChildProcessWithoutNullStreams\n}\n\ninterface PortReservation {\n readonly port: number\n readonly release: () => Promise<void>\n}\n\nfunction publicStatus(status: CpolarStatus): CpolarStatus {\n return Object.freeze({\n enabled: status.enabled,\n state: status.state,\n ...(status.origin === undefined ? {} : { origin: status.origin }),\n ...(status.errorCode === undefined ? {} : { errorCode: status.errorCode }),\n })\n}\n\nfunction isCpolarHost(hostname: string): boolean {\n return CPOLAR_HOST_SUFFIXES.some(suffix => hostname.endsWith(suffix))\n}\n\n/** Extract a validated public HTTPS origin from one cpolar log line. */\nexport function parseCpolarOrigin(line: string): string | undefined {\n if (!line.includes('Tunnel established at ')) return undefined\n const match = /Tunnel established at (https:\\/\\/[^\"\\s]+)/u.exec(line)\n if (match === null) return undefined\n let url: URL\n try { url = new URL(match[1]!) } catch { throw new Error('invalid_cpolar_origin') }\n if (url.protocol !== 'https:' || url.port !== '' || !isCpolarHost(url.hostname)\n || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '') throw new Error('invalid_cpolar_origin')\n return url.origin\n}\n\nasync function reserveLoopbackPort(): Promise<PortReservation> {\n const server: Server = createServer(socket => { socket.destroy() })\n await new Promise<void>((resolveListen, reject) => {\n server.once('error', reject)\n server.listen(0, '127.0.0.1', () => {\n server.off('error', reject)\n resolveListen()\n })\n })\n const address = server.address()\n if (address === null || typeof address === 'string') {\n server.close()\n throw new Error('cpolar_port_reservation_failed')\n }\n let released = false\n return {\n port: address.port,\n release: async () => {\n if (released) return\n released = true\n await new Promise<void>(resolveClose => { server.close(() => resolveClose()) })\n },\n }\n}\n\nfunction spawnCpolarProcess(\n executable: string,\n args: readonly string[],\n environment: NodeJS.ProcessEnv,\n): ChildProcessWithoutNullStreams {\n return spawn(executable, [...args], {\n env: environment,\n shell: false,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n })\n}\n\nfunction withoutProxyEnvironment(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const blocked = new Set(['HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'NO_PROXY'])\n return Object.fromEntries(Object.entries(environment).filter(([name]) => !blocked.has(name.toUpperCase())))\n}\n\n/** Owns an installed cpolar client and a provider-specific DSH remote gateway. */\nexport class CpolarController implements RemoteProviderController {\n private enabled = false\n private initialized = false\n private disposed = false\n private child: ChildProcessWithoutNullStreams | undefined\n private gatewayValue: MobileAccessGateway | undefined\n private reservation: PortReservation | undefined\n private generation = 0\n private buffer = ''\n private latest: CpolarStatus = publicStatus({ enabled: false, state: 'off' })\n private queue: Promise<void> = Promise.resolve()\n private startupTimer: NodeJS.Timeout | undefined\n\n constructor(private readonly options: CpolarControllerOptions) {\n if (!isAbsolute(options.executable) || !isAbsolute(options.configFile)) {\n throw new Error('cpolar paths must be absolute')\n }\n if (options.region !== undefined && !/^[a-z][a-z0-9_]{0,31}$/u.test(options.region)) {\n throw new Error('cpolar region is invalid')\n }\n }\n\n /** Restore the remembered cpolar switch independently from LAN and Funnel state. */\n async initialize(): Promise<void> {\n const state = await this.options.store.load()\n this.enabled = state.enabled\n this.initialized = true\n if (this.enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n }\n\n /** Return the active cpolar-backed DSH gateway. */\n gateway(): MobileAccessGateway | undefined {\n return this.gatewayValue\n }\n\n /** Return state safe for the desktop control UI. */\n status(): CpolarStatus {\n return publicStatus(this.latest)\n }\n\n /** Enable or disable cpolar without changing LAN or Tailscale state. */\n async setEnabled(enabled: boolean): Promise<CpolarStatus> {\n if (!this.initialized || this.disposed) throw new Error('cpolar controller is unavailable')\n await this.enqueue(async () => {\n if (this.enabled === enabled && (enabled === false || this.child !== undefined)) return\n if (!enabled) await this.stop()\n this.enabled = enabled\n await this.options.store.save({ version: 1, enabled })\n if (enabled) await this.start()\n else this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Restart cpolar while retaining its account configuration and DSH device store. */\n async reconnect(): Promise<CpolarStatus> {\n if (!this.initialized || this.disposed) throw new Error('cpolar controller is unavailable')\n await this.enqueue(async () => {\n if (!this.enabled) {\n this.enabled = true\n await this.options.store.save({ version: 1, enabled: true })\n }\n await this.stop()\n await this.start()\n })\n return this.status()\n }\n\n /** Disable cpolar without modifying the user's cpolar account or global tunnels. */\n async reset(): Promise<CpolarStatus> {\n if (!this.initialized || this.disposed) throw new Error('cpolar controller is unavailable')\n await this.enqueue(async () => {\n await this.stop()\n this.enabled = false\n await this.options.store.save({ version: 1, enabled: false })\n this.publish({ enabled: false, state: 'off' })\n })\n return this.status()\n }\n\n /** Stop owned resources without changing the remembered switch. */\n async close(): Promise<void> {\n if (this.disposed) return\n this.disposed = true\n await this.enqueue(() => this.stop())\n }\n\n private enqueue(operation: () => Promise<void>): Promise<void> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task\n }\n\n private publish(status: CpolarStatus): void {\n this.latest = publicStatus(status)\n try { this.options.onStatus?.(this.status()) } catch { /* UI observation cannot own runtime state. */ }\n }\n\n private async start(): Promise<void> {\n const generation = ++this.generation\n let executableEntry\n try { executableEntry = await lstat(this.options.executable) } catch {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'cpolar_component_missing' })\n return\n }\n if (!executableEntry.isFile() || executableEntry.isSymbolicLink()) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'cpolar_component_invalid' })\n return\n }\n let configEntry\n try { configEntry = await lstat(this.options.configFile) } catch {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'cpolar_config_missing' })\n return\n }\n if (!configEntry.isFile() || configEntry.isSymbolicLink()) {\n this.publish({ enabled: true, state: 'unavailable', errorCode: 'cpolar_config_invalid' })\n return\n }\n\n let reservation: PortReservation\n try { reservation = await reserveLoopbackPort() } catch {\n this.publish({ enabled: true, state: 'error', errorCode: 'cpolar_port_unavailable' })\n return\n }\n this.reservation = reservation\n this.buffer = ''\n this.publish({ enabled: true, state: 'starting' })\n const args = [\n 'http',\n `-config=${resolve(this.options.configFile)}`,\n ...(this.options.region === undefined ? [] : [`-region=${this.options.region}`]),\n '-inspect-addr=false',\n '-redirect-https=true',\n '-log=stdout',\n '-log-level=INFO',\n String(reservation.port),\n ]\n const child = (this.options.spawnProcess ?? spawnCpolarProcess)(\n this.options.executable,\n args,\n withoutProxyEnvironment(process.env),\n )\n this.child = child\n child.stdout.setEncoding('utf8')\n child.stderr.setEncoding('utf8')\n child.stdout.on('data', chunk => { this.consume(generation, String(chunk)) })\n child.stderr.on('data', chunk => { this.consume(generation, String(chunk)) })\n child.once('error', () => { void this.enqueue(() => this.failGeneration(generation, 'cpolar_launch_failed')) })\n child.once('close', code => {\n if (generation !== this.generation || this.child !== child) return\n this.child = undefined\n if (this.enabled) void this.enqueue(() => this.failGeneration(generation, code === 0 ? 'cpolar_stopped' : 'cpolar_exited'))\n })\n this.startupTimer = setTimeout(() => {\n void this.enqueue(() => this.failGeneration(generation, 'cpolar_start_timeout'))\n }, START_TIMEOUT_MS)\n this.startupTimer.unref()\n }\n\n private consume(generation: number, chunk: string): void {\n if (generation !== this.generation) return\n this.buffer += chunk\n if (Buffer.byteLength(this.buffer, 'utf8') > MAX_LOG_BUFFER_BYTES && !this.buffer.includes('\\n')) {\n void this.enqueue(() => this.failGeneration(generation, 'cpolar_invalid_output'))\n return\n }\n while (true) {\n const newline = this.buffer.indexOf('\\n')\n if (newline < 0) return\n const line = this.buffer.slice(0, newline).replace(/\\r$/u, '')\n this.buffer = this.buffer.slice(newline + 1)\n let origin: string | undefined\n try { origin = parseCpolarOrigin(line) } catch {\n void this.enqueue(() => this.failGeneration(generation, 'cpolar_invalid_origin'))\n return\n }\n if (origin !== undefined) void this.enqueue(() => this.attachGateway(generation, origin))\n }\n }\n\n private async attachGateway(generation: number, origin: string): Promise<void> {\n if (generation !== this.generation || !this.enabled || this.disposed) return\n const current = this.gatewayValue\n if (current !== undefined) {\n if (current.address().origin === origin) return\n await this.rotateGateway(generation, origin, current)\n return\n }\n const reservation = this.reservation\n if (reservation === undefined) return\n this.publish({ enabled: true, state: 'connecting', origin })\n await reservation.release()\n if (this.reservation === reservation) this.reservation = undefined\n let gateway: MobileAccessGateway\n try { gateway = await this.options.createGateway(origin, reservation.port) } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed || this.child === undefined) {\n await gateway.close()\n return\n }\n this.gatewayValue = gateway\n if (this.startupTimer !== undefined) clearTimeout(this.startupTimer)\n this.startupTimer = undefined\n this.publish({ enabled: true, state: 'ready', origin })\n }\n\n /** Replace the gateway authority when cpolar rotates a temporary public origin. */\n private async rotateGateway(\n generation: number,\n origin: string,\n current: MobileAccessGateway,\n ): Promise<void> {\n const listenPort = current.address().port\n // A replacement must bind the same port cpolar already forwards to. Stop\n // exposing the closing instance before releasing that port.\n if (this.gatewayValue === current) this.gatewayValue = undefined\n this.publish({ enabled: true, state: 'connecting', origin })\n try {\n await current.close()\n } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed || this.child === undefined) return\n\n let replacement: MobileAccessGateway\n try { replacement = await this.options.createGateway(origin, listenPort) } catch {\n await this.failGeneration(generation, 'gateway_start_failed')\n return\n }\n if (generation !== this.generation || !this.enabled || this.disposed || this.child === undefined) {\n await replacement.close()\n return\n }\n this.gatewayValue = replacement\n this.publish({ enabled: true, state: 'ready', origin })\n }\n\n private async failGeneration(generation: number, code: string): Promise<void> {\n if (generation !== this.generation) return\n await this.stopProcessAndGateway()\n if (this.enabled) this.publish({ enabled: true, state: 'error', errorCode: code })\n }\n\n private async stop(): Promise<void> {\n ++this.generation\n await this.stopProcessAndGateway()\n }\n\n private async stopProcessAndGateway(): Promise<void> {\n if (this.startupTimer !== undefined) clearTimeout(this.startupTimer)\n this.startupTimer = undefined\n const reservation = this.reservation\n this.reservation = undefined\n const child = this.child\n this.child = undefined\n const gateway = this.gatewayValue\n this.gatewayValue = undefined\n await settleRemoteResources([\n () => reservation?.release(),\n () => child !== undefined && child.exitCode === null ? terminateRemoteProcess(child) : undefined,\n () => gateway?.close(),\n ], 'cpolar resource cleanup failed')\n }\n}\n","import { createHash, randomBytes } from 'node:crypto'\nimport { execFile } from 'node:child_process'\nimport {\n chmod,\n copyFile,\n lstat,\n mkdir,\n mkdtemp,\n readFile,\n readdir,\n rename,\n rm,\n writeFile,\n} from 'node:fs/promises'\nimport { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport { downloadPinnedArtifact } from './component-download.js'\nimport { restrictPrivateFile } from './private-file.js'\n\n/** Pinned cpolar components fetched only after an explicit user action. */\ninterface CpolarArtifact {\n readonly version: string\n readonly platform: NodeJS.Platform\n readonly arch: string\n readonly downloadUrl: string\n readonly downloadBytes: number\n readonly downloadSha256: string\n readonly executableName: string\n readonly executableBytes: number\n readonly executableSha256: string\n /** Windows ships an MSI inside a zip; Linux ships the binary in a tarball. */\n readonly archiveKind: 'msi-zip' | 'tar.gz'\n}\n\nconst CPOLAR_VERSION = '3.3.18'\n\nconst releases = [\n {\n version: CPOLAR_VERSION,\n platform: 'win32',\n arch: 'x64',\n downloadUrl: `https://www.cpolar.com/static/downloads/releases/${CPOLAR_VERSION}/cpolar-stable-windows-amd64-setup.zip`,\n downloadBytes: 7_603_505,\n downloadSha256: 'fb8cf60289058ee26079f995d2eeea0b21768a742d90c93015afe96e83428830',\n executableName: 'cpolar.exe',\n executableBytes: 19_637_680,\n executableSha256: 'b2d865ee505e842d22ceca5493a872efa893a79b079a7a8ee2bd3aa5343a5c41',\n archiveKind: 'msi-zip',\n },\n {\n version: CPOLAR_VERSION,\n platform: 'linux',\n arch: 'x64',\n downloadUrl: `https://www.cpolar.com/static/downloads/releases/${CPOLAR_VERSION}/cpolar-stable-linux-amd64.tar.gz`,\n downloadBytes: 7_404_781,\n downloadSha256: '5cd3320c4369928ccb509c4f5fa2ec3b86151ead77e1b77c1694aa64c43e32e7',\n executableName: 'cpolar',\n executableBytes: 19_328_632,\n executableSha256: 'c076e1109372a3f88031841c5989030e49b6871b54aee3260c796da9122dec05',\n archiveKind: 'tar.gz',\n },\n {\n version: CPOLAR_VERSION,\n platform: 'linux',\n arch: 'arm64',\n downloadUrl: `https://www.cpolar.com/static/downloads/releases/${CPOLAR_VERSION}/cpolar-stable-linux-arm64.tar.gz`,\n downloadBytes: 6_855_666,\n downloadSha256: '8a61a97983f18ae5ffb4b8bac4c9b3d8e2399a6ee101844e7b0b30d7f326157c',\n executableName: 'cpolar',\n executableBytes: 19_017_169,\n executableSha256: '8d76a1b7e518df45f387f107c7a428079c67ecd9b971a2915c53b947ea9b443a',\n archiveKind: 'tar.gz',\n },\n] as const satisfies readonly CpolarArtifact[]\n\n/** Pinned official cpolar release metadata for supported desktop targets. */\nexport const CPOLAR_COMPONENT_RELEASES: Readonly<Record<string, CpolarArtifact>> = Object.freeze(Object.fromEntries(\n releases.map(release => [`${release.platform}-${release.arch}`, Object.freeze(release)]),\n))\n\n/**\n * Canonical release metadata. New code should select from\n * {@link CPOLAR_COMPONENT_RELEASES} by platform and architecture; this alias\n * preserves the original Windows x64 entry for existing callers.\n */\nexport const CPOLAR_COMPONENT_RELEASE = CPOLAR_COMPONENT_RELEASES['win32-x64'] as CpolarArtifact\n\nconst DOWNLOAD_PAGE = 'https://www.cpolar.com/download'\nconst SIGNUP_URL = 'https://dashboard.cpolar.com/signup'\nconst DASHBOARD_URL = 'https://dashboard.cpolar.com/auth'\nconst TERMS_URL = 'https://www.cpolar.com/tos'\n\n/** Public, credential-free description of the managed cpolar component. */\nexport interface CpolarComponentStatus {\n readonly supported: boolean\n readonly installed: boolean\n readonly configured: boolean\n readonly version: string\n readonly downloadBytes: number\n readonly installedBytes: number\n readonly sourceUrl: string\n readonly downloadPage: string\n readonly signupUrl: string\n readonly dashboardUrl: string\n readonly termsUrl: string\n readonly storagePath: string\n readonly errorCode?: string\n}\n\ninterface CpolarComponentManagerOptions {\n readonly stateDirectory: string\n readonly platform?: NodeJS.Platform\n readonly arch?: string\n readonly fetchArtifact?: (url: string, signal: AbortSignal) => Promise<Uint8Array>\n readonly extractArtifact?: (archive: string, destination: string) => Promise<void>\n}\n\nfunction inside(parent: string, child: string): boolean {\n const candidate = relative(parent, child)\n return candidate !== '' && !candidate.startsWith('..') && !isAbsolute(candidate)\n}\n\nasync function sha256(file: string): Promise<string> {\n return createHash('sha256').update(await readFile(file)).digest('hex')\n}\n\nasync function regularFile(file: string, expectedBytes?: number): Promise<boolean> {\n try {\n const stat = await lstat(file)\n return stat.isFile() && !stat.isSymbolicLink() && (expectedBytes === undefined || stat.size === expectedBytes)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false\n throw error\n }\n}\n\nasync function run(file: string, args: readonly string[]): Promise<void> {\n await new Promise<void>((resolveRun, reject) => {\n execFile(file, [...args], { windowsHide: true, timeout: 120_000 }, (error) => {\n if (error === null) resolveRun()\n else reject(error)\n })\n })\n}\n\nasync function defaultFetchArtifact(\n url: string,\n signal: AbortSignal,\n expectedBytes: number,\n): Promise<Uint8Array> {\n return downloadPinnedArtifact({\n url,\n expectedBytes,\n errorPrefix: 'cpolar',\n signal,\n })\n}\n\nasync function extractWindowsMsi(archive: string, destination: string, executableName: string): Promise<void> {\n if (process.platform !== 'win32') throw new Error('cpolar_component_unsupported')\n const unpacked = join(destination, 'archive')\n const administrative = join(destination, 'administrative')\n await mkdir(unpacked, { recursive: true, mode: 0o700 })\n await mkdir(administrative, { recursive: true, mode: 0o700 })\n await run('tar.exe', ['-xf', archive, '-C', unpacked])\n const archiveEntries = await readdir(unpacked, { recursive: true })\n const msiRelative = archiveEntries.find(entry => entry.toLowerCase().endsWith('.msi'))\n if (msiRelative === undefined) throw new Error('cpolar_installer_missing')\n await run('msiexec.exe', ['/a', join(unpacked, msiRelative), '/qn', `TARGETDIR=${administrative}`])\n const installedEntries = await readdir(administrative, { recursive: true })\n const executableRelative = installedEntries.find(entry => basename(entry).toLowerCase() === executableName.toLowerCase())\n if (executableRelative === undefined) throw new Error('cpolar_executable_missing')\n await copyFile(join(administrative, executableRelative), join(destination, executableName))\n}\n\nasync function extractLinuxTarball(archive: string, destination: string, executableName: string): Promise<void> {\n const unpacked = join(destination, 'archive')\n await mkdir(unpacked, { recursive: true, mode: 0o700 })\n // The Linux tarball carries the binary at its root; no installer involved.\n await run(process.platform === 'win32' ? 'tar.exe' : 'tar', ['-xzf', archive, '-C', unpacked])\n const archiveEntries = await readdir(unpacked, { recursive: true })\n const executableRelative = archiveEntries.find(entry => basename(entry).toLowerCase() === executableName.toLowerCase())\n if (executableRelative === undefined) throw new Error('cpolar_executable_missing')\n await copyFile(join(unpacked, executableRelative), join(destination, executableName))\n}\n\nasync function defaultExtractArtifact(archive: string, destination: string, release: CpolarArtifact): Promise<void> {\n if (release.archiveKind === 'tar.gz') return extractLinuxTarball(archive, destination, release.executableName)\n return extractWindowsMsi(archive, destination, release.executableName)\n}\n\n/** Validate a cpolar Authtoken before it crosses the durable-file boundary. */\nexport function validateCpolarAuthtoken(value: unknown): string {\n if (typeof value !== 'string' || value.length < 20 || value.length > 512\n || /[\\s\\u0000-\\u001f\\u007f]/u.test(value)) {\n throw new Error('cpolar_authtoken_invalid')\n }\n return value\n}\n\n/** Owns the optional cpolar binary and account configuration inside DSH Mobile state. */\nexport class CpolarComponentManager {\n readonly executable: string\n readonly configFile: string\n readonly componentRoot: string\n readonly componentStorage: string\n readonly stateRoot: string\n readonly logRoot: string\n private readonly stagingRoot: string\n private readonly platform: NodeJS.Platform\n private readonly arch: string\n private readonly release: CpolarArtifact | undefined\n private readonly fetchArtifact: (url: string, signal: AbortSignal) => Promise<Uint8Array>\n private readonly extractArtifact: (archive: string, destination: string) => Promise<void>\n private installed = false\n private configured = false\n private errorCode: string | undefined\n private queue: Promise<void> = Promise.resolve()\n\n constructor(options: CpolarComponentManagerOptions) {\n const stateDirectory = resolve(options.stateDirectory)\n if (!isAbsolute(stateDirectory)) throw new Error('cpolar state directory must be absolute')\n this.platform = options.platform ?? process.platform\n this.arch = options.arch ?? process.arch\n this.release = CPOLAR_COMPONENT_RELEASES[`${this.platform}-${this.arch}`]\n this.componentRoot = join(stateDirectory, 'components', 'cpolar')\n this.componentStorage = join(this.componentRoot, this.release?.version ?? CPOLAR_VERSION)\n this.executable = join(this.componentStorage, this.release?.executableName ?? 'cpolar')\n this.stateRoot = join(stateDirectory, 'state', 'cpolar')\n this.configFile = join(this.stateRoot, 'cpolar.yml')\n this.logRoot = join(stateDirectory, 'logs', 'cpolar')\n this.stagingRoot = join(stateDirectory, 'staging', 'cpolar')\n for (const child of [this.componentRoot, this.componentStorage, this.stateRoot, this.logRoot, this.stagingRoot]) {\n if (!inside(stateDirectory, child)) throw new Error('cpolar component path escaped its state directory')\n }\n const release = this.release\n this.fetchArtifact = options.fetchArtifact\n ?? ((url, signal) => defaultFetchArtifact(url, signal, release?.downloadBytes ?? 0))\n this.extractArtifact = options.extractArtifact\n ?? ((archive, destination) => {\n if (release === undefined) throw new Error('cpolar_component_unsupported')\n return defaultExtractArtifact(archive, destination, release)\n })\n }\n\n /** Inspect the managed binary and configuration without using global cpolar state. */\n async initialize(): Promise<void> {\n const release = this.release\n this.installed = release !== undefined && await regularFile(this.executable, release.executableBytes)\n if (this.installed && release !== undefined && await sha256(this.executable) !== release.executableSha256) {\n this.installed = false\n this.errorCode = 'cpolar_component_invalid'\n }\n this.configured = await regularFile(this.configFile)\n if (this.configured) await restrictPrivateFile(this.configFile)\n }\n\n /** Return a safe status that never includes the account token. */\n status(): CpolarComponentStatus {\n // Unsupported hosts still report the canonical entry so the panel can show\n // what would be installed elsewhere; `supported` carries the actual gate.\n const release = this.release ?? CPOLAR_COMPONENT_RELEASE\n return Object.freeze({\n supported: this.release !== undefined,\n installed: this.installed,\n configured: this.configured,\n version: release.version,\n downloadBytes: release.downloadBytes,\n installedBytes: release.executableBytes,\n sourceUrl: release.downloadUrl,\n downloadPage: DOWNLOAD_PAGE,\n signupUrl: SIGNUP_URL,\n dashboardUrl: DASHBOARD_URL,\n termsUrl: TERMS_URL,\n storagePath: this.componentRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n })\n }\n\n /** Download, verify, and administratively extract cpolar after explicit confirmation. */\n install(): Promise<CpolarComponentStatus> {\n return this.enqueue(async () => {\n const release = this.release\n if (release === undefined) throw new Error('cpolar_component_unsupported')\n await mkdir(this.stagingRoot, { recursive: true, mode: 0o700 })\n const staging = await mkdtemp(join(this.stagingRoot, 'install-'))\n try {\n const controller = new AbortController()\n const timeout = setTimeout(() => { controller.abort() }, 120_000)\n timeout.unref()\n let bytes: Uint8Array\n try { bytes = await this.fetchArtifact(release.downloadUrl, controller.signal) } finally { clearTimeout(timeout) }\n const digest = createHash('sha256').update(bytes).digest('hex')\n if (digest !== release.downloadSha256) throw new Error('cpolar_download_hash_mismatch')\n const archive = join(staging, release.archiveKind === 'tar.gz' ? 'cpolar.tar.gz' : 'cpolar.zip')\n await writeFile(archive, bytes, { flag: 'wx', mode: 0o600 })\n await this.extractArtifact(archive, staging)\n const extracted = join(staging, release.executableName)\n if (!await regularFile(extracted, release.executableBytes)\n || await sha256(extracted) !== release.executableSha256) {\n throw new Error('cpolar_executable_hash_mismatch')\n }\n const candidate = join(this.componentRoot, `.install-${randomBytes(12).toString('hex')}`)\n await mkdir(candidate, { recursive: true, mode: 0o700 })\n await copyFile(extracted, join(candidate, release.executableName))\n await chmod(join(candidate, release.executableName), 0o700)\n await rm(this.componentStorage, { recursive: true, force: true })\n await rename(candidate, this.componentStorage)\n this.installed = true\n this.errorCode = undefined\n } finally {\n await rm(staging, { recursive: true, force: true })\n }\n })\n }\n\n /** Store only the cpolar token in a private, self-update-disabled configuration. */\n configure(authtoken: unknown): Promise<CpolarComponentStatus> {\n return this.enqueue(async () => {\n const token = validateCpolarAuthtoken(authtoken)\n await mkdir(this.stateRoot, { recursive: true, mode: 0o700 })\n const temporary = join(this.stateRoot, `.cpolar.${randomBytes(12).toString('hex')}.tmp`)\n const body = `authtoken: ${JSON.stringify(token)}\\nconsole_ui: false\\nupdate: false\\ninspect_db_size: -1\\n`\n try {\n await writeFile(temporary, body, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, this.configFile)\n await restrictPrivateFile(this.configFile)\n } catch (error) {\n await rm(temporary, { force: true })\n throw error\n }\n this.configured = true\n this.errorCode = undefined\n })\n }\n\n /** Remove every cpolar file owned by DSH Mobile without touching global state. */\n purge(): Promise<CpolarComponentStatus> {\n return this.enqueue(async () => {\n await Promise.all([\n rm(this.componentRoot, { recursive: true, force: true }),\n rm(this.stateRoot, { recursive: true, force: true }),\n rm(this.logRoot, { recursive: true, force: true }),\n rm(this.stagingRoot, { recursive: true, force: true }),\n ])\n this.installed = false\n this.configured = false\n this.errorCode = undefined\n })\n }\n\n private enqueue(operation: () => Promise<void>): Promise<CpolarComponentStatus> {\n const task = this.queue.then(operation, operation)\n this.queue = task.then(() => undefined, () => undefined)\n return task.then(() => this.status())\n }\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport { readFile } from 'node:fs/promises'\nimport { isAbsolute, join } from 'node:path'\nimport { createRequire } from 'node:module'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { DSH_MOBILE_VERSION } from './version.js'\n\nconst PACKAGE_NAME = 'dsh-mobile'\nconst NPM_LATEST_URL = 'https://registry.npmjs.org/dsh-mobile/latest'\nconst GITHUB_LATEST_URL = 'https://github.com/saya-ch/dsh-mobile/releases/latest'\nconst GITHUB_API_LATEST_URL = 'https://api.github.com/repos/saya-ch/dsh-mobile/releases/latest'\nconst GITHUB_RELEASES_URL = 'https://github.com/saya-ch/dsh-mobile/releases'\nconst RELEASE_NOTES_MAX_CHARS = 4000\nconst STATUS_CACHE_MS = 10 * 60_000\nconst REQUEST_TIMEOUT_MS = 8_000\nconst UPDATE_TIMEOUT_MS = 120_000\nconst UPDATE_TERMINATION_GRACE_MS = 1_500\n\nconst NUMERIC_VERSION_IDENTIFIER = '(?:0|[1-9]\\\\d*)'\nconst WILDCARD_VERSION_IDENTIFIER = '(?:[xX*])'\nconst PARTIAL_VERSION = `(?:${WILDCARD_VERSION_IDENTIFIER}|${NUMERIC_VERSION_IDENTIFIER}(?:\\\\.(?:${WILDCARD_VERSION_IDENTIFIER}|${NUMERIC_VERSION_IDENTIFIER}(?:\\\\.(?:${WILDCARD_VERSION_IDENTIFIER}|${NUMERIC_VERSION_IDENTIFIER}))?))?)`\nconst FULL_VERSION = `${NUMERIC_VERSION_IDENTIFIER}\\\\.${NUMERIC_VERSION_IDENTIFIER}\\\\.${NUMERIC_VERSION_IDENTIFIER}(?:-[0-9A-Za-z-]+(?:\\\\.[0-9A-Za-z-]+)*)?(?:\\\\+[0-9A-Za-z-]+(?:\\\\.[0-9A-Za-z-]+)*)?`\nconst RANGE_VERSION = `(?:${FULL_VERSION}|${PARTIAL_VERSION})`\nconst COMPARATOR = new RegExp(`^(?:<=|>=|<|>|=|~|\\\\^)?${RANGE_VERSION}$`, 'u')\nconst HYPHEN_RANGE = new RegExp(`^${RANGE_VERSION} +[-] +${RANGE_VERSION}$`, 'u')\nconst DIST_TAG = /^[A-Za-z][A-Za-z0-9._-]{0,127}$/u\n\ninterface Semver {\n readonly core: readonly [number, number, number]\n readonly prerelease: readonly (number | string)[]\n}\n\n/** Release information safe to return through the loopback administration API. */\nexport interface PluginReleaseStatus {\n readonly installedVersion: string\n readonly latestVersion?: string\n readonly updateAvailable: boolean\n readonly updateSupported: boolean\n readonly androidVersion?: string\n readonly androidDownloadUrl: string\n /** Trimmed body of the latest GitHub release (What’s new + notices). */\n readonly releaseNotes?: string\n}\n\n/** Result returned after the profile package has been replaced successfully. */\nexport interface PluginUpdateResult {\n readonly installedVersion: string\n readonly restartRequired: true\n}\n\ninterface PluginReleaseManagerOptions {\n readonly profileDirectory: string | undefined\n readonly installedVersion?: string\n readonly fetch?: typeof globalThis.fetch\n readonly runUpdate?: (profileDirectory: string, version: string) => Promise<void>\n readonly readInstalledVersion?: (profileDirectory: string) => Promise<string | undefined>\n readonly now?: () => number\n readonly updateProcess?: PnpmUpdateRuntime\n}\n\ninterface UpdateProcessExit {\n readonly code: number | null\n readonly signal: NodeJS.Signals | null\n}\n\ninterface UpdateProcessRequest {\n readonly command: string\n readonly args: readonly string[]\n readonly cwd: string\n readonly detached: boolean\n readonly platform: NodeJS.Platform\n readonly shell: false\n}\n\ninterface ManagedUpdateProcess {\n readonly completion: Promise<UpdateProcessExit>\n readonly stderr?: NodeJS.ReadableStream\n terminateTree(): Promise<void>\n}\n\ninterface UpdateDeadline {\n readonly promise: Promise<void>\n cancel(): void\n}\n\ninterface PnpmUpdateRuntime {\n readonly platform?: NodeJS.Platform\n readonly timeoutMs?: number\n readonly windowsCommandInterpreter?: string\n readonly start?: (request: UpdateProcessRequest) => ManagedUpdateProcess\n readonly deadline?: (timeoutMs: number) => UpdateDeadline\n}\n\nfunction parseSemver(value: string): Semver | undefined {\n const match = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([0-9A-Za-z.-]+))?(?:\\+[0-9A-Za-z.-]+)?$/u.exec(value)\n if (match === null) return undefined\n const core = [Number(match[1]), Number(match[2]), Number(match[3])] as const\n if (core.some(part => !Number.isSafeInteger(part))) return undefined\n const prerelease = match[4] === undefined\n ? []\n : match[4].split('.').map((part): number | string => /^\\d+$/u.test(part) ? Number(part) : part)\n if (prerelease.some(part => typeof part === 'number' && !Number.isSafeInteger(part))) return undefined\n return Object.freeze({ core, prerelease: Object.freeze(prerelease) })\n}\n\n/** Compare two strict SemVer strings, including prerelease precedence. */\nexport function comparePluginVersions(left: string, right: string): number | undefined {\n const a = parseSemver(left)\n const b = parseSemver(right)\n if (a === undefined || b === undefined) return undefined\n for (let index = 0; index < a.core.length; index += 1) {\n const difference = a.core[index]! - b.core[index]!\n if (difference !== 0) return Math.sign(difference)\n }\n if (a.prerelease.length === 0 || b.prerelease.length === 0) {\n return a.prerelease.length === b.prerelease.length ? 0 : a.prerelease.length === 0 ? 1 : -1\n }\n const length = Math.max(a.prerelease.length, b.prerelease.length)\n for (let index = 0; index < length; index += 1) {\n const leftPart = a.prerelease[index]\n const rightPart = b.prerelease[index]\n if (leftPart === undefined || rightPart === undefined) return leftPart === undefined ? -1 : 1\n if (leftPart === rightPart) continue\n if (typeof leftPart === 'number' && typeof rightPart === 'number') return Math.sign(leftPart - rightPart)\n if (typeof leftPart === 'number') return -1\n if (typeof rightPart === 'number') return 1\n return leftPart < rightPart ? -1 : 1\n }\n return 0\n}\n\nfunction isComparatorSet(value: string): boolean {\n if (HYPHEN_RANGE.test(value)) return true\n const normalized = value.replace(/(<=|>=|<|>|=|~|\\^) +/gu, '$1')\n const comparators = normalized.split(/ +/u)\n return comparators.length > 0 && comparators.every(comparator => COMPARATOR.test(comparator))\n}\n\nfunction isNpmVersionRange(value: string): boolean {\n if (!/^[0-9xX*<>=~^|.+\\- ]+$/u.test(value)) return false\n const alternatives = value.split(/ *\\|\\| */u)\n return alternatives.length > 0 && alternatives.every(alternative => alternative !== '' && isComparatorSet(alternative))\n}\n\n/** Return whether pnpm may safely replace this profile dependency from an npm version, range, or tag. */\nexport function isRegistryPluginSpec(value: unknown): value is string {\n if (typeof value !== 'string' || value.trim() !== value || value === '' || /[\\u0000-\\u001f\\u007f]/u.test(value)) return false\n if (/\\.(?:tgz|tar(?:\\.gz)?)$/iu.test(value)) return false\n return parseSemver(value) !== undefined || isNpmVersionRange(value) || DIST_TAG.test(value)\n}\n\n/** Resolve the DSH profile named by the current launcher arguments. */\nexport function launchedProfileName(argv: readonly string[]): string {\n for (let index = 0; index < argv.length; index += 1) {\n if (argv[index] === '--profile') {\n const candidate = argv[index + 1]\n if (candidate !== undefined && /^[\\w.-]+$/u.test(candidate)) return candidate\n }\n const match = /^--profile=([\\w.-]+)$/u.exec(argv[index] ?? '')\n if (match?.[1] !== undefined) return match[1]\n }\n return 'web'\n}\n\ninterface DesktopProfiles {\n readonly current?: { readonly dir?: string }\n}\n\n/** Resolve the launcher-owned Desktop profile, or the CLI profile outside Desktop. */\nexport function releaseProfileDirectory(ctx: Pick<Context, 'get'>, dshHome: string, argv: readonly string[]): string | undefined {\n const desktopProfiles = ctx.get('desktopProfiles') as DesktopProfiles | undefined\n const desktopDirectory = desktopProfiles?.current?.dir\n if (typeof desktopDirectory === 'string' && isAbsolute(desktopDirectory)) return desktopDirectory\n // Desktop selects its profile outside argv; never update an unrelated Web profile.\n if (desktopProfiles !== undefined || ctx.get('desktopRuntime') !== undefined) return undefined\n return join(dshHome, 'profiles', launchedProfileName(argv))\n}\n\nasync function profileDependencySpec(profileDirectory: string): Promise<string | undefined> {\n try {\n const manifest = JSON.parse(await readFile(join(profileDirectory, 'package.json'), 'utf8')) as {\n readonly dependencies?: Readonly<Record<string, unknown>>\n }\n const value = manifest.dependencies?.[PACKAGE_NAME]\n return typeof value === 'string' ? value : undefined\n } catch {\n return undefined\n }\n}\n\nasync function fetchNpmVersion(fetcher: typeof globalThis.fetch): Promise<string | undefined> {\n const response = await fetcher(NPM_LATEST_URL, {\n headers: { accept: 'application/json', 'user-agent': 'dsh-mobile-release-check' },\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n })\n if (!response.ok) return undefined\n const payload = await response.json() as { readonly version?: unknown }\n return typeof payload.version === 'string' && parseSemver(payload.version) !== undefined\n ? payload.version\n : undefined\n}\n\nfunction githubReleaseVersion(location: string | null, responseUrl: string): string | undefined {\n let url: URL\n try { url = new URL(location ?? responseUrl, GITHUB_LATEST_URL) }\n catch { return undefined }\n if (url.origin !== 'https://github.com' || url.username !== '' || url.password !== '' || url.search !== '' || url.hash !== '') return undefined\n const prefix = '/saya-ch/dsh-mobile/releases/tag/v'\n if (!url.pathname.startsWith(prefix)) return undefined\n let version: string\n try { version = decodeURIComponent(url.pathname.slice(prefix.length)) } catch { return undefined }\n return parseSemver(version) === undefined ? undefined : version\n}\n\nfunction androidReleaseDownloadUrl(version: string | undefined): string {\n if (version === undefined) return GITHUB_RELEASES_URL\n const tag = `v${version}`\n return `https://github.com/saya-ch/dsh-mobile/releases/download/${encodeURIComponent(tag)}/dsh-mobile-android-${encodeURIComponent(tag)}.apk`\n}\n\nasync function fetchAndroidVersion(fetcher: typeof globalThis.fetch): Promise<string | undefined> {\n const response = await fetcher(GITHUB_LATEST_URL, {\n method: 'GET',\n redirect: 'manual',\n headers: { accept: 'text/html', 'user-agent': 'dsh-mobile-release-check' },\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n })\n return githubReleaseVersion(response.headers.get('location'), response.url)\n}\n\n/** Best-effort body of the latest GitHub release, trimmed to a bounded size. */\nasync function fetchReleaseNotes(fetcher: typeof globalThis.fetch): Promise<string | undefined> {\n const response = await fetcher(GITHUB_API_LATEST_URL, {\n headers: { accept: 'application/vnd.github+json', 'user-agent': 'dsh-mobile-release-check' },\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n })\n if (!response.ok) return undefined\n const payload = await response.json() as { readonly body?: unknown }\n if (typeof payload.body !== 'string') return undefined\n const notes = payload.body.trim()\n return notes === '' ? undefined : notes.slice(0, RELEASE_NOTES_MAX_CHARS)\n}\n\nasync function readProfileInstalledVersion(profileDirectory: string): Promise<string | undefined> {\n try {\n const manifestPath = createRequire(join(profileDirectory, 'package.json')).resolve(`${PACKAGE_NAME}/package.json`)\n const manifest = JSON.parse(await readFile(manifestPath, 'utf8')) as { readonly version?: unknown }\n return typeof manifest.version === 'string' ? manifest.version : undefined\n } catch {\n return undefined\n }\n}\n\nfunction childCompletion(child: ChildProcess): Promise<UpdateProcessExit> {\n return new Promise<UpdateProcessExit>((resolveCompletion, rejectCompletion) => {\n child.once('error', rejectCompletion)\n child.once('close', (code, signal) => { resolveCompletion({ code, signal }) })\n })\n}\n\nfunction createDeadline(timeoutMs: number): UpdateDeadline {\n let timer: NodeJS.Timeout | undefined\n const promise = new Promise<void>(resolveTimeout => {\n timer = setTimeout(resolveTimeout, timeoutMs)\n timer.unref()\n })\n return {\n promise,\n cancel: () => {\n if (timer !== undefined) clearTimeout(timer)\n timer = undefined\n },\n }\n}\n\nasync function taskkillProcessTree(pid: number): Promise<void> {\n const killer = spawn('taskkill.exe', ['/PID', String(pid), '/T', '/F'], {\n shell: false,\n windowsHide: true,\n stdio: 'ignore',\n })\n const result = await childCompletion(killer)\n if (result.code !== 0) throw new Error('plugin_update_tree_termination_failed')\n}\n\nasync function completionWithin(completion: Promise<UpdateProcessExit>, timeoutMs: number): Promise<boolean> {\n let timer: NodeJS.Timeout | undefined\n try {\n return await Promise.race([\n completion.then(() => true, () => true),\n new Promise<false>(resolveTimeout => {\n timer = setTimeout(() => { resolveTimeout(false) }, timeoutMs)\n timer.unref()\n }),\n ])\n } finally {\n if (timer !== undefined) clearTimeout(timer)\n }\n}\n\nfunction processMissing(error: unknown): boolean {\n return (error as NodeJS.ErrnoException).code === 'ESRCH'\n}\n\nasync function terminateProcessTree(child: ChildProcess, completion: Promise<UpdateProcessExit>, platform: NodeJS.Platform): Promise<void> {\n if (child.exitCode !== null || child.signalCode !== null) return\n const pid = child.pid\n if (pid === undefined) {\n child.kill('SIGKILL')\n if (!await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) {\n throw new Error('plugin_update_tree_termination_timeout')\n }\n return\n }\n if (platform === 'win32') {\n try { await taskkillProcessTree(pid) } catch (error) {\n if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL')\n if (!await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) {\n throw new AggregateError([error, new Error('plugin_update_tree_termination_timeout')], 'plugin update tree termination failed')\n }\n throw error\n }\n if (!await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) {\n if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL')\n if (!await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) {\n throw new Error('plugin_update_tree_termination_timeout')\n }\n }\n return\n }\n try { process.kill(-pid, 'SIGTERM') } catch (error) {\n if (!processMissing(error)) throw error\n if (!await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) {\n throw new Error('plugin_update_tree_termination_timeout')\n }\n return\n }\n if (await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) return\n try { process.kill(-pid, 'SIGKILL') } catch (error) {\n if (!processMissing(error)) throw error\n }\n if (!await completionWithin(completion, UPDATE_TERMINATION_GRACE_MS)) {\n throw new Error('plugin_update_tree_termination_timeout')\n }\n}\n\nfunction startUpdateProcess(request: UpdateProcessRequest): ManagedUpdateProcess {\n const child = spawn(request.command, [...request.args], {\n cwd: request.cwd,\n detached: request.detached,\n shell: request.shell,\n windowsHide: true,\n stdio: ['ignore', 'ignore', 'pipe'],\n })\n const completion = childCompletion(child)\n return {\n completion,\n ...(child.stderr === null ? {} : { stderr: child.stderr }),\n terminateTree: async () => terminateProcessTree(child, completion, request.platform),\n }\n}\n\nfunction updateFailure(cause?: unknown): Error {\n return cause === undefined ? new Error('plugin_update_failed') : new Error('plugin_update_failed', { cause })\n}\n\nasync function runPnpmUpdate(profileDirectory: string, version: string, runtime: PnpmUpdateRuntime = {}): Promise<void> {\n if (parseSemver(version) === undefined) throw new Error('plugin_update_unavailable')\n const platform = runtime.platform ?? process.platform\n const packageSpec = `${PACKAGE_NAME}@${version}`\n const managed = (runtime.start ?? startUpdateProcess)({\n command: platform === 'win32'\n ? (runtime.windowsCommandInterpreter ?? process.env.ComSpec ?? 'cmd.exe')\n : 'pnpm',\n args: platform === 'win32'\n ? ['/d', '/s', '/c', 'pnpm.cmd', 'add', packageSpec]\n : ['add', packageSpec],\n cwd: profileDirectory,\n detached: platform !== 'win32',\n platform,\n shell: false,\n })\n let diagnostics = ''\n managed.stderr?.on('data', chunk => {\n if (diagnostics.length < 4096) diagnostics += Buffer.from(chunk).toString('utf8').slice(0, 4096 - diagnostics.length)\n })\n const completion = managed.completion.then(\n result => ({ kind: 'exit' as const, result }),\n error => ({ kind: 'error' as const, error }),\n )\n const deadline = (runtime.deadline ?? createDeadline)(runtime.timeoutMs ?? UPDATE_TIMEOUT_MS)\n const first = await Promise.race([\n completion,\n deadline.promise.then(() => ({ kind: 'timeout' as const })),\n ])\n deadline.cancel()\n if (first.kind === 'error') throw updateFailure(first.error)\n if (first.kind === 'exit') {\n if (first.result.code === 0) return\n const detail = diagnostics.trim() || `pnpm exited with ${first.result.signal ?? String(first.result.code)}`\n throw updateFailure(new Error(detail))\n }\n let terminationError: unknown\n try { await managed.terminateTree() } catch (error) { terminationError = error }\n if (terminationError !== undefined) throw updateFailure(terminationError)\n const stopped = await completion\n if (stopped.kind === 'error') throw updateFailure(stopped.error)\n throw updateFailure(new Error('plugin update timed out'))\n}\n\n/** Cached npm/GitHub release lookup and guarded profile-local package update. */\nexport class PluginReleaseManager {\n private readonly profileDirectory: string | undefined\n private readonly installedVersion: string\n private readonly fetcher: typeof globalThis.fetch\n private readonly runner: (profileDirectory: string, version: string) => Promise<void>\n private readonly installedVersionReader: (profileDirectory: string) => Promise<string | undefined>\n private readonly now: () => number\n private cache: { readonly expiresAt: number; readonly status: PluginReleaseStatus } | undefined\n private activeUpdate: Promise<PluginUpdateResult> | undefined\n\n constructor(options: PluginReleaseManagerOptions) {\n this.profileDirectory = options.profileDirectory\n this.installedVersion = options.installedVersion ?? DSH_MOBILE_VERSION\n this.fetcher = options.fetch ?? globalThis.fetch\n this.runner = options.runUpdate ?? ((profileDirectory, version) => runPnpmUpdate(profileDirectory, version, options.updateProcess))\n this.installedVersionReader = options.readInstalledVersion ?? readProfileInstalledVersion\n this.now = options.now ?? Date.now\n }\n\n /** Read cached release metadata and suppress external lookup failures. */\n async status(force = false): Promise<PluginReleaseStatus> {\n if (!force && this.cache !== undefined && this.cache.expiresAt > this.now()) return this.cache.status\n const dependencySpec = this.profileDirectory === undefined ? undefined : await profileDependencySpec(this.profileDirectory)\n const updateSupported = isRegistryPluginSpec(dependencySpec)\n const [npmResult, androidResult, notesResult] = await Promise.allSettled([\n fetchNpmVersion(this.fetcher),\n fetchAndroidVersion(this.fetcher),\n fetchReleaseNotes(this.fetcher),\n ])\n const latestVersion = npmResult.status === 'fulfilled' ? npmResult.value : undefined\n const androidVersion = androidResult.status === 'fulfilled' ? androidResult.value : undefined\n const releaseNotes = notesResult.status === 'fulfilled' ? notesResult.value : undefined\n const comparison = latestVersion === undefined\n ? undefined\n : comparePluginVersions(latestVersion, this.installedVersion)\n const status: PluginReleaseStatus = Object.freeze({\n installedVersion: this.installedVersion,\n ...(latestVersion === undefined ? {} : { latestVersion }),\n updateAvailable: updateSupported && comparison === 1,\n updateSupported,\n ...(androidVersion === undefined ? {} : { androidVersion }),\n androidDownloadUrl: androidReleaseDownloadUrl(androidVersion),\n ...(releaseNotes === undefined ? {} : { releaseNotes }),\n })\n this.cache = { expiresAt: this.now() + STATUS_CACHE_MS, status }\n return status\n }\n\n /** Install the latest npm release into the active profile, then require a DSH restart. */\n async update(): Promise<PluginUpdateResult> {\n if (this.activeUpdate !== undefined) return this.activeUpdate\n this.activeUpdate = this.updateOnce()\n try { return await this.activeUpdate }\n finally { this.activeUpdate = undefined }\n }\n\n private async updateOnce(): Promise<PluginUpdateResult> {\n const profileDirectory = this.profileDirectory\n if (profileDirectory === undefined) throw new Error('plugin_update_unsupported')\n const status = await this.status(true)\n if (!status.updateSupported) throw new Error('plugin_update_unsupported')\n if (!status.updateAvailable || status.latestVersion === undefined) throw new Error('plugin_update_unavailable')\n await this.runner(profileDirectory, status.latestVersion)\n const installed = await this.installedVersionReader(profileDirectory)\n if (installed !== status.latestVersion) throw new Error('plugin_update_failed')\n this.cache = undefined\n return Object.freeze({ installedVersion: installed, restartRequired: true })\n }\n}\n","import { createWriteStream, type WriteStream } from 'node:fs'\nimport { lstat, mkdir, rename, rm } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { Logger, type Context, type Exporter, type Message } from '@deepseek-ai/cordis'\n\nconst MAX_LOG_BYTES = 5 * 1024 * 1024\n\n/** Install a plugin-scoped Cordis exporter backed by a private UTF-8 log file. */\nexport async function installMobileFileLogger(ctx: Context, stateDirectory: string): Promise<string> {\n const directory = join(stateDirectory, 'logs')\n const file = join(directory, 'dsh-mobile.log')\n const previous = `${file}.1`\n await mkdir(directory, { recursive: true, mode: 0o700 })\n const entry = await lstat(file).catch(() => undefined)\n if (entry !== undefined && entry.isFile() && !entry.isSymbolicLink() && entry.size >= MAX_LOG_BYTES) {\n await rm(previous, { force: true })\n await rename(file, previous)\n }\n const stream: WriteStream = createWriteStream(file, { flags: 'a', encoding: 'utf8', mode: 0o600 })\n const exporter: Exporter = {\n colors: false,\n maxLength: 16 * 1024,\n levels: { default: -1, 'dsh-mobile': 3 },\n export(message: Message): void {\n if (message.name !== 'dsh-mobile') return\n const record = {\n timestamp: new Date(message.ts).toISOString(),\n level: message.type,\n logger: message.name,\n message: Logger.format(exporter, message),\n }\n stream.write(`${JSON.stringify(record)}\\n`)\n },\n }\n ctx.logger.exporter(exporter)\n ctx.effect(() => () => { stream.end() }, 'dsh-mobile file logger')\n return file\n}\n","import { execFileText as execFile } from './exec-file.js'\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, join } from 'node:path'\nimport {\n ensureManagedCa,\n refreshManagedServerCertificate,\n type LanNetwork,\n type ManagedSetup,\n} from './managed-setup.js'\nimport { restrictPrivateFile } from './private-file.js'\n\nconst FIREWALL_TCP_RULE = 'DSH Mobile HTTPS'\nconst FIREWALL_UDP_RULE = 'DSH Mobile Discovery'\n\n/** Files and active address produced by one explicit LAN setup confirmation. */\nexport interface ManagedLanSetupResult {\n readonly setup: ManagedSetup\n readonly network: LanNetwork\n readonly origin: string\n readonly androidCertificate: string\n}\n\n/** Inputs required to prepare the managed LAN listener without starting it. */\nexport interface ManagedLanSetupOptions {\n readonly setupFile: string\n readonly controlFile: string\n readonly network: LanNetwork\n readonly listenPort: number\n readonly dshPort: number\n readonly configureFirewall: boolean\n}\n\nasync function runElevatedPowerShell(script: string): Promise<void> {\n const encoded = Buffer.from(script, 'utf16le').toString('base64')\n const launch = [\n \"$ErrorActionPreference = 'Stop'; $process = Start-Process -FilePath 'powershell.exe' -Verb RunAs -WindowStyle Hidden -Wait -PassThru\",\n ` -ArgumentList @('-NoProfile','-NonInteractive','-EncodedCommand','${encoded}')`,\n '; exit $process.ExitCode',\n ].join(' ')\n await execFile('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', launch], { windowsHide: true })\n}\n\n/** Install the two Windows inbound rules used by the managed LAN listener. */\nexport async function configureWindowsFirewall(port: number): Promise<void> {\n if (process.platform !== 'win32') return\n const script = [\n \"$ErrorActionPreference = 'Stop'\",\n `Get-NetFirewallRule -DisplayName '${FIREWALL_TCP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,\n `Get-NetFirewallRule -DisplayName '${FIREWALL_UDP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,\n `New-NetFirewallRule -DisplayName '${FIREWALL_TCP_RULE}' -Direction Inbound -Action Allow -Protocol TCP -LocalPort ${String(port)} -RemoteAddress LocalSubnet -Profile Any | Out-Null`,\n `New-NetFirewallRule -DisplayName '${FIREWALL_UDP_RULE}' -Direction Inbound -Action Allow -Protocol UDP -LocalPort ${String(port)} -RemoteAddress LocalSubnet -Profile Any | Out-Null`,\n ].join('; ')\n await runElevatedPowerShell(script)\n}\n\n/** Remove only the two Windows firewall rules owned by DSH Mobile. */\nexport async function removeWindowsFirewall(): Promise<void> {\n if (process.platform !== 'win32') return\n await runElevatedPowerShell([\n \"$ErrorActionPreference = 'Stop'\",\n `Get-NetFirewallRule -DisplayName '${FIREWALL_TCP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,\n `Get-NetFirewallRule -DisplayName '${FIREWALL_UDP_RULE}' -ErrorAction SilentlyContinue | Remove-NetFirewallRule`,\n ].join('; '))\n}\n\n/** Generate private TLS material and persist a managed setup selected by the user. */\nexport async function prepareManagedLanSetup(options: ManagedLanSetupOptions): Promise<ManagedLanSetupResult> {\n if (!isAbsolute(options.setupFile) || !isAbsolute(options.controlFile)) {\n throw new Error('managed LAN setup paths must be absolute')\n }\n if (!Number.isSafeInteger(options.listenPort) || options.listenPort < 1024 || options.listenPort > 65535) {\n throw new Error('managed LAN listenPort must be from 1024 through 65535')\n }\n if (!Number.isSafeInteger(options.dshPort) || options.dshPort < 1024 || options.dshPort > 65535) {\n throw new Error('managed LAN dshPort must be from 1024 through 65535')\n }\n const directory = dirname(options.setupFile)\n const tlsDirectory = join(directory, 'tls')\n await mkdir(tlsDirectory, { recursive: true, mode: 0o700 })\n const managedTls: ManagedSetup['tls'] = {\n mode: 'managed',\n caCertFile: join(tlsDirectory, 'ca.pem'),\n caKeyFile: join(tlsDirectory, 'ca-key.pem'),\n certFile: join(tlsDirectory, 'server-cert.pem'),\n keyFile: join(tlsDirectory, 'server-key.pem'),\n }\n const ca = await ensureManagedCa(managedTls, {\n certFile: join(tlsDirectory, 'cert.pem'),\n keyFile: join(tlsDirectory, 'key.pem'),\n })\n const setup: ManagedSetup = {\n version: 2,\n networkInterface: options.network.name,\n listenPort: options.listenPort,\n upstreamOrigin: `http://127.0.0.1:${String(options.dshPort)}`,\n tls: managedTls,\n }\n await refreshManagedServerCertificate(setup, options.network.address)\n const androidCertificate = join(tlsDirectory, 'dsh-mobile-ca.cer')\n await writeFile(androidCertificate, ca.raw, { mode: 0o600 })\n await Promise.all([\n ...Object.values(managedTls).filter(value => value !== 'managed').map(file => restrictPrivateFile(file)),\n restrictPrivateFile(androidCertificate),\n ])\n if (options.configureFirewall) await configureWindowsFirewall(options.listenPort)\n await Promise.all([\n writeFile(options.setupFile, `${JSON.stringify({\n ...setup,\n tls: Object.fromEntries(Object.entries(setup.tls)\n .map(([key, value]) => [key, typeof value === 'string' ? value.replaceAll('\\\\', '/') : value])),\n }, null, 2)}\\n`, { mode: 0o600 }),\n writeFile(options.controlFile, '{\"version\":1,\"enabled\":true}\\n', { mode: 0o600 }),\n ])\n await Promise.all([restrictPrivateFile(options.setupFile), restrictPrivateFile(options.controlFile)])\n return Object.freeze({\n setup: Object.freeze(setup),\n network: Object.freeze({ ...options.network }),\n origin: `https://${options.network.address}:${String(options.listenPort)}`,\n androidCertificate,\n })\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { boundContextSummary, createUserMessage } from '@deepseek-ai/dsh-llm/message'\ndeclare module '@deepseek-ai/dsh-llm' {\n interface MessageSourceMap {\n 'plugin:dsh-mobile': {\n kind: 'plugin:dsh-mobile'\n form: 'notice'\n summary: string\n }\n }\n}\n// Side-effect type import: activates dsh-commands' Context augmentation so\n// `ctx.commands` and its handler types resolve without a runtime dependency.\nimport type {} from '@deepseek-ai/dsh-commands'\nimport type { WebRoute } from '@deepseek-ai/dsh-host-webserver'\nimport { createRequire } from 'node:module'\nimport { X509Certificate } from 'node:crypto'\nimport { copyFile, lstat, readFile, rm } from 'node:fs/promises'\nimport { dirname, isAbsolute, join, resolve } from 'node:path'\nimport { parseControlFile, parseGatewayConfig, type PluginConfig, type ResolvedGatewayConfig } from './config.js'\nimport { collectConnectionDiagnostics } from './diagnostics.js'\nimport { buildMobileGuide, type MobileGuideState } from './mobile-guide.js'\nimport { createVpsUninstallScript, deployVps, fetchVpsHostKeys, parseVpsDeploymentInput, uninstallVps } from './vps-deploy.js'\nimport { TaskEventHub, watchTaskCompletions } from './task-events.js'\nimport {\n FollowingMobileAccessRuntime,\n JsonMobileAccessControlStore,\n MobileAccessGatewayController,\n type MobileAccessRuntime,\n} from './control.js'\nimport { MobileAccessGateway } from './gateway.js'\nimport { createMobileAccessService, type MobileAccessService } from './extensions.js'\nimport { listComputerImages, readComputerImage } from './computer-images.js'\nimport {\n HttpError,\n LOCAL_ADMIN_PREFIX,\n assertLocalAdminTrust,\n parseRequestTarget,\n readJsonObject,\n sendFailure,\n sendJson,\n} from './http-security.js'\nimport { JsonDeviceStore } from './storage.js'\nimport { BlockedUpgradePathLog, WebSocketPathStore } from './websocket-paths.js'\nimport { FunnelController, funnelExecutable } from './funnel.js'\nimport { CpolarController } from './cpolar.js'\nimport { CpolarComponentManager, type CpolarComponentStatus } from './cpolar-component.js'\nimport { CloudflaredComponentManager, type CloudflaredComponentStatus } from './cloudflared-component.js'\nimport { CloudflaredController } from './cloudflared.js'\nimport {\n CloudflaredTunnelStore,\n mergeSavedCloudflaredTunnelSettings,\n type CloudflaredTunnelStatus,\n} from './cloudflared-tunnel.js'\nimport { FrpComponentManager, type FrpComponentStatus } from './frp-component.js'\nimport {\n FrpConfigStore,\n isFrpSelfSignedIngress,\n mergeSavedFrpSettings,\n mergeSavedFrpTarget,\n resolveFrpPublicPort,\n resolveFrpVhostHttpPort,\n type FrpConfigurationStatus,\n type FrpSettings,\n} from './frp-config.js'\nimport { createFrpAttachTemplate } from './frp-attach.js'\nimport { createFrpAttachPlan } from './frp-attach-plan.js'\nimport { defaultProbeDiscovery, FrpController } from './frp.js'\nimport { ensureFrpIngressCertificate, frpIngressPaths, frpIngressSelfCheck, purgeFrpIngressCertificates, type FrpIngressCertificate } from './frp-ingress.js'\nimport { OriginConfigStore, parseOriginSettings, validateOriginListenPort, type OriginConfigurationStatus, type OriginSettings } from './origin-proxy-config.js'\nimport { OriginController } from './origin-proxy.js'\nimport { PluginReleaseManager, releaseProfileDirectory } from './release-update.js'\nimport { installMobileFileLogger } from './file-logger.js'\nimport {\n configuredRemoteProvider,\n JsonRemoteProviderStore,\n RemoteProviderCoordinator,\n REMOTE_PROVIDERS,\n type RemoteProvider,\n type RemoteProviderController,\n type RemoteProviderStatus,\n} from './remote.js'\nimport { parseAuthority, parseCidr, probeTcpReachable } from './network.js'\nimport {\n availableLanNetworks,\n isNetworkSelectionError,\n materializeManagedSetup,\n parseManagedSetup,\n preferredLanInterfaceNames,\n selectLanNetwork,\n type ManagedSetup,\n} from './managed-setup.js'\nimport { prepareManagedLanSetup, type ManagedLanSetupResult } from './lan-setup.js'\n\n/** Stable Cordis plugin name. */\nexport const name = 'dsh-mobile'\n\n/** The stock WebServer serves the control card; Connection authenticates the loopback DSH origin. */\nexport const inject = ['webServer', 'commands', 'connection']\n\n/** Run cleanup steps in ownership order and report every failure after all steps settle. */\nexport async function settleCleanupSteps(steps: readonly (() => void | Promise<void>)[]): Promise<void> {\n const errors: unknown[] = []\n for (const step of steps) {\n try { await step() } catch (error) { errors.push(error) }\n }\n if (errors.length === 1 && errors[0] instanceof Error) throw errors[0]\n if (errors.length > 0) throw new AggregateError(errors, 'DSH Mobile cleanup failed')\n}\n\ninterface BrowserAuthenticatedConnection {\n authenticatedUrl?: (baseUrl: string) => string\n}\n\n/**\n * Resolve the DSH launch-token URL the gateway exchanges for its upstream cookie.\n *\n * A layer that disables DSH browser authentication — dsh-lan-access with\n * `noAuth: true` replaces `authenticatedUrl` with one returning the bare origin\n * — produces a URL with no query string, so it cannot carry a launch token. That\n * is a legitimate \"this upstream needs no browser auth\" signal: report no URL and\n * the gateway proxies without a cookie instead of failing every route with\n * `upstream_unavailable`. A token-bearing URL keeps the existing exchange, and a\n * connection service that is absent or returns a malformed URL keeps failing\n * closed. Only parsing is guarded: a connection service that throws still fails\n * plugin activation loudly rather than silently proxying without authentication.\n */\nexport function upstreamAuthenticatedUrl(ctx: Context, upstreamOrigin: URL): string | undefined {\n const connection = (ctx as Context & { readonly connection?: BrowserAuthenticatedConnection }).connection\n if (typeof connection?.authenticatedUrl !== 'function') return undefined\n const authenticatedUrl = connection.authenticatedUrl(upstreamOrigin.origin)\n try {\n return new URL(authenticatedUrl).search === '' ? undefined : authenticatedUrl\n } catch {\n return undefined\n }\n}\n\nfunction installedDshVersion(): string {\n try {\n const manifest = createRequire(import.meta.url)('@deepseek-ai/dsh-host-webserver/package.json') as unknown\n if (manifest === null || typeof manifest !== 'object') return 'unknown'\n const version = (manifest as { readonly version?: unknown }).version\n return typeof version === 'string' && version !== '' ? version : 'unknown'\n } catch {\n return 'unknown'\n }\n}\n\nfunction mapAdminError(error: unknown): HttpError {\n if (error instanceof HttpError) return error\n const code = (error as NodeJS.ErrnoException).code\n if (error instanceof Error && error.message.includes('spawn UNKNOWN')) {\n return new HttpError(409, 'frp_component_launch_failed')\n }\n if (code === 'EADDRNOTAVAIL') return new HttpError(409, 'network_address_changed')\n if (code === 'EADDRINUSE') return new HttpError(409, 'listen_port_in_use')\n if (error instanceof Error && error.message.startsWith('saved LAN interface ')) {\n return new HttpError(409, 'network_interface_unavailable')\n }\n if (error instanceof Error && error.message === 'cpolar_authtoken_invalid') {\n return new HttpError(400, 'cpolar_authtoken_invalid')\n }\n if (error instanceof Error && error.message.startsWith('cpolar_')) {\n return new HttpError(409, error.message)\n }\n // Component download and hash failures carry their own stable code, so the panel can show why an\n // install failed instead of a generic server error.\n if (error instanceof Error && error.message.startsWith('cloudflared_')) {\n return new HttpError(409, error.message)\n }\n if (error instanceof Error && [\n 'frp_server_address_invalid',\n 'frp_server_port_invalid',\n 'frp_token_invalid',\n 'frp_public_origin_invalid',\n 'frp_settings_invalid',\n ].includes(error.message)) return new HttpError(400, error.message)\n // Attach-mode codes are environment/precondition conflicts by design:\n // `frp_attach_mode_requires_vhost_port` (the user's frps port is unknown),\n // `frp_attach_cert_unknown` (the ingress certificate is missing or unusable),\n // and `frp_entry_tls_invalid` (the requested entry mode cannot be provisioned)\n // all map to 409 here, while a reachable plaintext vhost keeps the upstream\n // `frp_vhost_publicly_reachable` / `frp_vhost_probe_failed` codes.\n if (error instanceof Error && error.message.startsWith('frp_')) {\n return new HttpError(409, error.message)\n }\n if (error instanceof Error && [\n 'origin_settings_invalid', 'origin_public_origin_invalid', 'origin_listen_host_invalid',\n 'origin_listen_port_invalid', 'origin_listen_port_reserved', 'origin_allowed_cidrs_invalid',\n ].includes(error.message)) return new HttpError(400, error.message)\n if (error instanceof Error && error.message.startsWith('origin_')) {\n return new HttpError(409, error.message)\n }\n if (error instanceof Error && error.message.startsWith('vps_')) {\n return new HttpError(409, error.message)\n }\n if (error instanceof Error && error.message === 'plugin_update_failed') {\n return new HttpError(500, error.message)\n }\n if (error instanceof Error && error.message.startsWith('plugin_update_')) {\n return new HttpError(409, error.message)\n }\n if (error instanceof Error && error.message.startsWith('lan_setup_')) {\n return new HttpError(409, error.message)\n }\n return new HttpError(500, 'internal_error')\n}\n\nconst SETUP_KEYS = new Set([\n 'version', 'publicOrigin', 'listenHost', 'listenPort', 'upstreamOrigin',\n 'publicAuthorities', 'allowedCidrs', 'instanceId', 'pairingCaFile', 'tls',\n])\n\n/** True when path names a regular file (not a directory or symlink). */\nasync function existsRegularFile(path: string): Promise<boolean> {\n try {\n const info = await lstat(path)\n return info.isFile() && !info.isSymbolicLink()\n } catch {\n return false\n }\n}\n\ntype LoadedSetup = {\n readonly kind: 'fixed'\n readonly config: PluginConfig\n} | {\n readonly kind: 'unconfigured'\n readonly config: PluginConfig\n readonly setupFile: string\n} | {\n readonly kind: 'managed'\n readonly config: PluginConfig\n readonly setup: ManagedSetup\n}\n\nfunction withoutSetupKeys(config: PluginConfig): PluginConfig {\n const merged = { ...config } as Record<string, unknown>\n for (const key of SETUP_KEYS) if (key !== 'version') delete merged[key]\n return merged as unknown as PluginConfig\n}\n\nasync function loadSetup(config: PluginConfig): Promise<LoadedSetup> {\n if (config.setupFile === undefined) return { kind: 'fixed', config }\n if (!isAbsolute(config.setupFile)) throw new Error('setupFile must be an absolute file path')\n let source: string\n try {\n source = await readFile(resolve(config.setupFile), 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return { kind: 'unconfigured', config, setupFile: resolve(config.setupFile) }\n }\n throw error\n }\n let parsed: unknown\n try { parsed = JSON.parse(source) as unknown }\n catch (error) { throw new Error('mobile setup file is not valid JSON', { cause: error }) }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('mobile setup file must be an object')\n }\n const record = parsed as Record<string, unknown>\n if (record.version === 2) {\n return { kind: 'managed', config: withoutSetupKeys(config), setup: parseManagedSetup(record) }\n }\n if (record.version !== 1 || Reflect.ownKeys(record).some(key => typeof key !== 'string' || !SETUP_KEYS.has(key))) {\n throw new Error('mobile setup file has an unsupported format')\n }\n const { version: _version, ...setup } = record\n delete setup.upstreamOrigin\n return {\n kind: 'fixed',\n config: { ...withoutSetupKeys(config), ...setup } as unknown as PluginConfig,\n }\n}\n\nfunction loopbackTemplate(loaded: LoadedSetup, webServerPort: number): ResolvedGatewayConfig {\n const base = withoutSetupKeys(loaded.config)\n const activeUpstreamOrigin = `http://127.0.0.1:${String(webServerPort)}`\n return parseGatewayConfig({\n ...base,\n ...(loaded.kind === 'managed'\n ? { upstreamOrigin: activeUpstreamOrigin }\n : { upstreamOrigin: loaded.config.upstreamOrigin ?? activeUpstreamOrigin }),\n listenHost: '127.0.0.1',\n listenPort: 0,\n publicAuthorities: ['127.0.0.1'],\n allowedCidrs: ['127.0.0.0/8'],\n tls: { mode: 'disabled' },\n })\n}\n\nasync function stableInstanceId(loaded: LoadedSetup, template: ResolvedGatewayConfig): Promise<string> {\n if (loaded.kind !== 'managed') return loaded.config.instanceId ?? template.instanceId\n const certificate = new X509Certificate(await readFile(loaded.setup.tls.caCertFile))\n return certificate.fingerprint256.replaceAll(':', '').toLowerCase()\n}\n\nexport function remoteGatewayConfig(\n template: ResolvedGatewayConfig,\n publicOrigin: string,\n stateFile: string,\n instanceId: string,\n listenPort = 0,\n): ResolvedGatewayConfig {\n const origin = new URL(publicOrigin)\n if (origin.protocol !== 'https:' || origin.username !== '' || origin.password !== ''\n || origin.pathname !== '/' || origin.search !== '' || origin.hash !== '') {\n throw new Error('remote public origin must be an HTTPS origin')\n }\n // The gateway listens on an ephemeral loopback port behind Funnel, while the\n // public authority is HTTPS on 443. Keep that external port explicit so the\n // trust policy never substitutes the private listener port into QR URLs.\n const publicAuthority = origin.port === '' ? `${origin.hostname}:443` : origin.host\n const { pairingCaFile: _pairingCaFile, ...shared } = template\n return Object.freeze({\n ...shared,\n listenHost: '127.0.0.1',\n listenPort,\n authorities: Object.freeze([parseAuthority(publicAuthority)]),\n allowedCidrs: Object.freeze([parseCidr('127.0.0.0/8')]),\n stateFile,\n instanceId,\n tls: Object.freeze({ mode: 'disabled' }),\n publicTls: true,\n discovery: false,\n })\n}\n\n/**\n * Gateway config for the self-signed FRP ingress.\n *\n * Unlike {@link remoteGatewayConfig}, the gateway itself is the TLS endpoint:\n * frps only forwards raw TCP, so the listener speaks HTTPS with the leaf that\n * `pairingCaFile` signed, and the pairing CA is deliberately retained so the\n * gateway can serve `GET /mobile-access/ca.cer` for the app to pin. The listener\n * stays on loopback because the public entry is the frps TCP proxy, never a\n * direct bind.\n */\nexport function frpIngressGatewayConfig(\n template: ResolvedGatewayConfig,\n settings: FrpSettings,\n stateFile: string,\n ingress: Pick<FrpIngressCertificate, 'paths' | 'caFingerprint'>,\n listenPort = 0,\n): ResolvedGatewayConfig {\n if (!isFrpSelfSignedIngress(settings)) throw new Error('frp_entry_tls_invalid')\n const publicHost = new URL(settings.publicOrigin).hostname\n const publicAuthority = `${publicHost}:${String(resolveFrpPublicPort(settings))}`\n const { pairingCaFile: _templateCaFile, ...shared } = template\n return Object.freeze({\n ...shared,\n listenHost: '127.0.0.1',\n listenPort,\n authorities: Object.freeze([parseAuthority(publicAuthority)]),\n // Only frpc originates the connection, from the same computer.\n allowedCidrs: Object.freeze([parseCidr('127.0.0.0/8')]),\n stateFile,\n // The app validates `instance` against the CA it pins (gateway.ts checks the\n // same fingerprint), so this channel must use the CA fingerprint, not the\n // plugin-wide installation id.\n instanceId: ingress.caFingerprint,\n pairingCaFile: ingress.paths.caCertFile,\n tls: Object.freeze({ mode: 'provided', certFile: ingress.paths.certFile, keyFile: ingress.paths.keyFile }),\n publicTls: true,\n discovery: false,\n })\n}\n\n/**\n * CA the FRP start-up self-check must pin, read from the ingress directory.\n *\n * The self-signed entry presents a leaf signed by this CA, which is absent from\n * the system trust store, so the probe must anchor it exactly as the app does\n * when it pins `pairingCaFile`. The public-CA entry answers with a publicly\n * trusted certificate and therefore returns `undefined` (system trust store).\n * An unreadable self-signed CA fails explicitly: falling back to the system\n * trust store would hide a broken pairing identity behind a start timeout.\n */\nexport async function readFrpIngressTrustAnchor(settings: FrpSettings, stateFile: string): Promise<string | undefined> {\n if (!isFrpSelfSignedIngress(settings)) return undefined\n try { return await readFile(frpIngressPaths(stateFile).caCertFile, 'utf8') } catch (error) {\n throw new Error('frp_ingress_ca_invalid', { cause: error })\n }\n}\n\n/** Reuse remote HTTPS policy while binding a separately validated private HTTP origin. */\nexport function originGatewayConfig(\n template: ResolvedGatewayConfig,\n settings: OriginSettings,\n stateFile: string,\n instanceId: string,\n listenPort = settings.listenPort,\n): ResolvedGatewayConfig {\n const validated = parseOriginSettings(settings)\n // Port zero is available only to in-process tests, never to saved settings.\n if (listenPort !== 0) validateOriginListenPort(listenPort)\n return Object.freeze({\n ...remoteGatewayConfig(template, validated.publicOrigin, stateFile, instanceId, listenPort),\n listenHost: validated.listenHost,\n allowedCidrs: Object.freeze(validated.allowedCidrs.map(parseCidr)),\n })\n}\n\nfunction remoteControlPayload(\n provider: RemoteProvider,\n status: RemoteProviderStatus,\n gateway: MobileAccessGateway | undefined,\n providerStatuses: Readonly<Record<RemoteProvider, RemoteProviderStatus>>,\n cpolarComponent: CpolarComponentStatus,\n cloudflaredComponent: CloudflaredComponentStatus,\n cloudflaredConfiguration: CloudflaredTunnelStatus,\n frpComponent: FrpComponentStatus,\n frpConfiguration: FrpConfigurationStatus,\n originConfiguration: OriginConfigurationStatus,\n): Record<string, unknown> {\n return {\n provider,\n running: status.enabled,\n state: status.state,\n ...(status.origin === undefined ? {} : { origin: status.origin }),\n ...(status.backendOrigin === undefined ? {} : { backendOrigin: status.backendOrigin }),\n ...(status.loginUrl === undefined ? {} : { loginUrl: status.loginUrl }),\n ...(status.setupUrl === undefined ? {} : { setupUrl: status.setupUrl }),\n ...(status.errorCode === undefined ? {} : { errorCode: status.errorCode }),\n ...(gateway === undefined ? {} : { extensions: gateway.extensionStatus() }),\n providers: {\n tailscale: { bundled: true, running: providerStatuses.tailscale.enabled, state: providerStatuses.tailscale.state },\n cpolar: {\n bundled: false,\n running: providerStatuses.cpolar.enabled,\n state: providerStatuses.cpolar.state,\n component: cpolarComponent,\n },\n cloudflared: {\n bundled: false,\n running: providerStatuses.cloudflared.enabled,\n state: providerStatuses.cloudflared.state,\n component: cloudflaredComponent,\n configuration: cloudflaredConfiguration,\n },\n frp: {\n bundled: false,\n running: providerStatuses.frp.enabled,\n state: providerStatuses.frp.state,\n component: frpComponent,\n configuration: frpConfiguration,\n },\n origin: {\n bundled: true,\n running: providerStatuses.origin.enabled,\n state: providerStatuses.origin.state,\n configuration: originConfiguration,\n },\n },\n }\n}\n\n/** Mount the resident control route and its optional authenticated LAN gateway. */\nexport async function apply(ctx: Context, config: PluginConfig): Promise<void> {\n const dshVersion = installedDshVersion()\n const loaded = await loadSetup(config)\n const mobileAccess: MobileAccessService = createMobileAccessService(ctx)\n const template = loopbackTemplate(loaded, ctx.webServer.port)\n const upstreamLoginUrl = upstreamAuthenticatedUrl(ctx, template.upstreamOrigin)\n const instanceId = await stableInstanceId(loaded, template)\n const stateDirectory = dirname(template.stateFile)\n const logFile = await installMobileFileLogger(ctx, stateDirectory)\n const logger = ctx.logger('dsh-mobile')\n logger.info('logging initialized file=%s', logFile)\n // Completed root turns fan out to every live mobile gateway, whose phones\n // render the notification text locally. One subscription serves all paths.\n const taskEventHub = new TaskEventHub()\n const disposeTaskEvents = watchTaskCompletions(ctx, {\n onTaskCompleted: event => taskEventHub.broadcast(event),\n log(event, fields) { logger.info('task event=%s fields=%o', event, fields) },\n })\n const remoteDirectory = join(stateDirectory, 'remote')\n const configuredDshHome = process.env.DSH_HOME?.trim()\n const dshHome = configuredDshHome === undefined || configuredDshHome === ''\n ? dirname(stateDirectory)\n : resolve(configuredDshHome)\n const releaseManager = new PluginReleaseManager({\n profileDirectory: releaseProfileDirectory(ctx, dshHome, process.argv.slice(2)),\n })\n const remoteProviderStore = new JsonRemoteProviderStore(\n join(remoteDirectory, 'provider.json'),\n configuredRemoteProvider(process.env),\n )\n const initialRemoteProvider = (await remoteProviderStore.load()).provider\n const cpolarComponent = new CpolarComponentManager({ stateDirectory })\n await cpolarComponent.initialize()\n const cloudflaredComponent = new CloudflaredComponentManager({ stateDirectory })\n await cloudflaredComponent.initialize()\n const frpComponent = new FrpComponentManager({ stateDirectory })\n await frpComponent.initialize()\n const frpConfig = new FrpConfigStore(join(remoteDirectory, 'frp', 'config'))\n await frpConfig.initialize()\n const originConfig = new OriginConfigStore(join(remoteDirectory, 'origin', 'config'))\n await originConfig.initialize()\n const cloudflaredTunnel = new CloudflaredTunnelStore(join(remoteDirectory, 'cloudflared'))\n await cloudflaredTunnel.initialize()\n const unregisterBuiltin = mobileAccess.registerExtension({\n schemaVersion: 1,\n id: 'computer-images',\n name: 'Computer images',\n version: '1.0.0',\n description: 'Authenticated computer-side image browser',\n routes: [\n {\n method: 'GET', path: 'list',\n async handle(request) {\n return { status: 200, contentType: 'application/json; charset=utf-8', body: JSON.stringify(await listComputerImages(request.query.get('path'))) }\n },\n },\n {\n method: 'GET', path: 'image',\n async handle(request) {\n const image = await readComputerImage(request.query.get('path'))\n return { status: 200, contentType: image.contentType, headers: { 'content-disposition': `inline; filename*=UTF-8''${encodeURIComponent(image.name)}` }, body: image.body }\n },\n },\n ],\n })\n // One upgrade-path policy shared by the LAN gateway and every remote\n // gateway: admin approvals apply immediately, no restart required.\n const webSocketPaths = new WebSocketPathStore(join(stateDirectory, 'websocket-paths.json'))\n await webSocketPaths.load()\n const blockedUpgradePaths = new BlockedUpgradePathLog()\n let lanGateway: MobileAccessGateway | undefined\n let preparedLanSetup: ManagedLanSetupResult | undefined\n const startGateway = async (candidateConfig: PluginConfig): Promise<MobileAccessRuntime> => {\n const resolved = parseGatewayConfig({\n ...candidateConfig,\n upstreamOrigin: template.upstreamOrigin.origin,\n })\n const candidate = new MobileAccessGateway(\n resolved,\n new JsonDeviceStore(resolved.stateFile, resolved.maxDevices),\n mobileAccess,\n upstreamLoginUrl,\n webSocketPaths,\n blockedUpgradePaths,\n (source, code) => { logger.warn('%s discovery degraded while DSH remains available: %s', source, code) },\n )\n await candidate.start()\n lanGateway = candidate\n const removeTaskSink = taskEventHub.add(candidate)\n return {\n close: async () => {\n if (lanGateway === candidate) lanGateway = undefined\n removeTaskSink()\n await candidate.close()\n },\n }\n }\n const startRuntime = async (): Promise<MobileAccessRuntime> => {\n if (loaded.kind === 'unconfigured') throw new Error(preparedLanSetup === undefined ? 'lan_setup_required' : 'lan_setup_restart_required')\n if (loaded.kind === 'fixed') return startGateway(loaded.config)\n const following = new FollowingMobileAccessRuntime(async () => {\n const network = selectLanNetwork(undefined, loaded.setup.networkInterface)\n return {\n key: `${network.name}\\0${network.address}\\0${network.cidr}`,\n start: async () => startGateway({\n ...loaded.config,\n ...await materializeManagedSetup(loaded.setup),\n }),\n }\n }, (error) => {\n process.emitWarning(`DSH Mobile could not follow the current LAN address: ${error instanceof Error ? error.message : String(error)}`, {\n code: 'DSH_MOBILE_NETWORK_REFRESH',\n })\n })\n try {\n await following.initialize(2_000)\n } catch (error) {\n // A missing saved interface (Wi-Fi/Ethernet switch, docked laptop…)\n // must not take down all of DSH: stay dormant with the poller armed so\n // a returning network recovers on its own. Anything else is a real\n // failure and still fails boot loudly.\n if (!isNetworkSelectionError(error)) throw error\n const detail = error instanceof Error ? error.message : String(error)\n process.emitWarning(\n `DSH Mobile: ${detail}; mobile access stays dormant until the network returns or setup is re-run — DSH itself keeps running.`,\n { code: 'DSH_MOBILE_NETWORK_UNAVAILABLE' },\n )\n following.beginPolling(2_000)\n }\n return following\n }\n const lanControlFile = parseControlFile(config.controlFile)\n const lanControlStore = new JsonMobileAccessControlStore(lanControlFile, config.initiallyEnabled)\n if (loaded.kind === 'unconfigured' && (await lanControlStore.load()).enabled) {\n await lanControlStore.save({ version: 1, enabled: false })\n }\n const lanController = new MobileAccessGatewayController(lanControlStore, startRuntime)\n const remoteDeviceFile = join(remoteDirectory, 'devices.json')\n const legacyCpolarDeviceFile = join(remoteDirectory, 'cpolar', 'devices.json')\n if (initialRemoteProvider === 'cpolar') {\n try {\n await lstat(remoteDeviceFile)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n try { await copyFile(legacyCpolarDeviceFile, remoteDeviceFile) } catch (copyError) {\n if ((copyError as NodeJS.ErrnoException).code !== 'ENOENT') throw copyError\n }\n }\n }\n const createRemoteGateway = async (publicOrigin: string, listenPort = 0): Promise<MobileAccessGateway> => {\n const resolved = remoteGatewayConfig(\n template,\n publicOrigin,\n remoteDeviceFile,\n instanceId,\n listenPort,\n )\n const candidate = new MobileAccessGateway(\n resolved,\n new JsonDeviceStore(resolved.stateFile, resolved.maxDevices),\n mobileAccess,\n upstreamLoginUrl,\n webSocketPaths,\n blockedUpgradePaths,\n )\n await candidate.start()\n return candidate\n }\n /**\n * Gateway factory for the FRP provider.\n *\n * The public-CA entry keeps the ordinary Caddy-fronted remote gateway. The\n * self-signed entry terminates TLS inside the gateway, so it first materializes\n * a leaf the pairing CA signed for the public IPv4 and then binds the ingress\n * configuration that keeps that CA pinned.\n */\n const createFrpGateway = async (publicOrigin: string, settings: FrpSettings): Promise<MobileAccessGateway> => {\n if (!isFrpSelfSignedIngress(settings)) return createRemoteGateway(publicOrigin)\n const ingress = await ensureFrpIngressCertificate(settings, remoteDeviceFile)\n const resolved = frpIngressGatewayConfig(template, settings, remoteDeviceFile, ingress)\n const candidate = new MobileAccessGateway(\n resolved,\n new JsonDeviceStore(resolved.stateFile, resolved.maxDevices),\n mobileAccess,\n upstreamLoginUrl,\n webSocketPaths,\n blockedUpgradePaths,\n )\n try { await candidate.start() } catch (error) {\n await candidate.close()\n throw error\n }\n return candidate\n }\n const createOriginGateway = async (settings: OriginSettings): Promise<MobileAccessGateway> => {\n const resolved = originGatewayConfig(template, settings, remoteDeviceFile, instanceId)\n const candidate = new MobileAccessGateway(\n resolved, new JsonDeviceStore(resolved.stateFile, resolved.maxDevices), mobileAccess,\n upstreamLoginUrl, webSocketPaths, blockedUpgradePaths,\n )\n try { await candidate.start() } catch (error) {\n await candidate.close()\n throw error\n }\n return candidate\n }\n const tailscaleStore = new JsonMobileAccessControlStore(join(remoteDirectory, 'control.json'), false)\n const cpolarStore = new JsonMobileAccessControlStore(join(remoteDirectory, 'cpolar', 'control.json'), false)\n const cloudflaredStore = new JsonMobileAccessControlStore(join(remoteDirectory, 'cloudflared', 'control.json'), false)\n const frpStore = new JsonMobileAccessControlStore(join(remoteDirectory, 'frp', 'control.json'), false)\n const originStore = new JsonMobileAccessControlStore(join(remoteDirectory, 'origin', 'control.json'), false)\n const remoteControllers: Record<RemoteProvider, RemoteProviderController> = {\n tailscale: new FunnelController({\n store: tailscaleStore,\n executable: funnelExecutable(import.meta.url),\n stateDirectory: join(remoteDirectory, 'tailscale'),\n hostname: `dsh-${instanceId.slice(0, 12)}`,\n createGateway: createRemoteGateway,\n }),\n cpolar: new CpolarController({\n store: cpolarStore,\n executable: cpolarComponent.executable,\n configFile: cpolarComponent.configFile,\n createGateway: createRemoteGateway,\n }),\n cloudflared: new CloudflaredController({\n store: cloudflaredStore,\n executable: cloudflaredComponent.executable,\n tunnel: cloudflaredTunnel,\n createGateway: createRemoteGateway,\n }),\n frp: new FrpController({\n store: frpStore,\n executable: frpComponent.executable,\n config: frpConfig,\n instanceId,\n createGateway: createFrpGateway,\n /**\n * Hand the start-up self-check the CA the app itself pins.\n *\n * Only this module knows where the ingress material lives, so the anchor is\n * read here from the ingress directory instead of being guessed downstream.\n */\n resolveDiscoveryTrustAnchor: settings => readFrpIngressTrustAnchor(settings, remoteDeviceFile),\n maintainIngressCertificate: async (settings, gateway) => {\n await ensureFrpIngressCertificate(settings, remoteDeviceFile, Date.now(), gateway.config.instanceId)\n await gateway.refreshProvidedTls()\n },\n }),\n origin: new OriginController({ store: originStore, config: originConfig, createGateway: createOriginGateway }),\n }\n const remoteProviders = new RemoteProviderCoordinator(initialRemoteProvider, remoteControllers, remoteProviderStore)\n const remoteController = () => remoteProviders.controller()\n // Remote gateways rotate inside their controllers; forward through the\n // current instance so stale gateways never receive events.\n for (const provider of REMOTE_PROVIDERS) {\n const controller = remoteControllers[provider]\n taskEventHub.add({ broadcastTaskEvent: event => { controller.gateway()?.broadcastTaskEvent(event) } })\n }\n const remotePayload = (): Record<string, unknown> => remoteControlPayload(\n remoteProviders.selected,\n remoteController().status(),\n remoteController().gateway(),\n {\n tailscale: remoteControllers.tailscale.status(),\n cpolar: remoteControllers.cpolar.status(),\n cloudflared: remoteControllers.cloudflared.status(),\n frp: remoteControllers.frp.status(),\n origin: remoteControllers.origin.status(),\n },\n cpolarComponent.status(),\n cloudflaredComponent.status(),\n cloudflaredTunnel.status(),\n frpComponent.status(),\n frpConfig.status(),\n originConfig.status(),\n )\n const lanPayload = (): Record<string, unknown> => ({\n ...(loaded.kind === 'unconfigured' ? {\n configured: preparedLanSetup !== undefined,\n restartRequired: preparedLanSetup !== undefined,\n } : {}),\n running: lanController.isRunning(),\n origin: lanGateway?.address().origin,\n ...(preparedLanSetup === undefined ? {} : { pendingOrigin: preparedLanSetup.origin }),\n ...(lanGateway === undefined ? {} : { extensions: lanGateway.extensionStatus() }),\n })\n const lanSetupPayload = async (): Promise<Record<string, unknown>> => {\n if (loaded.kind !== 'unconfigured' || preparedLanSetup !== undefined) return lanPayload()\n const networks = availableLanNetworks()\n const preferred = await preferredLanInterfaceNames()\n let recommendedAddress: string | undefined\n try { recommendedAddress = selectLanNetwork(undefined, undefined, undefined, preferred).address } catch { /* user chooses when selection is ambiguous */ }\n return {\n ...lanPayload(),\n networks: networks.map(network => ({\n name: network.name,\n address: network.address,\n cidr: network.cidr,\n recommended: network.address === recommendedAddress,\n })),\n listenPort: 3443,\n windowsFirewall: process.platform === 'win32',\n }\n }\n const diagnosticsPayload = async (): Promise<Record<string, unknown>> => {\n let interfaceName: string | undefined\n let networkError: string | undefined\n if (loaded.kind === 'managed') {\n try { interfaceName = selectLanNetwork(undefined, loaded.setup.networkInterface).name }\n catch { networkError = 'network_interface_unavailable' }\n }\n const remote = remoteController().status()\n const frpOrigin = remote.origin\n const frpSettings = remoteProviders.selected === 'frp' ? frpConfig.settings() : undefined\n const frpGateway = remoteControllers.frp.gateway()\n const pinnedFrpProbe = frpSettings !== undefined && isFrpSelfSignedIngress(frpSettings)\n && frpGateway !== undefined && frpOrigin !== undefined\n ? async (): Promise<{ state: 'ready' | 'unreachable'; latencyMs?: number }> => {\n try {\n const trustAnchorPem = await readFrpIngressTrustAnchor(frpSettings, remoteDeviceFile)\n if (trustAnchorPem === undefined) return { state: 'unreachable' }\n const started = performance.now()\n const reachable = await defaultProbeDiscovery(\n { origin: frpOrigin, trustAnchorPem },\n frpGateway.config.instanceId,\n AbortSignal.timeout(5_500),\n )\n return reachable\n ? { state: 'ready', latencyMs: Math.max(0, Math.round(performance.now() - started)) }\n : { state: 'unreachable' }\n } catch {\n return { state: 'unreachable' }\n }\n }\n : undefined\n return collectConnectionDiagnostics({\n dshVersion,\n lan: {\n configured: loaded.kind !== 'unconfigured' || preparedLanSetup !== undefined,\n running: lanController.isRunning(),\n ...(lanGateway === undefined ? {} : { origin: lanGateway.address().origin, port: lanGateway.address().port }),\n ...(loaded.kind === 'managed' ? { configuredInterface: loaded.setup.networkInterface, port: loaded.setup.listenPort } : {}),\n ...(interfaceName === undefined ? {} : { interfaceName }),\n ...(networkError === undefined ? {} : { networkError }),\n },\n remote: {\n provider: remoteProviders.selected,\n running: remote.enabled,\n state: remote.state,\n ...(remote.origin === undefined ? {} : { origin: remote.origin }),\n ...(remote.errorCode === undefined ? {} : { errorCode: remote.errorCode }),\n },\n }, pinnedFrpProbe === undefined ? {} : { remote: pinnedFrpProbe }) as unknown as Record<string, unknown>\n }\n\n const adminRoute: WebRoute = {\n kind: 'prefix',\n path: LOCAL_ADMIN_PREFIX,\n handler: async (request, response) => {\n try {\n const target = parseRequestTarget(request.url)\n assertLocalAdminTrust(request, request.method === 'POST')\n if (target.search !== '') throw new HttpError(400, 'bad_request')\n const lanControl = target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/control`\n || target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/lan/control`\n if (request.method === 'GET' && lanControl) {\n sendJson(response, 200, lanPayload(), false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/lan/setup`) {\n sendJson(response, 200, await lanSetupPayload(), false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/diagnostics`) {\n sendJson(response, 200, await diagnosticsPayload(), false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/release`) {\n sendJson(response, 200, await releaseManager.status(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/release/update`) {\n await readJsonObject(request, 4096)\n sendJson(response, 200, await releaseManager.update(), false)\n return\n }\n if (request.method === 'POST' && lanControl) {\n const body = await readJsonObject(request, 4096)\n if (typeof body.running !== 'boolean') throw new HttpError(400, 'bad_request')\n if (body.running && loaded.kind === 'unconfigured') {\n throw new HttpError(409, preparedLanSetup === undefined ? 'lan_setup_required' : 'lan_setup_restart_required')\n }\n await lanController.setRunning(body.running)\n sendJson(response, 200, lanPayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/lan/setup`) {\n const body = await readJsonObject(request, 4096)\n if (loaded.kind !== 'unconfigured') throw new HttpError(409, 'lan_setup_already_configured')\n if (preparedLanSetup !== undefined) {\n sendJson(response, 200, await lanSetupPayload(), false)\n return\n }\n if (body.confirm !== true || typeof body.address !== 'string') throw new HttpError(400, 'bad_request')\n const network = availableLanNetworks().find(candidate => candidate.address === body.address)\n if (network === undefined) throw new HttpError(409, 'lan_setup_network_unavailable')\n try {\n preparedLanSetup = await prepareManagedLanSetup({\n setupFile: loaded.setupFile,\n controlFile: lanControlFile,\n network,\n listenPort: 3443,\n dshPort: ctx.webServer.port,\n configureFirewall: body.configureFirewall !== false,\n })\n } catch (error) {\n logger.error('managed LAN setup failed: %s', error instanceof Error ? error.stack ?? error.message : String(error))\n throw new HttpError(409, 'lan_setup_failed')\n }\n sendJson(response, 200, await lanSetupPayload(), false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/control`) {\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/provider`) {\n const body = await readJsonObject(request, 4096)\n if (body.provider !== 'tailscale' && body.provider !== 'cpolar' && body.provider !== 'cloudflared'\n && body.provider !== 'frp' && body.provider !== 'origin') {\n throw new HttpError(400, 'bad_request')\n }\n await remoteProviders.select(body.provider)\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/origin/configure`) {\n const body = await readJsonObject(request, 8192)\n await remoteProviders.mutate(async () => {\n await originConfig.configure(body)\n if (remoteControllers.origin.status().enabled) await remoteControllers.origin.reconnect()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/origin/purge`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async () => {\n await remoteControllers.origin.setEnabled(false)\n await originConfig.purge()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cpolar/component/install`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async () => cpolarComponent.install())\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cpolar/configure`) {\n const body = await readJsonObject(request, 4096)\n await remoteProviders.mutate(async () => cpolarComponent.configure(body.authtoken))\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cpolar/component/purge`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async () => {\n await remoteControllers.cpolar.setEnabled(false)\n await cpolarComponent.purge()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cloudflared/component/install`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n logger.info('cloudflared component install started')\n try {\n await remoteProviders.mutate(async () => cloudflaredComponent.install())\n logger.info('cloudflared component install completed')\n } catch (error) {\n logger.error('cloudflared component install failed: %s', error instanceof Error ? error.stack ?? error.message : String(error))\n throw error\n }\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cloudflared/component/purge`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async () => {\n await remoteControllers.cloudflared.setEnabled(false)\n await cloudflaredComponent.purge()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cloudflared/tunnel`) {\n const body = await readJsonObject(request, 8192)\n await remoteProviders.mutate(async () => {\n await cloudflaredTunnel.configure(mergeSavedCloudflaredTunnelSettings(body, cloudflaredTunnel.settings()))\n // A live connector is bound to the previous hostname, port and token, so\n // the new configuration only takes effect through a restart.\n if (remoteControllers.cloudflared.status().enabled) await remoteControllers.cloudflared.reconnect()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/cloudflared/tunnel/purge`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async () => {\n await remoteControllers.cloudflared.setEnabled(false)\n await cloudflaredTunnel.purge()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/component/install`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n logger.info('frpc component install started')\n try {\n await remoteProviders.mutate(async () => frpComponent.install())\n logger.info('frpc component install completed')\n } catch (error) {\n logger.error('frpc component install failed: %s', error instanceof Error ? error.stack ?? error.message : String(error))\n throw error\n }\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/configure`) {\n const body = await readJsonObject(request, 4096)\n await remoteProviders.mutate(async () => {\n await frpConfig.configure(body)\n if (remoteControllers.frp.status().enabled) await remoteControllers.frp.reconnect()\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/attach-plan`) {\n const body = await readJsonObject(request, 8192)\n // Read-only preview: blank fields keep their saved values, and nothing\n // on the VPS or the local filesystem is touched.\n const settings = mergeSavedFrpSettings(body, frpConfig.settings())\n // This route is callable by any same-origin client script. Never return\n // the saved token, even if a caller asks for an unmasked preview.\n const options = { configFile: frpConfig.runtimeConfigFile }\n logger.info('frp attach plan requested mode=%s entryTls=%s vhostHttpPort=%d',\n settings.mode ?? 'deploy', settings.entryTls ?? 'public-ip-cert', resolveFrpVhostHttpPort(settings))\n sendJson(response, 200, {\n ...remotePayload(),\n frpAttachPlan: createFrpAttachPlan(settings, options),\n frpAttachTemplate: createFrpAttachTemplate(settings, options),\n }, false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/self-check`) {\n const settings = frpConfig.settings()\n if (settings === undefined) throw new HttpError(409, 'frp_config_missing')\n const check = await frpIngressSelfCheck(settings, remoteDeviceFile)\n // Two independent facts: the control port proves the user's frps is up,\n // and the entry port proves the tunnel actually forwards to this\n // computer. Both are advisory and never gate a security decision.\n const [frpsReachable, entryReachable] = await Promise.all([\n probeTcpReachable(settings.serverAddress, settings.serverPort),\n probeTcpReachable(settings.serverAddress, resolveFrpPublicPort(settings)),\n ])\n sendJson(response, 200, {\n ...remotePayload(),\n frpSelfCheck: {\n ...check,\n // Only stable booleans and the CA fingerprint leave this route: no\n // token, no key material, and no private file paths.\n frpsReachable,\n entryReachable,\n frpc: remoteController().status(),\n component: frpComponent.status(),\n },\n }, false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/websocket-paths`) {\n sendJson(response, 200, { paths: webSocketPaths.list() }, false)\n return\n }\n if (request.method === 'GET' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/websocket-paths/blocked`) {\n sendJson(response, 200, { blocked: blockedUpgradePaths.report() }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/websocket-paths`) {\n const body = await readJsonObject(request, 4096)\n if (!Array.isArray(body.paths)) throw new HttpError(400, 'bad_request')\n const paths = await webSocketPaths.replace(body.paths)\n logger.info('websocket upgrade paths updated count=%d paths=%o', paths.length, paths)\n sendJson(response, 200, { paths }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/vps/host-keys`) {\n const body = await readJsonObject(request, 8192)\n const serverAddress = typeof body.serverAddress === 'string' ? body.serverAddress : ''\n logger.info('vps host keys requested host=%s sshUser=%s sshPort=%d',\n serverAddress, String(body.sshUser), Number(body.sshPort))\n const hostKeys = await fetchVpsHostKeys(serverAddress, {\n sshUser: body.sshUser,\n sshPort: body.sshPort,\n ...(body.sshKeyPath === undefined || body.sshKeyPath === '' ? {} : { sshKeyPath: body.sshKeyPath }),\n }, {\n log(event, fields) { logger.info('vps host keys event=%s fields=%o', event, fields) },\n })\n for (const key of hostKeys) logger.info('vps host key host=%s type=%s fingerprint=%s', serverAddress, key.keyType, key.fingerprint)\n sendJson(response, 200, { ...remotePayload(), vpsHostKeys: hostKeys }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/vps/deploy`) {\n const body = await readJsonObject(request, 8192)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n logger.info('vps deploy requested host=%s port=%d sshUser=%s sshPort=%d keyProvided=%s fingerprints=%s',\n String(body.serverAddress), Number(body.serverPort), String(body.sshUser), Number(body.sshPort), body.sshKeyPath === undefined ? 'false' : 'true',\n Array.isArray(body.hostFingerprints) ? String(body.hostFingerprints.length) : 'none')\n const deployment = await remoteProviders.mutate(async () => {\n // Blank fields keep their saved values so a saved token can stay empty.\n const settings = mergeSavedFrpSettings(body, frpConfig.settings())\n const result = await deployVps(settings, parseVpsDeploymentInput({\n sshUser: body.sshUser,\n sshPort: body.sshPort,\n ...(body.sshKeyPath === undefined ? {} : { sshKeyPath: body.sshKeyPath }),\n hostFingerprints: body.hostFingerprints,\n }), {\n log(event, fields) { logger.info('vps deploy event=%s fields=%o', event, fields) },\n })\n await frpConfig.configure(settings)\n logger.info('vps deploy completed host=%s origin=%s checks=%d', settings.serverAddress, settings.publicOrigin, result.checks.length)\n return result\n })\n sendJson(response, 200, { ...remotePayload(), vpsDeployment: deployment }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/vps/uninstall-script`) {\n const body = await readJsonObject(request, 4096)\n const savedTarget = mergeSavedFrpTarget(body, frpConfig.settings())\n const script = createVpsUninstallScript({\n serverPort: savedTarget.serverPort,\n ...(body.certName === undefined || body.certName === '' ? {} : { certName: body.certName }),\n })\n sendJson(response, 200, { ...remotePayload(), vpsUninstallScript: script }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/vps/uninstall`) {\n const body = await readJsonObject(request, 8192)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n logger.info('vps uninstall requested host=%s sshUser=%s sshPort=%d',\n String(body.serverAddress), String(body.sshUser), Number(body.sshPort))\n const removal = await remoteProviders.mutate(async () => {\n const savedTarget = mergeSavedFrpTarget(body, frpConfig.settings())\n const result = await uninstallVps(savedTarget.serverAddress, {\n serverPort: savedTarget.serverPort,\n ...(body.certName === undefined || body.certName === '' ? {} : { certName: body.certName }),\n }, parseVpsDeploymentInput({\n sshUser: body.sshUser,\n sshPort: body.sshPort,\n ...(body.sshKeyPath === undefined ? {} : { sshKeyPath: body.sshKeyPath }),\n hostFingerprints: body.hostFingerprints,\n }), {\n log(event, fields) { logger.info('vps uninstall event=%s fields=%o', event, fields) },\n })\n logger.info('vps uninstall completed host=%s checks=%d', result.serverAddress, result.checks.length)\n return result\n })\n sendJson(response, 200, { ...remotePayload(), vpsUninstall: removal }, false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/frp/component/purge`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async () => {\n await remoteControllers.frp.setEnabled(false)\n await Promise.all([frpComponent.purge(), frpConfig.purge(), purgeFrpIngressCertificates(remoteDeviceFile)])\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/control`) {\n const body = await readJsonObject(request, 4096)\n const running = body.running\n if (typeof running !== 'boolean') throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async controller => controller.setEnabled(running))\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/reconnect`) {\n await readJsonObject(request, 4096)\n await remoteProviders.mutate(async controller => controller.reconnect())\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (request.method === 'POST' && target.decodedPathname === `${LOCAL_ADMIN_PREFIX}/remote/reset`) {\n const body = await readJsonObject(request, 4096)\n if (body.confirm !== true) throw new HttpError(400, 'bad_request')\n await remoteProviders.mutate(async controller => {\n await controller.reset()\n await rm(remoteDeviceFile, { force: true })\n })\n sendJson(response, 200, remotePayload(), false)\n return\n }\n if (target.decodedPathname.startsWith(`${LOCAL_ADMIN_PREFIX}/remote/`)) {\n const active = remoteController().gateway()\n if (active === undefined) throw new HttpError(409, 'gateway_stopped')\n await active.localAdminRoute(`${LOCAL_ADMIN_PREFIX}/remote`).handler(request, response)\n return\n }\n if (target.decodedPathname.startsWith(`${LOCAL_ADMIN_PREFIX}/lan/`)) {\n const active = lanGateway\n if (active === undefined) throw new HttpError(409, 'gateway_stopped')\n await active.localAdminRoute(`${LOCAL_ADMIN_PREFIX}/lan`).handler(request, response)\n return\n }\n const active = lanGateway\n if (active === undefined) throw new HttpError(409, 'gateway_stopped')\n await active.localAdminRoute().handler(request, response)\n } catch (error) {\n const mapped = mapAdminError(error)\n if (response.headersSent) response.destroy()\n else sendFailure(response, mapped.status, mapped.code, false)\n }\n },\n }\n\n await ctx.effect(async () => {\n const unregister = ctx.webServer.register(adminRoute)\n const disposeMobileCommand = ctx.commands.register({\n name: 'mobile',\n description: '按需求修改 DSH Mobile 的手机端界面或添加电脑端能力',\n input: { hint: '<要做什么>' },\n handler: async ({ agent, rawInput }) => {\n const task = rawInput.trim()\n if (task === '') return { kind: 'error', text: '请带上需求,例如:/mobile 把手机端改成深色主题' }\n // Collect the current customization state so the guide does not\n // overwrite earlier /mobile work blindly.\n const state: MobileGuideState = {\n directory: stateDirectory,\n hasCustomCss: await existsRegularFile(template.customCssFile),\n hasCustomJs: await existsRegularFile(template.customScriptFile),\n extensions: mobileAccess.manifest().map(entry => ({\n id: entry.id,\n name: entry.name,\n version: entry.version,\n })),\n failedExtensionCount: mobileAccess.status().failed,\n }\n const guide = buildMobileGuide(state)\n // A plugin-source message renders as a collapsed context-injection row\n // (label \"dsh-mobile\", one-line notice summary) instead of a user bubble,\n // while steering still wakes the agent with the full guide as input.\n agent.steer(createUserMessage({\n content: [{ type: 'text', text: `${guide}\\n\\n用户需求:${task}` }],\n source: {\n kind: 'plugin:dsh-mobile',\n form: 'notice',\n summary: boundContextSummary(`/mobile ${task}`),\n },\n }))\n return { kind: 'success', text: '已把需求交给 DSH 处理,改动会在手机端几秒内生效。' }\n },\n })\n try {\n await mobileAccess.startLocal(template.extensionsDir, ctx)\n await lanController.initialize()\n const stores: Record<RemoteProvider, JsonMobileAccessControlStore> = {\n tailscale: tailscaleStore,\n cpolar: cpolarStore,\n cloudflared: cloudflaredStore,\n frp: frpStore,\n origin: originStore,\n }\n await Promise.all((Object.keys(stores) as RemoteProvider[])\n .filter(provider => provider !== remoteProviders.selected)\n .map(provider => stores[provider].save({ version: 1, enabled: false })))\n for (const provider of REMOTE_PROVIDERS) await remoteControllers[provider].initialize()\n // Broadcast discovery degrades silently when its UDP port cannot be bound, which\n // otherwise leaves \"the phone cannot find this computer\" with no visible cause.\n const lanDiscovery = lanGateway?.discoveryStatus()\n if (lanDiscovery !== undefined && !lanDiscovery.broadcast) {\n logger.warn(\n 'broadcast discovery is unavailable (%s); mDNS is still published, so pair manually or free the UDP port',\n lanDiscovery.errorCode ?? 'unknown_cause',\n )\n }\n if (lanDiscovery?.mobileAssetsErrorCode !== undefined) {\n logger.warn(\n 'a bundled mobile asset is missing (%s); the phone frontend will serve without it',\n lanDiscovery.mobileAssetsErrorCode,\n )\n }\n } catch (error) {\n try {\n await settleCleanupSteps([\n unregister,\n disposeMobileCommand,\n disposeTaskEvents,\n async () => {\n const results = await Promise.allSettled(Object.values(remoteControllers).map(controller => controller.close()))\n const failures = results.filter(result => result.status === 'rejected').map(result => result.reason as unknown)\n if (failures.length > 0) throw new AggregateError(failures, 'remote provider cleanup failed')\n },\n () => lanController.close(),\n () => mobileAccess.stopLocal(),\n unregisterBuiltin,\n ])\n } catch (cleanupError) {\n throw new AggregateError([error, cleanupError], 'DSH Mobile initialization and cleanup failed')\n }\n throw error\n }\n return async () => {\n await settleCleanupSteps([\n unregister,\n disposeMobileCommand,\n disposeTaskEvents,\n async () => {\n const results = await Promise.allSettled(Object.values(remoteControllers).map(controller => controller.close()))\n const failures = results.filter(result => result.status === 'rejected').map(result => result.reason as unknown)\n if (failures.length > 0) throw new AggregateError(failures, 'remote provider cleanup failed')\n },\n () => lanController.close(),\n () => mobileAccess.stopLocal(),\n unregisterBuiltin,\n ])\n }\n }, 'dsh-mobile: independent LAN and selectable remote providers with /mobile command')\n}\n"],"mappings":"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"}
|