// SPDX-License-Identifier: AGPL-3.0-or-later use serde::{Deserialize, Serialize}; use std::cmp::Reverse; use std::fs::{self, OpenOptions}; use std::io::Write; use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; use std::path::{Component, Path, PathBuf}; use std::process::Command; use std::time::{SystemTime, UNIX_EPOCH}; use tauri::{AppHandle, Manager}; use tauri_plugin_dialog::DialogExt; use url::Url; use uuid::Uuid; const SNAPSHOT_SCHEMA: &str = "hololake.code-channel/v1"; const REGISTRY_SCHEMA: &str = "hololake.code-channel-registry/v1"; const ALLOWED_HOSTS: &[&str] = &["guanghulab.com", "guanghubingshuo.com"]; const MAX_TREE_ENTRIES: usize = 1_000; const MAX_CODE_FILE_BYTES: u64 = 2 * 1024 * 1024; #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct CloneCodeChannelInput { pub url: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct CodeChannelEntry { pub channel_id: String, pub name: String, pub source_kind: String, pub local_path: String, pub remote_url: Option, pub git_head: String, pub branch: String, pub repository_clean: bool, pub registered_at_unix_ms: u128, } #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeChannelSnapshot { pub schema: &'static str, pub state: &'static str, pub storage_root: String, pub channels: Vec, pub authority: &'static str, } #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct BrowseCodeChannelInput { pub channel_id: String, pub path: Option, } #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeTreeEntry { pub path: String, pub name: String, pub kind: &'static str, pub size_bytes: u64, } #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeTreeSnapshot { pub schema: &'static str, pub channel_id: String, pub path: String, pub entries: Vec, pub truncated: bool, } #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ReadCodeChannelFileInput { pub channel_id: String, pub path: String, } #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CodeFileProjection { pub schema: &'static str, pub channel_id: String, pub path: String, pub format: String, pub source: String, pub human_markdown: String, pub size_bytes: u64, } #[derive(Clone, Debug, Serialize, Deserialize)] struct CodeChannelRegistry { schema: String, channels: Vec, } #[derive(Clone, Debug)] struct ValidatedCloneUrl { normalized: String, owner: String, repository: String, } #[tauri::command] pub async fn get_code_channel_snapshot(app: AppHandle) -> Result { let root = code_channel_root(&app)?; tauri::async_runtime::spawn_blocking(move || snapshot_at(&root)) .await .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_JOIN_FAILED: {error}"))? } #[tauri::command] pub async fn clone_code_channel( app: AppHandle, input: CloneCodeChannelInput, ) -> Result { let root = code_channel_root(&app)?; tauri::async_runtime::spawn_blocking(move || clone_at(&root, input)) .await .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_JOIN_FAILED: {error}"))? } #[tauri::command] pub async fn select_local_code_channel( app: AppHandle, ) -> Result, String> { let picker = app.clone(); let selected = tauri::async_runtime::spawn_blocking(move || { picker .dialog() .file() .set_title("选择本地 Git 代码频道") .blocking_pick_folder() }) .await .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_PICKER_JOIN_FAILED: {error}"))?; let Some(selected) = selected else { return Ok(None); }; let selected = selected .into_path() .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_PICKER_PATH_INVALID: {error}"))?; let root = code_channel_root(&app)?; tauri::async_runtime::spawn_blocking(move || register_local_at(&root, &selected).map(Some)) .await .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_JOIN_FAILED: {error}"))? } #[tauri::command] pub async fn browse_code_channel( app: AppHandle, input: BrowseCodeChannelInput, ) -> Result { let root = code_channel_root(&app)?; tauri::async_runtime::spawn_blocking(move || browse_at(&root, input)) .await .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_JOIN_FAILED: {error}"))? } #[tauri::command] pub async fn read_code_channel_file( app: AppHandle, input: ReadCodeChannelFileInput, ) -> Result { let root = code_channel_root(&app)?; tauri::async_runtime::spawn_blocking(move || read_code_file_at(&root, input)) .await .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_JOIN_FAILED: {error}"))? } fn code_channel_root(app: &AppHandle) -> Result { let root = app .path() .app_data_dir() .map_err(|error| format!("HOLOLAKE_APP_DATA_UNAVAILABLE: {error}"))? .join("code-channel-v1"); ensure_root(&root)?; Ok(root) } pub(crate) fn register_managed_repository( app: &AppHandle, repository: &Path, name: String, ) -> Result { let root = code_channel_root(app)?; let local_path = repository.to_string_lossy().into_owned(); let existing_channel_id = read_registry(&root)? .channels .into_iter() .find(|item| item.local_path == local_path) .map(|item| item.channel_id); let mut entry = entry_for_repository(repository, "GH_PNCC_NATIVE", None, Some(name))?; if let Some(channel_id) = existing_channel_id { entry.channel_id = channel_id; } upsert_entry(&root, entry)?; read_registry(&root)? .channels .into_iter() .find(|item| item.local_path == local_path) .ok_or_else(|| "HOLOLAKE_USER_PNCC_CHANNEL_REGISTRATION_FAILED".to_string()) } fn ensure_root(root: &Path) -> Result<(), String> { fs::create_dir_all(root.join("repositories")) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_STORAGE_UNAVAILABLE: {error}"))?; fs::set_permissions(root, fs::Permissions::from_mode(0o700)) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_PERMISSION_FAILED: {error}"))?; Ok(()) } fn snapshot_at(root: &Path) -> Result { ensure_root(root)?; let mut registry = read_registry(root)?; for channel in &mut registry.channels { if let Ok(inspection) = inspect_repository(Path::new(&channel.local_path)) { channel.git_head = inspection.git_head; channel.branch = inspection.branch; channel.repository_clean = inspection.repository_clean; } } registry .channels .sort_by_key(|entry| Reverse(entry.registered_at_unix_ms)); Ok(CodeChannelSnapshot { schema: SNAPSHOT_SCHEMA, state: "READY", storage_root: root.to_string_lossy().into_owned(), channels: registry.channels, authority: "LOCAL_SOURCE_ACCESS_ONLY_NO_PUSH_OR_DEPLOY_AUTHORITY", }) } fn browse_at(root: &Path, input: BrowseCodeChannelInput) -> Result { let (channel, repository) = registered_repository(root, &input.channel_id)?; let relative = input.path.unwrap_or_default(); let directory = safe_repository_path(&repository, &relative)?; if !directory.is_dir() { return Err("HOLOLAKE_CODE_CHANNEL_DIRECTORY_INVALID".into()); } let mut entries = fs::read_dir(&directory) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_TREE_UNAVAILABLE: {error}"))? .collect::, _>>() .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_TREE_UNAVAILABLE: {error}"))?; entries.sort_by_key(|entry| { let is_file = entry.file_type().map(|kind| kind.is_file()).unwrap_or(true); (is_file, entry.file_name()) }); let mut projected = Vec::new(); for entry in entries { if projected.len() >= MAX_TREE_ENTRIES { break; } let name = entry.file_name().to_string_lossy().into_owned(); if name == ".git" || name == ".DS_Store" || name.starts_with("._") { continue; } let kind = entry .file_type() .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_TREE_UNAVAILABLE: {error}"))?; if kind.is_symlink() || (!kind.is_dir() && !kind.is_file()) { continue; } let entry_path = entry.path(); let relative_path = relative_posix(&repository, &entry_path)?; let size_bytes = if kind.is_file() { entry .metadata() .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_TREE_UNAVAILABLE: {error}"))? .len() } else { 0 }; projected.push(CodeTreeEntry { path: relative_path, name, kind: if kind.is_dir() { "directory" } else { "file" }, size_bytes, }); } Ok(CodeTreeSnapshot { schema: "hololake.code-channel-tree/v1", channel_id: channel.channel_id, path: relative, truncated: projected.len() >= MAX_TREE_ENTRIES, entries: projected, }) } fn read_code_file_at( root: &Path, input: ReadCodeChannelFileInput, ) -> Result { let (channel, repository) = registered_repository(root, &input.channel_id)?; let file = safe_repository_path(&repository, &input.path)?; let metadata = fs::metadata(&file) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_FILE_UNAVAILABLE: {error}"))?; if !metadata.is_file() || metadata.len() > MAX_CODE_FILE_BYTES || !is_supported_text_file(&file) { return Err("HOLOLAKE_CODE_CHANNEL_FILE_UNSUPPORTED".into()); } let source = fs::read_to_string(&file) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_FILE_INVALID_UTF8: {error}"))?; let format = file_format(&file); let human_markdown = human_projection(&input.path, &format, &source); Ok(CodeFileProjection { schema: "hololake.code-channel-file-projection/v1", channel_id: channel.channel_id, path: input.path, format, source, human_markdown, size_bytes: metadata.len(), }) } fn registered_repository( root: &Path, channel_id: &str, ) -> Result<(CodeChannelEntry, PathBuf), String> { let registry = read_registry(root)?; let channel = registry .channels .into_iter() .find(|entry| entry.channel_id == channel_id) .ok_or("HOLOLAKE_CODE_CHANNEL_NOT_REGISTERED")?; let repository = PathBuf::from(&channel.local_path) .canonicalize() .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REPOSITORY_UNAVAILABLE: {error}"))?; if !repository.is_dir() { return Err("HOLOLAKE_CODE_CHANNEL_REPOSITORY_UNAVAILABLE".into()); } Ok((channel, repository)) } fn safe_repository_path(root: &Path, relative: &str) -> Result { let relative_path = Path::new(relative); if relative_path.is_absolute() || relative_path .components() .any(|component| !matches!(component, Component::Normal(_))) { if relative.is_empty() { return Ok(root.to_path_buf()); } return Err("HOLOLAKE_CODE_CHANNEL_PATH_INVALID".into()); } if relative.is_empty() { return Ok(root.to_path_buf()); } let candidate = root.join(relative_path); let metadata = fs::symlink_metadata(&candidate) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_PATH_UNAVAILABLE: {error}"))?; if metadata.file_type().is_symlink() { return Err("HOLOLAKE_CODE_CHANNEL_SYMLINK_DENIED".into()); } let canonical = candidate .canonicalize() .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_PATH_UNAVAILABLE: {error}"))?; if !canonical.starts_with(root) { return Err("HOLOLAKE_CODE_CHANNEL_PATH_ESCAPE".into()); } Ok(canonical) } fn relative_posix(root: &Path, path: &Path) -> Result { Ok(path .strip_prefix(root) .map_err(|_| "HOLOLAKE_CODE_CHANNEL_PATH_INVALID".to_string())? .to_string_lossy() .replace('\\', "/")) } fn is_supported_text_file(path: &Path) -> bool { let name = path .file_name() .and_then(|value| value.to_str()) .unwrap_or(""); if ["README", "LICENSE", "Dockerfile", "Makefile", "Procfile"].contains(&name) { return true; } matches!( path.extension() .and_then(|value| value.to_str()) .unwrap_or("") .to_ascii_lowercase() .as_str(), "md" | "markdown" | "txt" | "json" | "jsonl" | "yaml" | "yml" | "toml" | "hdlp" | "rs" | "ts" | "tsx" | "js" | "jsx" | "mjs" | "cjs" | "py" | "sh" | "zsh" | "bash" | "css" | "scss" | "html" | "xml" | "sql" | "graphql" | "go" | "java" | "kt" | "swift" | "c" | "h" | "cpp" | "hpp" | "proto" | "ini" | "conf" | "env" ) } fn file_format(path: &Path) -> String { path.extension() .and_then(|value| value.to_str()) .filter(|value| !value.is_empty()) .unwrap_or("text") .to_ascii_lowercase() } fn human_projection(path: &str, format: &str, source: &str) -> String { if matches!(format, "md" | "markdown") { return source.to_string(); } let title = Path::new(path) .file_name() .and_then(|value| value.to_str()) .unwrap_or("代码文件"); if format == "json" { if let Ok(value) = serde_json::from_str::(source) { return format!( "# {title}\n\n> 来自代码频道的 JSON 结构,已转换为可读层级。\n\n{}", json_as_markdown(&value, 2) ); } } if matches!(format, "hdlp" | "yaml" | "yml" | "toml" | "ini" | "conf") { let rows = source .lines() .filter_map(|line| { let trimmed = line.trim(); if trimmed.is_empty() || trimmed.starts_with('#') { return None; } trimmed .split_once(':') .or_else(|| trimmed.split_once('=')) .map(|(key, value)| format!("| {} | {} |", key.trim(), value.trim())) }) .take(120) .collect::>(); if !rows.is_empty() { return format!( "# {title}\n\n> 来自代码频道的结构化文件。原始机器字段已转成阅读表格。\n\n| 字段 | 内容 |\n| --- | --- |\n{}", rows.join("\n") ); } } format!( "# {title}\n\n> 来自代码频道的 `{format}` 文件。下方保留原始内容,便于核验。\n\n```{format}\n{source}\n```" ) } fn json_as_markdown(value: &serde_json::Value, level: usize) -> String { match value { serde_json::Value::Object(map) => map .iter() .map(|(key, value)| match value { serde_json::Value::Object(_) | serde_json::Value::Array(_) => format!( "{} {key}\n\n{}", "#".repeat(level.min(5)), json_as_markdown(value, level + 1) ), _ => format!("- **{key}**:{}", scalar_json(value)), }) .collect::>() .join("\n\n"), serde_json::Value::Array(values) => values .iter() .enumerate() .map(|(index, value)| match value { serde_json::Value::Object(_) | serde_json::Value::Array(_) => { format!("{}. {}", index + 1, json_as_markdown(value, level + 1)) } _ => format!("- {}", scalar_json(value)), }) .collect::>() .join("\n"), _ => scalar_json(value), } } fn scalar_json(value: &serde_json::Value) -> String { value .as_str() .map(ToOwned::to_owned) .unwrap_or_else(|| value.to_string()) } fn clone_at(root: &Path, input: CloneCodeChannelInput) -> Result { ensure_root(root)?; let validated = validate_clone_url(&input.url)?; let destination = root.join("repositories").join(format!( "{}--{}", safe_segment(&validated.owner), safe_segment(&validated.repository) )); if destination.exists() { return Err("HOLOLAKE_CODE_CHANNEL_DESTINATION_EXISTS".into()); } let output = Command::new("/usr/bin/git") .env("GIT_TERMINAL_PROMPT", "0") .env("GIT_ASKPASS", "/usr/bin/false") .env("SSH_ASKPASS", "/usr/bin/false") .env("GIT_CONFIG_NOSYSTEM", "1") .args([ "-c", "credential.helper=", "-c", "core.askPass=", "clone", "--origin", "origin", "--no-tags", ]) .arg(&validated.normalized) .arg(&destination) .output() .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_CLONE_FAILED: {error}"))?; if !output.status.success() { if destination.starts_with(root.join("repositories")) { let _ = fs::remove_dir_all(&destination); } return Err(format!( "HOLOLAKE_CODE_CHANNEL_CLONE_FAILED: {}", String::from_utf8_lossy(&output.stderr).trim() )); } let entry = entry_for_repository( &destination, "CLONED_HTTPS", Some(validated.normalized), Some(format!("{}/{}", validated.owner, validated.repository)), )?; upsert_entry(root, entry)?; snapshot_at(root) } fn register_local_at(root: &Path, selected: &Path) -> Result { ensure_root(root)?; let canonical = selected .canonicalize() .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_LOCAL_UNAVAILABLE: {error}"))?; if !canonical.is_dir() { return Err("HOLOLAKE_CODE_CHANNEL_LOCAL_INVALID".into()); } let remote = git_optional(&canonical, &["remote", "get-url", "origin"]) .and_then(|value| redact_remote_url(value.trim())); let entry = entry_for_repository(&canonical, "LOCAL_GIT", remote, None)?; upsert_entry(root, entry)?; snapshot_at(root) } struct RepositoryInspection { git_head: String, branch: String, repository_clean: bool, } fn inspect_repository(path: &Path) -> Result { let inside = git(path, &["rev-parse", "--is-inside-work-tree"], "INSPECT")?; if inside.trim() != "true" { return Err("HOLOLAKE_CODE_CHANNEL_NOT_GIT".into()); } let git_head = git(path, &["rev-parse", "HEAD"], "READ_HEAD")? .trim() .to_string(); let branch = git_optional(path, &["symbolic-ref", "--short", "HEAD"]) .map(|value| value.trim().to_string()) .filter(|value| !value.is_empty()) .unwrap_or_else(|| "DETACHED".into()); let repository_clean = git(path, &["status", "--porcelain"], "READ_STATUS")? .trim() .is_empty(); Ok(RepositoryInspection { git_head, branch, repository_clean, }) } fn entry_for_repository( path: &Path, source_kind: &str, remote_url: Option, explicit_name: Option, ) -> Result { let inspection = inspect_repository(path)?; let name = explicit_name.unwrap_or_else(|| { path.file_name() .and_then(|value| value.to_str()) .unwrap_or("代码频道") .to_string() }); Ok(CodeChannelEntry { channel_id: format!("channel-{}", Uuid::new_v4()), name, source_kind: source_kind.into(), local_path: path.to_string_lossy().into_owned(), remote_url, git_head: inspection.git_head, branch: inspection.branch, repository_clean: inspection.repository_clean, registered_at_unix_ms: now_unix_ms()?, }) } fn validate_clone_url(raw: &str) -> Result { let raw = raw.trim(); if raw.len() > 2_048 { return Err("HOLOLAKE_CODE_CHANNEL_URL_INVALID".into()); } let mut parsed = Url::parse(raw).map_err(|_| "HOLOLAKE_CODE_CHANNEL_URL_INVALID")?; if parsed.scheme() != "https" || !parsed.username().is_empty() || parsed.password().is_some() || parsed.query().is_some() || parsed.fragment().is_some() || parsed.port().is_some() { return Err("HOLOLAKE_CODE_CHANNEL_URL_INVALID".into()); } let host = parsed .host_str() .ok_or("HOLOLAKE_CODE_CHANNEL_URL_INVALID")? .to_lowercase(); if !ALLOWED_HOSTS.contains(&host.as_str()) { return Err("HOLOLAKE_CODE_CHANNEL_HOST_NOT_TRUSTED".into()); } let segments = parsed .path_segments() .ok_or("HOLOLAKE_CODE_CHANNEL_URL_INVALID")? .filter(|segment| !segment.is_empty()) .collect::>(); if segments.len() != 3 || segments[0] != "code" { return Err("HOLOLAKE_CODE_CHANNEL_URL_INVALID".into()); } let owner = segments[1].to_string(); let repository = segments[2] .strip_suffix(".git") .unwrap_or(segments[2]) .to_string(); if !valid_git_segment(&owner) || !valid_git_segment(&repository) { return Err("HOLOLAKE_CODE_CHANNEL_URL_INVALID".into()); } parsed.set_path(&format!("/code/{owner}/{repository}.git")); Ok(ValidatedCloneUrl { normalized: parsed.to_string(), owner, repository, }) } fn valid_git_segment(value: &str) -> bool { !value.is_empty() && value.len() <= 100 && value != "." && value != ".." && value .chars() .all(|character| character.is_ascii_alphanumeric() || "-_.".contains(character)) } fn redact_remote_url(raw: &str) -> Option { let mut parsed = Url::parse(raw).ok()?; if parsed.scheme() != "https" { return None; } parsed.set_username("").ok()?; parsed.set_password(None).ok()?; parsed.set_query(None); parsed.set_fragment(None); Some(parsed.to_string()) } fn safe_segment(value: &str) -> String { value .chars() .map(|character| { if character.is_ascii_alphanumeric() || "-_.".contains(character) { character } else { '-' } }) .collect() } fn upsert_entry(root: &Path, entry: CodeChannelEntry) -> Result<(), String> { let mut registry = read_registry(root)?; registry .channels .retain(|existing| existing.local_path != entry.local_path); registry.channels.push(entry); write_registry(root, ®istry) } fn read_registry(root: &Path) -> Result { let path = root.join("channels.json"); if !path.exists() { return Ok(CodeChannelRegistry { schema: REGISTRY_SCHEMA.into(), channels: Vec::new(), }); } let bytes = fs::read(&path) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REGISTRY_UNAVAILABLE: {error}"))?; let registry: CodeChannelRegistry = serde_json::from_slice(&bytes) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REGISTRY_INVALID: {error}"))?; if registry.schema != REGISTRY_SCHEMA { return Err("HOLOLAKE_CODE_CHANNEL_REGISTRY_SCHEMA_INVALID".into()); } Ok(registry) } fn write_registry(root: &Path, registry: &CodeChannelRegistry) -> Result<(), String> { let bytes = serde_json::to_vec_pretty(registry) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REGISTRY_INVALID: {error}"))?; let temporary = root.join(format!("channels.json.tmp-{}", Uuid::new_v4())); let final_path = root.join("channels.json"); let mut file = OpenOptions::new() .create_new(true) .write(true) .mode(0o600) .open(&temporary) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REGISTRY_WRITE_FAILED: {error}"))?; file.write_all(&bytes) .and_then(|_| file.sync_all()) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REGISTRY_WRITE_FAILED: {error}"))?; fs::rename(&temporary, &final_path) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_REGISTRY_WRITE_FAILED: {error}"))?; Ok(()) } fn git(root: &Path, args: &[&str], operation: &str) -> Result { let output = Command::new("/usr/bin/git") .current_dir(root) .env("GIT_TERMINAL_PROMPT", "0") .args(args) .output() .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_GIT_{operation}_FAILED: {error}"))?; if !output.status.success() { return Err(format!( "HOLOLAKE_CODE_CHANNEL_GIT_{operation}_FAILED: {}", String::from_utf8_lossy(&output.stderr).trim() )); } String::from_utf8(output.stdout) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_GIT_{operation}_INVALID_UTF8: {error}")) } fn git_optional(root: &Path, args: &[&str]) -> Option { let output = Command::new("/usr/bin/git") .current_dir(root) .env("GIT_TERMINAL_PROMPT", "0") .args(args) .output() .ok()?; output .status .success() .then(|| String::from_utf8_lossy(&output.stdout).into_owned()) } fn now_unix_ms() -> Result { SystemTime::now() .duration_since(UNIX_EPOCH) .map(|duration| duration.as_millis()) .map_err(|error| format!("HOLOLAKE_CLOCK_INVALID: {error}")) } #[cfg(test)] mod tests { use super::*; use tempfile::tempdir; #[test] fn clone_url_is_normalized_to_official_https_git_endpoint() { let parsed = validate_clone_url("https://guanghulab.com/code/bingshuo/guanghu-ice-heart").unwrap(); assert_eq!( parsed.normalized, "https://guanghulab.com/code/bingshuo/guanghu-ice-heart.git" ); assert_eq!(parsed.owner, "bingshuo"); assert_eq!(parsed.repository, "guanghu-ice-heart"); } #[test] fn clone_url_rejects_credentials_queries_and_unknown_hosts() { assert!( validate_clone_url("https://user:secret@guanghulab.com/code/bingshuo/repo").is_err() ); assert!( validate_clone_url("https://guanghulab.com/code/bingshuo/repo?token=secret").is_err() ); assert!(validate_clone_url("https://example.com/code/bingshuo/repo").is_err()); } #[test] fn remote_redaction_removes_credentials_and_fragments() { assert_eq!( redact_remote_url("https://user:secret@example.com/a/b.git?x=1#readme"), Some("https://example.com/a/b.git".into()) ); assert_eq!(redact_remote_url("git@example.com:a/b.git"), None); } #[test] fn local_repository_registration_is_persistent_and_deduplicated() { let root = tempdir().unwrap(); let repository = tempdir().unwrap(); ensure_root(root.path()).unwrap(); git( repository.path(), &["init", "--initial-branch=main"], "TEST_INIT", ) .unwrap(); git( repository.path(), &["config", "user.name", "Test"], "TEST_CONFIG", ) .unwrap(); git( repository.path(), &["config", "user.email", "test@example.invalid"], "TEST_CONFIG", ) .unwrap(); fs::write(repository.path().join("README.md"), "# test\n").unwrap(); git(repository.path(), &["add", "README.md"], "TEST_ADD").unwrap(); git( repository.path(), &["commit", "-m", "initial"], "TEST_COMMIT", ) .unwrap(); register_local_at(root.path(), repository.path()).unwrap(); register_local_at(root.path(), repository.path()).unwrap(); let snapshot = snapshot_at(root.path()).unwrap(); assert_eq!(snapshot.channels.len(), 1); assert_eq!(snapshot.channels[0].branch, "main"); assert!(snapshot.channels[0].repository_clean); } #[test] fn registered_repository_can_be_browsed_and_projected_as_knowledge() { let root = tempdir().unwrap(); let repository = tempdir().unwrap(); ensure_root(root.path()).unwrap(); git( repository.path(), &["init", "--initial-branch=main"], "TEST_INIT", ) .unwrap(); git( repository.path(), &["config", "user.name", "Test"], "TEST_CONFIG", ) .unwrap(); git( repository.path(), &["config", "user.email", "test@example.invalid"], "TEST_CONFIG", ) .unwrap(); fs::create_dir(repository.path().join("config")).unwrap(); fs::write( repository.path().join("config/system.json"), r#"{"name":"光湖","state":"ready","routes":["native","mcp"]}"#, ) .unwrap(); git(repository.path(), &["add", "."], "TEST_ADD").unwrap(); git( repository.path(), &["commit", "-m", "initial"], "TEST_COMMIT", ) .unwrap(); let snapshot = register_local_at(root.path(), repository.path()).unwrap(); let channel_id = snapshot.channels[0].channel_id.clone(); let tree = browse_at( root.path(), BrowseCodeChannelInput { channel_id: channel_id.clone(), path: None, }, ) .unwrap(); assert!(tree .entries .iter() .any(|entry| entry.name == "config" && entry.kind == "directory")); let projection = read_code_file_at( root.path(), ReadCodeChannelFileInput { channel_id, path: "config/system.json".into(), }, ) .unwrap(); assert_eq!(projection.format, "json"); assert!(projection .human_markdown .contains("来自代码频道的 JSON 结构")); assert!(projection.human_markdown.contains("**state**:ready")); } #[test] fn code_tree_denies_parent_escape() { let repository = tempdir().unwrap(); assert!(safe_repository_path(repository.path(), "../secret").is_err()); } }