mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-11-08 11:14:51 +00:00
106 lines
4.1 KiB
Swift
106 lines
4.1 KiB
Swift
import Foundation
|
||
|
||
// MARK: - 只读事实投影与静态缝
|
||
|
||
extension ProxyRuntimeController {
|
||
var isActiveTargetPresent: Bool { machine.activeTargetID != nil }
|
||
|
||
/// Agent 开关意图(持久化事实;开关 UI 的绑定来源)。
|
||
var agentIntentEnabled: Bool { settings.agentEnabled }
|
||
|
||
/// 系统代理开关意图(持久化事实;开关 UI 的绑定来源)。
|
||
var systemProxyIntentEnabled: Bool { settings.systemProxyEnabled }
|
||
|
||
/// 系统代理观察机的呈现事实门面:存储与发布在 `SystemProxyObserver`,
|
||
/// 此处只读投影供既有测试与诊断面沿用原名。
|
||
var systemProxyState: SystemProxyApplicationFacts { systemProxyObserver.systemProxyState }
|
||
var systemProxyApprovalRequired: Bool { systemProxyObserver.systemProxyApprovalRequired }
|
||
var systemProxyInspection: SystemProxyInspectionFacts {
|
||
systemProxyObserver.systemProxyInspection
|
||
}
|
||
|
||
/// Desired system proxy configuration:当前保存设置要求的代理值(ADR-0022)。
|
||
/// 观察机经 init 闭包读取同一事实源。`nil` 只表示尚无运行时文档。
|
||
var desiredSystemProxyConfiguration: SystemProxyConfiguration? {
|
||
guard let document = runtimeObservation else { return nil }
|
||
return proxyMode.systemProxyConfiguration(
|
||
for: document.listen, exceptions: settings.proxyExceptionList)
|
||
}
|
||
|
||
/// Stable app-facing projection for status-menu presentation. The menu does
|
||
/// not depend on this controller's nested state representation.
|
||
var runtimeFacts: ProxyRuntimeFacts {
|
||
let facts = ProxyRuntimeFacts(state: state)
|
||
return ProxyRuntimeFacts(
|
||
status: facts.status, isOn: facts.isOn,
|
||
failure: facts.failure
|
||
?? (proxyMode == .rule && settings.agentEnabled ? ruleApplicationFailure : nil))
|
||
}
|
||
|
||
static var defaultFirewallExecutableURLs: [URL] {
|
||
let bundle = Bundle.main.bundleURL
|
||
return [
|
||
bundle.appendingPathComponent("Contents/MacOS/ShadowsocksX-NG2Agent"),
|
||
bundle.appendingPathComponent("Contents/Helpers/sslocal"),
|
||
]
|
||
}
|
||
|
||
/// 诊断只读面:当前监听设置;监听地址在导出中只以回环/非回环
|
||
/// 两态呈现。
|
||
var listenSettings: ProxyListeningConfiguration { settings.listen }
|
||
|
||
/// 当前已部署 runtime 的完整监听身份。已停止或启动失败时没有有效的
|
||
/// runtime 例外,不能拿最后一次设置快照冒充仍在监听。
|
||
var effectiveRuntimeListenFacts: RuntimeListenFacts? {
|
||
let isListening: Bool
|
||
switch state {
|
||
case .running, .firewallBlocked:
|
||
isListening = true
|
||
case .off, .starting, .launchFailed, .requiresApproval, .serviceFailed:
|
||
isListening = false
|
||
}
|
||
guard isListening, let runtimeObservation else { return nil }
|
||
return runtimeObservation.listen
|
||
}
|
||
|
||
/// PID of the sslocal child that owns the active runtime document. A process
|
||
/// name alone is not sufficient to treat an occupied listener as replaceable.
|
||
var effectiveRuntimeListenerProcessID: Int32? {
|
||
guard effectiveRuntimeListenFacts != nil,
|
||
let runtimeObservation,
|
||
let receipt = runtimeFileStore.readRuntimeReceipt(),
|
||
receipt.deploymentID
|
||
== runtimeFileStore.receiptIdentity(for: runtimeObservation.intentSHA256),
|
||
processIsAlive(receipt.processID)
|
||
else { return nil }
|
||
return receipt.processID
|
||
}
|
||
|
||
/// 运行时契约的脱敏摘要(数量与协议元数据);契约缺失或无效返回 nil。
|
||
/// 诊断导出不读契约内容,只携带此摘要。
|
||
func runtimeDocumentSummary() -> String? {
|
||
runtimeFileStore.publishedSummary()
|
||
}
|
||
}
|
||
|
||
extension ProxyRuntimeController.AgentRunState {
|
||
/// Agent 运行状态的「在跑」投影(状态摘要与诊断口径):启动失败等未运行
|
||
/// 态视为未跑。
|
||
var isOn: Bool {
|
||
switch self {
|
||
case .off, .launchFailed, .serviceFailed:
|
||
false
|
||
case .starting, .running, .firewallBlocked, .requiresApproval:
|
||
true
|
||
}
|
||
}
|
||
}
|
||
|
||
extension ProxyRuntimeController: Activating {}
|
||
|
||
extension ProxyRuntimeController {
|
||
static func makeProxyMode(from settings: ProxyRuntimeSettings) -> ProxyMode {
|
||
ProxyMode.availableModes.first { $0.kind == settings.preferredMode } ?? .rule
|
||
}
|
||
}
|