4992d916d0
MethodChannel pangolin/vpn (start/stop/getStatus/selectOutbound/ getActiveOutbound/setKillSwitch) + 双 EventChannel (status/stats) 契约定义在 lib/bridge/vpn_bridge.dart 头部注释冻结。 - Dart: VpnBridge 抽象接口 + VpnNativeBridge 原生实现 + VpnBridgeMock 假内核(含 connectDelay/statsInterval 可配参数) - Dart 桌面: kernel_process.dart KernelProcess/ClashApiClient 接口骨架 - Android: PangolinVpnService (VpnService 骨架 + 前台通知占位) + VpnEventBus 解耦总线 + MainActivity MethodChannel/EventChannel 注册 - iOS: PacketTunnelProvider (NEPacketTunnelProvider 骨架) + VpnManager (NETunnelProviderManager + NEVPNStatus 订阅) + AppDelegate 通道注册 + Xcode project.pbxproj 添加 PacketTunnel extension target - Widget: VpnStatus enum 迁移至 bridge/vpn_bridge.dart (+error 态), connect_button/home_shell 更新穷举匹配 - Test: test/bridge/vpn_bridge_mock_test.dart 覆盖 start→connecting→on →stop→off 序列、stats 周期推帧、selectOutbound 重连序列、schema 回测 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
113 lines
3.9 KiB
Dart
113 lines
3.9 KiB
Dart
// kernel_process.dart — 桌面端内核子进程管理接口
|
||
//
|
||
// 桌面平台(macOS / Windows / Linux)直接将 sing-box 二进制作为
|
||
// 子进程运行,通过其 Clash 兼容 REST API 进行控制。
|
||
//
|
||
// 本文件仅定义接口契约;具体实现由任务 11D(桌面 PoC)完成。
|
||
//
|
||
// 进程生命周期:
|
||
// spawn(configPath) → 子进程启动,REST API 开始监听
|
||
// kill() → 优雅停止(SIGTERM),超时后 SIGKILL
|
||
//
|
||
// Clash API 通信由 [ClashApiClient] 封装,基地址固定为
|
||
// http://127.0.0.1:9090(可覆盖)。
|
||
|
||
import 'dart:async';
|
||
|
||
// ── Clash API 客户端 ─────────────────────────────────────────────
|
||
|
||
/// sing-box Clash 兼容 REST API 客户端。
|
||
/// 实现由 11D(桌面 PoC)填充;本骨架仅定义调用约定。
|
||
class ClashApiClient {
|
||
ClashApiClient({this.baseUrl = 'http://127.0.0.1:9090'});
|
||
|
||
final String baseUrl;
|
||
|
||
/// GET /traffic — 流量统计
|
||
Future<Map<String, dynamic>> getTraffic() {
|
||
throw UnimplementedError('ClashApiClient.getTraffic — 11D 实现');
|
||
}
|
||
|
||
/// GET /proxies — 节点列表
|
||
Future<Map<String, dynamic>> getProxies() {
|
||
throw UnimplementedError('ClashApiClient.getProxies — 11D 实现');
|
||
}
|
||
|
||
/// PUT /proxies/{group} — 切换出口
|
||
Future<void> selectProxy(String group, String proxy) {
|
||
throw UnimplementedError('ClashApiClient.selectProxy — 11D 实现');
|
||
}
|
||
|
||
/// GET /connections — 实时连接
|
||
Future<Map<String, dynamic>> getConnections() {
|
||
throw UnimplementedError('ClashApiClient.getConnections — 11D 实现');
|
||
}
|
||
|
||
/// DELETE /connections — 关闭所有连接
|
||
Future<void> closeAllConnections() {
|
||
throw UnimplementedError('ClashApiClient.closeAllConnections — 11D 实现');
|
||
}
|
||
}
|
||
|
||
// ── 进程管理接口 ─────────────────────────────────────────────────
|
||
|
||
/// 桌面端内核子进程管理接口。
|
||
///
|
||
/// 使用方式:
|
||
/// ```dart
|
||
/// final kernel = DesktopKernelProcess();
|
||
/// await kernel.spawn('/path/to/config.json');
|
||
/// final traffic = await kernel.clashApiClient.getTraffic();
|
||
/// await kernel.kill();
|
||
/// ```
|
||
abstract class KernelProcess {
|
||
/// 启动内核子进程。
|
||
///
|
||
/// [configPath] 为 sing-box JSON 配置文件的绝对路径。
|
||
/// 返回的 [Future] 在进程就绪(REST API 可用)后 complete。
|
||
Future<void> spawn(String configPath);
|
||
|
||
/// 停止内核子进程。
|
||
///
|
||
/// 先发 SIGTERM,[gracePeriod] 内未退出则 SIGKILL。
|
||
Future<void> kill({
|
||
Duration gracePeriod = const Duration(seconds: 3),
|
||
});
|
||
|
||
/// 进程是否正在运行。
|
||
bool get isRunning;
|
||
|
||
/// Clash 兼容 REST API 客户端(进程运行时可用)。
|
||
ClashApiClient get clashApiClient;
|
||
|
||
/// 进程标准输出/标准错误日志流(用于调试面板)。
|
||
Stream<String> get logStream;
|
||
}
|
||
|
||
// ── 占位实现(防 analyze 报错)────────────────────────────────────
|
||
|
||
/// 占位实现,抛出 [UnimplementedError]。
|
||
/// 11D 替换为真实子进程实现时删除本类。
|
||
class DesktopKernelProcess implements KernelProcess {
|
||
@override
|
||
Future<void> spawn(String configPath) {
|
||
throw UnimplementedError('DesktopKernelProcess.spawn — 11D 实现');
|
||
}
|
||
|
||
@override
|
||
Future<void> kill({Duration gracePeriod = const Duration(seconds: 3)}) {
|
||
throw UnimplementedError('DesktopKernelProcess.kill — 11D 实现');
|
||
}
|
||
|
||
@override
|
||
bool get isRunning => false;
|
||
|
||
@override
|
||
ClashApiClient get clashApiClient =>
|
||
throw UnimplementedError('DesktopKernelProcess.clashApiClient — 11D 实现');
|
||
|
||
@override
|
||
Stream<String> get logStream =>
|
||
throw UnimplementedError('DesktopKernelProcess.logStream — 11D 实现');
|
||
}
|