fix: resolve clippy warnings and formatting issues for CI
- Fix doc_markdown warnings in WhatsApp channel - Fix needless_pass_by_value in cron, health, migration, service modules - Fix match_same_arms in migration.rs - Fix too_many_lines in skills/mod.rs - Fix manual_let_else in tools/file_write.rs - Apply cargo fmt formatting fixes All 435 tests pass, clippy clean.
This commit is contained in:
parent
4fce8a5004
commit
153d6ff149
12 changed files with 46 additions and 54 deletions
|
|
@ -188,7 +188,7 @@ pub fn build_system_prompt(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inject OpenClaw (markdown) identity files into the prompt
|
/// Inject `OpenClaw` (markdown) identity files into the prompt
|
||||||
fn inject_openclaw_identity(prompt: &mut String, workspace_dir: &std::path::Path) {
|
fn inject_openclaw_identity(prompt: &mut String, workspace_dir: &std::path::Path) {
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use super::traits::{Channel, ChannelMessage};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// WhatsApp channel — uses WhatsApp Business Cloud API
|
/// `WhatsApp` channel — uses `WhatsApp` Business Cloud API
|
||||||
///
|
///
|
||||||
/// This channel operates in webhook mode (push-based) rather than polling.
|
/// This channel operates in webhook mode (push-based) rather than polling.
|
||||||
/// Messages are received via the gateway's `/whatsapp` webhook endpoint.
|
/// Messages are received via the gateway's `/whatsapp` webhook endpoint.
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,11 @@ pub struct CronJob {
|
||||||
pub last_status: Option<String>,
|
pub last_status: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_command(command: super::CronCommands, config: Config) -> Result<()> {
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
|
pub fn handle_command(command: super::CronCommands, config: &Config) -> Result<()> {
|
||||||
match command {
|
match command {
|
||||||
super::CronCommands::List => {
|
super::CronCommands::List => {
|
||||||
let jobs = list_jobs(&config)?;
|
let jobs = list_jobs(config)?;
|
||||||
if jobs.is_empty() {
|
if jobs.is_empty() {
|
||||||
println!("No scheduled tasks yet.");
|
println!("No scheduled tasks yet.");
|
||||||
println!("\nUsage:");
|
println!("\nUsage:");
|
||||||
|
|
@ -33,8 +34,7 @@ pub fn handle_command(command: super::CronCommands, config: Config) -> Result<()
|
||||||
for job in jobs {
|
for job in jobs {
|
||||||
let last_run = job
|
let last_run = job
|
||||||
.last_run
|
.last_run
|
||||||
.map(|d| d.to_rfc3339())
|
.map_or_else(|| "never".into(), |d| d.to_rfc3339());
|
||||||
.unwrap_or_else(|| "never".into());
|
|
||||||
let last_status = job.last_status.unwrap_or_else(|| "n/a".into());
|
let last_status = job.last_status.unwrap_or_else(|| "n/a".into());
|
||||||
println!(
|
println!(
|
||||||
"- {} | {} | next={} | last={} ({})\n cmd: {}",
|
"- {} | {} | next={} | last={} ({})\n cmd: {}",
|
||||||
|
|
@ -52,14 +52,14 @@ pub fn handle_command(command: super::CronCommands, config: Config) -> Result<()
|
||||||
expression,
|
expression,
|
||||||
command,
|
command,
|
||||||
} => {
|
} => {
|
||||||
let job = add_job(&config, &expression, &command)?;
|
let job = add_job(config, &expression, &command)?;
|
||||||
println!("✅ Added cron job {}", job.id);
|
println!("✅ Added cron job {}", job.id);
|
||||||
println!(" Expr: {}", job.expression);
|
println!(" Expr: {}", job.expression);
|
||||||
println!(" Next: {}", job.next_run.to_rfc3339());
|
println!(" Next: {}", job.next_run.to_rfc3339());
|
||||||
println!(" Cmd : {}", job.command);
|
println!(" Cmd : {}", job.command);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
super::CronCommands::Remove { id } => remove_job(&config, &id),
|
super::CronCommands::Remove { id } => remove_job(config, &id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ async fn execute_job_with_retry(
|
||||||
}
|
}
|
||||||
|
|
||||||
if attempt < retries {
|
if attempt < retries {
|
||||||
let jitter_ms = (Utc::now().timestamp_subsec_millis() % 250) as u64;
|
let jitter_ms = u64::from(Utc::now().timestamp_subsec_millis() % 250);
|
||||||
time::sleep(Duration::from_millis(backoff_ms + jitter_ms)).await;
|
time::sleep(Duration::from_millis(backoff_ms + jitter_ms)).await;
|
||||||
backoff_ms = (backoff_ms.saturating_mul(2)).min(30_000);
|
backoff_ms = (backoff_ms.saturating_mul(2)).min(30_000);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,25 +52,21 @@ pub fn run(config: &Config) -> Result<()> {
|
||||||
let scheduler_ok = scheduler
|
let scheduler_ok = scheduler
|
||||||
.get("status")
|
.get("status")
|
||||||
.and_then(serde_json::Value::as_str)
|
.and_then(serde_json::Value::as_str)
|
||||||
.map(|s| s == "ok")
|
.is_some_and(|s| s == "ok");
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
let scheduler_last_ok = scheduler
|
let scheduler_last_ok = scheduler
|
||||||
.get("last_ok")
|
.get("last_ok")
|
||||||
.and_then(serde_json::Value::as_str)
|
.and_then(serde_json::Value::as_str)
|
||||||
.and_then(parse_rfc3339)
|
.and_then(parse_rfc3339)
|
||||||
.map(|dt| Utc::now().signed_duration_since(dt).num_seconds())
|
.map_or(i64::MAX, |dt| {
|
||||||
.unwrap_or(i64::MAX);
|
Utc::now().signed_duration_since(dt).num_seconds()
|
||||||
|
});
|
||||||
|
|
||||||
if scheduler_ok && scheduler_last_ok <= SCHEDULER_STALE_SECONDS {
|
if scheduler_ok && scheduler_last_ok <= SCHEDULER_STALE_SECONDS {
|
||||||
println!(
|
println!(" ✅ scheduler healthy (last ok {scheduler_last_ok}s ago)");
|
||||||
" ✅ scheduler healthy (last ok {}s ago)",
|
|
||||||
scheduler_last_ok
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
" ❌ scheduler unhealthy/stale (status_ok={}, age={}s)",
|
" ❌ scheduler unhealthy/stale (status_ok={scheduler_ok}, age={scheduler_last_ok}s)"
|
||||||
scheduler_ok, scheduler_last_ok
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -86,14 +82,14 @@ pub fn run(config: &Config) -> Result<()> {
|
||||||
let status_ok = component
|
let status_ok = component
|
||||||
.get("status")
|
.get("status")
|
||||||
.and_then(serde_json::Value::as_str)
|
.and_then(serde_json::Value::as_str)
|
||||||
.map(|s| s == "ok")
|
.is_some_and(|s| s == "ok");
|
||||||
.unwrap_or(false);
|
|
||||||
let age = component
|
let age = component
|
||||||
.get("last_ok")
|
.get("last_ok")
|
||||||
.and_then(serde_json::Value::as_str)
|
.and_then(serde_json::Value::as_str)
|
||||||
.and_then(parse_rfc3339)
|
.and_then(parse_rfc3339)
|
||||||
.map(|dt| Utc::now().signed_duration_since(dt).num_seconds())
|
.map_or(i64::MAX, |dt| {
|
||||||
.unwrap_or(i64::MAX);
|
Utc::now().signed_duration_since(dt).num_seconds()
|
||||||
|
});
|
||||||
|
|
||||||
if status_ok && age <= CHANNEL_STALE_SECONDS {
|
if status_ok && age <= CHANNEL_STALE_SECONDS {
|
||||||
println!(" ✅ {name} fresh (last ok {age}s ago)");
|
println!(" ✅ {name} fresh (last ok {age}s ago)");
|
||||||
|
|
@ -107,10 +103,7 @@ pub fn run(config: &Config) -> Result<()> {
|
||||||
if channel_count == 0 {
|
if channel_count == 0 {
|
||||||
println!(" ℹ️ no channel components tracked in state yet");
|
println!(" ℹ️ no channel components tracked in state yet");
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(" Channel summary: {channel_count} total, {stale_channels} stale");
|
||||||
" Channel summary: {} total, {} stale",
|
|
||||||
channel_count, stale_channels
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ pub fn mark_component_ok(component: &str) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
pub fn mark_component_error(component: &str, error: impl ToString) {
|
pub fn mark_component_error(component: &str, error: impl ToString) {
|
||||||
let err = error.to_string();
|
let err = error.to_string();
|
||||||
upsert_component(component, move |entry| {
|
upsert_component(component, move |entry| {
|
||||||
|
|
|
||||||
|
|
@ -169,9 +169,9 @@ enum Commands {
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
enum MigrateCommands {
|
enum MigrateCommands {
|
||||||
/// Import memory from an OpenClaw workspace into this ZeroClaw workspace
|
/// Import memory from an `OpenClaw` workspace into this `ZeroClaw` workspace
|
||||||
Openclaw {
|
Openclaw {
|
||||||
/// Optional path to OpenClaw workspace (defaults to ~/.openclaw/workspace)
|
/// Optional path to `OpenClaw` workspace (defaults to ~/.openclaw/workspace)
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
source: Option<std::path::PathBuf>,
|
source: Option<std::path::PathBuf>,
|
||||||
|
|
||||||
|
|
@ -387,9 +387,9 @@ async fn main() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::Cron { cron_command } => cron::handle_command(cron_command, config),
|
Commands::Cron { cron_command } => cron::handle_command(cron_command, &config),
|
||||||
|
|
||||||
Commands::Service { service_command } => service::handle_command(service_command, &config),
|
Commands::Service { service_command } => service::handle_command(&service_command, &config),
|
||||||
|
|
||||||
Commands::Doctor => doctor::run(&config),
|
Commands::Doctor => doctor::run(&config),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,7 @@ fn read_openclaw_markdown_entries(source_workspace: &Path) -> Result<Vec<SourceE
|
||||||
Ok(all)
|
Ok(all)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
fn parse_markdown_file(
|
fn parse_markdown_file(
|
||||||
_path: &Path,
|
_path: &Path,
|
||||||
content: &str,
|
content: &str,
|
||||||
|
|
@ -306,10 +307,9 @@ fn parse_structured_memory_line(line: &str) -> Option<(&str, &str)> {
|
||||||
|
|
||||||
fn parse_category(raw: &str) -> MemoryCategory {
|
fn parse_category(raw: &str) -> MemoryCategory {
|
||||||
match raw.trim().to_ascii_lowercase().as_str() {
|
match raw.trim().to_ascii_lowercase().as_str() {
|
||||||
"core" => MemoryCategory::Core,
|
"core" | "" => MemoryCategory::Core,
|
||||||
"daily" => MemoryCategory::Daily,
|
"daily" => MemoryCategory::Daily,
|
||||||
"conversation" => MemoryCategory::Conversation,
|
"conversation" => MemoryCategory::Conversation,
|
||||||
"" => MemoryCategory::Core,
|
|
||||||
other => MemoryCategory::Custom(other.to_string()),
|
other => MemoryCategory::Custom(other.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -350,7 +350,7 @@ fn pick_optional_column_expr(columns: &[String], candidates: &[&str]) -> Option<
|
||||||
candidates
|
candidates
|
||||||
.iter()
|
.iter()
|
||||||
.find(|candidate| columns.iter().any(|c| c == *candidate))
|
.find(|candidate| columns.iter().any(|c| c == *candidate))
|
||||||
.map(|s| s.to_string())
|
.map(std::string::ToString::to_string)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pick_column_expr(columns: &[String], candidates: &[&str], fallback: &str) -> String {
|
fn pick_column_expr(columns: &[String], candidates: &[&str], fallback: &str) -> String {
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,10 @@ impl SecretStore {
|
||||||
let _ = std::process::Command::new("icacls")
|
let _ = std::process::Command::new("icacls")
|
||||||
.arg(&self.key_path)
|
.arg(&self.key_path)
|
||||||
.args(["/inheritance:r", "/grant:r"])
|
.args(["/inheritance:r", "/grant:r"])
|
||||||
.arg(format!("{}:F", std::env::var("USERNAME").unwrap_or_default()))
|
.arg(format!(
|
||||||
|
"{}:F",
|
||||||
|
std::env::var("USERNAME").unwrap_or_default()
|
||||||
|
))
|
||||||
.output();
|
.output();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use std::process::Command;
|
||||||
|
|
||||||
const SERVICE_LABEL: &str = "com.zeroclaw.daemon";
|
const SERVICE_LABEL: &str = "com.zeroclaw.daemon";
|
||||||
|
|
||||||
pub fn handle_command(command: super::ServiceCommands, config: &Config) -> Result<()> {
|
pub fn handle_command(command: &super::ServiceCommands, config: &Config) -> Result<()> {
|
||||||
match command {
|
match command {
|
||||||
super::ServiceCommands::Install => install(config),
|
super::ServiceCommands::Install => install(config),
|
||||||
super::ServiceCommands::Start => start(config),
|
super::ServiceCommands::Start => start(config),
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,7 @@ fn copy_dir_recursive(src: &Path, dest: &Path) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle the `skills` CLI command
|
/// Handle the `skills` CLI command
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
pub fn handle_command(command: super::SkillCommands, workspace_dir: &Path) -> Result<()> {
|
pub fn handle_command(command: super::SkillCommands, workspace_dir: &Path) -> Result<()> {
|
||||||
match command {
|
match command {
|
||||||
super::SkillCommands::List => {
|
super::SkillCommands::List => {
|
||||||
|
|
|
||||||
|
|
@ -69,15 +69,12 @@ impl Tool for FileWriteTool {
|
||||||
tokio::fs::create_dir_all(parent).await?;
|
tokio::fs::create_dir_all(parent).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let parent = match full_path.parent() {
|
let Some(parent) = full_path.parent() else {
|
||||||
Some(p) => p,
|
|
||||||
None => {
|
|
||||||
return Ok(ToolResult {
|
return Ok(ToolResult {
|
||||||
success: false,
|
success: false,
|
||||||
output: String::new(),
|
output: String::new(),
|
||||||
error: Some("Invalid path: missing parent directory".into()),
|
error: Some("Invalid path: missing parent directory".into()),
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Resolve parent before writing to block symlink escapes.
|
// Resolve parent before writing to block symlink escapes.
|
||||||
|
|
@ -103,15 +100,12 @@ impl Tool for FileWriteTool {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let file_name = match full_path.file_name() {
|
let Some(file_name) = full_path.file_name() else {
|
||||||
Some(name) => name,
|
|
||||||
None => {
|
|
||||||
return Ok(ToolResult {
|
return Ok(ToolResult {
|
||||||
success: false,
|
success: false,
|
||||||
output: String::new(),
|
output: String::new(),
|
||||||
error: Some("Invalid path: missing file name".into()),
|
error: Some("Invalid path: missing file name".into()),
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let resolved_target = resolved_parent.join(file_name);
|
let resolved_target = resolved_parent.join(file_name);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue