Update: 将子项目从 submodule 转为完整内容

- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
This commit is contained in:
freedak
2026-07-04 19:20:46 +08:00
parent 54d6465fa7
commit f7a720204a
3360 changed files with 802660 additions and 3 deletions
@@ -0,0 +1,277 @@
//! **P3 W4dstorage_state 持久化 vault(加密)+ 启动 inject 持久登录 端到端集成**
//! `#[ignore]`,本机/打包 chrome)。
//!
//! 验证 W4d(吸收原 P6 持久登录):W4b/c 是**内存往返**capture↔restore),W4d 在两端之间塞**磁盘
//! vault**,证**跨引擎/会话的持久登录闭环**:
//!
//! ```text
//! 引擎 A(会话 A 登录):navigate example.com → 写 cookie + localStorage(模拟登录)
//! → capture_cookies + capture_local_storage → save_storage_state(vault) [加密]
//! ── A close(登录态只在加密 vault 文件里)──
//! 引擎 B(新引擎/会话 B):load_storage_state(vault) → EngineConfig.storage_state
//! → 引擎启动注入 cookienavigate 前即灌)
//! → navigate example.com(带上恢复的 cookie)→ restore_local_storageorigin-bound
//! → cookie + localStorage **都恢复存活**(读回)= 持久登录成立。
//! ```
//!
//! **加密验收**vault 文件落盘是 AES-256-GCM 密文(不含明文 cookie token / JWT),换 key 解不开
//! (纯逻辑已在 `vault::tests` 钉死;本集成跑真 save/load 往返证密文可解回真登录态)。
//!
//! **默认 None 零回归**`storage_state=None`(不灌)→ 引擎启动不碰 cookie/localStorage(现行为)。
//!
//! 手动跑(本机 Windows 有系统 Chrome):
//! set NOMIFUN_CHROME_BINARY=...\chrome.exe
//! cargo nextest run -p nomi-browser-engine --run-ignored all -E 'test(w4d)'
//! 跑完核对任务管理器无残留 chromeBuilder kill_on_drop + disposeOnDetach 自动清)。
//!
//! 真实结果(本机首跑 eprintln 出 vault 路径 + 恢复读回的 cookie/localStorage——填回任务汇报)。
use nomi_browser_engine::storage_state::{SameSite, StorageState, StorageStateCookie};
use nomi_browser_engine::{
load_storage_state, save_storage_state, shared_storage_state_path, storage_state_path,
};
use nomi_browser_engine::BrowserEngine;
mod common;
const ORIGIN: &str = "https://example.com";
/// 测试用 32 字节 key(机器绑定 key 的占位;真机用 app provision 的 encryption_key)。
fn machine_key() -> [u8; 32] {
[0x5a; 32]
}
/// 造一条登录态 cookie(持久 + sameSite=Lax,绑 example.com——与 navigate 目标同域)。
fn login_cookie(name: &str, value: &str) -> StorageStateCookie {
StorageStateCookie {
name: name.into(),
value: value.into(),
domain: ".example.com".into(),
path: "/".into(),
expires: 4_102_444_800.0, // 2100-01-01Chrome 钳到 ~400d,仍是持久 cookie
http_only: false,
secure: false,
session: false,
same_site: Some(SameSite::Lax),
priority: nomi_browser_engine::storage_state::Priority::Medium,
source_scheme: nomi_browser_engine::storage_state::SourceScheme::NonSecure,
source_port: -1,
partition_key: None,
}
}
/// 在当前页面 `setItem` 一组 localStorage 键值(模拟「页面写了 localStorage 登录态」)。
async fn seed_local_storage(backend: &nomi_browser_engine::backend::CdpBackend, pairs: &[(&str, &str)]) {
let pairs_owned: Vec<[&str; 2]> = pairs.iter().map(|(k, v)| [*k, *v]).collect();
let pairs_json = serde_json::to_string(&pairs_owned).expect("json pairs");
let script = format!(
"(() => {{ const pairs = {pairs_json}; for (const [k, v] of pairs) localStorage.setItem(k, v); return localStorage.length; }})()"
);
backend
.__eval_page_world_for_test(&script)
.await
.expect("seed localStorage");
}
/// 读回当前页面某 localStorage 键(None = 不存在)。
async fn read_local_storage(backend: &nomi_browser_engine::backend::CdpBackend, key: &str) -> Option<String> {
let key_json = serde_json::to_string(key).expect("json key");
let script = format!("localStorage.getItem({key_json})");
let r = backend
.__eval_page_world_for_test(&script)
.await
.expect("read localStorage");
r.get("value").and_then(|v| v.as_str()).map(|s| s.to_string())
}
fn find_cookie<'a>(state: &'a StorageState, name: &str) -> Option<&'a StorageStateCookie> {
state.cookies.iter().find(|c| c.name == name)
}
/// **核心:跨引擎/会话持久登录闭环(cookie + localStorage 经加密 vault 持久 → 启动注入恢复)**。
#[tokio::test]
#[ignore = "需本机/打包 chromeset NOMIFUN_CHROME_BINARY 后 --run-ignored all"]
async fn w4d_persistent_login_cross_engine_via_encrypted_vault() {
// vault 目录(临时目录当 workspace)。
let vault_dir = tempfile::tempdir().expect("vault tempdir");
let vault_path = storage_state_path(vault_dir.path());
let key = machine_key();
eprintln!("=== w4d vault path = {} ===", vault_path.display());
// ── 会话 A:登录 → capture → save 加密 vault ──────────────────────────────────
{
let backend_a = common::build_backend_for_fixture("w4d-A").await;
// 灌一条登录态 cookie(模拟「会话 A 登录拿到了 cookie」)。
let seed_state = StorageState {
cookies: vec![login_cookie("nomi_session", "persisted-login-tok-abc")],
..Default::default()
};
backend_a.restore_cookies(&seed_state).await.expect("seed cookie into A");
// navigate + 写 localStorage 登录态。
backend_a.navigate(ORIGIN, false).await.expect("A navigate example.com");
seed_local_storage(&backend_a, &[("ls_auth", "jwt.persisted.signature")]).await;
// capture A 的登录态(cookie + localStorage)。
let cookies = backend_a.capture_cookies().await.expect("capture A cookies");
let ls = backend_a
.capture_local_storage()
.await
.expect("capture A localStorage")
.expect("A page has origin");
let state = StorageState {
cookies: cookies.cookies,
local_storage: vec![ls],
};
assert!(find_cookie(&state, "nomi_session").is_some(), "A must have the login cookie before save");
eprintln!(
"=== w4d A captured: {} cookie(s), localStorage origin={} ===",
state.cookies.len(),
state.local_storage[0].origin
);
// **save 加密 vault**(登录态落盘,加密)。
save_storage_state(&state, &vault_path, &key).expect("save_storage_state encrypted vault");
}
// **加密验收**:vault 文件内容是密文,不含明文 cookie token / JWT。
let raw = std::fs::read_to_string(&vault_path).expect("read raw vault");
assert!(!raw.contains("persisted-login-tok-abc"), "cookie token must NOT be plaintext in vault");
assert!(!raw.contains("jwt.persisted.signature"), "localStorage JWT must NOT be plaintext in vault");
eprintln!("=== w4d vault is ciphertext ({} base64 chars), no plaintext login state ===", raw.len());
// ── 会话 Bload 加密 vault → 启动注入 → 登录态恢复 ───────────────────────────
// load_storage_state 解密读回(坏 vault 会返 None,这里应 Some)。
let loaded = load_storage_state(&vault_path, &key).expect("load_storage_state decrypts vault");
assert!(find_cookie(&loaded, "nomi_session").is_some(), "vault must carry the login cookie");
// 喂给新引擎的 EngineConfig.storage_stateJSON 形态)。
let inject = loaded.to_json().expect("storage_state to_json for inject");
// **全新引擎 B + storage_state 注入**——启动即 restore_cookies。
let backend_b = common::build_backend_for_fixture_with_storage_state(
"w4d-B",
Some(inject),
)
.await;
// navigate example.com:恢复的 cookie 是启动时已灌,navigate 后该域 cookie 仍在。
backend_b.navigate(ORIGIN, false).await.expect("B navigate example.com");
let after = backend_b.capture_cookies().await.expect("B capture cookies after nav");
eprintln!("=== w4d B has {} cookie(s) after startup inject + nav ===", after.cookies.len());
let c = find_cookie(&after, "nomi_session")
.expect("login cookie must be restored into B at startup (persistent login via vault)");
assert_eq!(c.value, "persisted-login-tok-abc", "restored cookie value must match the persisted login");
// localStorage 是 origin-boundB 现已在 example.comrestore_local_storage 灌回该 origin 的项。
backend_b
.restore_local_storage(&loaded)
.await
.expect("restore localStorage into B (now on the matching origin)");
let ls_auth = read_local_storage(&backend_b, "ls_auth").await;
eprintln!("=== w4d B localStorage ls_auth after restore = {ls_auth:?} ===");
assert_eq!(
ls_auth.as_deref(),
Some("jwt.persisted.signature"),
"localStorage login state must be restored into B (round-trip via vault)"
);
}
/// **默认 None 零回归**`storage_state=None` → 引擎启动不注入任何登录态(干净起点)。
#[tokio::test]
#[ignore = "需本机/打包 chromeset NOMIFUN_CHROME_BINARY 后 --run-ignored all"]
async fn w4d_none_storage_state_injects_nothing() {
// 无注入(storage_state=None)——与现行为完全一致。
let backend = common::build_backend_for_fixture_with_storage_state(
"w4d-none",
None,
)
.await;
backend.navigate(ORIGIN, false).await.expect("navigate example.com");
// 未注入 → example.com 无任何我们灌的 cookie(干净起点;可能有页面自设 cookie,
// 但绝不该有 w4d 的 nomi_session)。
let captured = backend.capture_cookies().await.expect("capture");
eprintln!("=== w4d none-inject: {} cookie(s) (must not contain injected login) ===", captured.cookies.len());
assert!(
find_cookie(&captured, "nomi_session").is_none(),
"None storage_state must inject nothing (zero regression): {:?}",
captured.cookies
);
// localStorage 同样干净(未注入)。
assert_eq!(
read_local_storage(&backend, "ls_auth").await,
None,
"None storage_state must not inject localStorage"
);
}
/// **共享浏览器身份端到端**:伙伴 A 登录 → save 到**共享** vault
/// `{data_dir}/browser-state/storage_state.enc` → 伙伴 B load **同一份共享** vault
/// → 启动注入 → B 直接拥有 A 的登录态(cookie + localStorage 跨伙伴共享)。
///
/// set NOMIFUN_CHROME_BINARY=...\chrome.exe
/// cargo nextest run -p nomi-browser-engine --run-ignored all -E 'test(shared_identity)'
#[tokio::test]
#[ignore = "需本机/打包 chromeset NOMIFUN_CHROME_BINARY 后 --run-ignored all -E 'test(shared_identity)'"]
async fn shared_identity_login_crosses_companions_via_shared_vault() {
// 共享 vault 落 {data_dir}/browser-state/storage_state.encdata_dir 用临时目录当 app data-dir)。
let data_dir = tempfile::tempdir().expect("data_dir tempdir");
let vault_path = shared_storage_state_path(data_dir.path());
let key = machine_key();
eprintln!("=== shared identity vault path = {} ===", vault_path.display());
// 共享单例硬证据:两次解析同一份(与伙伴/会话无关)。
assert_eq!(vault_path, shared_storage_state_path(data_dir.path()), "shared vault path is a singleton");
// ── 伙伴 A:登录 → capture → save 到共享 vault ──────────────────────────────────
{
let backend_a = common::build_backend_for_fixture("shared-A").await;
let seed_state = StorageState {
cookies: vec![login_cookie("nomi_session", "shared-login-tok-xyz")],
..Default::default()
};
backend_a.restore_cookies(&seed_state).await.expect("seed cookie into A");
backend_a.navigate(ORIGIN, false).await.expect("A navigate example.com");
seed_local_storage(&backend_a, &[("ls_auth", "jwt.shared.signature")]).await;
let cookies = backend_a.capture_cookies().await.expect("capture A cookies");
let ls = backend_a
.capture_local_storage()
.await
.expect("capture A localStorage")
.expect("A page has origin");
let state = StorageState { cookies: cookies.cookies, local_storage: vec![ls] };
// **save 到共享 vault**(不绑 A 的 workspace——这是「共享」的本质)。
save_storage_state(&state, &vault_path, &key).expect("save to SHARED vault");
}
// ── 伙伴 Bload 同一共享 vault → 启动注入 → 登录态恢复 ──
let loaded = load_storage_state(&vault_path, &key).expect("B loads the SHARED vault");
assert!(
find_cookie(&loaded, "nomi_session").is_some(),
"shared vault must carry A's login cookie (cross-companion sharing)"
);
let inject = loaded.to_json().expect("storage_state to_json");
// B 用独立引擎但共享同一登录 vault。
let backend_b = common::build_backend_for_fixture_with_storage_state(
"shared-B",
Some(inject),
)
.await;
backend_b.navigate(ORIGIN, false).await.expect("B navigate example.com");
let after = backend_b.capture_cookies().await.expect("B capture cookies after nav");
let c = find_cookie(&after, "nomi_session")
.expect("A's login cookie must be present in B via the SHARED vault (shared identity)");
assert_eq!(c.value, "shared-login-tok-xyz", "B inherits A's login (shared browser identity)");
backend_b
.restore_local_storage(&loaded)
.await
.expect("restore localStorage into B");
assert_eq!(
read_local_storage(&backend_b, "ls_auth").await.as_deref(),
Some("jwt.shared.signature"),
"A's localStorage login must be visible to B (shared identity via shared vault)"
);
// 共享 vault 不在 B 的 per-workspace 下(证明用的是共享路径)。
let b_workspace_vault = storage_state_path(data_dir.path().join("companionB").join("workspace").as_path());
assert_ne!(vault_path, b_workspace_vault, "B reads the SHARED vault, not a per-workspace one");
}