Skip to content

feat: 远程服务器管理、跨平台编译、安全加固与多项修复#23

Open
bfzha wants to merge 37 commits into
SuperJJ007:mainfrom
bfzha:feat/remote-management-and-cross-platform
Open

feat: 远程服务器管理、跨平台编译、安全加固与多项修复#23
bfzha wants to merge 37 commits into
SuperJJ007:mainfrom
bfzha:feat/remote-management-and-cross-platform

Conversation

@bfzha

@bfzha bfzha commented Jul 6, 2026

Copy link
Copy Markdown

@

概述

本次 PR 合并了 35 个提交,涉及 55 个文件(+10,365 / -3,606 行),主要包括以下改进:


🚀 远程服务器管理(Remote Server Management)

通过 SSH 远程管理 Linux 服务器上的 CSSwitch helper,实现一键远程启动:

  • 远程 SSH 登录认证:支持密码登录,安全存储凭证
  • 一键远程启动:配置阶段自动准备远程 helper,闭环远程启动流程
  • 沙箱管理增强:收紧沙箱监听、内置代理脚本、固定远程端口、返回沙箱访问链接
  • Helper 部署:实现 remote_install_helper 和 sandbox 命令
  • 进程管理proc_manager + logger 实现,proxy status/stop 改为无状态端口探活
  • 兼容性:兼容用户级路径、避让占用端口

💻 跨平台编译

  • 修复 macOS 停沙箱编译作用域
  • 修复 macOS CI 并完善发布产物矩阵
  • 为 macOS 桌面包内置 Linux helper
  • 修复桌面和 helper 构建参数
  • 跨平台编译 + 远程服务器管理支持

🔒 安全加固

  • CLI config 读写复用 config.rs 安全路径
  • 密钥以 0600 权限安全存储
  • 代理路径 Secret 验证请求

🎨 前端改进

  • Toast/状态机 + CI/CD
  • DOM null 引用防护
  • Profile 加载时序修复
  • 「管理」按钮打开弹窗前先加载远程 profiles

🛠 代码质量

  • 代码格式化统一
  • 清理 unused warnings 至 0
  • 修复代码审核 Critical/Important 问题
  • tauri-plugin-opener 改为 optional
  • 修复 Linux 编译缺少 use std::fsuse Command/Stdio

📋 Profile / Templates

  • 合入上游 profile/templates 多 provider 系统
  • Profile 管理与模板支持

测试

  • 新增 test_desktop_ui_contract.mjs(461 行)桌面 UI 契约测试
  • 更新 CI 测试脚本

致谢

感谢原项目 CSSwitch 提供的优秀基础。
@

bfzha and others added 30 commits July 4, 2026 10:57
新增 Windows 编译兼容和基于 SSH 的远程服务器管理,macOS 功能不变。

## 跨平台基础 (Phase 1)
- 新增 fs_ext.rs:条件编译抽象文件权限操作,Unix 设 0600/0700,Windows no-op
- 替换所有 std::os::unix::fs 导入为 crate::fs_ext
- /dev/urandom → rand::OsRng 跨平台随机数生成
- HOME → dirs::home_dir() 跨平台家目录
- #[cfg(target_os = "macos")] 守卫所有 macOS 本地代码路径
- #[cfg(unix)] 守卫符号链接相关测试

## 远程服务器管理 (Phase 2-4)
- 新增 remote/ 模块:SSH 连接、Profile 存储、JSON 协议类型
- 新增 remote_commands.rs:18 个 Tauri async command
- remote/types.rs:RemoteHostProfile, RemoteHealth, RemoteAuthMethod 等
- remote/ssh.rs:SSH 参数构建、命令执行、指数退避重试、结构化错误映射
- remote/store.rs:Profile CRUD 本地持久化 + 字段校验 + 单元测试

## 远程 Helper CLI (Phase 3)
- 新增 csswitch-helper 独立 Rust 二进制(无 Tauri 依赖)
- cli/mod.rs:命令路由(pattern match 风格)
- cli/commands.rs:代理管理、配置读写、日志查看、Key 验证、诊断
- cli/serve.rs:JSON-line stdin/stdout 持久会话模式
- 支持 --json 标志的单次命令模式和 serve 持久模式

## 前端适配 (Phase 5)
- index.html:本地/远程模式切换器、Profile 管理弹窗、编辑表单
- main.js:远程状态管理、操作分派(saveKey/oneClick/stopAll/refreshStatus)
- styles.css:remote-only/local-only 样式、弹窗、连接动画

## Cargo 配置更新
- autobins=false,显式声明 csswitch 和 csswitch-helper 两个 binary
- desktop feature gate(tauri 依赖)使 helper 可独立编译
- 新增 rand=0.8, dirs=5 依赖

Co-Authored-By: Claude <noreply@anthropic.com>
问题:helper 二进制编译失败,因为 lib.rs 无条件依赖 tauri crate,
且 tauri-build 在所有目标构建时运行。

修复:
- lib.rs: 拆分为模块声明 + #[cfg(feature = desktop)] include!
  tauri 相关代码移到 lib_tauri.rs,helper 编译时跳过
- Cargo.toml: tauri-build 改为 optional,仅在 desktop feature 启用
- build.rs: #[cfg(feature = tauri-build)] 条件执行
- remote_commands.rs: 移除 tokio::spawn_blocking 依赖,
  所有远程命令改为 sync fn,由 Tauri 运行时自动分派到线程池
- fs_ext.rs: Windows Perissions::from_mode 使用 metadata 获取权限
- proc.rs: #[cfg(unix)] 守卫 sh 相关测试
- 修复 unused variable 和 import warnings

验证:
- cargo check --bin csswitch-helper --no-default-features ✅
- cargo check --lib ✅
- cargo test --lib: 31 passed, 0 failed ✅

Co-Authored-By: Claude <noreply@anthropic.com>
- remote_install_helper: 使用 build_helper_install_args 构建 SSH 安装脚本,
  直接执行 ssh 命令完成 helper 下载/部署/验证,不再传空参数
- helper sandbox 命令完善:
  • sandbox status: 通过 pgrep + TCP 端口探活检测 Science 运行状态
  • sandbox start: 用独立 HOME/data-dir 启动 Science,通过 ANTHROPIC_BASE_URL 指向代理
  • sandbox stop: 调用 claude-science stop --data-dir 停止
- remote/mod.rs: 修复 cargo fix 移除的 re-export
- 清理未使用的 import

验证: cargo check --lib + test --lib 31 passed + helper check 全绿

Co-Authored-By: Claude <noreply@anthropic.com>
处理策略:
- 删除:config.rs/lib_tauri.rs 顶层不再需要的 PermissionsExt import
- #[cfg] 守卫:sandbox_home/first_http_url 加 #[cfg(target_os = "macos")],
  ere_escape 加 #[allow(dead_code)](仅 Unix pkill 路径使用)
- #[allow(dead_code)] + 注释(预留给未来功能):
  • RemoteRequest/RemoteResponse: serve 持久会话模式预留
  • MIN_HELPER_VERSION/OPTIONAL_CAPABILITIES: 版本兼容性检查预留
  • run_blocking: 将来 async 上下文中的阻塞 I/O 辅助
  • Windows PermissionsExt trait: 跨平台抽象层,外部模块引用
- 测试守卫:first_http_url/sandbox_home 测试函数加 #[cfg(target_os = "macos")]

验证: cargo check --lib 0 warning, cargo test --lib 29 passed

Co-Authored-By: Claude <noreply@anthropic.com>
修复 helper 二进制在 Linux 服务器上直接编译时报 glib-2.0/gtk 找不到的问题。
Root cause: tauri-plugin-opener 是无条件依赖,间接引入 tauri → gtk 全家桶。
改为 optional 并在 desktop feature 启用,helper 编译时跳过。

Co-Authored-By: Claude <noreply@anthropic.com>
cargo fix 在 Windows 上移除了仅 Unix 路径使用的 import:
- fs_ext.rs: #[cfg(unix)] mod imp 内缺少 use std::fs
- proc.rs: which_via_login_shell 需要的 Command/Stdio 被移除

修复: fs_ext.rs 加 use std::fs; proc.rs 加回 #[allow(unused)] import。

Co-Authored-By: Claude <noreply@anthropic.com>
修复问题:helper 是独立 CLI 进程,每次调用后丢失内存中
存储的 PID/PROXY_INFO,导致 proxy status 始终返回 false。

改为:
- cmd_proxy_status: 从配置文件读取端口,TCP connect 探活
- cmd_proxy_stop: 通过 fuser -k/lsof 按端口杀进程
- cmd_status: 同样改为端口探活
- cmd_proxy_start: 改为 spawn+forget 模式
- 移除不再需要的 PROXY_CHILD/PROXY_INFO/Mutex 全局状态

验证: cargo check --lib + test 29 passed + helper check OK

Co-Authored-By: Claude <noreply@anthropic.com>
修复点击「管理」按钮无响应问题:openProfileModal 内部调用
loadRemoteProfiles 确保数据已加载,加 null 检查防护。

Co-Authored-By: Claude <noreply@anthropic.com>
- loadRemoteProfiles/updateRemoteHealthUI/onProfileChange: 加 null check
- openProfileModal: 打开前先 loadRemoteProfiles
- 修复 'Cannot set properties of null' 错误

Co-Authored-By: Claude <noreply@anthropic.com>
审核报告修复(按优先级):

P0:
- 硬编码弱 secret csswitch → 随机生成并持久化到 proxy.secret 文件
- 远程 key 经 CLI args 传递风险(留待后续 stdin 改造)

P1:
- remote-hosts.json 增加 symlink 防护 + pid/thread 随机化临时文件名
- proxy status 从文件读取 secret 做准确健康检查

P2:
- HTTP 状态码严格解析(split_whitespace nth(1)==200)
- sandbox_running_ours 用 serde_json 替代 contains 字符串匹配
- fuser 先 SIGTERM 优雅退出,1s 后 SIGKILL 强杀

Other:
- SSH timeout 保留原方式,添加注释说明
- 删除 index.html 重复 script 标签
- 前端 DOM null check 防护

Co-Authored-By: Claude <noreply@anthropic.com>
新增:
- cli/proc_manager.rs: PID 文件管理、send kill -0 探活、僵尸清理、日志轮转
- cli/logger.rs: helper 操作日志,ISO8601 时间戳 + 三级日志
- cmd_proxy_start 记录 PID + secret + 操作日志
- cmd_proxy_stop 清理 PID + 记录操作日志
- bin 入口初始化 logger
- logs_dir/config_dir 改为 pub 供 proc_manager 使用

Co-Authored-By: Claude <noreply@anthropic.com>
- 前端: RemoteState 状态机 + Toast 通知组件
- CI/CD: GitHub Actions (Windows MSI + Linux Helper + 测试 + Release)
- 用户文档: docs/remote-setup.md 远程模式设置完整指南

Co-Authored-By: Claude <noreply@anthropic.com>
审查发现 CLI commands.rs 用裸 fs::write 读写 config.json,
绕过桌面端 config.rs 的所有安全防护(symlink 拒绝/0600/原子写)。

修复:
- cmd_config_set: 用 config::save_to 替代裸 fs::write
- cmd_config_save_key: 用 config::update(读-改-写锁内安全操作)
- Helper 二进制导入 config + fs_ext 模块以使用共享安全函数
- 消除 CLI 与桌面端的双重标准安全差距

Co-Authored-By: Claude <noreply@anthropic.com>
上游(d468819)主要变更:
- 多 profile 配置系统:Profile 列表 + active_id 替代旧固定槽
- templates 注册表:DeepSeek/千问/智谱/小米/硅基流动/Kimi/MiniMax/OpenRouter/自定义
- 模型选择器:CAP 三态(native 映射/relay 跟随/relay 固定)+ 模型拉取
- Chip 模板选择 UI + 向导/连接/元信息三表单
- lifecycle 启动迁移 + config_legacy 向后兼容
- DSML shim、修复 relay 空值守卫、a11y 键盘焦点

本地保留功能:
- SSH 远程服务器管理(remote/ + remote_commands)
- csswitch-helper 独立二进制(--no-default-features 编译)
- 跨平台文件权限抽象(fs_ext)
- 前端远程状态机 + Toast + 远程命令 mock
- 拆分架构:lib.rs 模块声明 + lib_tauri.rs Tauri 命令

杂项:.gitignore 添加 .vscode/
Bug 1: cmd_sandbox_start 补全 4 项缺失
- 启动前调用 oauth_forge::ensure_virtual_login 注入虚拟 OAuth 凭证
- 添加 --host 0.0.0.0 允许外部 Windows 客户端访问
- 设置 https_proxy/HTTPS_PROXY (剥掉 /secret path,对齐 launch-virtual-sandbox.sh)
- 设置 no_proxy/NO_PROXY="127.0.0.1,localhost,::1"
- helper 二进制添加 oauth_forge 模块声明

Bug 2: remote_one_click 增加第三步启沙箱
- 提取随机 secret 为变量,供 proxy start 和 sandbox start 复用
- 代理启动成功后调用 helper sandbox start <port> <proxy_url>
- _sandbox_port 改为 sandbox_port (不再 unused)

同步附带改动:
- ssh.rs: CREATE_NO_WINDOW + recv_timeout 实现真正超时
- store.rs: remote-hosts.json 写入 0600 权限
- main.js/index.html: 远程模式一键开始/停止/状态刷新 UI

Co-Authored-By: Claude <noreply@anthropic.com>
- SSH: CREATE_NO_WINDOW + recv_timeout 超时机制
- Profile: 0600 权限 + 文件锁安全
- Helper: sandbox start OAuth 注入 + --host 0.0.0.0
- remote_one_click: 补全 sandbox start 步骤
- 前端: oneClick/stopAll/refreshStatus 远程分派
- 跨平台: lib_tauri.rs 条件编译守卫

Co-Authored-By: Proma Agent <noreply@anthropic.com>
合并引入的 UTF-16LE 版本存在编码和语法双重问题。
回退到合并前语法正确的 UTF-8 版本,仅保留 11 个 API 兼容错误
(新旧 Config 结构不匹配),可后续按新 Profile/Template API 逐一修复。

远程功能代码不受影响——Helper 编译走 --no-default-features 路径。
- lib_tauri.rs: 适配新 Config Profile/Template API
- config.rs: 新增 active_profile_mut(), atomic_copy 导入 OpenOptionsExt
- csswitch-helper.rs: 补全 config_legacy/templates/proc 模块声明
- cli/commands.rs: 适配新 Config 的 key 读写 API
- 恢复 build.rs (tauri-build)

Windows 桌面端和 Linux Helper 均已通过编译。
同步本地 active Profile 到远程 helper 后再启动代理和沙箱。

前端一键开始改为调用完整远程链路,并返回本地访问地址与端口转发提示。

helper 状态改为读取配置端口并真实上报沙箱运行状态,sandbox stop 在未运行时保持幂等。
内置 csswitch_proxy.py 与 dsml_shim.py,远程 helper 可自愈写入 ~/.csswitch/proxy。

沙箱仅监听 127.0.0.1,配合 SSH 端口转发访问,补充远程一键启动契约测试。
**安全加固 (High):**
- ssh.rs: detect_remote_platform 增加 OS 名称白名单,非 Linux/Darwin 返回错误
- ssh.rs: 远程安装脚本 JSON 解析改为 jq → python3 → awk 三级降级

**健壮性 (Medium):**
- prompt.rs: 修复 classify_prompt 冗余的大小写双重检查
- askpass.rs: remember_secret 过滤空密码,防止存储空凭证
- remote_commands.rs: HEALTH_CACHE 添加上限(32条)和淘汰策略,delete profile 时同步清理
- .gitignore: 删除重复的 docs/superpowers/ 行

**代码质量 (Low):**
- askpass.rs: 移除未使用的 cancelled() 和 session_id() 死代码
- ssh.rs: 标注 SshAuthPlan 重复计算为已知低开销项

**工程规范:**
- 新增 .gitattributes: 统一 LF 换行,二进制文件标记
- 新增 .editorconfig: UTF-8 + LF + 4空格缩进
- lib.rs / lib_tauri.rs: CRLF → LF 换行符统一

Co-Authored-By: Claude <noreply@anthropic.com>
bfzha added 7 commits July 5, 2026 22:29
修复 macOS cfg 分支下 one_click_login 参数名导致的编译失败。

发布产物调整为 Windows x64/arm64 NSIS,macOS 按上游方案保留 arm64 DMG,并补充契约测试。

同时提交本地未提交的远程 helper 稳定性与安全性改动,包括沙箱启动等待、端口护栏、askpass 并发临时文件名和 helper release 仓库校验。
- cli/commands.rs: 新增 matching_sandbox_pids/wait_sandbox_pids_exit/terminate_sandbox_processes,
  沙箱启动前清理残留进程,停止失败时强制终止兜底
- cli/commands.rs: sandbox_is_running 增加进程匹配校验(仅端口健康不够)
- cli/proc_manager.rs: 格式化统一
- remote/prompt.rs: 格式化统一
- remote/ssh.rs: 格式化统一
- test: 更新沙箱进程管理相关的 UI contract 测试
将 stop_sandbox_inner 的 _app 参数改为 app,避免 macOS cfg 分支调用 asset_root(app) 时找不到变量。

补充桌面契约测试,防止后续再次把 macOS cfg 内需要使用的 app handle 写成下划线参数。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant