Commit Graph

107 Commits

Author SHA1 Message Date
wangjia fe16f89bc3 feat(client): 账户子页 desktop 内容区下钻(档1)
- desktop 上套餐选择/兑换购买/设备管理改为内容区下钻(侧栏保留、shell 顶栏带
  返回箭头),不再全屏 push 盖住侧栏;mobile/tablet 仍全屏 push
- _SubScaffold 加 embedded 参数(true 只返回内容),PlansScreen/DevicesScreen/
  RedeemScreen 透传;NavView 加 devices + kAccountSubViews 集合
- desktop_shell 渲染子页(embedded) + 顶栏 onBack→account;account_page 按
  formFactor 选下钻(切 navView)或 push
- desktop 整页 golden 补 套餐/兑换/设备 三子页(共 8 页)

测试: flutter test 84 通过(+3 子页 golden), analyze 0 error

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 12:36:14 +08:00
wangjia 4886416e8b feat(client): settings 完整版(开关组) + 联系/设置整页 golden
- settings_page 补全对照 dapp.jsx DSettings: 功能开关组(开机自启/智能分流/
  Kill Switch, 本地演示态) + 配置组补检查更新行; 改 ConsumerStatefulWidget 持开关态
- l10n 新增 autostart/smartRoute/killSwitch(+Sub) + checkUpdate(中英)
- desktop 整页 golden 补 联系页 + 设置页(凑齐可截 5 页: 节点/统计/账户/联系/设置)

测试: flutter test 81 通过(+2 golden), analyze 0 error

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 12:22:28 +08:00
wangjia 22e4eb1ead feat(client): desktop 其他页精还原 + 整页 golden 闸
- stats_page 对照 dapp.jsx DStats 精对齐: padding 32/8、指标卡 padding 16/18·
  gap 14·值 mono 24、柱状图卡 padding 20/18·标题 13、柱 height 140(容 bar 86)·宽 34
- nodes_page 对照 DServers 微调: padding top 4、网格 gap 10(智能卡置顶保留, 符 §5)
- 新增 test/golden/desktop_pages_golden_test.dart: 用 macOS 平台覆盖 + 920×600 surface
  让 formFactor=desktop, 截 节点/统计/账户 三页整页 golden 作回归闸(连接页含旋转动画
  由组件级 golden 覆盖, 不在此截)

测试: flutter test 79 通过(+3 desktop 整页 golden), analyze 0 error

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 12:02:24 +08:00
wangjia 1340b477a7 feat(client): 字体可注入 + 启用 golden 视觉回归闸
- pangolin_theme: PangolinFonts 加 useBundled 开关 + sora/manrope/jetBrainsMono/
  manropeTextTheme 封装(生产走 google_fonts / 测试走 bundled family);
  PangolinText 与 _build 改调封装,移除散落的 GoogleFonts 直接调用
- test/flutter_test_config.dart: 全测试前置,FontLoader 注册 test/fonts 真实
  Sora/Manrope/JetBrainsMono + Lucide 图标字体,置 useBundled=true
- 启用 test/golden(去 @Skip):连接键三态+推荐卡+额度卡×明暗 12 基准图
- tool/visual-diff.md: 标注 golden 严格闸已落地

测试: flutter test 76 通过(原 64+1skip → 76 无 skip), analyze 0 error

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 11:36:50 +08:00
wangjia 281a6261fb feat(client): tablet 外壳 + 视觉 diff 工作流文档
- tablet_shell: 侧栏 232·4 项触控(minHeight 48, 字号 15) + 顶栏无主题切换 + 连接页双栏
  对照 ui_kits/tablet/tabapp.jsx;home_shell 分发 tablet→TabletShell
- nav_sidebar 加 dense 参数(desktop 紧凑 / tablet 触控)
- content_top_bar 加 showThemeToggle + titleSize 参数
- tool/visual-diff.md: 截图 diff 工作流(design-distill 工具)+ 实现侧三路径
  现状(golden 被 google_fonts 阻塞 / screencapture 受 macOS chrome 限制)+ 推荐

注: tablet 形态在 macOS 桌面平台不触发(只 desktop/mobile),运行验证需 iPad。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:38:18 +08:00
wangjia 4dc4127252 feat(client): 三端布局架构 + macOS 桌面端 + app 图标
三端布局(mobile/tablet/desktop):
- core/responsive/form_factor.dart 形态判定 + shell/ 分发器(home_shell→desktop/mobile)
- desktop_shell 对照 ui_kits/desktop/dapp.jsx: 侧栏204·6项 + 套餐卡 + 顶栏(标题/状态/主题切换) + 连接页居中单列
- 新增组件 nav_sidebar / plan_badge_card / content_top_bar / bottom_tab_bar
- 新增一级页 contact_page / settings_page; navigation_provider(NavView)
- 删除旧 widgets/home_shell.dart(逻辑迁入 shell/)

macOS 桌面端:
- 窗口默认 920×600 + 最小 720×560(MainFlutterWindow.swift)
- app 图标替换为穿山甲(AppIcon.appiconset 全套, 由 app-icon.svg 渲染)

其余(本会话):
- Phase2 接线: auth_api/token_store/auth_provider/vpn_bridge_provider + 真实 connection/nodes
- lucide_icons 兼容补丁(packages/lucide_icons_patched) 修复 IconData final 报错
- 测试修复: connect_passthrough(UTF-8) / harness / golden @Skip
- l10n 新增 settingsTitle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:23:20 +08:00
wangjia 71a4cddf0b sync(edge): jiu.conf 反向同步线上版本(OG 注入 + /downloads + /app/product 重写)
EC2 上的 jiu.conf 早于本次已含商品页反代后端注入微信 OG 标签、
/app/product 重写、/downloads 安装包下载,但从未同步回仓库。
本次以线上为准回灌,避免下次 deploy.sh 用旧版覆盖线上配置。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 08:38:27 +08:00
wangjia cadd527680 feat(backend): 挂载 /v1 API + 实现 nodes/connect 端到端
- 新增 internal/dpcred 包,统一 DeriveHy2Password + DefaultFlow
  agentd 与 HTTP connect handler 共享同一实现
- 新增迁移 000011:nodes 表拆分 reality_prk 私钥 / reality_pbk 公钥
  reality_short_id;修正 handler_grpc.go 使用私钥字段
- 新增迁移 000012:connect_credentials 持久化凭证
  实现 CredentialsForNode 修复 agent 重连 resync 原先返回空的桩
- 扩展 NodeStore 接口:ListUp / EntitlementForUser /
  PersistCredential / DeleteCredential;同步 grpc_test.go mock
- 新增 httpapi/nodes.go:GET /nodes、POST /nodes/id/connect
  Hub.Push + PersistCredential + 渲染完整 sing-box client 配置 JSON
  POST /nodes/id/disconnect
- 新增 httpapi/account.go:GET /me、GET /plans、GET /notices
- 新增 httpapi/clientconfig.go:BuildClientConfig 服务端渲染
- 重写 cmd/server/main.go:手写 chi public/protected 分组
  nodes.Service/Hub 在 main 构造并共享;SMTPMailer/LogMailer

go build ./... && go vet ./... && go test ./... 全部通过

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 23:09:56 +08:00
wangjia b35bfe10dc merge: 自动更换编排 + 养机灰度 [tsk_mxEFSvKHX64G] 2026-06-15 22:05:22 +08:00
wangjia 7627c8a195 feat(scheduler): 自动更换编排 + 养机灰度 [tsk_mxEFSvKHX64G]
实现 15E 任务:Replacer 状态机(pending→creating→probing→activating→draining_old→done/failed)
+ Grayscale 权重梯度(10→25→50→75→100,每步 6h)。

关键设计:
- Redis JSON 记录(sched:replace:{uuid})+ SetNX 崩溃安全,防止重复 CreateNode
- 幂等 key:attempt 0 = uuid,retry N = uuid:retry:N
- probePass:国内 ≥2/3 ISP 通过 + 海外 L1 全通,连续 2 Tick 才晋级
- 探活超时 15min → failProbeAttempt → 轮换 provider;3 次失败 → failed + NotifyFault
- StubBreaker(15F 未就绪);NewReplacer(Config{}) 依赖注入
- 6 个集成测试全部通过(happy path / max-retry / crash recovery / queue idempotency / grayscale)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 21:26:07 +08:00
wangjia fb267e1114 merge: iOS 端 PoC M3 + entitlement 申请 [tsk_F5r4Kt9m_wQU] 2026-06-15 20:56:46 +08:00
wangjia be44ef1226 merge: iOS M3 PoC — PacketTunnelProvider + entitlement [tsk_nsobbj_rJdy0] 2026-06-15 20:56:46 +08:00
wangjia 2ebd8c4054 merge: Android M2 VPN 隧道 PangolinVpnService [tsk_w1NvZdmwGPyd] 2026-06-15 20:56:37 +08:00
wangjia 323e1da04d merge: 桌面端 PoC M1 — sing-box 子进程 TUN 接线 [tsk_SLCsjNgtmng3] 2026-06-15 20:56:37 +08:00
wangjia 58e2bb0190 merge: gRPC server + hub + 跨实例 pub/sub [tsk_mF5RWPEwngai] 2026-06-15 20:56:28 +08:00
wangjia 5d54aa2fb2 merge: 判定引擎 signals+rules+streak [tsk_OYEiDCzM9_0Y] 2026-06-15 20:56:22 +08:00
wangjia 690f990722 merge: devices + 订阅校验中间件 [tsk_x7wrlA87orsY] 2026-06-15 20:56:14 +08:00
wangjia 2c182fcdcf merge: probe ingest endpoint + Redis store [tsk_rBPr0Xuy10bz] 2026-06-15 20:55:32 +08:00
wangjia bfecb4314c merge: gRPC server + hub + 跨实例 pub/sub [tsk__58l3wTLvaSn] 2026-06-15 20:53:25 +08:00
wangjia bf3b6fd210 merge: apierr + idgen + CONVENTIONS.md [tsk_nI2T8qpcoier] 2026-06-15 20:51:08 +08:00
wangjia 1648871a2a docs(infra): 补全拓扑图 CSS;todo: 更新任务进度
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 19:26:08 +08:00
wangjia b2cb20be47 chore(design): 删除 design/flutter fork,更新 .gitignore
删除 design/flutter/(21 个文件):
- Flutter token/组件的 canonical 实现已在 client/lib/
- Token 数值层改由 design/codegen/gen_flutter_tokens.mjs codegen
- 设计规格由 design/preview/ specimens + SKILL.md 表达

.gitignore 新增:
- client/android/local.properties、client/android/app/src/main/java/
- client/ios/Flutter/ephemeral/、client/pubspec.lock
- .claude/worktrees/(临时工作目录)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 19:11:01 +08:00
wangjia b04cef3cc4 chore(design): 建立 token 单源管线,删除 design/flutter fork
- 新增 design/codegen/gen_flutter_tokens.mjs:
  从 colors_and_type.css codegen 出 client/lib/pangolin_tokens.gen.dart
  覆盖 PangolinColors/Spacing/Radius/Motion/Shadow(纯数值层)
- 重构 client/lib/pangolin_theme.dart:
  删除手抄数值,import+export pangolin_tokens.gen.dart
  保留实现层 PangolinScheme/PangolinText/PangolinTheme/PangolinContext
  19 个引用方零改动,对外 API 完全不变
- 新增 web/usercenter/scripts/build-tokens.mjs:
  仿 website 样板,prebuild/predev 自动从 css 生成 public/colors_and_type.css
  去除 usercenter 手抄副本的漂移风险
- 更新 CLAUDE.md:补 token codegen 使用说明与单源模型
- 更新 design/SKILL.md:移除已删 flutter/ 引用,补 codegen 入口

验证:改 clay-500 → 三端 codegen 同步变化;flutter analyze 无新增 error。
注:design/flutter/ fork 手动删除(git rm 需用户执行)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:27:22 +08:00
wangjia 88757b2ac4 feat(detect): 判定引擎 signals+rules+streak (tsk_OYEiDCzM9_0Y)
新建 server/internal/scheduler/detect/ 包,实现任务 15D:

## 核心模块

**lifecycle.go**
- 定义 LifecycleService Go interface(ListNodes / TransitionStatus / SetWeight /
  BumpVersion / GetLoad / GetLoadHistory),供 #5 真实实现,内含乐观锁语义(0 行
  受影响即本周期放弃,幂等可重入)
- 提供 MockLifecycle 内存 stub(SetConflict / Events 等测试辅助方法)

**signals.go**
- computeSignals:读 SnapshotsByNode,归一化为 NodeSignal
  - domesticFailISPs:按 ISP 聚合(stripPrefix "3rd-" 合并 15B/15C 同 ISP 视角),
    L3 失败权重最高(L3 挂即记该 ISP 失败,即使 L1/L2 通),无数据 vantage 不计分母
  - overseasOK:境外对照点全部 L1 通为真
  - trafficDropPct:15min 前后半段在线数降幅百分比
- ProbeSnapshotter interface(解耦 probe.Store,便于 mock)

**rules.go**
- DetectConfig:全量阈值常量(DomesticFailNumerator/Denominator=2/3、
  SuspectStreakMin=2、TrafficDropThreshold=80%、TrafficBaselineMin=20、
  ConfirmedStreakMin=6、RecoverStreakMin=2、SuspectWeight=10)
- isSuspectTriggered:整数算术避免浮点误差(fail×den >= total×num)
- isFault / effectiveSuspectStreakMin / isTrafficWarning 规则助手

**streak.go**
- StreakStore:Redis hash detect:streak:{node}(fail_streak / suspect_streak /
  recover_streak),TTL 2h(宽于 6×5min=30min),进程重启后自动恢复

**engine.go**
- Engine.Tick(ctx):列举 up/blocked_suspect 节点,按节点依序运行五条规则
- Rule 1 suspect:≥2/3 ISP 失败 + 境外正常 → 连续 2 周期 → up→blocked_suspect,
  weight→10,写 probe:freq:{node}=60(1min 提频标记),BumpVersion
- Rule 2 流量预警:trafficDrop≥80% + 基线≥20 → suspect 门槛放宽至 1 周期
- Rule 3 confirmed:blocked_suspect 连续 6 周期 → blocked_confirmed → down(跳过
  draining),入队 detect:replace:queue(JSON: nodeId + replacementUuid)
- Rule 4 recover:suspect 期间境内连续 2 周期恢复 → up(weight 交 15E warmup)
- Rule 5 fault:境内+境外同时失败 → 不迁移,调 Notifier.NotifyFault(log stub)
- Notifier interface + LogNotifier stub(15G 就绪前输出 slog.Warn)

**engine_test.go**(19 个测试,全部通过)
- 表驱动覆盖:恰好 2/3 失败、1/3 失败不触发、9min vs 10min streak、
  流量预警 1 周期即 suspect、suspect 第 6 周期 confirmed、境内外同挂 fault 不迁移、
  恢复 2 周期回 up、3rd- 前缀合并、L3 覆盖 L1/L2
- 乐观锁冲突:SetConflict → 放弃本周期 → 无副作用 → 解冲突后正常重试
- 进程重启:同一 miniredis,新 Engine 读取已存在 streak 继续计数不误清零
- 产出验证:replace queue JSON 结构、probe:freq 标记、weight 变更、事件 detail 字段

**migrations/000010_node_blocked_statuses.{up,down}.sql**
- 扩展 nodes.status ENUM 加入 blocked_suspect / blocked_confirmed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 20:15:13 +08:00
wangjia 7a1715bd09 merge: iOS 端 PoC M3 + entitlement 申请 [tsk_F5r4Kt9m_wQU]
解决合并冲突(原任务 tsk_nsobbj_rJdy0 分支 maestro/tsk_nsobbj_rJdy0):
两分支修改文件集合无重叠,手动 apply iOS PoC commit (e129f09)。

变更内容:
- client/ios/PacketTunnel/PacketTunnelProvider.swift:完整 M3 实现
  (startTunnel/stopTunnel/handleAppMessage/LibboxPlatformInterface)
- client/ios/PacketTunnel/MemoryMonitor.swift:新增 NE 进程内存打点
- client/ios/PacketTunnel/PacketTunnel.entitlements:NE + App Group 权限
- client/ios/Runner/Runner.entitlements:主 App NE + App Group 权限
- client/ios/Runner/VpnManager.swift:红线词修复 + App Group 缓存 + NEVPNStatus 订阅
- client/ios/Runner/Info.plist:NSVPNUsageDescription 红线词修复
- client/ios/PacketTunnel/Info.plist:CFBundleDisplayName 红线词修复
- client/ios/Runner.xcodeproj/project.pbxproj:
  CODE_SIGN_ENTITLEMENTS 添加到全部 build config;MemoryMonitor 加入 Sources
- app/kernel/build-ios.sh:M3 内存裁剪决策记录
- doc/ne-entitlement-申请指引.md:新增 NE entitlement 申请完整指引

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 20:14:32 +08:00
wangjia 8ed1cfcc75 merge: gRPC server + hub + 跨实例 pub/sub [tsk_mF5RWPEwngai]
Resolve merge conflict between maestro/tsk__58l3wTLvaSn (gRPC nodes feature)
and main (admin backend + JWT config + OpenAPI routes):

- server/cmd/server/main.go: keep admin backend (startAdminIfConfigured) AND
  add gRPC agent server (startGRPC) — both coexist as independent optional
  listeners governed by their respective env vars
- server/internal/config/config.go: keep JWT fields (JWTPrivateKeyPath /
  JWTKeyID / JWTPublicKeys) AND add gRPC fields (GRPCAddr / CAKeyPath /
  CACertPath / GRPCCertPath / GRPCKeyPath)
- server/internal/nodes/: add all new files from tsk__58l3wTLvaSn
  (hub.go, hub_test.go, load.go, handler_grpc.go, grpc_test.go,
   service.go, store.go) — 18 tests, all passing

Test: go test ./internal/nodes/... → PASS (18/18)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 20:10:01 +08:00
wangjia 628440b3f9 style(pangolin): gofmt store/mysql.go(import 排序)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 18:52:30 +08:00
wangjia 20a1f8e2af merge: 管理端最小后台 + TOTP 双因素 [tsk_SCMtcGF4F434]
以 main 现有 cmd/server 设计为准(保留 probe + /v1 路由),叠加分支新增:internal/admin/ 自包含后台(session/2FA/IP 白名单/审计/模板)、internal/totp/ + cmd/adminctl/(TOTP + 管理员创建 CLI)、cmd/server/main.go 的 startAdminIfConfigured()(opt-in:ADMIN_SECRET_KEY+DB_DSN 才起)。统一 getenvDefault 命名;go.mod 取 HEAD + go mod tidy(x/term 提为直接依赖);admins 迁移与 main 既有 000008_provision 撞号,重排为 000009_admins。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 18:47:38 +08:00
wangjia 03b00b9a0f merge: config + store(UTC DSN) + 自动迁移 [tsk_zRA6fGU1JuHj]
以 main 现有 config 设计为准(FromEnv/*Config/单 DSN/RS256),移植分支新增的 store 层:mysql.go 的 Open(*config.Config) 改用 cfg.DSN、结构化强制 UTC + 会话时区断言;migrate.go + migrations/embed.go 提供 golang-migrate 嵌入式迁移;cmd/migrate/main.go 实现 migrate CLI(读 DB_DSN)。未采用分支的 Load()/PANGOLIN_ 前缀/MySQL 拆分字段(与 main 设计冲突)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 18:42:46 +08:00
wangjia e91f0063e0 chore(pangolin): 还原批量合并误并入的 doc/todo WIP(回退到 WIP 前,WIP 抽回为未提交) 2026-06-13 18:06:53 +08:00
wangjia 3c6a8a517a feat: 桌面端 PoC M1 — sing-box 子进程 TUN 接线 [tsk_SLCsjNgtmng3]
实现桌面端(macOS PoC)内核子进程管理,打通 sing-box TUN 模式全链路:

### client/lib/bridge/kernel_process.dart(完整实现,替换原有 stub)
- ClashApiClient: HTTP 客户端,支持 /connections / /proxies / /traffic(SSE) / /traffic(plain)
  - Bearer Token 鉴权;getConnections 用于就绪探测与流量统计;getTraffic SSE 读首帧后断开
- KernelProcess 接口: 新增 statusStream / statsStream 至接口定义
- DesktopKernelProcess:
  - spawn(configPath): 解析 Clash API 端口/secret → sudo sing-box run(macOS PoC)→
    轮询 /connections 等待就绪(20s 超时)→ emit connecting→on
  - kill(): SIGTERM + 等待 gracePeriod(5s) → SIGKILL → emit off
  - 意外退出: emit error(UI 可一键重连,不崩溃)
  - 统计轮询: 每秒 GET /connections,差分算 uploadSpeed/downloadSpeed
  - 二进制解析: ENV > exe同目录 > macOS Bundle Resources > 开发目录 > /usr/local/bin
- 辅助函数: generateClashApiPort(高位随机)、generateClashApiSecret(32B hex)

### client/lib/bridge/desktop_vpn_bridge.dart(新文件)
- DesktopVpnBridge implements VpnBridge:
  - start(configJson): injectClashApi(注入随机端口+secret)→ writeConfig(0600)→ kernel.spawn
  - stop(): kernel.kill(5s)
  - statusStream / statsStream: 代理 KernelProcess 事件流
  - selectOutbound: Clash API PUT /proxies/{group}
  - getActiveOutbound: 从 /proxies 读 now 字段
  - configDirOverride: 测试注入支持
  - injectClashApi: 静态方法,尊重已有 clash_api 配置,合并保留 experimental 其他字段

### client/test/bridge/kernel_process_test.dart(新文件)
- ClashApiClient 完整测试: headers / 解析 / PUT body / DELETE / 非 200 抛 HttpException
- generateClashApiPort / generateClashApiSecret 生成范围和格式测试
- DesktopVpnBridge.injectClashApi: 注入 / 尊重已有 / 保留字段 / 保留其他 experimental / 非法 JSON
- DesktopVpnBridge + FakeKernelProcess 集成: connecting→on / stop→off / statsStream / 意外退出 / spawn失败

### app/kernel/poc/(新目录)
- reality_client.config.json.tmpl: VLESS+REALITY+TUN 客户端配置模板
  - TUN inbound: auto_route + strict_route(macOS kill-switch 基础保护)
  - DNS: 防泄露(remote via VPN + cn 直连)
  - experimental.clash_api: 随机端口 + secret 占位符
- gen-poc-config.sh: 渲染模板为可用 JSON(从环境变量读 REALITY 参数)
- README.md: 完整 PoC 接线与 M1 验收步骤

提权说明(macOS PoC):
  · sudo 提权(开发机需配 /etc/sudoers.d/pangolin-singbox 或有 sudo 缓存)
  · 正式版: SMJobBless Helper + 公证(BACKLOG-11D-HELPER)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:53:50 +08:00
wangjia 83c23f968a feat(android): 实现 M2 Android VPN 隧道 — PangolinVpnService 接入 libbox [tsk_w1NvZdmwGPyd]
接线 11B libbox.aar + 11C Dart 桥接骨架,完成 Android 端 M2 PoC:

### PangolinVpnService(完整实现)
- `handleStart(configJson)` → 后台线程 → `Libbox.newBoxService(platformInterface, json)` → `start()`
- 内 inner class `PangolinPlatformInterface` 实现 libbox `PlatformInterface`:
  - `openTun(TunOptions)`: 调用 `VpnService.Builder()` 配置地址/MTU/路由,`establish()` 取 TUN fd
  - `autoDetectInterfaceControl(fd)`: `VpnService.protect(fd)` 防环路
  - 安全 getter 兜底:TunOptions 方法名与 libbox.aar 不符时降级到 PoC 默认值
- 统计双轨:优先 libbox `CommandClient`(status/traffic)→ 失败时退回 `TrafficStats` 定时器
- 幂等 `doStop()`:commandClient.disconnect → boxService.close → tunPfd.close → stopForeground
- `onRevoke()` 推 error 状态再 doStop,不留假 on

### VpnEventBus
- 新增 `currentStatus` 字段(volatile),供 `getStatus()` 一次性查询

### MainActivity
- `VpnService.prepare()` 权限流程:未授权 → 弹 prepare Intent → `onActivityResult` 后启动
- `getStatus()` 从 `VpnEventBus.currentStatus` 返回真实状态
- 首次连接时引导 `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`(后台存活)

### AndroidManifest.xml
- `android:foregroundServiceType="specialUse"`(Android 14 API 34 强制要求)
- 新增权限:`FOREGROUND_SERVICE_SPECIAL_USE`、`REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`

### build.gradle
- `implementation files('../../../app/kernel/dist/android/libbox.aar')`(条件存在检查)
- `minSdkVersion` 提升至 21(gomobile -androidapi 21 要求)

### 新增文件
- `assets/poc_config.json`: PoC 单节点测试配置模板(含占位符 __NODE_HOST__ 等)

通知文案已过红线词扫描:「加速已开启」「正在连接…」「穿山甲加速」,无 VPN/翻墙等禁词。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:45:13 +08:00
wangjia e129f094c2 feat(ios): M3 PoC — PacketTunnelProvider + entitlement + 内存监控 [tsk_nsobbj_rJdy0]
### 新增文件
- client/ios/PacketTunnel/MemoryMonitor.swift
  周期打点 NE 进程内存(os_proc_available_memory iOS13+ / mach_task_basic_info 兼容
  iOS12);每 10s 采样一次;availableMemory < 5MB 时输出 ⚠️ 警告;提供
  summaryString() 供 handleAppMessage/getMemory IPC 返回。

- client/ios/PacketTunnel/PacketTunnel.entitlements
  Extension 的 NE + App Group entitlement 文件;申请说明写入注释。

- client/ios/Runner/Runner.entitlements
  主 App 的 NE + App Group entitlement 文件;申请说明写入注释。

- doc/ne-entitlement-申请指引.md
  完整申请操作手册:Bundle ID 清单、Apple Developer Portal 步骤、
  专项审批英文申请文案(已脱敏,无红线词)、审批 lead time 说明。

### 修改文件
- client/ios/PacketTunnel/PacketTunnelProvider.swift
  完整 M3 实现(替换原骨架 TODO):
  · startTunnel:解析 configJson(options/App Group 两路回退)→
    buildNetworkSettings → setTunnelNetworkSettings → setupTunBridge
    → startLibbox → startPacketBridging → memoryMonitor.start()
  · stopTunnel:memoryMonitor.stop() → stopLibbox → closeTunBridge
  · handleAppMessage:JSON IPC 协议(getStatus/getMemory/selectOutbound)
  · LibboxPlatformInterface 扩展(#if canImport(Libbox)):
    openTun 返回 socketpair libbox 侧 fd,autoDetectInterfaceControl no-op,
    writeLog 转 os.log,useProcFS 返回 false
  · packetFlow 双向桥接(readFromPacketFlow/writeToPacketFlow 循环)
  · parseTunAddress/parseDNSServers 从 JSON 提取网络参数

- client/ios/Runner/VpnManager.swift
  · localizedDescription "穿山甲 Pangolin VPN" → "Pangolin 加速"(红线词修复)
  · 新增 appGroup 常量(group.com.pangolin.pangolinVpn)
  · start() 写入 App Group UserDefaults 缓存 configJson

- client/ios/Runner/Info.plist
  NSVPNUsageDescription "穿山甲使用 VPN…" → "Pangolin 使用网络加速通道…"(红线词修复)

- client/ios/PacketTunnel/Info.plist
  CFBundleDisplayName "穿山甲 Tunnel" → "Pangolin 加速通道"(红线词修复)

- client/ios/Runner.xcodeproj/project.pbxproj
  · Runner + PacketTunnel 所有 build config 添加 CODE_SIGN_ENTITLEMENTS
  · 新增 MemoryMonitor.swift 到 PacketTunnel Sources 构建阶段
  · 新增 Runner.entitlements / PacketTunnel.entitlements / MemoryMonitor.swift
    文件引用及分组

- app/kernel/build-ios.sh
  添加 M3 内存裁剪决策记录:gVisor→grpc→QUIC 优先裁减顺序及内存估算,
  等待真机 10min 压测结果后更新最终结论

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:44:11 +08:00
wangjia ed5eabea58 merge: 第三方拨测 API 适配 [tsk_-WC9smQK7YRt] 2026-06-13 17:32:26 +08:00
wangjia bf1717a369 merge: 控制面联调 M6 [tsk_nuoKSM4Vt-zK]
# Conflicts:
#	.gitignore
#	client/lib/widgets/home_shell.dart
#	client/lib/widgets/server_tile.dart
#	client/pubspec.yaml
2026-06-13 17:32:26 +08:00
wangjia b89aeb0905 merge: app/kernel 构建管线(AAR/XCFramewor [tsk_d8KmVEzJTCjl]
# Conflicts:
#	.gitignore
2026-06-13 17:32:26 +08:00
wangjia b05d7ed416 merge: oapi-codegen 接线 + 生成代码入库 [tsk_Kp80nvHV2yLc]
# Conflicts:
#	server/Makefile
#	server/cmd/server/main.go
#	server/go.mod
#	server/go.sum
2026-06-13 17:32:25 +08:00
wangjia 605bfa1ec9 feat(nodes): gRPC server + Hub + Redis pub/sub cross-instance delivery (tsk__58l3wTLvaSn)
Implements AgentService gRPC server with all 6 RPCs (Enroll/Register/Heartbeat/
Subscribe/Ack/ReportUsage), Hub command routing with Redis ZSET at-least-once
persistence and cross-instance pub/sub delivery, LoadCache for node:load metrics,
NodeStore SQL interface + MySQL implementation, and full mTLS gRPC listener in main.

Integration tests: 18 tests covering full Enroll→Register→Heartbeat→Subscribe→Ack
flow, reconnect resume with last_command_id, and cross-instance pub/sub delivery
via bufconn + miniredis + mockNodeStore + real mTLS certificates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:30:42 +08:00
wangjia 8de32eb611 merge: 弹性节点基建:Terraform + 一键更换 [tsk_6u0FxmbC7Yeq]
# Conflicts:
#	.gitignore
#	infra/cloud-init/node.yaml.tmpl
2026-06-13 17:30:37 +08:00
wangjia 6f6cb97509 merge: Flutter 逐屏还原 + iPad 断点 [tsk_gpPXi-icyeOE]
# Conflicts:
#	client/lib/widgets/connect_button.dart
#	client/lib/widgets/home_shell.dart
#	todo/todo.html
#	todo/todo.json
2026-06-13 17:30:36 +08:00
wangjia 4e2a444572 merge: usage 用量 + 广告解锁 [tsk_1taxhtV2k3RP]
# Conflicts:
#	server/internal/apierr/apierr.go
2026-06-13 17:30:36 +08:00
wangjia 34c9909608 merge: auth 模块 [tsk_2PFfyviECIXh]
# Conflicts:
#	server/go.mod
2026-06-13 17:29:49 +08:00
wangjia a47121f99b feat(probe): add third-party dial-test adapter for 15C (tsk_-WC9smQK7YRt)
Implements ProberAgent interface + Alibaba Cloud CloudMonitor Synthetic adapter:
- ProbeTarget / VantageResult types aligned with 15A schema (L3 always nil)
- AliyunSyntheticAgent: CreateSiteMonitor → poll → map to VantageResult
- ISP vantage uses "3rd-{ISP}" prefix; Redis key probe:{node}:CN:{region}:3rd-*
- Degradation: API error/rate-limit/timeout → skip vantage, log + counter (no write)
- RunOnce(ctx, targets) shape for 15H assembly wiring
- Credentials via cfg only (env-var injection, zero hardcoded secrets)
- 6 new tests (mock vendor server): normalization, rate-limit, timeout, Redis keys,
  failed-target L1, no-credentials check; all 22 probe-package tests pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:21:43 +08:00
wangjia 9423069a0b merge: maestro/tsk_iP5-_Ztq5THx [tsk_iP5-_Ztq5THx] 2026-06-13 17:04:18 +08:00
wangjia b456a8816f merge: maestro/tsk_V9IrIk2g5Q78 [tsk_V9IrIk2g5Q78] 2026-06-13 17:04:17 +08:00
wangjia 3bcf1d7b4c merge: maestro/tsk_NU9JuUweHWMt [tsk_NU9JuUweHWMt] 2026-06-13 17:04:17 +08:00
wangjia 96040c802f merge: maestro/tsk_3FIPC8lSnAfJ [tsk_3FIPC8lSnAfJ] 2026-06-13 17:04:17 +08:00
wangjia c642daec42 merge: maestro/tsk_acMYQ-Z-EIF_ [tsk_acMYQ-Z-EIF_] 2026-06-13 17:04:17 +08:00
wangjia 5a6b4a2dbb merge: maestro/tsk__R8M4jEw43JR [tsk__R8M4jEw43JR] 2026-06-13 17:04:17 +08:00
wangjia 5a485fbcf6 merge: maestro/tsk_FrRarw9YO8Nf [tsk_FrRarw9YO8Nf] 2026-06-13 17:04:17 +08:00