Skip to content

feat: 从 Tauri 迁移到 Electron + Rust Daemon 架构#13

Merged
Shadow-Azure merged 43 commits into
mainfrom
design/electron-rust-architecture
May 31, 2026
Merged

feat: 从 Tauri 迁移到 Electron + Rust Daemon 架构#13
Shadow-Azure merged 43 commits into
mainfrom
design/electron-rust-architecture

Conversation

@Shadow-Azure

@Shadow-Azure Shadow-Azure commented May 31, 2026

Copy link
Copy Markdown
Owner

Summary

架构迁移:Tauri → Electron + Rust Daemon

本 PR 是一次重大架构变更,将项目从 Tauri (WKWebView) 迁移到 Electron (Chromium) + 独立 Rust daemon 的架构。核心动机是解决 Tauri WKWebView 在终端渲染中的已知问题(setTimeout 失效、渲染残留、无 DevTools),参见 架构设计文档

主要变更:

  • 新增 sandbox-daemon:独立 Rust daemon 进程,管理所有沙箱实例(PTY + macOS 应用),通过单一 HTTP API 对外服务
  • 新增 electron-app:Electron 应用作为 GUI 前端,提供 tab 管理、xterm.js 终端和截图预览
  • 删除 Tauri 代码:移除 src-tauri/sandbox-web/(旧 Tauri 前端)
  • 修复 PTY 清理 bug:daemon 使用内部 tracked_id 而非 OS PID 检测进程存活,导致所有 CLI 沙箱被立即清理
  • 新增 os_pid 字段ProcessInfo 增加 os_pid 字段和 ProcessManager::is_session_alive() 方法
  • 添加 Phase 3-8 实施计划

架构对比

项目 旧架构 (Tauri) 新架构 (Electron + Daemon)
渲染引擎 WKWebView (WebKit) Chromium
终端渲染 有残留/鬼影,需要 writeDirect hack 与 VS Code 一致,标准 term.write()
多实例 每个沙箱一个 Tauri 进程 (~30MB) 单 daemon + 单 Electron,Tab 管理
DevTools 需手动开启 原生支持
IPC 同进程(无 IPC) HTTP + WebSocket

Test plan

  • 发布测试:opencode、claude、zsh 沙箱正常工作(11/12 通过)
  • PTY 输入/输出验证
  • 多标签页支持验证
  • 截图功能验证
  • cargo check --all-targets 构建通过
  • CI 门禁全部通过(fmt、clippy、test、frontend test、security)

🤖 Generated with Claude Code

ZN-Ice and others added 30 commits May 30, 2026 04:24
- I-1: Route cmd_inspect/cmd_processes through daemon API instead of
  per-instance SandboxClient (which fails for daemon-managed sandboxes
  with port:0). Added GET /sandbox/{id}/processes endpoint to daemon
  router, plus daemon_inspect/daemon_processes client functions.
- I-2: Replace std::thread::sleep with tokio::time::sleep in async
  cmd_start_daemon polling loop to avoid blocking the runtime.
- I-3: cargo fmt --all applied.
- M-1: Replace .map().flatten() with .and_then() in find_daemon_binary
  to satisfy clippy.
pkill -f 'sandbox' matches Electron apps (VSCode, Feishu, Chrome) whose
command lines contain --no-sandbox or --enable-sandbox. Now reads
daemon.json for exact daemon PID, uses pkill -x for exact name matches.
- Add POST /sandbox/{id}/pty/write for direct PTY stdin input
- Uses ProcessManager::send_input() for writing to PTY process
- Update release.sh to build and include sandbox-daemon binary
- Fixes release.sh cleanup to use PID-based approach (avoids killing unrelated apps)
Previously, sandbox type --pty and sandbox key --pty were ignored in
daemon mode — both always routed through CGEvent. Now when --pty is
specified, the commands use the daemon's /sandbox/{id}/pty/write endpoint
to write directly to PTY stdin, which is required for CLI processes
like Claude Code that use raw terminal mode.
macOS caches code signatures by file path. When cp replaces a binary
at the same path, the cached signature may not match the new ad-hoc
signature, causing dyld to reject loading (process enters UE state).
Adding codesign --force --sign - after copy ensures the signature
is refreshed.
7-task plan covering: electron-vite scaffold, daemon spawning,
requestSingleInstanceLock, tab manager (WebContentsView), xterm.js
renderer with standard term.write(), CLI integration, end-to-end testing.
- Update release.sh to build Electron app instead of Tauri
- Fix electron-builder paths (daemon resource, output dir)
- Fix package.json main field (dist/ -> out/)
- CLI always spawns Electron (triggers second-instance for tab sync)
- Add ELECTRON_MIRROR for Chinese network

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Redesign renderer with single-instance architecture
- Add macOS-style title bar, tab bar, terminal, status bar
- Add dark/light/system theme toggle with CSS variables
- Use SF Pro font and macOS-native color palette
- Add xterm.js terminal with refined color scheme
- Poll daemon for sandbox list and auto-refresh tabs
- Remove WebContentsView tab manager (simplified)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add /sandbox/{id}/ui/inspect (by sandbox id), /sandbox/{id}/ui/find,
and /sandbox/{id}/ui/value routes to the daemon. These expose the
existing AXUIElement inspection functions via HTTP.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add CLI commands for AXUIElement UI inspection via daemon HTTP API.
Commands: sandbox ui-inspect --id <id>, sandbox ui-find --id <id> --role <role>,
sandbox ui-value --id <id> --element-id <eid>.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- recorder.rs: JSONL action recording with timing
- player.rs: async playback of recorded action sequences
- diff.rs: pixel-by-pixel screenshot comparison with diff image output

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- sandbox diff --a <png> --b <png>: pixel-by-pixel screenshot comparison
- sandbox record/playback: stub commands showing action details

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ZN-Ice and others added 13 commits May 31, 2026 10:57
4 tests covering identical images, different images, threshold sensitivity,
and diff image generation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add periodic cleanup of dead sandboxes (30s interval)
- Add POST /sandbox/{id}/window endpoint for window_id propagation
- Use InstanceRegistry::update_status/update_window_id for lifecycle

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add createSandbox, takeScreenshot, closeSandbox, setWindowId to api.ts
- Create AppPanel component for APP mode screenshot preview
- Add New Sandbox dialog with CLI/App mode selection
- Add dialog and app panel CSS styles

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds `sandbox mcp-serve` command that implements JSON-RPC over stdio
for agent integration. Supports tools: list_sandboxes, start_sandbox,
close_sandbox, screenshot_sandbox, type_text, press_key, inspect_ui.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tests health endpoint, list empty, create with bad mode, close/screenshot
nonexistent sandbox, and unknown routes using tower oneshot.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Update CLAUDE.md and README.md to reflect the new single-daemon +
Electron architecture, replacing the old Tauri multi-instance model.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CLI sandboxes now get the Electron window_id set on creation via
ScreenCapture::find_window_by_title. A background task also discovers
the window 2 seconds after daemon start for sandboxes created before
the Electron app launches.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Use spawn_app_with_window in the app/spawn endpoint to discover and
store the app's window_id for subsequent screenshots.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds GET /sandbox/{id}/screenshot/region?x=&y=&width=&height= for
capturing a sub-region of the sandbox window.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The daemon's cleanup_dead_sandboxes was calling libc::kill(pty_pid, 0)
to check if PTY processes were alive. But pty_pid is the internal
tracked_id (1000+), not the actual OS PID, so the check always failed
and all CLI sandboxes were immediately cleaned up.

Changes:
- Add os_pid field to ProcessInfo for actual OS PID tracking
- Add ProcessManager::is_session_alive() to check SESSIONS HashMap
- Update daemon cleanup to use session existence check
- Add implementation plans for phases 3-8
- Update release README and test docs

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Remove src-tauri/ and sandbox-web/ directories as the project has
fully migrated to Electron + daemon architecture. Update Cargo.toml
workspace members and fix ProcessInfo tests for os_pid field.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Replace &PathBuf with &Path in player.rs and main.rs (clippy::ptr_arg)
- Use output.as_deref() instead of output.as_ref() for Option<&Path> coercion
- Add #[allow(dead_code)] to unused daemon_set_window_id and find_running_electron
- Update ci.yml frontend test job: sandbox-web → electron-app
- Apply cargo fmt formatting fixes

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add vitest and @vitest/coverage-v8 as devDependencies
- Create vitest.config.ts for electron-app
- Add unit tests for daemon API client pure functions
- Add test and test:unit scripts to package.json

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🔒 门禁检查结果

检查项 状态
Rust 格式化 ✅ success
Rust Clippy ✅ success
Rust 测试 & 覆盖率 ✅ success
前端测试 & 覆盖率 ✅ success
安全检查 ✅ success

Rust 测试覆盖率

指标 覆盖率
行覆盖率 71.6%
分支覆盖率 0.0%
模块 行覆盖率
crates.sandbox-core.src.capture ████████████████████ 100.0%
crates.sandbox-core.src.daemon ███████████░░░░░░░░░ 59.5%
crates.sandbox-core.src.process █████████████░░░░░░░ 65.7%
crates.sandbox-core.src.instance ███████████████████░ 99.1%
crates.sandbox-core.src ███████████░░░░░░░░░ 55.3%
crates.sandbox-core.src.automation ███████████░░░░░░░░░ 58.8%
crates.sandbox-core.src.sandbox ███████████████████░ 98.8%
crates.sandbox-core.src.server █████████████████░░░ 87.5%

详细报告见 Rust 覆盖率 artifact

前端测试覆盖率

指标 覆盖率
行覆盖率 ██░░░░░░░░░░░░░░░░░░ 11.4%
分支覆盖率 0.0%
函数覆盖率 17.6%
语句覆盖率 9.1%
文件 行覆盖率
src/renderer/api.ts ██░░░░░░░░░░░░░░░░░░ 11.4%

详细报告见前端覆盖率 artifact

✅ 所有检查通过,可以合入

点击 Squash and merge 合并此PR

@Shadow-Azure Shadow-Azure changed the title fix(process): session liveness check + remove Tauri code feat: 从 Tauri 迁移到 Electron + Rust Daemon 架构 May 31, 2026
@Shadow-Azure
Shadow-Azure merged commit e282933 into main May 31, 2026
7 checks passed
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