diff --git a/App/FirstSetupView.swift b/App/FirstSetupView.swift index 627c64e..5164591 100644 --- a/App/FirstSetupView.swift +++ b/App/FirstSetupView.swift @@ -394,8 +394,22 @@ struct FirstSetupView: View { } } + private var thirdPartyMITMWarning: some View { + Label { + Text("iOS 27 beta 6 起,系统已禁止对 gs-loc.apple.com 进行 MITM 拦截。该版本及之后的 beta 版本暂时无法使用本项目,等待后续适配方案。") + .font(.footnote) + } icon: { + Image(systemName: "exclamationmark.triangle.fill") + } + .foregroundStyle(.orange) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(10) + .background(.orange.opacity(0.12), in: RoundedRectangle(cornerRadius: 10)) + } + private var thirdPartyClientStep: some View { VStack(alignment: .leading, spacing: 16) { + thirdPartyMITMWarning if !setup.message.isEmpty { Label(setup.message, systemImage: "exclamationmark.triangle.fill") .font(.footnote) @@ -476,6 +490,7 @@ struct FirstSetupView: View { private var thirdPartyImportStep: some View { let client = thirdPartyClient.selectedClient return VStack(alignment: .leading, spacing: 16) { + thirdPartyMITMWarning if showThirdPartyRepairReason { Label( "检测到第三方代理连接异常,请检查模块、MITM 和代理连接后重新检测。", diff --git a/README.en.md b/README.en.md index 6e75822..922da07 100644 --- a/README.en.md +++ b/README.en.md @@ -9,6 +9,9 @@ An open-source project for **iOS location-service research, software development The project uses either an on-device proxy or a third-party proxy client to simulate selected Apple location-service responses in a controlled test environment. +> ⚠️ **Starting with iOS 27 beta 6, the system blocks MITM interception of `gs-loc.apple.com`.** This project is +> temporarily unusable on that version and later betas until a workaround is found. + [![iOS 15+](https://img.shields.io/badge/iOS-15%2B-111111?logo=apple)](project.yml) [![Swift 5.9](https://img.shields.io/badge/Swift-5.9-F05138)](project.yml) [![Go 1.23+](https://img.shields.io/badge/Go-1.23%2B-00ADD8?logo=go)](Core/go.mod) diff --git a/README.md b/README.md index 0b3573d..f189c3b 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ 项目通过本机代理或第三方代理客户端,对 Apple 定位服务的指定响应进行测试环境模拟,帮助开发者验证应用在 不同地理位置和定位场景下的行为。 +> ⚠️ **iOS 27 beta 6 起,系统已禁止对 `gs-loc.apple.com` 进行 MITM 拦截。** 目前该版本及之后的 beta 版本暂时无法使用本项目,等待后续适配方案。 + [![iOS 15+](https://img.shields.io/badge/iOS-15%2B-111111?logo=apple)](project.yml) [![Swift 5.9](https://img.shields.io/badge/Swift-5.9-F05138)](project.yml) [![Go 1.23+](https://img.shields.io/badge/Go-1.23%2B-00ADD8?logo=go)](Core/go.mod) diff --git a/Resources/ThirdPartyProxyModules/wloc.conf b/Resources/ThirdPartyProxyModules/wloc.conf deleted file mode 100644 index c84336a..0000000 --- a/Resources/ThirdPartyProxyModules/wloc.conf +++ /dev/null @@ -1,11 +0,0 @@ -#!name=Apple WLOC 定位修改 -#!desc=修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/ -#!author=Yu9191 Rewrite -#!homepage=https://github.com/Yu9191/wloc - -[rewrite_local] -^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc url script-response-body https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js -^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save url script-echo-response https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js - -[mitm] -hostname = gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com diff --git a/Resources/ThirdPartyProxyModules/wloc.lpx b/Resources/ThirdPartyProxyModules/wloc.lpx deleted file mode 100644 index 8c264c8..0000000 --- a/Resources/ThirdPartyProxyModules/wloc.lpx +++ /dev/null @@ -1,20 +0,0 @@ -#!name=Apple WLOC 定位修改 -#!desc=修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f -#!icon=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/wloc.jpg -#!author=Yu9191 Rewrite -#!homepage=https://github.com/Yu9191/wloc -#!openUrl=https://wloc-pages.pages.dev/ - -[Argument] -longitude = input, "113.94114", tag=经度(在线选点优先) -latitude = input, "22.544577", tag=纬度(在线选点优先) -accuracy = input, "25", tag=精度(米) -randomRadius = input, "0", tag=扰动半径(米,0为关闭) -logLevel = select, "info", "off", "error", "warn", "debug", "all", tag=日志级别 - -[Script] -http-response ^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc script-path=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js, requires-body=true, binary-body-mode=true, timeout=30, tag=Apple WLOC, argument=[{longitude},{latitude},{accuracy},{randomRadius},{logLevel}] -http-request ^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save script-path=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js, timeout=10, tag=WLOC Settings - -[MITM] -hostname = gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com diff --git a/Resources/ThirdPartyProxyModules/wloc.module b/Resources/ThirdPartyProxyModules/wloc.module deleted file mode 100644 index cf2f1d5..0000000 --- a/Resources/ThirdPartyProxyModules/wloc.module +++ /dev/null @@ -1,13 +0,0 @@ -#!name=Apple WLOC 定位修改 -#!desc=修改 Apple 网络定位返回坐标 (Shadowrocket 小火箭) | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/ -#!author=Yu9191 Rewrite -#!homepage=https://github.com/Yu9191/wloc -#!icon=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/wloc.jpg -#!category=Tools - -[Script] -Apple WLOC = type=http-response,pattern=^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc,requires-body=1,binary-body-mode=1,max-size=0,timeout=30,script-path=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js,argument=longitude=113.94114&latitude=22.544577&accuracy=25&randomRadius=0&logLevel=info -WLOC Settings = type=http-request,pattern=^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save,requires-body=0,max-size=0,timeout=10,script-path=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js - -[MITM] -hostname = %APPEND% gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com diff --git a/Resources/ThirdPartyProxyModules/wloc.sgmodule b/Resources/ThirdPartyProxyModules/wloc.sgmodule deleted file mode 100644 index 08ad1c5..0000000 --- a/Resources/ThirdPartyProxyModules/wloc.sgmodule +++ /dev/null @@ -1,14 +0,0 @@ -#!name=Apple WLOC 定位修改 -#!desc=修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/ -#!author=Yu9191 Rewrite -#!homepage=https://github.com/Yu9191/wloc -#!category=Tools -#!arguments=经度:113.94114, 纬度:22.544577, 精度:25, 扰动半径:0, 日志级别:info -#!arguments-desc=经度/纬度: 默认坐标(在线选点储存后优先)\n精度: GPS精度(米)\n扰动半径: 每次响应在目标点周围随机偏移的最大距离(米),0为关闭\n日志级别: off/error/warn/info/debug/all\n\n使用方法: 打开选点页面 -> 选位置 -> 储存到设备 - -[Script] -Apple WLOC = type=http-response, pattern="^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc", requires-body=1, binary-body-mode=1, max-size=0, timeout=30, script-path=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js, argument=longitude={{{经度}}}&latitude={{{纬度}}}&accuracy={{{精度}}}&randomRadius={{{扰动半径}}}&logLevel={{{日志级别}}} -WLOC Settings = type=http-request, pattern="^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save", requires-body=0, max-size=0, timeout=10, script-path=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js - -[MITM] -hostname = %APPEND% gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com diff --git a/Resources/ThirdPartyProxyModules/wloc.stoverride b/Resources/ThirdPartyProxyModules/wloc.stoverride deleted file mode 100644 index 523f0f8..0000000 --- a/Resources/ThirdPartyProxyModules/wloc.stoverride +++ /dev/null @@ -1,36 +0,0 @@ -name: Apple WLOC 定位修改 -desc: "修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/" -author: Yu9191 Rewrite -homepage: https://github.com/Yu9191/wloc -icon: https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/wloc.jpg -category: Tools - -http: - mitm: - - "gs-loc.apple.com" - - "gs-loc-cn.apple.com" - - "gsp-ssl.ls.apple.com" - - "bluedot.is.autonavi.com" - - "bluedot.is.autonavi.com.gds.alibabadns.com" - script: - - match: ^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc - name: WLOC.Location - type: response - require-body: true - binary-mode: true - max-size: 0 - timeout: 30 - argument: longitude=113.94114&latitude=22.544577&accuracy=25&randomRadius=0&logLevel=info - - match: ^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save - name: WLOC.Settings - type: request - require-body: false - timeout: 10 - -script-providers: - WLOC.Location: - url: https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js - interval: 86400 - WLOC.Settings: - url: https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js - interval: 86400 diff --git a/Shared/ThirdPartyProxyManager.swift b/Shared/ThirdPartyProxyManager.swift index 301af0a..bbcd650 100644 --- a/Shared/ThirdPartyProxyManager.swift +++ b/Shared/ThirdPartyProxyManager.swift @@ -127,8 +127,7 @@ final class ThirdPartyProxyManager: ObservableObject { let response = try await perform(action: .save( latitude: wgs84.latitude, longitude: wgs84.longitude, - accuracy: favorite.accuracy, - motionEnabled: MotionSimulationStore.shared.isEnabled + accuracy: favorite.accuracy )) guard response.success else { throw ThirdPartyProxyError.rejected(response.error ?? "第三方代理拒绝保存坐标") @@ -150,25 +149,13 @@ final class ThirdPartyProxyManager: ObservableObject { } func updateMotionSimulation(_ enabled: Bool) async throws -> ThirdPartyProxySettingsResponse { - guard let current = activeSettings, - let latitude = current.latitude, - let longitude = current.longitude else { - throw ThirdPartyProxyError.rejected("第三方虚拟定位尚未开启") - } - guard await refreshAdvancedFeatureAvailability() else { - throw ThirdPartyProxyError.moduleOutdated - } - let response = try await perform(action: .save( - latitude: latitude, - longitude: longitude, - accuracy: current.accuracy ?? 25, - motionEnabled: enabled - )) - guard response.success else { - throw ThirdPartyProxyError.rejected(response.error ?? "第三方代理拒绝更新运动状态") - } - activeSettings = response - return response + // Upstream Yu9191/wloc scripts do not implement motion-state simulation, + // so this can never succeed in third-party mode. + RuntimeLogger.warning("APP", "ThirdPartyProxy", "第三方模式不支持运动状态模拟", details: [ + "请求动作": "motion simulation", + "处理建议": "运动状态模拟仅在 APP模式(内置代理)下可用" + ]) + throw ThirdPartyProxyError.moduleOutdated } func validateConnection() async throws -> ThirdPartyProxySettingsResponse { @@ -251,7 +238,7 @@ final class ThirdPartyProxyManager: ObservableObject { private enum Action { case query - case save(latitude: Double, longitude: Double, accuracy: Int, motionEnabled: Bool) + case save(latitude: Double, longitude: Double, accuracy: Int) case clear } @@ -268,15 +255,11 @@ final class ThirdPartyProxyManager: ObservableObject { components.queryItems = [URLQueryItem(name: "action", value: "query")] case .clear: components.queryItems = [URLQueryItem(name: "action", value: "clear")] - case .save(let latitude, let longitude, let accuracy, let motionEnabled): + case .save(let latitude, let longitude, let accuracy): components.queryItems = [ URLQueryItem(name: "lon", value: String(format: "%.8f", locale: Locale(identifier: "en_US_POSIX"), longitude)), URLQueryItem(name: "lat", value: String(format: "%.8f", locale: Locale(identifier: "en_US_POSIX"), latitude)), - URLQueryItem(name: "acc", value: String(accuracy)), - URLQueryItem( - name: "motion", - value: motionEnabled ? "1" : "0" - ) + URLQueryItem(name: "acc", value: String(accuracy)) ] } guard let url = components.url else { throw ThirdPartyProxyError.invalidResponse } @@ -309,8 +292,6 @@ final class ThirdPartyProxyManager: ObservableObject { } enum ThirdPartyProxyClient: String, CaseIterable, Identifiable { - static let moduleSubscriptionVersion = "1.0.1" - case shadowrocket case surge case quantumultX @@ -347,26 +328,21 @@ enum ThirdPartyProxyClient: String, CaseIterable, Identifiable { @MainActor var subscriptionURL: URL { - let url: String - let directory = ThirdPartyModuleSourceStore.shared.useMirror - ? "Resources/ThirdPartyProxyModules" - : "ThirdParty/WlocScripts/modules/direct" - let prefix = ThirdPartyModuleSourceStore.shared.useMirror - ? "https://gh-proxy.org/https://raw.githubusercontent.com/xweiba/location-spoofer/main/" - : "https://raw.githubusercontent.com/xweiba/location-spoofer/main/" + // Third-party modules are served directly from the upstream Yu9191/wloc + // repository (mirror via gh-proxy when enabled). We no longer maintain + // project-owned module copies, so the URL tracks upstream releases. + let fileName: String switch self { - case .surge, .egern: - url = "\(prefix)\(directory)/wloc.sgmodule" - case .quantumultX: - url = "\(prefix)\(directory)/wloc.conf" - case .loon: - url = "\(prefix)\(directory)/wloc.lpx" - case .stash: - url = "\(prefix)\(directory)/wloc.stoverride" - case .shadowrocket: - url = "\(prefix)\(directory)/wloc.module" + case .surge, .egern: fileName = "wloc.sgmodule" + case .quantumultX: fileName = "wloc.conf" + case .loon: fileName = "wloc.lpx" + case .stash: fileName = "wloc.stoverride" + case .shadowrocket: fileName = "wloc.module" } - return URL(string: "\(url)?v=\(Self.moduleSubscriptionVersion)")! + let base = ThirdPartyModuleSourceStore.shared.useMirror + ? "https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/\(fileName)" + : "https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/\(fileName)" + return URL(string: base)! } var launchURL: URL? { diff --git a/Tests/PaopaoLocationSpooferTests/ThirdPartyProxyManagerTests.swift b/Tests/PaopaoLocationSpooferTests/ThirdPartyProxyManagerTests.swift index ef70854..b29eac9 100644 --- a/Tests/PaopaoLocationSpooferTests/ThirdPartyProxyManagerTests.swift +++ b/Tests/PaopaoLocationSpooferTests/ThirdPartyProxyManagerTests.swift @@ -37,7 +37,6 @@ final class ThirdPartyProxyManagerTests: XCTestCase { XCTAssertEqual(latitude, wgs84.latitude, accuracy: 0.000_000_01) XCTAssertEqual(longitude, wgs84.longitude, accuracy: 0.000_000_01) XCTAssertEqual(values["acc"], "20") - XCTAssertEqual(values["motion"], "0") XCTAssertEqual(manager.connectionState, .connected(active: true)) } @@ -144,16 +143,16 @@ final class ThirdPartyProxyManagerTests: XCTestCase { } } - func testClientLinksUseProjectOwnedMirrorModulesAndVerificationLabels() { + func testClientLinksUseUpstreamModulesAndVerificationLabels() { XCTAssertEqual( ThirdPartyProxyManager.interceptionHostnamesText, - "gs-loc.apple.com, gs-loc-cn.apple.com" + "gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com" ) XCTAssertNil(ThirdPartyProxyClient.shadowrocket.verificationText) XCTAssertTrue(ThirdPartyProxyClient.surge.verificationText?.contains("尚未验证") == true) XCTAssertEqual(ThirdPartyProxyClient.egern.subscriptionURL, ThirdPartyProxyClient.surge.subscriptionURL) XCTAssertTrue(ThirdPartyProxyClient.stash.subscriptionURL.absoluteString.hasPrefix( - "https://gh-proxy.org/https://raw.githubusercontent.com/xweiba/location-spoofer/main/" + "https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/" )) let stashComponents = URLComponents( url: ThirdPartyProxyClient.stash.subscriptionURL, @@ -163,10 +162,10 @@ final class ThirdPartyProxyManagerTests: XCTestCase { url: ThirdPartyProxyClient.shadowrocket.subscriptionURL, resolvingAgainstBaseURL: false ) - XCTAssertEqual(stashComponents?.path, "/https://raw.githubusercontent.com/xweiba/location-spoofer/main/Resources/ThirdPartyProxyModules/wloc.stoverride") - XCTAssertEqual(shadowrocketComponents?.path, "/https://raw.githubusercontent.com/xweiba/location-spoofer/main/Resources/ThirdPartyProxyModules/wloc.module") - XCTAssertEqual(stashComponents?.queryItems?.first?.value, ThirdPartyProxyClient.moduleSubscriptionVersion) - XCTAssertEqual(shadowrocketComponents?.queryItems?.first?.value, ThirdPartyProxyClient.moduleSubscriptionVersion) + XCTAssertEqual(stashComponents?.path, "/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/wloc.stoverride") + XCTAssertEqual(shadowrocketComponents?.path, "/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/wloc.module") + XCTAssertTrue(stashComponents?.queryItems?.isEmpty ?? true) + XCTAssertTrue(shadowrocketComponents?.queryItems?.isEmpty ?? true) XCTAssertEqual(ThirdPartyProxyClient.shadowrocket.launchURL?.scheme, "shadowrocket") XCTAssertEqual(ThirdPartyProxyClient.surge.launchURL?.scheme, "surge") XCTAssertEqual(ThirdPartyProxyClient.quantumultX.launchURL?.scheme, "quantumult-x") diff --git a/Tests/third_party_mode_contract_test.sh b/Tests/third_party_mode_contract_test.sh index b6a1f12..1741b0a 100755 --- a/Tests/third_party_mode_contract_test.sh +++ b/Tests/third_party_mode_contract_test.sh @@ -9,7 +9,6 @@ MANAGER="$ROOT/Shared/ThirdPartyProxyManager.swift" CONTENT="$ROOT/App/ContentView.swift" SETUP="$ROOT/App/FirstSetupView.swift" SETTINGS="$ROOT/App/SettingsView.swift" -MODULES="$ROOT/Resources/ThirdPartyProxyModules" grep -q 'return "APP模式"' "$MODE" || fail "APP mode display name is missing" grep -q 'return "第三方代理模式"' "$MODE" || fail "third-party mode display name is missing" @@ -38,14 +37,8 @@ grep -Fq '保存:GET ?lon=<经度>&lat=<纬度>&acc=<精度>' "$SETUP" \ grep -Fq '清除:GET ?action=clear' "$SETUP" \ || fail "client integration guidance must document the clear action" -for file in wloc.module wloc.sgmodule wloc.conf wloc.lpx wloc.stoverride; do - test -s "$MODULES/$file" || fail "missing bundled module: $file" - grep -q 'gs-loc.apple.com' "$MODULES/$file" \ - || fail "$file must include gs-loc.apple.com in its MITM hostnames" - grep -q 'gs-loc-cn.apple.com' "$MODULES/$file" \ - || fail "$file must include gs-loc-cn.apple.com in its MITM hostnames" -done - +grep -q 'Yu9191/wloc/refs/heads/main/modules' "$MANAGER" \ + || fail "third-party subscription must point at upstream Yu9191 modules" grep -q 'wloc.sgmodule' "$MANAGER" || fail "Surge/Egern module mapping is missing" grep -q 'wloc.stoverride' "$MANAGER" || fail "Stash must use .stoverride directly" grep -q 'shadowrocket://' "$MANAGER" || fail "Shadowrocket launch URL is missing" diff --git a/ThirdParty/WlocScripts/THIRD_PARTY_NOTICES.md b/ThirdParty/WlocScripts/THIRD_PARTY_NOTICES.md deleted file mode 100644 index 5779f54..0000000 --- a/ThirdParty/WlocScripts/THIRD_PARTY_NOTICES.md +++ /dev/null @@ -1,6 +0,0 @@ -# Third-Party Notices - -The generated proxy scripts bundle: - -- `pako` 2.1.0, Copyright (C) 2014-2017 Vitaly Puzrin and contributors, MIT License. -- `esbuild` is used only as a development/build dependency and is distributed under the MIT License. diff --git a/ThirdParty/WlocScripts/build.mjs b/ThirdParty/WlocScripts/build.mjs deleted file mode 100644 index 0c6cdaa..0000000 --- a/ThirdParty/WlocScripts/build.mjs +++ /dev/null @@ -1,19 +0,0 @@ -import { build } from "esbuild"; -import { mkdir } from "node:fs/promises"; - -await mkdir(new URL("./dist/v1/", import.meta.url), { recursive: true }); - -for (const [entry, outfile] of [ - ["src/response-entry.js", "dist/v1/wloc.js"], - ["src/settings-entry.js", "dist/v1/wloc-settings.js"] -]) { - await build({ - entryPoints: [entry], - outfile, - bundle: true, - format: "iife", - target: ["es2017"], - minify: true, - legalComments: "eof" - }); -} diff --git a/ThirdParty/WlocScripts/dist/v1/wloc-settings.js b/ThirdParty/WlocScripts/dist/v1/wloc-settings.js deleted file mode 100644 index c60e3f7..0000000 --- a/ThirdParty/WlocScripts/dist/v1/wloc-settings.js +++ /dev/null @@ -1,2 +0,0 @@ -/* wloc-settings.js - Build 2026-08-16 16:56:29 */ -const e=(()=>{const e=e=>e in globalThis;switch(!0){case e("$task"):return"Quantumult X";case e("$loon"):return"Loon";case e("$rocket"):return"Shadowrocket";case e("Egern"):return"Egern";case Boolean(globalThis.$environment?.["surge-version"]):return"Surge";case Boolean(globalThis.$environment?.["stash-version"]):return"Stash";case e("Cloudflare"):return"Worker";case Boolean(globalThis.process?.versions?.node):return"Node.js";default:return}})();class t{static#e=new Map([]);static#t=[];static#a=new Map([]);static clear=()=>{};static count=(e="default")=>{switch(t.#e.has(e)){case!0:t.#e.set(e,t.#e.get(e)+1);break;case!1:t.#e.set(e,0)}t.log(`${e}: ${t.#e.get(e)}`)};static countReset=(e="default")=>{switch(t.#e.has(e)){case!0:t.#e.set(e,0),t.log(`${e}: ${t.#e.get(e)}`);break;case!1:t.warn(`Counter "${e}" doesn’t exist`)}};static debug=(...e)=>{t.#s<4||(e=e.map(e=>` ${e}`),t.log(...e))};static error(...a){if(!(t.#s<1)){switch(e){case"Surge":case"Loon":case"Stash":case"Egern":case"Shadowrocket":case"Quantumult X":default:a=a.map(e=>` ${e}`);break;case"Worker":case"Node.js":a=a.map(e=>` ${e?.stack??e}`)}t.log(...a)}}static exception=(...e)=>t.error(...e);static group=e=>t.#t.unshift(e);static groupEnd=()=>t.#t.shift();static info(...e){t.#s<3||(e=e.map(e=>` ${e}`),t.log(...e))}static#s=3;static get logLevel(){switch(t.#s){case 0:return"OFF";case 1:return"ERROR";case 2:return"WARN";case 3:default:return"INFO";case 4:return"DEBUG";case 5:return"ALL"}}static set logLevel(e){switch(typeof e){case"string":e=e.toLowerCase();break;case"number":break;default:e="warn"}switch(e){case 0:case"off":t.#s=0;break;case 1:case"error":t.#s=1;break;case 2:case"warn":case"warning":default:t.#s=2;break;case 3:case"info":t.#s=3;break;case 4:case"debug":t.#s=4;break;case 5:case"all":t.#s=5}}static log=(...e)=>{0!==t.#s&&(e=e.flatMap(e=>{switch(typeof e){case"object":return[JSON.stringify(e)];case"bigint":case"number":case"boolean":return[e.toString()];case"string":return e.split(/\r?\n/u);default:return[e]}}),t.#t.forEach(t=>{(e=e.map(e=>` ${e}`)).unshift(` ${t}:`)}),e=["",...e],console.log(e.join("\n")))};static time=(e="default")=>t.#a.set(e,Date.now());static timeEnd=(e="default")=>t.#a.delete(e);static timeLog=(e="default")=>{const a=t.#a.get(e);a?t.log(`${e}: ${Date.now()-a}ms`):t.warn(`Timer "${e}" doesn’t exist`)};static warn(...e){t.#s<2||(e=e.map(e=>` ${e}`),t.log(...e))}}class a{static escape(e){const t={"&":"&","<":"<",">":">",'"':""","'":"'"};return e.replace(/[&<>"']/g,e=>t[e])}static get(e={},t="",s=void 0){Array.isArray(t)||(t=a.toPath(t));const r=t.reduce((e,t)=>Object(e)[t],e);return void 0===r?s:r}static merge(e,...t){if(null==e)return e;for(const s of t)if(null!=s)for(const t of Object.keys(s)){const r=s[t],o=e[t];switch(!0){case a.#r(r)&&a.#r(o):e[t]=a.merge(o,r);break;case r instanceof Map&&o instanceof Map:if(r.size>0)for(const[e,t]of r)o.set(e,t);break;case r instanceof Set&&o instanceof Set:if(r.size>0)for(const e of r)o.add(e);break;case Array.isArray(r)&&0===r.length&&void 0!==o:case r instanceof Map&&0===r.size&&void 0!==o:case r instanceof Set&&0===r.size&&void 0!==o:break;case void 0!==r:e[t]=r}}return e}static#r(e){if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}static omit(e={},t=[]){return Array.isArray(t)||(t=[t.toString()]),t.forEach(t=>a.unset(e,t)),e}static pick(e={},t=[]){Array.isArray(t)||(t=[t.toString()]);const a=Object.entries(e).filter(([e,a])=>t.includes(e));return Object.fromEntries(a)}static set(e,t,s){return Array.isArray(t)||(t=a.toPath(t)),t.slice(0,-1).reduce((e,a,s)=>Object(e[a])===e[a]?e[a]:e[a]=/^\d+$/.test(t[s+1])?[]:{},e)[t[t.length-1]]=s,e}static toPath(e){return e.replace(/\[(\d+)\]/g,".$1").split(".").filter(Boolean)}static unescape(e){const t={"&":"&","<":"<",">":">",""":'"',"'":"'"};return e.replace(/&|<|>|"|'/g,e=>t[e])}static unset(e={},t=""){Array.isArray(t)||(t=a.toPath(t));return t.reduce((e,a,s)=>s===t.length-1?(delete e[a],!0):Object(e)[a],e)}}class s{static parse(e){let t={};switch(typeof e){case"string":{const r=e.replace(/^\?/,"");if(!r)break;const o=Object.fromEntries(r.split("&").filter(Boolean).map(e=>{const[t="",a=""]=e.split("=",2);return[s.#o(t).replace(/\[([^\[\]]+)\]/g,".$1"),s.#o(a).replace(/\"/g,"")]}));Object.keys(o).forEach(e=>a.set(t,e,o[e]));break}case"object":switch(e){case null:break;default:{const s={};Object.keys(e).forEach(t=>a.set(s,t,e[t])),t=s;break}}break;case"undefined":t={}}return t}static stringify(e={}){if(!e||"object"!=typeof e)return"";const t=[];return Object.keys(e).forEach(a=>s.#c(e,a,t)),0===t.length?"":t.map(([e,t])=>`${s.#n(s.#i(e))}=${s.#n(t)}`).join("&")}static#c(e,t,r){const o=a.get(e,t);void 0!==o&&(null!==o?Array.isArray(o)?o.forEach((a,o)=>{void 0!==a&&s.#c(e,`${t}[${o}]`,r)}):s.#r(o)?Object.keys(o).forEach(a=>s.#c(e,`${t}.${a}`,r)):r.push([t,String(o)]):r.push([t,""]))}static#i(e){const[t,...s]=a.toPath(e);return s.reduce((e,t)=>/^\d+$/.test(t)?`${e}[${t}]`:`${e}.${t}`,t)}static#r(e){if(null===e||"object"!=typeof e||Array.isArray(e))return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}static#n(e){return encodeURIComponent(e)}static#o(e){return decodeURIComponent(e.replace(/\+/g," "))}}t.debug(" $argument"),globalThis.$argument=s.parse(globalThis.$argument),globalThis.$argument.LogLevel&&(t.logLevel=globalThis.$argument.LogLevel),t.debug(" $argument",`$argument: ${JSON.stringify(globalThis.$argument)}`);const r={100:"Continue",101:"Switching Protocols",102:"Processing",103:"Early Hints",200:"OK",201:"Created",202:"Accepted",203:"Non-Authoritative Information",204:"No Content",205:"Reset Content",206:"Partial Content",207:"Multi-Status",208:"Already Reported",226:"IM Used",300:"Multiple Choices",301:"Moved Permanently",302:"Found",304:"Not Modified",307:"Temporary Redirect",308:"Permanent Redirect",400:"Bad Request",401:"Unauthorized",402:"Payment Required",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",406:"Not Acceptable",407:"Proxy Authentication Required",408:"Request Timeout",409:"Conflict",410:"Gone",411:"Length Required",412:"Precondition Failed",413:"Content Too Large",414:"URI Too Long",415:"Unsupported Media Type",416:"Range Not Satisfiable",417:"Expectation Failed",418:"I'm a teapot",421:"Misdirected Request",422:"Unprocessable Entity",423:"Locked",424:"Failed Dependency",425:"Too Early",426:"Upgrade Required",428:"Precondition Required",429:"Too Many Requests",431:"Request Header Fields Too Large",451:"Unavailable For Legal Reasons",500:"Internal Server Error",501:"Not Implemented",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout",505:"HTTP Version Not Supported",506:"Variant Also Negotiates",507:"Insufficient Storage",508:"Loop Detected",510:"Not Extended",511:"Network Authentication Required"};function o(s={}){switch(e){case"Surge":s.policy&&a.set(s,"headers.X-Surge-Policy",s.policy),t.log(" 执行结束!",` ${(new Date).getTime()/1e3-$script.startTime} 秒`),$done(s);break;case"Loon":s.policy&&(s.node=s.policy),t.log(" 执行结束!",` ${(new Date-$script.startTime)/1e3} 秒`),$done(s);break;case"Stash":s.policy&&a.set(s,"headers.X-Stash-Selected-Proxy",encodeURI(s.policy)),t.log(" 执行结束!",` ${(new Date-$script.startTime)/1e3} 秒`),$done(s);break;case"Egern":case"Shadowrocket":t.log(" 执行结束!"),$done(s);break;case"Quantumult X":switch(s.policy&&a.set(s,"opts.policy",s.policy),typeof(s=a.pick(s,["status","url","headers","body","bodyBytes"])).status){case"number":s.status=`HTTP/1.1 ${s.status} ${r[s.status]}`;break;case"string":case"undefined":break;default:throw new TypeError(`${Function.name}: 参数类型错误, status 必须为数字或字符串`)}s.body instanceof ArrayBuffer?(s.bodyBytes=s.body,s.body=void 0):ArrayBuffer.isView(s.body)?(s.bodyBytes=s.body.buffer.slice(s.body.byteOffset,s.body.byteLength+s.body.byteOffset),s.body=void 0):s.body&&(s.bodyBytes=void 0),t.log(" 执行结束!"),$done(s);break;case"Worker":default:t.log(" 执行结束!");break;case"Node.js":t.log(" 执行结束!"),process.exit(1)}}class c{static data=null;static dataFile="box.dat";static#l=/^@(?[^.]+)(?:\.(?.*))?$/;static getItem(t,s=null){let r=s;switch(t.startsWith("@")){case!0:{const{key:e,path:s}=t.match(c.#l)?.groups;t=e;let o=c.getItem(t,{});"object"!=typeof o&&(o={}),r=a.get(o,s);try{r=JSON.parse(r)}catch{}break}default:switch(e){case"Surge":case"Loon":case"Stash":case"Egern":case"Shadowrocket":r=$persistentStore.read(t);break;case"Quantumult X":r=$prefs.valueForKey(t);break;case"Worker":c.data=c.data??{},r=c.data[t];break;case"Node.js":c.data=c.#u(c.dataFile),r=c.data?.[t];break;default:r=c.data?.[t]||null}try{r=JSON.parse(r)}catch{}}return r??s}static setItem(t=new String,s=new String){let r=!1;if("object"==typeof s)s=JSON.stringify(s);else s=String(s);switch(t.startsWith("@")){case!0:{const{key:e,path:o}=t.match(c.#l)?.groups;t=e;let n=c.getItem(t,{});"object"!=typeof n&&(n={}),a.set(n,o,s),r=c.setItem(t,n);break}default:switch(e){case"Surge":case"Loon":case"Stash":case"Egern":case"Shadowrocket":r=$persistentStore.write(s,t);break;case"Quantumult X":r=$prefs.setValueForKey(s,t);break;case"Worker":c.data=c.data??{},c.data[t]=s,r=!0;break;case"Node.js":c.data=c.#u(c.dataFile),c.data[t]=s,c.#d(c.dataFile),r=!0;break;default:r=c.data?.[t]||null}}return r}static removeItem(t){let s=!1;switch(t.startsWith("@")){case!0:{const{key:e,path:r}=t.match(c.#l)?.groups;t=e;let o=c.getItem(t);"object"!=typeof o&&(o={}),a.unset(o,r),s=c.setItem(t,o);break}default:switch(e){case"Surge":s=$persistentStore.write(null,t);break;case"Loon":case"Stash":case"Egern":case"Shadowrocket":default:s=!1;break;case"Quantumult X":s=$prefs.removeValueForKey(t);break;case"Worker":c.data=c.data??{},delete c.data[t],s=!0;break;case"Node.js":c.data=c.#u(c.dataFile),delete c.data[t],c.#d(c.dataFile),s=!0}}return s}static clear(){let t=!1;switch(e){case"Surge":case"Loon":case"Stash":case"Egern":case"Shadowrocket":default:t=!1;break;case"Quantumult X":t=$prefs.removeAllValues();break;case"Worker":c.data={},t=!0;break;case"Node.js":c.data=c.#u(c.dataFile),c.data={},c.#d(c.dataFile),t=!0}return t}static#u=t=>{if("Node.js"!==e)return{};{this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const e=this.path.resolve(t),a=this.path.resolve(process.cwd(),t),s=this.fs.existsSync(e),r=!s&&this.fs.existsSync(a);if(!s&&!r)return{};{const t=s?e:a;try{return JSON.parse(this.fs.readFileSync(t))}catch(e){return{}}}}};static#d=(t=this.dataFile)=>{if("Node.js"===e){this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const e=this.path.resolve(t),a=this.path.resolve(process.cwd(),t),s=this.fs.existsSync(e),r=!s&&this.fs.existsSync(a),o=JSON.stringify(this.data);s?this.fs.writeFileSync(e,o):r?this.fs.writeFileSync(a,o):this.fs.writeFileSync(e,o)}}}const n="wloc_settings",i=$request.url||"";function l(e){return parseFloat(String(e||"0").replace(",","."))}const u=function(e){const t=e.split("?")[1]||"",a=new Map;for(const e of t.split("&")){if(!e)continue;const t=e.indexOf("="),s=-1===t?e:e.slice(0,t),r=-1===t?"":e.slice(t+1);let o,c;try{o=decodeURIComponent(s.replace(/\+/g," "))}catch{o=s}try{c=decodeURIComponent(r.replace(/\+/g," "))}catch{c=r}a.has(o)||a.set(o,c)}return a}(i),d=u.get("action")||"save";let g;if(t.debug(`[wloc-settings] url=${i}, action=${d}`),"query"===d)try{const e=c.getItem(n);e&&"object"==typeof e&&e.longitude&&e.latitude?(g={success:!0,longitude:e.longitude,latitude:e.latitude,accuracy:e.accuracy||25,randomRadius:e.randomRadius??0,updatedAt:e.updatedAt||null},t.debug(`[wloc-settings] 查询: ${e.longitude}, ${e.latitude}`)):g={success:!1,error:"无已保存的坐标"}}catch(e){g={success:!1,error:e.message||"读取失败"}}else if("clear"===d)try{c.setItem(n,null),g={success:!0},t.info("[wloc-settings] 已清除坐标数据")}catch(e){g={success:!1,error:e.message||"清除失败"},t.error(`[wloc-settings] 清除失败: ${e.message}`)}else{const e=l(u.get("lon")||u.get("longitude")),a=l(u.get("lat")||u.get("latitude")),s=parseInt(u.get("acc")||u.get("accuracy")||"25",10);if(!Number.isFinite(e)||!Number.isFinite(a)||Math.abs(a)>90||Math.abs(e)>180)g={success:!1,error:"缺少 lon/lat 参数"};else{let r={};try{const e=c.getItem(n);e&&"object"==typeof e&&(r=e)}catch{}const o={...r,longitude:e,latitude:a,accuracy:s,updatedAt:new Date(Date.now()+288e5).toISOString().replace("Z","+08:00")},i=u.get("randomRadius");null!=i&&""!==i&&(o.randomRadius=l(i));try{c.setItem(n,o)?(g={success:!0,longitude:e,latitude:a,accuracy:s,randomRadius:o.randomRadius??0},t.info(`[wloc-settings] 已保存: ${e}, ${a}`)):(g={success:!1,error:"Storage.setItem 返回 false"},t.error("[wloc-settings] setItem 返回 false"))}catch(e){g={success:!1,error:e.message||"写入失败"},t.error(`[wloc-settings] ${e.message}`)}}}const p={status:200,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, OPTIONS"},body:JSON.stringify(g)};o("Quantumult X"===e?p:{response:p}); diff --git a/ThirdParty/WlocScripts/dist/v1/wloc.js b/ThirdParty/WlocScripts/dist/v1/wloc.js deleted file mode 100644 index 2b83c7d..0000000 --- a/ThirdParty/WlocScripts/dist/v1/wloc.js +++ /dev/null @@ -1,2 +0,0 @@ -/* wloc.js - Build 2026-08-08 21:31:58 */ -const e=(()=>{const e=e=>e in globalThis;switch(!0){case e("$task"):return"Quantumult X";case e("$loon"):return"Loon";case e("$rocket"):return"Shadowrocket";case e("Egern"):return"Egern";case Boolean(globalThis.$environment?.["surge-version"]):return"Surge";case Boolean(globalThis.$environment?.["stash-version"]):return"Stash";case e("Cloudflare"):return"Worker";case Boolean(globalThis.process?.versions?.node):return"Node.js";default:return}})();class t{static#e=new Map([]);static#t=[];static#a=new Map([]);static clear=()=>{};static count=(e="default")=>{switch(t.#e.has(e)){case!0:t.#e.set(e,t.#e.get(e)+1);break;case!1:t.#e.set(e,0)}t.log(`${e}: ${t.#e.get(e)}`)};static countReset=(e="default")=>{switch(t.#e.has(e)){case!0:t.#e.set(e,0),t.log(`${e}: ${t.#e.get(e)}`);break;case!1:t.warn(`Counter "${e}" doesn’t exist`)}};static debug=(...e)=>{t.#r<4||(e=e.map(e=>` ${e}`),t.log(...e))};static error(...a){if(!(t.#r<1)){switch(e){case"Surge":case"Loon":case"Stash":case"Egern":case"Shadowrocket":case"Quantumult X":default:a=a.map(e=>` ${e}`);break;case"Worker":case"Node.js":a=a.map(e=>` ${e?.stack??e}`)}t.log(...a)}}static exception=(...e)=>t.error(...e);static group=e=>t.#t.unshift(e);static groupEnd=()=>t.#t.shift();static info(...e){t.#r<3||(e=e.map(e=>` ${e}`),t.log(...e))}static#r=3;static get logLevel(){switch(t.#r){case 0:return"OFF";case 1:return"ERROR";case 2:return"WARN";case 3:default:return"INFO";case 4:return"DEBUG";case 5:return"ALL"}}static set logLevel(e){switch(typeof e){case"string":e=e.toLowerCase();break;case"number":break;default:e="warn"}switch(e){case 0:case"off":t.#r=0;break;case 1:case"error":t.#r=1;break;case 2:case"warn":case"warning":default:t.#r=2;break;case 3:case"info":t.#r=3;break;case 4:case"debug":t.#r=4;break;case 5:case"all":t.#r=5}}static log=(...e)=>{0!==t.#r&&(e=e.flatMap(e=>{switch(typeof e){case"object":return[JSON.stringify(e)];case"bigint":case"number":case"boolean":return[e.toString()];case"string":return e.split(/\r?\n/u);default:return[e]}}),t.#t.forEach(t=>{(e=e.map(e=>` ${e}`)).unshift(` ${t}:`)}),e=["",...e],console.log(e.join("\n")))};static time=(e="default")=>t.#a.set(e,Date.now());static timeEnd=(e="default")=>t.#a.delete(e);static timeLog=(e="default")=>{const a=t.#a.get(e);a?t.log(`${e}: ${Date.now()-a}ms`):t.warn(`Timer "${e}" doesn’t exist`)};static warn(...e){t.#r<2||(e=e.map(e=>` ${e}`),t.log(...e))}}class a{static escape(e){const t={"&":"&","<":"<",">":">",'"':""","'":"'"};return e.replace(/[&<>"']/g,e=>t[e])}static get(e={},t="",r=void 0){Array.isArray(t)||(t=a.toPath(t));const n=t.reduce((e,t)=>Object(e)[t],e);return void 0===n?r:n}static merge(e,...t){if(null==e)return e;for(const r of t)if(null!=r)for(const t of Object.keys(r)){const n=r[t],i=e[t];switch(!0){case a.#n(n)&&a.#n(i):e[t]=a.merge(i,n);break;case n instanceof Map&&i instanceof Map:if(n.size>0)for(const[e,t]of n)i.set(e,t);break;case n instanceof Set&&i instanceof Set:if(n.size>0)for(const e of n)i.add(e);break;case Array.isArray(n)&&0===n.length&&void 0!==i:case n instanceof Map&&0===n.size&&void 0!==i:case n instanceof Set&&0===n.size&&void 0!==i:break;case void 0!==n:e[t]=n}}return e}static#n(e){if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}static omit(e={},t=[]){return Array.isArray(t)||(t=[t.toString()]),t.forEach(t=>a.unset(e,t)),e}static pick(e={},t=[]){Array.isArray(t)||(t=[t.toString()]);const a=Object.entries(e).filter(([e,a])=>t.includes(e));return Object.fromEntries(a)}static set(e,t,r){return Array.isArray(t)||(t=a.toPath(t)),t.slice(0,-1).reduce((e,a,r)=>Object(e[a])===e[a]?e[a]:e[a]=/^\d+$/.test(t[r+1])?[]:{},e)[t[t.length-1]]=r,e}static toPath(e){return e.replace(/\[(\d+)\]/g,".$1").split(".").filter(Boolean)}static unescape(e){const t={"&":"&","<":"<",">":">",""":'"',"'":"'"};return e.replace(/&|<|>|"|'/g,e=>t[e])}static unset(e={},t=""){Array.isArray(t)||(t=a.toPath(t));return t.reduce((e,a,r)=>r===t.length-1?(delete e[a],!0):Object(e)[a],e)}}class r{static parse(e){let t={};switch(typeof e){case"string":{const n=e.replace(/^\?/,"");if(!n)break;const i=Object.fromEntries(n.split("&").filter(Boolean).map(e=>{const[t="",a=""]=e.split("=",2);return[r.#i(t).replace(/\[([^\[\]]+)\]/g,".$1"),r.#i(a).replace(/\"/g,"")]}));Object.keys(i).forEach(e=>a.set(t,e,i[e]));break}case"object":switch(e){case null:break;default:{const r={};Object.keys(e).forEach(t=>a.set(r,t,e[t])),t=r;break}}break;case"undefined":t={}}return t}static stringify(e={}){if(!e||"object"!=typeof e)return"";const t=[];return Object.keys(e).forEach(a=>r.#o(e,a,t)),0===t.length?"":t.map(([e,t])=>`${r.#s(r.#l(e))}=${r.#s(t)}`).join("&")}static#o(e,t,n){const i=a.get(e,t);void 0!==i&&(null!==i?Array.isArray(i)?i.forEach((a,i)=>{void 0!==a&&r.#o(e,`${t}[${i}]`,n)}):r.#n(i)?Object.keys(i).forEach(a=>r.#o(e,`${t}.${a}`,n)):n.push([t,String(i)]):n.push([t,""]))}static#l(e){const[t,...r]=a.toPath(e);return r.reduce((e,t)=>/^\d+$/.test(t)?`${e}[${t}]`:`${e}.${t}`,t)}static#n(e){if(null===e||"object"!=typeof e||Array.isArray(e))return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}static#s(e){return encodeURIComponent(e)}static#i(e){return decodeURIComponent(e.replace(/\+/g," "))}}t.debug(" $argument"),globalThis.$argument=r.parse(globalThis.$argument),globalThis.$argument.LogLevel&&(t.logLevel=globalThis.$argument.LogLevel),t.debug(" $argument",`$argument: ${JSON.stringify(globalThis.$argument)}`);const n={100:"Continue",101:"Switching Protocols",102:"Processing",103:"Early Hints",200:"OK",201:"Created",202:"Accepted",203:"Non-Authoritative Information",204:"No Content",205:"Reset Content",206:"Partial Content",207:"Multi-Status",208:"Already Reported",226:"IM Used",300:"Multiple Choices",301:"Moved Permanently",302:"Found",304:"Not Modified",307:"Temporary Redirect",308:"Permanent Redirect",400:"Bad Request",401:"Unauthorized",402:"Payment Required",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",406:"Not Acceptable",407:"Proxy Authentication Required",408:"Request Timeout",409:"Conflict",410:"Gone",411:"Length Required",412:"Precondition Failed",413:"Content Too Large",414:"URI Too Long",415:"Unsupported Media Type",416:"Range Not Satisfiable",417:"Expectation Failed",418:"I'm a teapot",421:"Misdirected Request",422:"Unprocessable Entity",423:"Locked",424:"Failed Dependency",425:"Too Early",426:"Upgrade Required",428:"Precondition Required",429:"Too Many Requests",431:"Request Header Fields Too Large",451:"Unavailable For Legal Reasons",500:"Internal Server Error",501:"Not Implemented",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout",505:"HTTP Version Not Supported",506:"Variant Also Negotiates",507:"Insufficient Storage",508:"Loop Detected",510:"Not Extended",511:"Network Authentication Required"};function i(r={}){switch(e){case"Surge":r.policy&&a.set(r,"headers.X-Surge-Policy",r.policy),t.log(" 执行结束!",` ${(new Date).getTime()/1e3-$script.startTime} 秒`),$done(r);break;case"Loon":r.policy&&(r.node=r.policy),t.log(" 执行结束!",` ${(new Date-$script.startTime)/1e3} 秒`),$done(r);break;case"Stash":r.policy&&a.set(r,"headers.X-Stash-Selected-Proxy",encodeURI(r.policy)),t.log(" 执行结束!",` ${(new Date-$script.startTime)/1e3} 秒`),$done(r);break;case"Egern":case"Shadowrocket":t.log(" 执行结束!"),$done(r);break;case"Quantumult X":switch(r.policy&&a.set(r,"opts.policy",r.policy),typeof(r=a.pick(r,["status","url","headers","body","bodyBytes"])).status){case"number":r.status=`HTTP/1.1 ${r.status} ${n[r.status]}`;break;case"string":case"undefined":break;default:throw new TypeError(`${Function.name}: 参数类型错误, status 必须为数字或字符串`)}r.body instanceof ArrayBuffer?(r.bodyBytes=r.body,r.body=void 0):ArrayBuffer.isView(r.body)?(r.bodyBytes=r.body.buffer.slice(r.body.byteOffset,r.body.byteLength+r.body.byteOffset),r.body=void 0):r.body&&(r.bodyBytes=void 0),t.log(" 执行结束!"),$done(r);break;case"Worker":default:t.log(" 执行结束!");break;case"Node.js":t.log(" 执行结束!"),process.exit(1)}}class o{static data=null;static dataFile="box.dat";static#c=/^@(?[^.]+)(?:\.(?.*))?$/;static getItem(t,r=null){let n=r;switch(t.startsWith("@")){case!0:{const{key:e,path:r}=t.match(o.#c)?.groups;t=e;let i=o.getItem(t,{});"object"!=typeof i&&(i={}),n=a.get(i,r);try{n=JSON.parse(n)}catch{}break}default:switch(e){case"Surge":case"Loon":case"Stash":case"Egern":case"Shadowrocket":n=$persistentStore.read(t);break;case"Quantumult X":n=$prefs.valueForKey(t);break;case"Worker":o.data=o.data??{},n=o.data[t];break;case"Node.js":o.data=o.#d(o.dataFile),n=o.data?.[t];break;default:n=o.data?.[t]||null}try{n=JSON.parse(n)}catch{}}return n??r}static setItem(t=new String,r=new String){let n=!1;if("object"==typeof r)r=JSON.stringify(r);else r=String(r);switch(t.startsWith("@")){case!0:{const{key:e,path:i}=t.match(o.#c)?.groups;t=e;let s=o.getItem(t,{});"object"!=typeof s&&(s={}),a.set(s,i,r),n=o.setItem(t,s);break}default:switch(e){case"Surge":case"Loon":case"Stash":case"Egern":case"Shadowrocket":n=$persistentStore.write(r,t);break;case"Quantumult X":n=$prefs.setValueForKey(r,t);break;case"Worker":o.data=o.data??{},o.data[t]=r,n=!0;break;case"Node.js":o.data=o.#d(o.dataFile),o.data[t]=r,o.#u(o.dataFile),n=!0;break;default:n=o.data?.[t]||null}}return n}static removeItem(t){let r=!1;switch(t.startsWith("@")){case!0:{const{key:e,path:n}=t.match(o.#c)?.groups;t=e;let i=o.getItem(t);"object"!=typeof i&&(i={}),a.unset(i,n),r=o.setItem(t,i);break}default:switch(e){case"Surge":r=$persistentStore.write(null,t);break;case"Loon":case"Stash":case"Egern":case"Shadowrocket":default:r=!1;break;case"Quantumult X":r=$prefs.removeValueForKey(t);break;case"Worker":o.data=o.data??{},delete o.data[t],r=!0;break;case"Node.js":o.data=o.#d(o.dataFile),delete o.data[t],o.#u(o.dataFile),r=!0}}return r}static clear(){let t=!1;switch(e){case"Surge":case"Loon":case"Stash":case"Egern":case"Shadowrocket":default:t=!1;break;case"Quantumult X":t=$prefs.removeAllValues();break;case"Worker":o.data={},t=!0;break;case"Node.js":o.data=o.#d(o.dataFile),o.data={},o.#u(o.dataFile),t=!0}return t}static#d=t=>{if("Node.js"!==e)return{};{this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const e=this.path.resolve(t),a=this.path.resolve(process.cwd(),t),r=this.fs.existsSync(e),n=!r&&this.fs.existsSync(a);if(!r&&!n)return{};{const t=r?e:a;try{return JSON.parse(this.fs.readFileSync(t))}catch(e){return{}}}}};static#u=(t=this.dataFile)=>{if("Node.js"===e){this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const e=this.path.resolve(t),a=this.path.resolve(process.cwd(),t),r=this.fs.existsSync(e),n=!r&&this.fs.existsSync(a),i=JSON.stringify(this.data);r?this.fs.writeFileSync(e,i):n?this.fs.writeFileSync(a,i):this.fs.writeFileSync(e,i)}}}function s(e){let t=e.length;for(;--t>=0;)e[t]=0}s(new Array(576));s(new Array(60));s(new Array(512));s(new Array(256));s(new Array(29));s(new Array(30));var l=(e,t,a,r)=>{let n=65535&e,i=e>>>16&65535,o=0;for(;0!==a;){o=a>2e3?2e3:a,a-=o;do{n=n+t[r++]|0,i=i+n|0}while(--o);n%=65521,i%=65521}return n|i<<16};const c=new Uint32Array((()=>{let e,t=[];for(var a=0;a<256;a++){e=a;for(var r=0;r<8;r++)e=1&e?3988292384^e>>>1:e>>>1;t[a]=e}return t})());var d=(e,t,a,r)=>{const n=c,i=r+a;e^=-1;for(let a=r;a>>8^n[255&(e^t[a])];return-1^e},u={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"},f={Z_NO_FLUSH:0,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_MEM_ERROR:-4,Z_BUF_ERROR:-5,Z_DEFLATED:8};const h=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var w=function(e){const t=Array.prototype.slice.call(arguments,1);for(;t.length;){const a=t.shift();if(a){if("object"!=typeof a)throw new TypeError(a+"must be non-object");for(const t in a)h(a,t)&&(e[t]=a[t])}}return e},g=e=>{let t=0;for(let a=0,r=e.length;a=252?6:e>=248?5:e>=240?4:e>=224?3:e>=192?2:1;b[254]=b[255]=1;var m=e=>{if("function"==typeof TextEncoder&&TextEncoder.prototype.encode)return(new TextEncoder).encode(e);let t,a,r,n,i,o=e.length,s=0;for(n=0;n>>6,t[i++]=128|63&a):a<65536?(t[i++]=224|a>>>12,t[i++]=128|a>>>6&63,t[i++]=128|63&a):(t[i++]=240|a>>>18,t[i++]=128|a>>>12&63,t[i++]=128|a>>>6&63,t[i++]=128|63&a);return t},y=(e,t)=>{const a=t||e.length;if("function"==typeof TextDecoder&&TextDecoder.prototype.decode)return(new TextDecoder).decode(e.subarray(0,t));let r,n;const i=new Array(2*a);for(n=0,r=0;r4)i[n++]=65533,r+=o-1;else{for(t&=2===o?31:3===o?15:7;o>1&&r1?i[n++]=65533:t<65536?i[n++]=t:(t-=65536,i[n++]=55296|t>>10&1023,i[n++]=56320|1023&t)}}return((e,t)=>{if(t<65534&&e.subarray&&p)return String.fromCharCode.apply(null,e.length===t?e:e.subarray(0,t));let a="";for(let r=0;r{(t=t||e.length)>e.length&&(t=e.length);let a=t-1;for(;a>=0&&128==(192&e[a]);)a--;return a<0||0===a?t:a+b[e[a]]>t?a:t};var v=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0};const _=16209;var x=function(e,t){let a,r,n,i,o,s,l,c,d,u,f,h,w,g,p,b,m,y,k,v,x,E,A,$;const R=e.state;a=e.next_in,A=e.input,r=a+(e.avail_in-5),n=e.next_out,$=e.output,i=n-(t-e.avail_out),o=n+(e.avail_out-257),s=R.dmax,l=R.wsize,c=R.whave,d=R.wnext,u=R.window,f=R.hold,h=R.bits,w=R.lencode,g=R.distcode,p=(1<>>24,f>>>=y,h-=y,y=m>>>16&255,0===y)$[n++]=65535&m;else{if(!(16&y)){if(64&y){if(32&y){R.mode=16191;break e}e.msg="invalid literal/length code",R.mode=_;break e}m=w[(65535&m)+(f&(1<>>=y,h-=y),h<15&&(f+=A[a++]<>>24,f>>>=y,h-=y,y=m>>>16&255,16&y){if(v=65535&m,y&=15,hs){e.msg="invalid distance too far back",R.mode=_;break e}if(f>>>=y,h-=y,y=n-i,v>y){if(y=v-y,y>c&&R.sane){e.msg="invalid distance too far back",R.mode=_;break e}if(x=0,E=u,0===d){if(x+=l-y,y2;)$[n++]=E[x++],$[n++]=E[x++],$[n++]=E[x++],k-=3;k&&($[n++]=E[x++],k>1&&($[n++]=E[x++]))}else{x=n-v;do{$[n++]=$[x++],$[n++]=$[x++],$[n++]=$[x++],k-=3}while(k>2);k&&($[n++]=$[x++],k>1&&($[n++]=$[x++]))}break}if(64&y){e.msg="invalid distance code",R.mode=_;break e}m=g[(65535&m)+(f&(1<>3,a-=k,h-=k<<3,f&=(1<{const l=s.bits;let c,d,u,f,h,w,g=0,p=0,b=0,m=0,y=0,k=0,v=0,_=0,x=0,T=0,N=null;const M=new Uint16Array(16),O=new Uint16Array(16);let j,F,I,L=null;for(g=0;g<=E;g++)M[g]=0;for(p=0;p=1&&0===M[m];m--);if(y>m&&(y=m),0===m)return n[i++]=20971520,n[i++]=20971520,s.bits=1,0;for(b=1;b0&&(0===e||1!==m))return-1;for(O[1]=0,g=1;g852||2===e&&x>592)return 1;for(;;){j=g-v,o[p]+1=w?(F=L[o[p]-w],I=N[o[p]-w]):(F=96,I=0),c=1<>v)+d]=j<<24|F<<16|I}while(0!==d);for(c=1<>=1;if(0!==c?(T&=c-1,T+=c):T=0,p++,0===--M[g]){if(g===m)break;g=t[a+o[p]]}if(g>y&&(T&f)!==u){for(0===v&&(v=y),h+=b,k=g-v,_=1<852||2===e&&x>592)return 1;u=T&f,n[u]=y<<24|k<<16|h-i}}return 0!==T&&(n[h+T]=g-v<<24|64<<16),s.bits=y,0};const{Z_FINISH:N,Z_BLOCK:M,Z_TREES:O,Z_OK:j,Z_STREAM_END:F,Z_NEED_DICT:I,Z_STREAM_ERROR:L,Z_DATA_ERROR:B,Z_MEM_ERROR:C,Z_BUF_ERROR:U,Z_DEFLATED:P}=f,D=16180,Z=16190,z=16191,q=16192,H=16194,W=16199,K=16200,X=16206,Q=16209,V=e=>(e>>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24);function J(){this.strm=null,this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new Uint16Array(320),this.work=new Uint16Array(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}const G=e=>{if(!e)return 1;const t=e.state;return!t||t.strm!==e||t.mode16211?1:0},Y=e=>{if(G(e))return L;const t=e.state;return e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=D,t.last=0,t.havedict=0,t.flags=-1,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new Int32Array(852),t.distcode=t.distdyn=new Int32Array(592),t.sane=1,t.back=-1,j},ee=e=>{if(G(e))return L;const t=e.state;return t.wsize=0,t.whave=0,t.wnext=0,Y(e)},te=(e,t)=>{let a;if(G(e))return L;const r=e.state;return t<0?(a=0,t=-t):(a=5+(t>>4),t<48&&(t&=15)),t&&(t<8||t>15)?L:(null!==r.window&&r.wbits!==t&&(r.window=null),r.wrap=a,r.wbits=t,ee(e))},ae=(e,t)=>{if(!e)return L;const a=new J;e.state=a,a.strm=e,a.window=null,a.mode=D;const r=te(e,t);return r!==j&&(e.state=null),r};let re,ne,ie=!0;const oe=e=>{if(ie){re=new Int32Array(512),ne=new Int32Array(32);let t=0;for(;t<144;)e.lens[t++]=8;for(;t<256;)e.lens[t++]=9;for(;t<280;)e.lens[t++]=7;for(;t<288;)e.lens[t++]=8;for(T(1,e.lens,0,288,re,0,e.work,{bits:9}),t=0;t<32;)e.lens[t++]=5;T(2,e.lens,0,32,ne,0,e.work,{bits:5}),ie=!1}e.lencode=re,e.lenbits=9,e.distcode=ne,e.distbits=5},se=(e,t,a,r)=>{let n;const i=e.state;return null===i.window&&(i.window=new Uint8Array(1<=i.wsize?(i.window.set(t.subarray(a-i.wsize,a),0),i.wnext=0,i.whave=i.wsize):(n=i.wsize-i.wnext,n>r&&(n=r),i.window.set(t.subarray(a-r,a-r+n),i.wnext),(r-=n)?(i.window.set(t.subarray(a-r,a),0),i.wnext=r,i.whave=i.wsize):(i.wnext+=n,i.wnext===i.wsize&&(i.wnext=0),i.whaveae(e,15),inflateInit2:ae,inflate:(e,t)=>{let a,r,n,i,o,s,c,u,f,h,w,g,p,b,m,y,k,v,_,E,A,$,R=0;const S=new Uint8Array(4);let J,Y;const ee=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);if(G(e)||!e.output||!e.input&&0!==e.avail_in)return L;a=e.state,a.mode===z&&(a.mode=q),o=e.next_out,n=e.output,c=e.avail_out,i=e.next_in,r=e.input,s=e.avail_in,u=a.hold,f=a.bits,h=s,w=c,$=j;e:for(;;)switch(a.mode){case D:if(0===a.wrap){a.mode=q;break}for(;f<16;){if(0===s)break e;s--,u+=r[i++]<>>8&255,a.check=d(a.check,S,2,0),u=0,f=0,a.mode=16181;break}if(a.head&&(a.head.done=!1),!(1&a.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg="incorrect header check",a.mode=Q;break}if((15&u)!==P){e.msg="unknown compression method",a.mode=Q;break}if(u>>>=4,f-=4,A=8+(15&u),0===a.wbits&&(a.wbits=A),A>15||A>a.wbits){e.msg="invalid window size",a.mode=Q;break}a.dmax=1<>8&1),512&a.flags&&4&a.wrap&&(S[0]=255&u,S[1]=u>>>8&255,a.check=d(a.check,S,2,0)),u=0,f=0,a.mode=16182;case 16182:for(;f<32;){if(0===s)break e;s--,u+=r[i++]<>>8&255,S[2]=u>>>16&255,S[3]=u>>>24&255,a.check=d(a.check,S,4,0)),u=0,f=0,a.mode=16183;case 16183:for(;f<16;){if(0===s)break e;s--,u+=r[i++]<>8),512&a.flags&&4&a.wrap&&(S[0]=255&u,S[1]=u>>>8&255,a.check=d(a.check,S,2,0)),u=0,f=0,a.mode=16184;case 16184:if(1024&a.flags){for(;f<16;){if(0===s)break e;s--,u+=r[i++]<>>8&255,a.check=d(a.check,S,2,0)),u=0,f=0}else a.head&&(a.head.extra=null);a.mode=16185;case 16185:if(1024&a.flags&&(g=a.length,g>s&&(g=s),g&&(a.head&&(A=a.head.extra_len-a.length,a.head.extra||(a.head.extra=new Uint8Array(a.head.extra_len)),a.head.extra.set(r.subarray(i,i+g),A)),512&a.flags&&4&a.wrap&&(a.check=d(a.check,r,g,i)),s-=g,i+=g,a.length-=g),a.length))break e;a.length=0,a.mode=16186;case 16186:if(2048&a.flags){if(0===s)break e;g=0;do{A=r[i+g++],a.head&&A&&a.length<65536&&(a.head.name+=String.fromCharCode(A))}while(A&&g>9&1,a.head.done=!0),e.adler=a.check=0,a.mode=z;break;case 16189:for(;f<32;){if(0===s)break e;s--,u+=r[i++]<>>=7&f,f-=7&f,a.mode=X;break}for(;f<3;){if(0===s)break e;s--,u+=r[i++]<>>=1,f-=1,3&u){case 0:a.mode=16193;break;case 1:if(oe(a),a.mode=W,t===O){u>>>=2,f-=2;break e}break;case 2:a.mode=16196;break;case 3:e.msg="invalid block type",a.mode=Q}u>>>=2,f-=2;break;case 16193:for(u>>>=7&f,f-=7&f;f<32;){if(0===s)break e;s--,u+=r[i++]<>>16^65535)){e.msg="invalid stored block lengths",a.mode=Q;break}if(a.length=65535&u,u=0,f=0,a.mode=H,t===O)break e;case H:a.mode=16195;case 16195:if(g=a.length,g){if(g>s&&(g=s),g>c&&(g=c),0===g)break e;n.set(r.subarray(i,i+g),o),s-=g,i+=g,c-=g,o+=g,a.length-=g;break}a.mode=z;break;case 16196:for(;f<14;){if(0===s)break e;s--,u+=r[i++]<>>=5,f-=5,a.ndist=1+(31&u),u>>>=5,f-=5,a.ncode=4+(15&u),u>>>=4,f-=4,a.nlen>286||a.ndist>30){e.msg="too many length or distance symbols",a.mode=Q;break}a.have=0,a.mode=16197;case 16197:for(;a.have>>=3,f-=3}for(;a.have<19;)a.lens[ee[a.have++]]=0;if(a.lencode=a.lendyn,a.lenbits=7,J={bits:a.lenbits},$=T(0,a.lens,0,19,a.lencode,0,a.work,J),a.lenbits=J.bits,$){e.msg="invalid code lengths set",a.mode=Q;break}a.have=0,a.mode=16198;case 16198:for(;a.have>>24,y=R>>>16&255,k=65535&R,!(m<=f);){if(0===s)break e;s--,u+=r[i++]<>>=m,f-=m,a.lens[a.have++]=k;else{if(16===k){for(Y=m+2;f>>=m,f-=m,0===a.have){e.msg="invalid bit length repeat",a.mode=Q;break}A=a.lens[a.have-1],g=3+(3&u),u>>>=2,f-=2}else if(17===k){for(Y=m+3;f>>=m,f-=m,A=0,g=3+(7&u),u>>>=3,f-=3}else{for(Y=m+7;f>>=m,f-=m,A=0,g=11+(127&u),u>>>=7,f-=7}if(a.have+g>a.nlen+a.ndist){e.msg="invalid bit length repeat",a.mode=Q;break}for(;g--;)a.lens[a.have++]=A}}if(a.mode===Q)break;if(0===a.lens[256]){e.msg="invalid code -- missing end-of-block",a.mode=Q;break}if(a.lenbits=9,J={bits:a.lenbits},$=T(1,a.lens,0,a.nlen,a.lencode,0,a.work,J),a.lenbits=J.bits,$){e.msg="invalid literal/lengths set",a.mode=Q;break}if(a.distbits=6,a.distcode=a.distdyn,J={bits:a.distbits},$=T(2,a.lens,a.nlen,a.ndist,a.distcode,0,a.work,J),a.distbits=J.bits,$){e.msg="invalid distances set",a.mode=Q;break}if(a.mode=W,t===O)break e;case W:a.mode=K;case K:if(s>=6&&c>=258){e.next_out=o,e.avail_out=c,e.next_in=i,e.avail_in=s,a.hold=u,a.bits=f,x(e,w),o=e.next_out,n=e.output,c=e.avail_out,i=e.next_in,r=e.input,s=e.avail_in,u=a.hold,f=a.bits,a.mode===z&&(a.back=-1);break}for(a.back=0;R=a.lencode[u&(1<>>24,y=R>>>16&255,k=65535&R,!(m<=f);){if(0===s)break e;s--,u+=r[i++]<>v)],m=R>>>24,y=R>>>16&255,k=65535&R,!(v+m<=f);){if(0===s)break e;s--,u+=r[i++]<>>=v,f-=v,a.back+=v}if(u>>>=m,f-=m,a.back+=m,a.length=k,0===y){a.mode=16205;break}if(32&y){a.back=-1,a.mode=z;break}if(64&y){e.msg="invalid literal/length code",a.mode=Q;break}a.extra=15&y,a.mode=16201;case 16201:if(a.extra){for(Y=a.extra;f>>=a.extra,f-=a.extra,a.back+=a.extra}a.was=a.length,a.mode=16202;case 16202:for(;R=a.distcode[u&(1<>>24,y=R>>>16&255,k=65535&R,!(m<=f);){if(0===s)break e;s--,u+=r[i++]<>v)],m=R>>>24,y=R>>>16&255,k=65535&R,!(v+m<=f);){if(0===s)break e;s--,u+=r[i++]<>>=v,f-=v,a.back+=v}if(u>>>=m,f-=m,a.back+=m,64&y){e.msg="invalid distance code",a.mode=Q;break}a.offset=k,a.extra=15&y,a.mode=16203;case 16203:if(a.extra){for(Y=a.extra;f>>=a.extra,f-=a.extra,a.back+=a.extra}if(a.offset>a.dmax){e.msg="invalid distance too far back",a.mode=Q;break}a.mode=16204;case 16204:if(0===c)break e;if(g=w-c,a.offset>g){if(g=a.offset-g,g>a.whave&&a.sane){e.msg="invalid distance too far back",a.mode=Q;break}g>a.wnext?(g-=a.wnext,p=a.wsize-g):p=a.wnext-g,g>a.length&&(g=a.length),b=a.window}else b=n,p=o-a.offset,g=a.length;g>c&&(g=c),c-=g,a.length-=g;do{n[o++]=b[p++]}while(--g);0===a.length&&(a.mode=K);break;case 16205:if(0===c)break e;n[o++]=a.length,c--,a.mode=K;break;case X:if(a.wrap){for(;f<32;){if(0===s)break e;s--,u|=r[i++]<{if(G(e))return L;let t=e.state;return t.window&&(t.window=null),e.state=null,j},inflateGetHeader:(e,t)=>{if(G(e))return L;const a=e.state;return 2&a.wrap?(a.head=t,t.done=!1,j):L},inflateSetDictionary:(e,t)=>{const a=t.length;let r,n,i;return G(e)?L:(r=e.state,0!==r.wrap&&r.mode!==Z?L:r.mode===Z&&(n=1,n=l(n,t,a,0),n!==r.check)?B:(i=se(e,t,a,a),i?(r.mode=16210,C):(r.havedict=1,j)))},inflateInfo:"pako inflate (from Nodeca project)"};var ce=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1};const de=Object.prototype.toString,{Z_NO_FLUSH:ue,Z_FINISH:fe,Z_OK:he,Z_STREAM_END:we,Z_NEED_DICT:ge,Z_STREAM_ERROR:pe,Z_DATA_ERROR:be,Z_MEM_ERROR:me,Z_BUF_ERROR:ye}=f,ke={chunkSize:65536,windowBits:15,to:""};function ve(e){this.options=w({},ke,e||{});const t=this.options;t.raw&&t.windowBits>=0&&t.windowBits<16&&(t.windowBits=-t.windowBits,0===t.windowBits&&(t.windowBits=-15)),!(t.windowBits>=0&&t.windowBits<16)||e&&e.windowBits||(t.windowBits+=32),t.windowBits>15&&t.windowBits<48&&(15&t.windowBits||(t.windowBits|=15)),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new v,this.strm.avail_out=0;let a=le.inflateInit2(this.strm,t.windowBits);if(a!==he)throw new Error(u[a]);if(this.header=new ce,le.inflateGetHeader(this.strm,this.header),t.dictionary&&("string"==typeof t.dictionary?t.dictionary=m(t.dictionary):"[object ArrayBuffer]"===de.call(t.dictionary)&&(t.dictionary=new Uint8Array(t.dictionary)),t.raw&&(a=le.inflateSetDictionary(this.strm,t.dictionary),a!==he)))throw new Error(u[a])}ve.prototype.push=function(e,t){const a=this.strm,r=this.options.chunkSize,n=this.options.dictionary;let i,o,s;if(this.ended)return!1;for(o=t===~~t?t:!0===t?fe:ue,"[object ArrayBuffer]"===de.call(e)?a.input=new Uint8Array(e):a.input=e,a.next_in=0,a.avail_in=a.input.length;;){for(0===a.avail_out&&(a.output=new Uint8Array(r),a.next_out=0,a.avail_out=r),i=le.inflate(a,o),i===ge&&n&&(i=le.inflateSetDictionary(a,n),i===he?i=le.inflate(a,o):i===be&&(i=ge));a.avail_in>0&&i===we&&2&a.state.wrap&&0!==a.state.flags&&0!==a.input[a.next_in];)le.inflateReset(a),i=le.inflate(a,o);switch(i){case pe:case be:case ge:case me:return this.onEnd(i),this.ended=!0,!1}if(s=a.avail_out,a.next_out&&(0===a.avail_out||i===we||o>0))if("string"===this.options.to){let e=k(a.output,a.next_out),t=a.next_out-e,n=y(a.output,e);a.next_out=t,a.avail_out=r-t,t&&a.output.set(a.output.subarray(e,e+t),0),this.onData(n)}else this.onData(a.output.length===a.next_out?a.output:a.output.subarray(0,a.next_out)),a.avail_out=0,a.next_out=0;if(i!==he&&i!==ye||0!==s){if(i===we)return i=le.inflateEnd(this.strm),this.onEnd(i),this.ended=!0,!0;if(0===a.avail_in){if(o===fe)return i=le.inflateEnd(this.strm),this.onEnd(i===he?ye:i),this.ended=!0,!1;break}}}return!0},ve.prototype.onData=function(e){this.chunks.push(e)},ve.prototype.onEnd=function(e){e===he&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=g(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg};var _e={ungzip:function(e,t){const a=new ve(t);if(a.push(e,!0),a.err)throw a.msg||u[a.err];return a.result}};const{ungzip:xe}=_e;var Ee=xe;function Ae(e,t){let a=0,r=1,n=0;for(;t=70)throw new Error("varint too long at "+t)}throw new Error("truncated varint")}function $e(e){let t=Math.floor(e);if(t>=0){const e=[];for(;t>=128;)e.push(t%128|128),t=Math.floor(t/128);return e.push(t),e}const a=[0,0,0,0,0,0,0,0];let r=-t;for(let e=0;e<8;e++)a[e]=255&r,r=Math.floor(r/256);let n=1;for(let e=0;e<8;e++){const t=(255&~a[e])+n;a[e]=255&t,n=t>>8}const i=[];for(let e=0;e<10;e++){let t=0;for(let r=0;r<7;r++){const n=7*e+r;n<64&&(t|=(a[n>>3]>>(7&n)&1)<String.fromCharCode(255&e)).join("");n=/^[0-9a-fA-F]{1,2}(:[0-9a-fA-F]{1,2}){5}$/.test(t)}if(!n)return e;let i=!1;const o=[];for(const e of r)if(2===e.fieldNo&&2===e.wireType)try{const r=Ne(e.value,t,a);r.length===e.value.length&&r.join(",")===e.value.join(",")||(i=!0),o.push(Te(e.fieldNo,e.wireType,r))}catch{a.skipped++,o.push(e.raw)}else o.push(e.raw);return i&&a.wifi++,Re(o)}function Oe(e,t,a){const r=Se(e);let n=!1;const i=[];for(const e of r)if(5===e.fieldNo&&2===e.wireType)try{const r=Ne(e.value,t,a);r.length===e.value.length&&r.join(",")===e.value.join(",")||(n=!0),i.push(Te(e.fieldNo,e.wireType,r))}catch{a.skipped++,i.push(e.raw)}else i.push(e.raw);return n&&a.cell++,Re(i)}function je(e,t,a){const r=Se(e),n=[];for(const e of r)2===e.wireType&&2===e.fieldNo?n.push(Te(e.fieldNo,e.wireType,Me(e.value,t,a))):2!==e.wireType||22!==e.fieldNo&&24!==e.fieldNo?n.push(e.raw):n.push(Te(e.fieldNo,e.wireType,Oe(e.value,t,a)));return Re(n)}function Fe(e){return{wifi:e.wifi||0,cell:e.cell||0,locations:e.locations||0,skipped:e.skipped||0}}function Ie(e,t){e.wifi=t.wifi,e.cell=t.cell,e.locations=t.locations,e.skipped=t.skipped}function Le(e,t){if(e.length!==t.length)return!1;for(let a=0;ae.length)throw new Error("invalid frame length "+n+" at "+t+" for "+e.length);const i=e.slice(0,t+8),o=e.slice(t+10,t+10+n),s=e.slice(t+10+n),l=Fe(r),c=je(o,a,r),d=r.locations-l.locations+(r.wifi-l.wifi)+(r.cell-l.cell);if(c.length>65535)throw new Error("patched payload too large: "+c.length);if(d<=0||Le(o,c))throw Ie(r,l),new Error("frame parsed but no patchable wloc payload at "+t);return Re([i,[c.length>>8&255,255&c.length],c,s])}function Ce(e,a){const r={wifi:0,cell:0,locations:0,skipped:0};if(e.length<10)throw new Error("body too short: "+e.length);const n=[],i=[0,2,4,6,8,10,12,14,16],o=Math.min(96,Math.max(0,e.length-10));for(let e=0;e<=o;e++)i.indexOf(e)<0&&i.push(e);for(let o=0;o0&&!Le(r,o))return Re([e.slice(0,i),o]);Ie(a,n)}catch(e){Ie(a,n),r.length<6&&r.push("raw@"+i+":"+(e&&e.message?e.message:String(e)))}}throw new Error("raw protobuf scan failed; "+r.join(" | "))}(e,a,r);return t.info(`[wloc] patched via raw fallback locations=${r.locations} wifi=${r.wifi} cell=${r.cell} skipped=${r.skipped}`),{data:n,stats:r}}catch(e){n.push("raw:"+(e&&e.message?e.message:String(e)))}throw new Error("no patchable wloc payload found; "+n.join(" | "))}function Ue(e){return e.length>=2&&31===e[0]&&139===e[1]}async function Pe(e,a,r){const n=e.url||"";t.group(`[wloc] Response ${n}`);try{let e=function(e){if(!e)return[];if("undefined"!=typeof ArrayBuffer){if(e instanceof ArrayBuffer)return Array.prototype.slice.call(new Uint8Array(e));if(ArrayBuffer.isView&&ArrayBuffer.isView(e))return Array.prototype.slice.call(new Uint8Array(e.buffer,e.byteOffset,e.byteLength))}if("number"==typeof e.length&&"string"!=typeof e){const t=[];for(let a=0;a{const e=function(){try{return"undefined"!=typeof $response?$response:void 0}catch{return}}();if(!e)return void t.warn("[wloc] 非响应模式,跳过");const a=Ze();t.logLevel=a.logLevel,ze=await Pe($request,e,a)})().catch(e=>t.error(e)).finally(()=>{switch(typeof ze){case"object":ze.headers?.["Content-Encoding"]&&(ze.headers["Content-Encoding"]="identity"),ze.headers?.["content-encoding"]&&(ze.headers["content-encoding"]="identity"),"Quantumult X"===e?(ze.status||(ze.status=200),delete ze.headers?.["Content-Length"],delete ze.headers?.["content-length"],delete ze.headers?.["Transfer-Encoding"],i(ze)):i("Stash"===e?ze:{response:ze});break;case"undefined":i({});break;default:t.error("[wloc] 不合法的 response 类型: "+typeof ze),i({})}}); diff --git a/ThirdParty/WlocScripts/modules/direct/wloc.conf b/ThirdParty/WlocScripts/modules/direct/wloc.conf deleted file mode 100644 index 000afd9..0000000 --- a/ThirdParty/WlocScripts/modules/direct/wloc.conf +++ /dev/null @@ -1,11 +0,0 @@ -#!name=Apple WLOC 定位修改 -#!desc=修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/ -#!author=Yu9191 Rewrite -#!homepage=https://github.com/Yu9191/wloc - -[rewrite_local] -^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc url script-response-body https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js -^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save url script-echo-response https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js - -[mitm] -hostname = gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com diff --git a/ThirdParty/WlocScripts/modules/direct/wloc.lpx b/ThirdParty/WlocScripts/modules/direct/wloc.lpx deleted file mode 100644 index ce82fe7..0000000 --- a/ThirdParty/WlocScripts/modules/direct/wloc.lpx +++ /dev/null @@ -1,20 +0,0 @@ -#!name=Apple WLOC 定位修改 -#!desc=修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f -#!icon=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/wloc.jpg -#!author=Yu9191 Rewrite -#!homepage=https://github.com/Yu9191/wloc -#!openUrl=https://wloc-pages.pages.dev/ - -[Argument] -longitude = input, "113.94114", tag=经度(在线选点优先) -latitude = input, "22.544577", tag=纬度(在线选点优先) -accuracy = input, "25", tag=精度(米) -randomRadius = input, "0", tag=扰动半径(米,0为关闭) -logLevel = select, "info", "off", "error", "warn", "debug", "all", tag=日志级别 - -[Script] -http-response ^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc script-path=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js, requires-body=true, binary-body-mode=true, timeout=30, tag=Apple WLOC, argument=[{longitude},{latitude},{accuracy},{randomRadius},{logLevel}] -http-request ^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save script-path=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js, timeout=10, tag=WLOC Settings - -[MITM] -hostname = gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com diff --git a/ThirdParty/WlocScripts/modules/direct/wloc.module b/ThirdParty/WlocScripts/modules/direct/wloc.module deleted file mode 100644 index 1d2e415..0000000 --- a/ThirdParty/WlocScripts/modules/direct/wloc.module +++ /dev/null @@ -1,13 +0,0 @@ -#!name=Apple WLOC 定位修改 -#!desc=修改 Apple 网络定位返回坐标 (Shadowrocket 小火箭) | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/ -#!author=Yu9191 Rewrite -#!homepage=https://github.com/Yu9191/wloc -#!icon=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/wloc.jpg -#!category=Tools - -[Script] -Apple WLOC = type=http-response,pattern=^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc,requires-body=1,binary-body-mode=1,max-size=0,timeout=30,script-path=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js,argument=longitude=113.94114&latitude=22.544577&accuracy=25&randomRadius=0&logLevel=info -WLOC Settings = type=http-request,pattern=^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save,requires-body=0,max-size=0,timeout=10,script-path=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js - -[MITM] -hostname = %APPEND% gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com diff --git a/ThirdParty/WlocScripts/modules/direct/wloc.sgmodule b/ThirdParty/WlocScripts/modules/direct/wloc.sgmodule deleted file mode 100644 index 428df43..0000000 --- a/ThirdParty/WlocScripts/modules/direct/wloc.sgmodule +++ /dev/null @@ -1,14 +0,0 @@ -#!name=Apple WLOC 定位修改 -#!desc=修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/ -#!author=Yu9191 Rewrite -#!homepage=https://github.com/Yu9191/wloc -#!category=Tools -#!arguments=经度:113.94114, 纬度:22.544577, 精度:25, 扰动半径:0, 日志级别:info -#!arguments-desc=经度/纬度: 默认坐标(在线选点储存后优先)\n精度: GPS精度(米)\n扰动半径: 每次响应在目标点周围随机偏移的最大距离(米),0为关闭\n日志级别: off/error/warn/info/debug/all\n\n使用方法: 打开选点页面 -> 选位置 -> 储存到设备 - -[Script] -Apple WLOC = type=http-response, pattern="^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc", requires-body=1, binary-body-mode=1, max-size=0, timeout=30, script-path=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js, argument=longitude={{{经度}}}&latitude={{{纬度}}}&accuracy={{{精度}}}&randomRadius={{{扰动半径}}}&logLevel={{{日志级别}}} -WLOC Settings = type=http-request, pattern="^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save", requires-body=0, max-size=0, timeout=10, script-path=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js - -[MITM] -hostname = %APPEND% gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com diff --git a/ThirdParty/WlocScripts/modules/direct/wloc.stoverride b/ThirdParty/WlocScripts/modules/direct/wloc.stoverride deleted file mode 100644 index 21b03d3..0000000 --- a/ThirdParty/WlocScripts/modules/direct/wloc.stoverride +++ /dev/null @@ -1,36 +0,0 @@ -name: Apple WLOC 定位修改 -desc: "修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/" -author: Yu9191 Rewrite -homepage: https://github.com/Yu9191/wloc -icon: https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/wloc.jpg -category: Tools - -http: - mitm: - - "gs-loc.apple.com" - - "gs-loc-cn.apple.com" - - "gsp-ssl.ls.apple.com" - - "bluedot.is.autonavi.com" - - "bluedot.is.autonavi.com.gds.alibabadns.com" - script: - - match: ^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc - name: WLOC.Location - type: response - require-body: true - binary-mode: true - max-size: 0 - timeout: 30 - argument: longitude=113.94114&latitude=22.544577&accuracy=25&randomRadius=0&logLevel=info - - match: ^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save - name: WLOC.Settings - type: request - require-body: false - timeout: 10 - -script-providers: - WLOC.Location: - url: https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js - interval: 86400 - WLOC.Settings: - url: https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js - interval: 86400 diff --git a/ThirdParty/WlocScripts/package-lock.json b/ThirdParty/WlocScripts/package-lock.json deleted file mode 100644 index 344233c..0000000 --- a/ThirdParty/WlocScripts/package-lock.json +++ /dev/null @@ -1,508 +0,0 @@ -{ - "name": "@location-spoofer/wloc-scripts", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@location-spoofer/wloc-scripts", - "version": "1.0.0", - "dependencies": { - "pako": "2.1.0" - }, - "devDependencies": { - "esbuild": "0.25.8" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", - "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.8.tgz", - "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", - "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.8.tgz", - "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", - "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", - "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", - "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", - "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", - "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", - "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", - "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", - "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", - "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", - "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", - "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", - "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", - "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", - "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", - "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", - "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", - "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", - "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", - "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", - "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", - "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", - "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/esbuild": { - "version": "0.25.8", - "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.8.tgz", - "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.8", - "@esbuild/android-arm": "0.25.8", - "@esbuild/android-arm64": "0.25.8", - "@esbuild/android-x64": "0.25.8", - "@esbuild/darwin-arm64": "0.25.8", - "@esbuild/darwin-x64": "0.25.8", - "@esbuild/freebsd-arm64": "0.25.8", - "@esbuild/freebsd-x64": "0.25.8", - "@esbuild/linux-arm": "0.25.8", - "@esbuild/linux-arm64": "0.25.8", - "@esbuild/linux-ia32": "0.25.8", - "@esbuild/linux-loong64": "0.25.8", - "@esbuild/linux-mips64el": "0.25.8", - "@esbuild/linux-ppc64": "0.25.8", - "@esbuild/linux-riscv64": "0.25.8", - "@esbuild/linux-s390x": "0.25.8", - "@esbuild/linux-x64": "0.25.8", - "@esbuild/netbsd-arm64": "0.25.8", - "@esbuild/netbsd-x64": "0.25.8", - "@esbuild/openbsd-arm64": "0.25.8", - "@esbuild/openbsd-x64": "0.25.8", - "@esbuild/openharmony-arm64": "0.25.8", - "@esbuild/sunos-x64": "0.25.8", - "@esbuild/win32-arm64": "0.25.8", - "@esbuild/win32-ia32": "0.25.8", - "@esbuild/win32-x64": "0.25.8" - } - }, - "node_modules/pako": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "license": "(MIT AND Zlib)" - } - } -} diff --git a/ThirdParty/WlocScripts/package.json b/ThirdParty/WlocScripts/package.json deleted file mode 100644 index 11f7080..0000000 --- a/ThirdParty/WlocScripts/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "@location-spoofer/wloc-scripts", - "version": "1.0.0", - "private": true, - "type": "module", - "scripts": { - "build": "node build.mjs", - "test": "node --test" - }, - "dependencies": { - "pako": "2.1.0" - }, - "devDependencies": { - "esbuild": "0.25.8" - } -} diff --git a/ThirdParty/WlocScripts/src/core.js b/ThirdParty/WlocScripts/src/core.js deleted file mode 100644 index 15f66f0..0000000 --- a/ThirdParty/WlocScripts/src/core.js +++ /dev/null @@ -1,263 +0,0 @@ -export const MOTION_ACTIVITY_TYPE = 63; -export const MOTION_ACTIVITY_CONFIDENCE = 467; -export const WLOC_MARKER = Uint8Array.from([0, 0, 0, 1, 0, 0]); -const UINT32_RANGE = 0x100000000; - -const concat = (...parts) => { - const length = parts.reduce((sum, part) => sum + part.length, 0); - const out = new Uint8Array(length); - let offset = 0; - for (const part of parts) { - out.set(part, offset); - offset += part.length; - } - return out; -}; - -const equal = (left, right) => - left.length === right.length && left.every((value, index) => value === right[index]); - -function readVarint(data, offset) { - let value = 0; - let multiplier = 1; - for (let index = 0; index < 10 && offset + index < data.length; index += 1) { - const byte = data[offset + index]; - const chunk = byte & 0x7f; - if (value !== null && chunk <= Math.floor((Number.MAX_SAFE_INTEGER - value) / multiplier)) { - value += chunk * multiplier; - } else { - value = null; - } - if ((byte & 0x80) === 0) return { value, next: offset + index + 1 }; - multiplier *= 0x80; - } - throw new Error("invalid varint"); -} - -function writeVarint(input) { - const value = Math.trunc(Number(input)); - if (!Number.isSafeInteger(value)) throw new Error("varint value is not a safe integer"); - let low = value >>> 0; - let high = Math.floor(value / UINT32_RANGE) >>> 0; - const bytes = []; - do { - const byte = low & 0x7f; - low = ((low >>> 7) | ((high & 0x7f) << 25)) >>> 0; - high >>>= 7; - const hasMore = high !== 0 || low !== 0; - bytes.push(byte | (hasMore ? 0x80 : 0)); - } while (high !== 0 || low !== 0); - return Uint8Array.from(bytes); -} - -const writeTag = (number, wireType) => writeVarint((number << 3) | wireType); - -function writeLengthDelimited(number, value) { - return concat(writeTag(number, 2), writeVarint(value.length), value); -} - -export function parseFields(data) { - const fields = []; - let offset = 0; - while (offset < data.length) { - const start = offset; - const tag = readVarint(data, offset); - offset = tag.next; - if (!Number.isSafeInteger(tag.value)) throw new Error("protobuf tag is too large"); - const number = Math.floor(tag.value / 8); - const wireType = tag.value & 7; - if (number === 0) throw new Error("invalid protobuf field 0"); - let value; - if (wireType === 0) { - const item = readVarint(data, offset); - value = data.slice(offset, item.next); - offset = item.next; - } else if (wireType === 1) { - if (offset + 8 > data.length) throw new Error("truncated fixed64"); - value = data.slice(offset, offset + 8); - offset += 8; - } else if (wireType === 2) { - const length = readVarint(data, offset); - offset = length.next; - const size = length.value; - if (!Number.isSafeInteger(size) || offset + size > data.length) { - throw new Error("truncated length-delimited field"); - } - value = data.slice(offset, offset + size); - offset += size; - } else if (wireType === 5) { - if (offset + 4 > data.length) throw new Error("truncated fixed32"); - value = data.slice(offset, offset + 4); - offset += 4; - } else { - throw new Error(`unsupported wire type ${wireType}`); - } - fields.push({ number, wireType, value, raw: data.slice(start, offset) }); - } - return fields; -} - -function patchLocation(data, config, stats) { - const fields = parseFields(data); - if (!fields.some((field) => field.number === 1 && field.wireType === 0) || - !fields.some((field) => field.number === 2 && field.wireType === 0)) { - return data; - } - let hasMotionType = false; - let hasMotionConfidence = false; - const parts = fields.map((field) => { - if (field.wireType !== 0) return field.raw; - if (field.number === 1) return concat(writeTag(1, 0), writeVarint(Math.round(config.latitude * 1e8))); - if (field.number === 2) return concat(writeTag(2, 0), writeVarint(Math.round(config.longitude * 1e8))); - if (field.number === 3) return concat(writeTag(3, 0), writeVarint(config.accuracy)); - if (config.motionSimulationEnabled && field.number === 11) { - hasMotionType = true; - return concat(writeTag(11, 0), writeVarint(MOTION_ACTIVITY_TYPE)); - } - if (config.motionSimulationEnabled && field.number === 12) { - hasMotionConfidence = true; - return concat(writeTag(12, 0), writeVarint(MOTION_ACTIVITY_CONFIDENCE)); - } - return field.raw; - }); - if (config.motionSimulationEnabled && !hasMotionType) { - parts.push(concat(writeTag(11, 0), writeVarint(MOTION_ACTIVITY_TYPE))); - } - if (config.motionSimulationEnabled && !hasMotionConfidence) { - parts.push(concat(writeTag(12, 0), writeVarint(MOTION_ACTIVITY_CONFIDENCE))); - } - const out = concat(...parts); - if (!equal(out, data)) stats.locations += 1; - return out; -} - -function patchWifi(data, config, stats) { - const fields = parseFields(data); - const hasMac = fields.some((field) => - field.number === 1 && field.wireType === 2 && - /^[0-9a-fA-F]{1,2}(:[0-9a-fA-F]{1,2}){5}$/.test( - Array.from(field.value, (byte) => String.fromCharCode(byte)).join("") - ) - ); - if (!hasMac) return data; - let changed = false; - const parts = fields.map((field) => { - if (field.number !== 2 || field.wireType !== 2) return field.raw; - const value = patchLocation(field.value, config, stats); - changed ||= !equal(value, field.value); - return writeLengthDelimited(2, value); - }); - if (changed) stats.wifi += 1; - return concat(...parts); -} - -function patchCell(data, config, stats) { - let changed = false; - const parts = parseFields(data).map((field) => { - if (field.number !== 5 || field.wireType !== 2) return field.raw; - const value = patchLocation(field.value, config, stats); - changed ||= !equal(value, field.value); - return writeLengthDelimited(5, value); - }); - if (changed) stats.cell += 1; - return concat(...parts); -} - -export function patchPayload(data, config, stats = { wifi: 0, cell: 0, locations: 0 }) { - const parts = parseFields(data).map((field) => { - if (field.number === 2 && field.wireType === 2) { - return writeLengthDelimited(2, patchWifi(field.value, config, stats)); - } - if ((field.number === 22 || field.number === 24) && field.wireType === 2) { - return writeLengthDelimited(field.number, patchCell(field.value, config, stats)); - } - return field.raw; - }); - return { data: concat(...parts), stats }; -} - -const uint16 = (data, offset) => (data[offset] << 8) | data[offset + 1]; -const uint32 = (data, offset) => - ((data[offset] * 0x1000000) + (data[offset + 1] << 16) + - (data[offset + 2] << 8) + data[offset + 3]) >>> 0; -const writeUint16 = (value) => Uint8Array.from([(value >>> 8) & 0xff, value & 0xff]); -const writeUint32 = (value) => Uint8Array.from([ - (value >>> 24) & 0xff, (value >>> 16) & 0xff, (value >>> 8) & 0xff, value & 0xff -]); - -function findBytes(data, marker) { - outer: for (let offset = 0; offset <= data.length - marker.length; offset += 1) { - for (let index = 0; index < marker.length; index += 1) { - if (data[offset + index] !== marker[index]) continue outer; - } - return offset; - } - return -1; -} - -function patchARPC(body, config) { - if (body.length < 2) throw new Error("short ARPC"); - let offset = 2; - for (let index = 0; index < 3; index += 1) { - if (offset + 2 > body.length) throw new Error("truncated ARPC string"); - offset += 2 + uint16(body, offset); - } - const lengthOffset = offset + 4; - const payloadOffset = lengthOffset + 4; - if (payloadOffset > body.length) throw new Error("truncated ARPC header"); - const length = uint32(body, lengthOffset); - if (!length || payloadOffset + length > body.length) throw new Error("invalid ARPC length"); - const patched = patchPayload(body.slice(payloadOffset, payloadOffset + length), config); - if (equal(patched.data, body.slice(payloadOffset, payloadOffset + length))) throw new Error("unchanged ARPC"); - return { data: concat(body.slice(0, lengthOffset), writeUint32(patched.data.length), - patched.data, body.slice(payloadOffset + length)), stats: patched.stats }; -} - -function patchMarker(body, config) { - const markerOffset = findBytes(body, WLOC_MARKER); - if (markerOffset < 0) throw new Error("marker not found"); - const lengthOffset = markerOffset + WLOC_MARKER.length; - const payloadOffset = lengthOffset + 2; - const length = uint16(body, lengthOffset); - if (!length || payloadOffset + length > body.length) throw new Error("invalid marker length"); - const patched = patchPayload(body.slice(payloadOffset, payloadOffset + length), config); - if (patched.data.length > 65535 || equal(patched.data, body.slice(payloadOffset, payloadOffset + length))) { - throw new Error("unchanged marker"); - } - return { data: concat(body.slice(0, lengthOffset), writeUint16(patched.data.length), - patched.data, body.slice(payloadOffset + length)), stats: patched.stats }; -} - -function patchSynthetic(body, offset, config) { - if (offset + 10 > body.length) throw new Error("short frame"); - const length = uint16(body, offset + 8); - if (!length || offset + 10 + length > body.length) throw new Error("invalid frame"); - const patched = patchPayload(body.slice(offset + 10, offset + 10 + length), config); - if (patched.data.length > 65535 || equal(patched.data, body.slice(offset + 10, offset + 10 + length))) { - throw new Error("unchanged frame"); - } - return { data: concat(body.slice(0, offset + 8), writeUint16(patched.data.length), - patched.data, body.slice(offset + 10 + length)), stats: patched.stats }; -} - -export function patchWlocBody(body, config) { - for (const patcher of [patchARPC, patchMarker]) { - try { return patcher(body, config); } catch {} - } - const offsets = [...new Set([0, 2, 4, 6, 8, 10, 12, 14, 16, - ...Array.from({ length: Math.min(96, Math.max(0, body.length - 10)) + 1 }, (_, index) => index)])]; - for (const offset of offsets) { - try { return patchSynthetic(body, offset, config); } catch {} - } - for (let offset = 0; offset <= Math.min(256, body.length); offset += 1) { - try { - const patched = patchPayload(body.slice(offset), config); - if (!equal(patched.data, body.slice(offset))) { - return { data: concat(body.slice(0, offset), patched.data), stats: patched.stats }; - } - } catch {} - } - throw new Error("no patchable wloc payload found"); -} - -export const internals = { concat, writeVarint, writeTag, writeLengthDelimited, equal }; diff --git a/ThirdParty/WlocScripts/src/response-entry.js b/ThirdParty/WlocScripts/src/response-entry.js deleted file mode 100644 index 49f46ab..0000000 --- a/ThirdParty/WlocScripts/src/response-entry.js +++ /dev/null @@ -1,26 +0,0 @@ -import { ungzip } from "pako"; -import { patchWlocBody } from "./core.js"; -import { - STORAGE_KEY, finishBinary, finishPassthrough, readPersistent, responseBytes -} from "./runtime.js"; - -try { - const settings = readPersistent(STORAGE_KEY); - const input = responseBytes(); - if (!settings || !settings.enabled || !input.length) { - finishPassthrough(); - } else { - const isGzip = input.length >= 2 && input[0] === 0x1f && input[1] === 0x8b; - const body = isGzip ? ungzip(input) : input; - const patched = patchWlocBody(body, { - latitude: Number(settings.latitude), - longitude: Number(settings.longitude), - accuracy: Number(settings.accuracy != null ? settings.accuracy : 25), - motionSimulationEnabled: settings.motionSimulationEnabled === true - }); - finishBinary(patched.data); - } -} catch (error) { - console.log(`[Location Spoofer] ${error && error.message ? error.message : error}`); - finishPassthrough(); -} diff --git a/ThirdParty/WlocScripts/src/runtime.js b/ThirdParty/WlocScripts/src/runtime.js deleted file mode 100644 index 0423b9e..0000000 --- a/ThirdParty/WlocScripts/src/runtime.js +++ /dev/null @@ -1,109 +0,0 @@ -export const MODULE_VERSION = "1.0.0"; -export const PROTOCOL_VERSION = 1; -export const CAPABILITIES = [ - "wifi", "cellTower", "arpc", "marker", "synthetic", "bare", "motionSimulation" -]; -export const STORAGE_KEY = "locationSpoofer.settings.v1"; - -export function environment() { - if (typeof $task !== "undefined") return "quantumultX"; - if (typeof $loon !== "undefined") return "loon"; - if (typeof $rocket !== "undefined") return "shadowrocket"; - if (typeof Egern !== "undefined") return "egern"; - if (typeof $environment !== "undefined" && $environment["stash-version"]) return "stash"; - if (typeof $environment !== "undefined" && $environment["surge-version"]) return "surge"; - return "unknown"; -} - -export function readPersistent(key) { - const raw = environment() === "quantumultX" - ? $prefs.valueForKey(key) - : $persistentStore.read(key); - if (!raw) return null; - try { return JSON.parse(raw); } catch { return null; } -} - -export function writePersistent(key, value) { - const raw = value == null ? "" : JSON.stringify(value); - return environment() === "quantumultX" - ? $prefs.setValueForKey(raw, key) - : $persistentStore.write(raw, key); -} - -export function responseBytes() { - const response = typeof $response === "undefined" ? null : $response; - const value = response && response.bodyBytes != null ? response.bodyBytes : response && response.body; - if (value instanceof ArrayBuffer) return new Uint8Array(value); - if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength); - if (typeof value === "string") { - return Uint8Array.from(value, (character) => character.charCodeAt(0) & 0xff); - } - return new Uint8Array(); -} - -function cleanHeaders(headers, length) { - const out = Object.assign({}, headers || {}); - for (const name of ["Content-Encoding", "content-encoding", "Transfer-Encoding", "transfer-encoding"]) { - delete out[name]; - } - out["Content-Length"] = String(length); - return out; -} - -export function finishBinary(bytes) { - const response = typeof $response === "undefined" ? {} : $response; - const headers = cleanHeaders(response.headers, bytes.length); - const env = environment(); - if (env === "quantumultX") { - delete headers["Content-Length"]; - $done({ status: "HTTP/1.1 200 OK", headers, bodyBytes: bytes.buffer }); - } else if (env === "stash") { - $done(Object.assign({}, response, { status: 200, headers, body: bytes })); - } else { - $done({ response: Object.assign({}, response, { status: 200, headers, body: bytes }) }); - } -} - -export function finishPassthrough() { - $done({}); -} - -export function finishJSON(value) { - const response = { - status: 200, - headers: { "Content-Type": "application/json; charset=utf-8" }, - body: JSON.stringify(value) - }; - if (environment() === "quantumultX") { - $done(Object.assign({}, response, { status: "HTTP/1.1 200 OK" })); - } else if (environment() === "stash") { - $done(response); - } else { - $done({ response }); - } -} - -export function queryParameters(url) { - const query = url.split("?")[1] || ""; - const values = {}; - query.split("&").forEach((item) => { - if (!item) return; - const separator = item.indexOf("="); - const rawKey = separator < 0 ? item : item.slice(0, separator); - const rawValue = separator < 0 ? "" : item.slice(separator + 1); - let key = rawKey; - let value = rawValue; - try { key = decodeURIComponent(rawKey.replace(/\+/g, " ")); } catch {} - try { value = decodeURIComponent(rawValue.replace(/\+/g, " ")); } catch {} - if (!Object.prototype.hasOwnProperty.call(values, key)) values[key] = value; - }); - return values; -} - -export function requestPath(url) { - const withoutQuery = url.split("?")[0]; - const scheme = withoutQuery.indexOf("://"); - if (scheme < 0) return withoutQuery; - const path = withoutQuery.indexOf("/", scheme + 3); - return path < 0 ? "/" : withoutQuery.slice(path); -} diff --git a/ThirdParty/WlocScripts/src/settings-entry.js b/ThirdParty/WlocScripts/src/settings-entry.js deleted file mode 100644 index 626d0d8..0000000 --- a/ThirdParty/WlocScripts/src/settings-entry.js +++ /dev/null @@ -1,48 +0,0 @@ -import { - CAPABILITIES, MODULE_VERSION, PROTOCOL_VERSION, STORAGE_KEY, - finishJSON, queryParameters, readPersistent, requestPath, writePersistent -} from "./runtime.js"; - -const url = typeof $request === "undefined" ? "" : ($request.url || ""); -const path = requestPath(url); -const parameters = queryParameters(url); - -if (path === "/wloc-settings/version") { - finishJSON({ - success: true, - moduleVersion: MODULE_VERSION, - protocolVersion: PROTOCOL_VERSION, - capabilities: CAPABILITIES - }); -} else if (parameters.action === "query") { - const settings = readPersistent(STORAGE_KEY); - finishJSON(settings && settings.enabled - ? { success: true, longitude: settings.longitude, latitude: settings.latitude, - accuracy: settings.accuracy, motionSimulationEnabled: settings.motionSimulationEnabled === true } - : { success: false, error: "无已保存的坐标" }); -} else if (parameters.action === "clear") { - writePersistent(STORAGE_KEY, null); - finishJSON({ success: true }); -} else { - const longitude = Number(parameters.lon != null ? parameters.lon : parameters.longitude); - const latitude = Number(parameters.lat != null ? parameters.lat : parameters.latitude); - const accuracy = Number(parameters.acc != null - ? parameters.acc - : (parameters.accuracy != null ? parameters.accuracy : 25)); - if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) { - finishJSON({ success: false, error: "缺少 lon/lat 参数" }); - } else { - const settings = { - enabled: true, - longitude, - latitude, - accuracy, - motionSimulationEnabled: parameters.motion === "1" - }; - const success = writePersistent(STORAGE_KEY, settings); - finishJSON(success - ? { success: true, longitude, latitude, accuracy, - motionSimulationEnabled: settings.motionSimulationEnabled } - : { success: false, error: "保存配置失败" }); - } -} diff --git a/ThirdParty/WlocScripts/test/bundle-compatibility.test.js b/ThirdParty/WlocScripts/test/bundle-compatibility.test.js deleted file mode 100644 index 10b5b71..0000000 --- a/ThirdParty/WlocScripts/test/bundle-compatibility.test.js +++ /dev/null @@ -1,52 +0,0 @@ -import test from "node:test"; -import assert from "node:assert/strict"; -import { readFile } from "node:fs/promises"; -import vm from "node:vm"; - -const bundles = [ - new URL("../dist/v1/wloc.js", import.meta.url), - new URL("../dist/v1/wloc-settings.js", import.meta.url) -]; - -test("generated bundles avoid newer JavaScriptCore runtime requirements", async () => { - for (const bundle of bundles) { - const source = await readFile(bundle, "utf8"); - for (const unsupported of [ - /\bBigInt\b/, - /\bglobalThis\b/, - /\bURLSearchParams\b/, - /\bObject\.fromEntries\b/, - /\?\./, - /\?\?/ - ]) { - assert.equal(unsupported.test(source), false, `${bundle.pathname} contains ${unsupported}`); - } - } -}); - -test("settings bundle runs without modern URL and text globals", async () => { - const source = await readFile(bundles[1], "utf8"); - let result; - const storage = new Map(); - vm.runInNewContext(source, { - $rocket: {}, - $request: { - url: "https://gs-loc.apple.com/wloc-settings/save?lon=121.1&lat=31.2&acc=25" - }, - $persistentStore: { - read: (key) => storage.get(key) || null, - write: (value, key) => { - storage.set(key, value); - return true; - } - }, - $done: (value) => { result = value; }, - JSON, - Number, - Object, - String, - decodeURIComponent - }); - - assert.equal(JSON.parse(result.response.body).success, true); -}); diff --git a/ThirdParty/WlocScripts/test/core.test.js b/ThirdParty/WlocScripts/test/core.test.js deleted file mode 100644 index ad1733b..0000000 --- a/ThirdParty/WlocScripts/test/core.test.js +++ /dev/null @@ -1,85 +0,0 @@ -import test from "node:test"; -import assert from "node:assert/strict"; -import { - MOTION_ACTIVITY_CONFIDENCE, MOTION_ACTIVITY_TYPE, internals, parseFields, patchWlocBody -} from "../src/core.js"; - -const { concat, writeVarint, writeTag, writeLengthDelimited } = internals; - -function location(withMotion = false) { - const fields = [ - concat(writeTag(1, 0), writeVarint(100)), - concat(writeTag(2, 0), writeVarint(200)), - concat(writeTag(3, 0), writeVarint(25)) - ]; - if (withMotion) { - fields.push(concat(writeTag(11, 0), writeVarint(7))); - fields.push(concat(writeTag(12, 0), writeVarint(88))); - } - return concat(...fields); -} - -function wifiPayload(value = location()) { - const device = concat( - writeLengthDelimited(1, new TextEncoder().encode("aa:bb:cc:dd:ee:ff")), - writeLengthDelimited(2, value) - ); - return writeLengthDelimited(2, device); -} - -function frame(payload) { - return concat(Uint8Array.from([0, 1, 0, 0, 0, 1, 0, 0]), - Uint8Array.from([payload.length >> 8, payload.length & 0xff]), payload); -} - -const config = { - latitude: 31.230416, - longitude: 121.473701, - accuracy: 50, - motionSimulationEnabled: false -}; - -test("patches synthetic Wi-Fi response", () => { - const result = patchWlocBody(frame(wifiPayload()), config); - assert.equal(result.stats.wifi, 1); - assert.equal(result.stats.locations, 1); -}); - -test("preserves motion fields while disabled", () => { - const result = patchWlocBody(frame(wifiPayload(location(true))), config); - const payload = result.data.slice(10); - assert.ok(payload.includes(7)); - assert.ok(payload.includes(88)); -}); - -test("replaces motion fields while enabled", () => { - const result = patchWlocBody(frame(wifiPayload(location(true))), { - ...config, motionSimulationEnabled: true - }); - const root = parseFields(result.data.slice(10)); - const device = parseFields(root[0].value); - const fields = parseFields(device.find((field) => field.number === 2).value); - const motionType = fields.find((field) => field.number === 11); - const motionConfidence = fields.find((field) => field.number === 12); - assert.deepEqual(motionType.value, writeVarint(MOTION_ACTIVITY_TYPE)); - assert.deepEqual(motionConfidence.value, writeVarint(MOTION_ACTIVITY_CONFIDENCE)); -}); - -test("patches CellTower fields 22 and 24", () => { - for (const number of [22, 24]) { - const cell = writeLengthDelimited(5, location()); - const result = patchWlocBody(frame(writeLengthDelimited(number, cell)), config); - assert.equal(result.stats.cell, 1); - } -}); - -test("encodes signed int64 coordinates without BigInt", () => { - assert.deepEqual( - Array.from(writeVarint(-18_000_000_000)), - [128, 152, 247, 248, 188, 255, 255, 255, 255, 1] - ); - assert.deepEqual( - Array.from(writeVarint(18_000_000_000)), - [128, 232, 136, 135, 67] - ); -}); diff --git a/docs/THIRD_PARTY_MODULES.md b/docs/THIRD_PARTY_MODULES.md index ebb5bba..8a0dea5 100644 --- a/docs/THIRD_PARTY_MODULES.md +++ b/docs/THIRD_PARTY_MODULES.md @@ -1,26 +1,20 @@ # Third-party proxy modules -The files under `Resources/ThirdPartyProxyModules/` are project-owned module -definitions used by the App's default domestic-mirror subscription path. Their -executable scripts are built from `ThirdParty/WlocScripts/src/` and checked in -under the versioned `ThirdParty/WlocScripts/dist/v1/` directory. +Third-party proxy mode (Surge / Quantumult X / Loon / Shadowrocket / Stash / +Egern) now relies entirely on the upstream +[Yu9191/wloc](https://github.com/Yu9191/wloc) modules. The App no longer +maintains or ships project-owned module/script copies, so it never drifts +from the upstream protocol. -The direct GitHub Raw variants are stored under -`ThirdParty/WlocScripts/modules/direct/`. The Settings switch selects which -module URL the App copies: +## Subscription addresses -- enabled by default: `gh-proxy.org` in front of GitHub Raw; -- disabled: GitHub Raw directly. +The App builds each client's module subscription URL directly from the +upstream repository: -The App appends the module version as a query parameter, such as `?v=1.0.1`. -Increment this value whenever an existing module path changes so proxy clients -do not reuse a previously cached subscription body. - -The Apple WLOC response rule and MITM hostname list cover all known network -location endpoints: `gs-loc.apple.com`, `gs-loc-cn.apple.com`, -`gsp-ssl.ls.apple.com`, `bluedot.is.autonavi.com`, and -`bluedot.is.autonavi.com.gds.alibabadns.com`. The settings endpoint -(`/wloc-settings/*`) stays scoped to the `gs-loc` hostnames. +- default mirror (gh-proxy): + `https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/` +- direct: + `https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/` | Module file | Client | |---|---| @@ -30,35 +24,17 @@ location endpoints: `gs-loc.apple.com`, `gs-loc-cn.apple.com`, | `wloc.lpx` | Loon | | `wloc.stoverride` | Stash | -Egern reuses the Surge module. Stash imports `.stoverride` directly. +No `?v=` cache-bust is appended: the URL points at upstream's latest content, +and re-importing the subscription in the proxy client re-fetches it. -Both script entry points are owned by this repository: +## Script protocol -- `wloc.js` patches Apple WLOC response bodies; -- `wloc-settings.js` implements `/wloc-settings/save` and - `/wloc-settings/version`. +The upstream `wloc.js` patches Apple WLOC responses and reads coordinates from +the `wloc_settings` persistent key or the module `argument` config. The +upstream `wloc-settings.js` implements `wloc-settings/save` (query/clear/save) +using `lon`/`lat`/`acc`/`randomRadius` parameters. -The generated scripts target ES2017 and avoid hard dependencies on `BigInt`, -optional chaining, nullish coalescing, `globalThis`, `URLSearchParams`, and -`Object.fromEntries`. This keeps them usable by older proxy-client releases -that still implement the established module syntax, binary response body, -`$done`, and persistent-storage APIs. - -Already-installed legacy modules that point to another repository are not -silently migrated because their remote script URL is outside this project's -control. Those users must re-import the project-owned module before using the -versioned protocol and motion setting. - -Current bundled module SHA-256 values: - -```text -1987adf691738544ee44ee67749872ac3ef36efbe54cd8751cf874eaa029d7f4 wloc.conf -5562020a8de1a25e3162584fa77c50d8c7e1052cdc08c068180fc2e46d69e7da wloc.lpx -cad43570ffb0a16cf3ba7d06d9c0d99ea1b7994ff665eea192011961f1385e87 wloc.module -96be7cc7e710436ffa320ccc2e2d45d55d9db7fe26dc6a5bab1fa54333102a99 wloc.sgmodule -0cefbabf294acff3382c575578ed7e4c84fa16242cf0808df581aec004ca8058 wloc.stoverride -``` - -The project acknowledges [Yu9191/wloc](https://github.com/Yu9191/wloc) as a -reference for earlier WLOC implementation ideas. That acknowledgement is not -an executable dependency or subscription source. +The App's third-party save sends `lon`/`lat`/`acc`, matching the upstream +script. Motion-state simulation (fields 11/12) is **not** implemented by the +upstream scripts and is unavailable in third-party mode; it remains available +in APP mode (built-in proxy).