1use super::args::{
2 CcSwitchCommand, Cli, Command, HistoryCommand, RelayCommand, SmbCommand, SshCommand, UsbCommand,
3};
4use crate::{cc_switch, config::Config, discovery, history, sync, web};
5use anyhow::Result;
6use clap::Parser;
7
8pub async fn run() -> Result<()> {
9 let cli = Cli::parse();
10 let path = cli.config.unwrap_or_else(Config::default_path);
11 if let Command::Init { force } = cli.command {
12 Config::init(&path, force)?;
13 println!("配置已创建:{}", path.display());
14 return Ok(());
15 }
16
17 if let Command::CcSwitch {
18 command,
19 codex_home,
20 cc_switch_home,
21 } = &cli.command
22 {
23 let paths = cc_switch::Paths::resolve(codex_home.clone(), cc_switch_home.clone())?;
24 match command {
25 CcSwitchCommand::Status => {
26 let report = cc_switch::inspect(&paths)?;
27 cc_switch::print_report(&report);
28 }
29 CcSwitchCommand::Merge { apply } => {
30 let report = cc_switch::merge(&paths, *apply)?;
31 cc_switch::print_report(&report);
32 if *apply {
33 if let Some(backup) = report.backup {
34 println!("合并完成;备份:{}", backup.display());
35 }
36 } else {
37 println!("以上为预览。关闭 Codex 和 CC-Switch 后,添加 --apply 执行合并。");
38 }
39 }
40 }
41 return Ok(());
42 }
43
44 if let Command::History {
45 command,
46 codex_home,
47 } = &cli.command
48 {
49 let paths = history::Paths::resolve(codex_home.clone())?;
50 match command {
51 HistoryCommand::Status => history::print_report(&history::inspect(&paths)?),
52 HistoryCommand::Doctor {
53 apply,
54 restore_missing,
55 dedupe_session_meta,
56 } => {
57 let report = history::doctor(
58 &paths,
59 *apply,
60 restore_missing,
61 dedupe_session_meta.as_deref(),
62 )?;
63 history::print_doctor_report(&report);
64 if !*apply {
65 println!("以上为只读诊断;添加 --apply 可原子修复侧栏索引。");
66 }
67 }
68 HistoryCommand::Merge { apply } => {
69 let report = history::merge(&paths, *apply)?;
70 history::print_report(&report);
71 if *apply {
72 if let Some(backup) = report.backup {
73 println!("本地历史合并完成;备份:{}", backup.display());
74 }
75 } else {
76 println!("以上为预览;添加 --apply 后才会修改本地历史。");
77 }
78 }
79 HistoryCommand::Backup => {
80 let backup = history::backup(&paths, "manual")?;
81 println!("历史备份已创建:{}", backup.display());
82 }
83 HistoryCommand::Restore { backup, apply } => {
84 if *apply {
85 let safety = history::restore(&paths, backup)?;
86 println!("历史已从 {} 恢复", backup.display());
87 println!("恢复前安全备份:{}", safety.display());
88 } else {
89 history::validate_backup(backup)?;
90 println!("将从 {} 恢复;添加 --apply 后才会执行。", backup.display());
91 }
92 }
93 }
94 return Ok(());
95 }
96
97 if let Command::Ssh { command } = &cli.command {
98 match command {
99 SshCommand::Push {
100 host,
101 codex_home,
102 mirror,
103 } => {
104 let report = sync::ssh::push(host, codex_home.clone(), *mirror)?;
105 println!(
106 "SSH 单向同步完成:新增/更新 {},跳过 {},移除远端独有 {},冲突 {},数据库变更 {},侧栏索引补齐 {}",
107 report.copied,
108 report.skipped,
109 report.removed,
110 report.conflicts,
111 report.database_rows,
112 report.index_entries_added
113 );
114 println!("远端备份:{}", report.backup);
115 }
116 SshCommand::Receive {
117 bundle,
118 codex_home,
119 mirror,
120 } => {
121 let report = sync::ssh::receive(bundle, codex_home.clone(), *mirror)?;
122 println!("CODEX_SYNC_REPORT={}", serde_json::to_string(&report)?);
123 }
124 }
125 return Ok(());
126 }
127
128 let config = Config::load(&path)?;
129 match cli.command {
130 Command::Serve => web::serve(config).await?,
131 Command::Peers { seconds } => {
132 for peer in discovery::discover(&config, seconds).await? {
133 println!("{}\t{}\t{}", peer.name, peer.address, peer.node_id);
134 }
135 }
136 Command::Pull { peer, overwrite } => {
137 let report = sync::http::pull(&config, &peer, overwrite).await?;
138 println!(
139 "下载 {},跳过 {},冲突 {}",
140 report.downloaded, report.skipped, report.conflicts
141 );
142 }
143 Command::Status => {
144 let manifest = sync::core::manifest(&config)?;
145 println!("节点:{}", config.node_name);
146 println!("目录:{}", config.sync_dir.display());
147 println!("文件:{}", manifest.files.len());
148 println!("Web:http://{}", config.listen);
149 }
150 Command::Usb { command } => run_directory_command(&config, command, "快照已导出").await?,
151 Command::Smb { command } => run_smb_command(&config, command).await?,
152 Command::Relay { command } => run_relay_command(&config, command).await?,
153 Command::CcSwitch { .. }
154 | Command::History { .. }
155 | Command::Ssh { .. }
156 | Command::Init { .. } => unreachable!(),
157 }
158 Ok(())
159}
160
161async fn run_directory_command(cfg: &Config, command: UsbCommand, label: &str) -> Result<()> {
162 match command {
163 UsbCommand::Export { destination } => {
164 let path = sync::directory::export(cfg, &destination)?;
165 println!("{label}:{}", path.display());
166 }
167 UsbCommand::Import { source, overwrite } => {
168 print_pull_report(
169 "导入",
170 sync::directory::import(cfg, &source, overwrite).await?,
171 );
172 }
173 UsbCommand::List { source } => print_snapshots(sync::directory::list(&source)?),
174 }
175 Ok(())
176}
177
178async fn run_smb_command(cfg: &Config, command: SmbCommand) -> Result<()> {
179 match command {
180 SmbCommand::Push { share } => {
181 let path = sync::directory::export(cfg, &share)?;
182 println!("快照已推送到 SMB:{}", path.display());
183 }
184 SmbCommand::Pull { share, overwrite } => {
185 print_pull_report(
186 "拉取",
187 sync::directory::import(cfg, &share, overwrite).await?,
188 );
189 }
190 SmbCommand::List { share } => print_snapshots(sync::directory::list(&share)?),
191 }
192 Ok(())
193}
194
195async fn run_relay_command(cfg: &Config, command: RelayCommand) -> Result<()> {
196 match command {
197 RelayCommand::Push { directory } => {
198 let path = sync::directory::export(cfg, &directory)?;
199 println!("快照已发布:{}", path.display());
200 }
201 RelayCommand::Pull {
202 directory,
203 overwrite,
204 } => print_pull_report(
205 "获取",
206 sync::directory::import(cfg, &directory, overwrite).await?,
207 ),
208 RelayCommand::List { directory } => print_snapshots(sync::directory::list(&directory)?),
209 }
210 Ok(())
211}
212
213fn print_pull_report(verb: &str, report: sync::core::PullReport) {
214 println!(
215 "{verb} {},跳过 {},冲突 {}",
216 report.downloaded, report.skipped, report.conflicts
217 );
218}
219
220fn print_snapshots(snapshots: Vec<sync::directory::SnapshotInfo>) {
221 for snapshot in snapshots {
222 println!(
223 "{}\t{}\t{} 个文件\t{}",
224 snapshot.node_name,
225 snapshot.node_id,
226 snapshot.files,
227 snapshot.path.display()
228 );
229 }
230}