Commit Graph
18 Commits
Author SHA1 Message Date
Qiu Yuzhou 7f1dbc6d94 refactor(ui): 系统代理暂停与不可读文案收成单一呈现常量 2026-10-04 12:37:06 +08:00
Qiu Yuzhou 4a0a72c6cf feat: observe and repair system proxy configuration (#73) 2026-10-01 23:29:56 +08:00
Qiu Yuzhou 020d7a62d9 ui(app): drop redundant mode badge from home mode card 2026-09-30 22:23:52 +08:00
Qiu Yuzhou d2a8d6105c feat(system-proxy): route writes through privileged LaunchDaemon helper
Implements issue #71 with ADR-0020 (supersedes ADR-0019's endpoint-signature
cleanup, startup cleanup, and health-gate cleanup).

- New ShadowsocksX-NG2SystemProxyHelper target: app-bundled root LaunchDaemon
  (SMAppService.daemon, on-demand via MachServices) that mechanically applies
  a closed typed configuration or executes an unconditional clear. Clients are
  accepted by code-signing bundle identifier only; requests run serially on a
  shared queue (last-writer-wins).
- SystemProxyConfiguration is now the closed typed apply payload (Codable over
  JSON XPC): explicit SOCKS/HTTP/HTTPS enable + endpoints, PAC and
  auto-discovery disabled with URL/JS removed, explicit simple-hostname
  exclusion, complete ExceptionsList. Unmodeled Proxies keys are preserved and
  equivalent values skip the commit.
- Cleanup is a side effect of a persisted system-proxy intent on-to-off
  transition only: the switch itself, or the agent-off cascade (persist both
  intents off, clear, then stop listeners; failed cascade persistence records
  no transition and clears nothing). Launch with the switch off, health-gate
  closure, invalid targets, and passive events never clear. Failed clears are
  surfaced without deferred retry.
- Endpoint-signature store, ownership migration, and direct Authorization
  Services writes are removed; old signature files stay inert. Helper
  unavailability or pending approval keeps an enabled intent pending and
  surfaces the Login Items approval path (HomeView button + menu label).
2026-09-30 12:26:40 +08:00
Qiu Yuzhou 08682724c9 fix(system-proxy): reconcile configuration across network changes 2026-09-29 23:39:25 +08:00
Qiu Yuzhou a8cd6262c2 feat(proxy): remove PAC and accept ACL system proxy path
Remove the PAC product feature entirely: PAC HTTP service, port, health
probe, runtime extension, PAC URL system proxy projection, PAC rule
editing, and GFWList URL settings. The product now exposes rule / global /
direct modes only, all projected as a local SOCKS system-proxy target.

First-run defaults are rule + proxyWhenUnmatched, agent on, system proxy
off. Homepage and status menu share one ProxyControlSnapshot showing the
three modes and rule sub-options. Manual servers, subscriptions, Legacy
import, and independent SOCKS/HTTP inbounds are retained.

Fix: include verbose in SslocalListenFingerprint so a verbose-only
contract change restarts sslocal instead of leaving RUST_LOG stale.

Revise CONTEXT.md vocabulary, ADR-0007, and ADR-0009. Record GFWList
exception shadowing, IPv6 system-exception, DNS, and traffic-coverage
boundaries. Add host acceptance evidence document.

Closes #67
2026-09-26 21:58:12 +08:00
Qiu Yuzhou 827fb98be3 refactor(ng2): fix all swiftlint warnings
77 → 0 warnings. Split oversized functions and files: controller and
agent extension files, standalone RuleSet/ProxySettingsFileStore/
SslocalListenSettings/LegacyImportService, test classes into topic
extensions. Regroup activation options into RuntimeDocumentOptions and
converter metadata into Provenance.

Runtime controller DI seams widen private → internal so the extension
files stay cohesive; new controller files are registered in the
architecture guard exemption list; Agent target adds RuleSet and
SslocalListenSettings to project.yml. Test JSON literals wrap inside
the string literal only.
2026-09-26 17:13:31 +08:00
Qiu Yuzhou 41379bb1cb fix(ng2): review fixes for agent/system-proxy switch split
- setAgentEnabled(true) 在意图已开启且未达健康态(off/启动失败/服务
  失败)时执行重收敛,成为失败态的显式重试入口;健康运行中不做无谓
  的注销重拉
- StatusMenuModel.Summary 增加 systemProxyStateLabel 短标签,去掉
  HomeView 对呈现文本的字符串剥离耦合
- 控制器头注释改为如实描述:launchd/契约动作序列在 ProxyRuntimePlan,
  系统代理门禁收敛决策留在控制器(issue #60)
- 新增重试路径测试
2026-09-26 00:55:52 +08:00
Qiu Yuzhou 398e8d84b6 feat(ng2): split agent and system proxy switches
实现 #60(ACL 1/8):agent 意图与系统代理意图拆为两个独立开关,
首次运行默认 agent 开、系统代理关;显式关闭 agent 持久化并在 GUI
重启后生效;关闭系统代理只恢复 NG2 持有的系统设置,本地 SOCKS/HTTP
继续监听;关闭 agent 先恢复系统设置再停止监听。无活动目标时 agent
以空服务器列表契约提供本地监听(isWellFormed 放行空 servers,上游
sslocal v1.25.0 接受);需要代理出口的模式在活动目标缺失或端点不健
康时系统代理保持待应用,条件恢复后随下次收敛自动应用。首页、状态菜
单与侧栏状态卡经同一代理控制快照展示/操作 agent 意图/运行状态与系
统代理意图/实际应用及 typed 错误。测试覆盖启动顺序、持久化、健康门
禁、ownership 冲突、无效活动目标与 GUI 重同步(含真实 sslocal 空监
听回环证据);替身测试不表述为真实系统写入验证。
2026-09-26 00:38:28 +08:00
Qiu Yuzhou 7951d5aa65 feat(ng2): add proxy control workflow as unified UI seam
Issue #47: application-layer ProxyControlWorkflow wrapping the existing
ProxyRuntimeController with a whole-published ProxyControlSnapshot (typed
runtime facts, current mode, available modes, active-target facts with
safe path summary, skipped-invalid-server count, HTTP export capability)
and typed async commands (enable/disable/mode) that republish the full
snapshot on completion. Production runtime adapter delegates to the
controller without duplicating generation, health gating or system-proxy
ownership; catalog target facts arrive through a narrow seam
(CatalogTreeSnapshot.pathSummary) that never exposes the tree.

Status menu now reads runtime state and issues proxy commands only via
the workflow snapshot; catalog tree, activation and subscription actions
stay on CatalogWorkflow. MainApp wires the production adapters once as
composition root. No change to persistence order, failure taxonomy,
mode-availability policy (#46), catalog commit sync (#40), settings
(#44) or diagnostics (#43) seams.

Tests: fake-adapter snapshot/command/boundary contract tests plus
real-controller integration matrix (enable failures, approval, system
proxy, mode persistence failure and no-op) and an integration-level
status-menu model test proving the menu derives everything from the
workflow. Unavailable-mode no-op and runtime-stop-failure items of the
spec matrix are vacuous after ADR-0007 (no constructible unavailable
ProxyMode; controller stop path has no distinct failure category).
2026-09-23 20:54:08 +08:00
Qiu Yuzhou 6c98930999 refactor(ng2): unify runtime facts and proxy mode availability 2026-09-23 11:48:13 +08:00
Qiu Yuzhou fe4c3e166e refactor(ng2): centralize typed failure presentation 2026-09-23 10:51:26 +08:00
Qiu Yuzhou 3df1624d91 refactor(ng2): add settings workflow module as single UI-facing seam
建立设置工作流深 module(App/SettingsWorkflow/),把设置编辑的编排从视图
收拢到一个 seam,镜像 #41 的目录工作流模式:

- UI-facing interface:可编辑 draft 投影(binding 直连)、占用/校验/门禁/
  保存中/待确认 PAC 失效提示/错误等只读投影,与 suggestPort、save、
  confirmPACNotice、cancelPACNotice、reset、reloadFromCommitted 等 typed
  command;登录启动项是独立偏好域(ADR-0005),视图直连其控制器,不经本
  module。
- implementation(灰置):端口占用探测的 Task.detached 编排(代际去竞态)、
  保存门禁(validationErrors+占用)、「当前运行端口」例外、PAC 失效确认
  流、重置事务与 ProxySettingsStoreError 文案映射。
- isOn 判定从 StatusMenuModel 迁至 ProxyRuntimeController.ProxyState 扩展,
  菜单摘要与设置工作流共用同一事实来源;设置视图不再持有
  ProxyRuntimeController 与探测缝,组合根在 MainApp 接线一次,设置窗口
  scene 的显式触达路径不变。

测试:设置编辑 workflow 不经 SwiftUI 即可全测——占用反应与门禁阻塞、
运行端口例外放行、PAC 失效确认/取消、校验阻塞、提交失败保留旧值并点名、
重置回出厂、建议端口只写草稿(8 项;全量 376 项通过)。

Out of scope:设置视觉重设计、ProxyRuntimeController 拆分、独立编译
target。
2026-09-22 17:36:40 +08:00
Qiu Yuzhou 3dc6043dc6 refactor(ng2): remove system-level shortcuts
Keep proxy controls explicit and leave existing shortcut preferences untouched.
2026-09-22 16:16:58 +08:00
Qiu Yuzhou 2fe401cd50 feat(ng2): add catalog workflow module as single UI-facing seam (#41)
建立目录工作流深 module(App/CatalogWorkflow/),收窄主窗口与配置 UI 对
配置目录、凭据存储、订阅快照、Legacy 导入与运行时收敛的访问:

- UI-facing interface:非敏感树/编辑面/订阅 projection、opaque NodeID 查询、
  typed command(ServerEditDraft 等)、typed error 与 structured outcome
  (BatchImportOutcome 逐行点名、RemovalOutcome selection invalidation);
  秘密值只在显式编辑/分享/订阅 URL 命令中出现。
- 服务器编辑:配置与凭据作为一个逻辑变更,CredentialWriteJournal 保证目录
  持久化失败时旧配置与旧凭据都保持有效(story 12/13,含测试)。
- 提交与收敛分离:目录提交成功与 runtimeSync 收敛阶段是独立观察面,运行时
  失败不回滚已提交目录(story 38/39,含测试)。
- 订阅:创建/刷新/换址/删除沿用原子快照、last-good 保留与凭据回滚;卡片
  projection 只含 host,结构化刷新状态由呈现层本地化。
- Legacy 导入端到端编排:不驱动运行时收敛、不自动重复、显式再导入创建独立
  分组;handoff 仍经组合根注入的独立边界。
- Deletion test:删除 CatalogViewModel/Types/+Subscriptions 旧 facade,全部
  视图与状态菜单迁移到新 seam;选择、pane、sheet、alert 状态收归 UI 持有
  (ErrorAlertPresenter)。导入失败行以行号点名,不再回显 ss:// 行内容。

测试经 module interface 观察(395 项全部通过):持久化失败凭据回滚、提交/
收敛阶段分离、收敛失败不回滚、导入不触发运行时等新增场景;既有 Domain、
协调器与运行时测试保持不变。

Out of scope(spec #41):设置编辑、ProxyRuntimeController、Legacy handoff、
新视觉设计、独立编译 target。
2026-09-22 11:39:07 +08:00
Qiu Yuzhou ea801d5f38 feat(catalog): remove node enable state
Migrate legacy enabled fields to v3 without preserving their semantics.
Keep recoverable invalid imports visible and filter them during activation.
2026-09-22 08:40:13 +08:00
Qiu Yuzhou 8ec4a57deb feat(ng2): import Legacy configuration (#36) 2026-09-21 11:27:05 +08:00
Qiu Yuzhou c3a9f62216 feat(ng2): menu bar quick actions and global shortcuts (#31)
Status menu rebuilt around the D11 eight-item whitelist: header summary
(running status, mode, active target path), proxy toggle, mode picker with
checkmark, read-only active-target cascade over the catalog tree, update
all subscriptions, copy HTTP export line, open main window, and quit that
states the proxy keeps running in the background. Out-of-whitelist items
(copy PAC URL) removed. Two global shortcuts (KeyboardShortcuts 3.1.0)
toggle the proxy and cycle modes through the same controller entry points
as the menu.

Presentation logic extracted to StatusMenuModel (state summary mapping,
HTTP export line, mode cycle, read-only target tree) with unit tests;
activeTargetID is now published so the menu stays live when activation
happens while the proxy is off.
2026-09-21 07:39:22 +08:00