mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-30 02:14:43 +08:00
feat: implement Hermes session manager with SQLite + JSONL support (Phase 6)
- Add hermes.rs session provider with dual-source scanning: SQLite (state.db) as primary, JSONL transcripts as fallback - Dynamic schema discovery via PRAGMA table_info for SQLite resilience - Use read_head_tail_lines for efficient JSONL metadata extraction (head 30 lines for metadata, tail 10 for last_active_at) - Support both flat and nested JSONL message formats - Add SQLite session loading and transactional deletion - Register hermes in parallel session scan (thread::scope) - Add "hermes" to frontend ProviderFilter type - 7 unit tests covering JSONL parsing, SQLite source parsing, deletion
This commit is contained in:
@@ -4,7 +4,7 @@ pub mod terminal;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use providers::{claude, codex, gemini, openclaw, opencode};
|
||||
use providers::{claude, codex, gemini, hermes, openclaw, opencode};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -56,18 +56,20 @@ pub struct DeleteSessionOutcome {
|
||||
}
|
||||
|
||||
pub fn scan_sessions() -> Vec<SessionMeta> {
|
||||
let (r1, r2, r3, r4, r5) = std::thread::scope(|s| {
|
||||
let (r1, r2, r3, r4, r5, r6) = std::thread::scope(|s| {
|
||||
let h1 = s.spawn(codex::scan_sessions);
|
||||
let h2 = s.spawn(claude::scan_sessions);
|
||||
let h3 = s.spawn(opencode::scan_sessions);
|
||||
let h4 = s.spawn(openclaw::scan_sessions);
|
||||
let h5 = s.spawn(gemini::scan_sessions);
|
||||
let h6 = s.spawn(hermes::scan_sessions);
|
||||
(
|
||||
h1.join().unwrap_or_default(),
|
||||
h2.join().unwrap_or_default(),
|
||||
h3.join().unwrap_or_default(),
|
||||
h4.join().unwrap_or_default(),
|
||||
h5.join().unwrap_or_default(),
|
||||
h6.join().unwrap_or_default(),
|
||||
)
|
||||
});
|
||||
|
||||
@@ -77,6 +79,7 @@ pub fn scan_sessions() -> Vec<SessionMeta> {
|
||||
sessions.extend(r3);
|
||||
sessions.extend(r4);
|
||||
sessions.extend(r5);
|
||||
sessions.extend(r6);
|
||||
|
||||
sessions.sort_by(|a, b| {
|
||||
let a_ts = a.last_active_at.or(a.created_at).unwrap_or(0);
|
||||
@@ -88,10 +91,13 @@ pub fn scan_sessions() -> Vec<SessionMeta> {
|
||||
}
|
||||
|
||||
pub fn load_messages(provider_id: &str, source_path: &str) -> Result<Vec<SessionMessage>, String> {
|
||||
// OpenCode SQLite sessions use a "sqlite:" prefixed source_path
|
||||
// SQLite sessions use a "sqlite:" prefixed source_path
|
||||
if provider_id == "opencode" && source_path.starts_with("sqlite:") {
|
||||
return opencode::load_messages_sqlite(source_path);
|
||||
}
|
||||
if provider_id == "hermes" && source_path.starts_with("sqlite:") {
|
||||
return hermes::load_messages_sqlite(source_path);
|
||||
}
|
||||
|
||||
let path = Path::new(source_path);
|
||||
match provider_id {
|
||||
@@ -100,7 +106,7 @@ pub fn load_messages(provider_id: &str, source_path: &str) -> Result<Vec<Session
|
||||
"opencode" => opencode::load_messages(path),
|
||||
"openclaw" => openclaw::load_messages(path),
|
||||
"gemini" => gemini::load_messages(path),
|
||||
"hermes" => Err("Hermes session loading not yet implemented".to_string()),
|
||||
"hermes" => hermes::load_messages(path),
|
||||
_ => Err(format!("Unsupported provider: {provider_id}")),
|
||||
}
|
||||
}
|
||||
@@ -110,10 +116,13 @@ pub fn delete_session(
|
||||
session_id: &str,
|
||||
source_path: &str,
|
||||
) -> Result<bool, String> {
|
||||
// OpenCode SQLite sessions bypass the file-based deletion path
|
||||
// SQLite sessions bypass the file-based deletion path
|
||||
if provider_id == "opencode" && source_path.starts_with("sqlite:") {
|
||||
return opencode::delete_session_sqlite(session_id, source_path);
|
||||
}
|
||||
if provider_id == "hermes" && source_path.starts_with("sqlite:") {
|
||||
return hermes::delete_session_sqlite(session_id, source_path);
|
||||
}
|
||||
|
||||
let root = provider_root(provider_id)?;
|
||||
delete_session_with_root(provider_id, session_id, Path::new(source_path), &root)
|
||||
@@ -151,7 +160,7 @@ fn delete_session_with_root(
|
||||
"opencode" => opencode::delete_session(&validated_root, &validated_source, session_id),
|
||||
"openclaw" => openclaw::delete_session(&validated_root, &validated_source, session_id),
|
||||
"gemini" => gemini::delete_session(&validated_root, &validated_source, session_id),
|
||||
"hermes" => Err("Hermes session deletion not yet implemented".to_string()),
|
||||
"hermes" => hermes::delete_session(&validated_root, &validated_source, session_id),
|
||||
_ => Err(format!("Unsupported provider: {provider_id}")),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user