Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .ai/knowledge/crates/core/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ The native app and CLI depend on these crates directly through Rust APIs.

## Crate Map

- `session`: API boundary for live sessions, operations, observing, search, exports, attachments, and unbound utility commands.
- `session`: API boundary for live sessions, operations, observing, search, exports, and attachments.
- `processor`: ingestion/search pipeline; `MessageProducer` coordinates `ByteSource` and `Parser` implementations.
- `sources`: byte ingestion from files, TCP, UDP, serial, processes, and pcap inputs.
- `parsers`: DLT, SOME/IP, text, and plugin parser integration.
- `indexer_base`: shared chunk, time, progress, and indexing primitives.
- `indexer_base`: shared indexing primitives: index sections, timed lines, and file/line utilities.
- `plugins_host`: runtime support for loading and executing WASM plugins.
- `merging`: multi-source chronological merge logic.
- `dlt_tools`: DLT-specific utility operations.
Expand All @@ -29,7 +29,7 @@ The native app and CLI depend on these crates directly through Rust APIs.
## If You Need X, Go to Y

- Change app/backend coordination: `crates/core/session/src/`.
- Add or modify session operations: `crates/core/session/src/handlers/`, `crates/core/session/src/unbound/commands/`, and `crates/stypes/src/operations/`.
- Add or modify session operations: `crates/core/session/src/handlers/` and `crates/stypes/src/operations/`.
- Add or modify ingestion sources: `crates/core/sources/src/` and `crates/core/processor/src/producer/`.
- Add or modify parser formats: `crates/core/parsers/src/` and `crates/core/processor/src/`.
- Change search/filter behavior: `crates/core/processor/src/search/` and `crates/core/session/src/state/searchers/`.
Expand All @@ -47,15 +47,15 @@ The native app and CLI depend on these crates directly through Rust APIs.
- `session` owns operation state and coordinates processor work for live sessions.
- `processor` reads bytes from `sources`, parses them with `parsers` or plugin-backed parsers, indexes chunks, and serves search/grab/export paths.
- `plugins_host` loads plugin components and adapts them into parser/source flows.
- `stypes` carries shared command, callback, progress, plugin, observe, and error types across crate boundaries.
- `stypes` carries shared callback, plugin, observe, operation, and error types across crate boundaries.

## Landmarks and Hotspots

- `MessageProducer` in `crates/core/processor/src/producer/` for pipeline orchestration.
- `Session` APIs in `crates/core/session/src/session.rs` for frontend/backend coordination.
- Operation handlers in `crates/core/session/src/handlers/`.
- State controllers under `crates/core/session/src/state/`.
- `TimedLine` and chunk/progress types in `crates/core/indexer_base/src/`.
- `TimedLine` and index-section types in `crates/core/indexer_base/src/`.
- `PluginsManager` and plugin host wrappers in `crates/core/plugins_host/src/`.
- Pcap handling under `crates/core/sources/src/binary/pcap/`.

Expand Down
8 changes: 3 additions & 5 deletions .ai/knowledge/crates/stypes/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,20 @@ Treat changes here as cross-crate API changes.

1. `crates/stypes/Cargo.toml`
2. `crates/stypes/src/lib.rs`
3. `crates/stypes/src/command/`
3. `crates/stypes/src/observe/`
4. `crates/stypes/src/callback/`
5. `crates/stypes/src/plugins/`
6. `crates/stypes/src/error/`

## If You Need X, Go to Y

- Add or modify command/config types: `crates/stypes/src/command/`.
- Add or modify callback/result payloads: `crates/stypes/src/callback/`.
- Change progress reporting types: `crates/stypes/src/progress/`.
- Change plugin-facing metadata/config/result types: `crates/stypes/src/plugins/`.
- Change observe/session source types: `crates/stypes/src/observe/`.
- Change attachment types: `crates/stypes/src/attachment/`.
- Change operation identifiers or operation payloads: `crates/stypes/src/operations/`.
- Change shared error types or formatting: `crates/stypes/src/error/`.
- Change folder/profile utility command types: `crates/stypes/src/command/folders/`, `crates/stypes/src/command/profiles/`.
- Change shell profile types: `crates/stypes/src/shell/`.

## Coding Guidelines

Expand All @@ -37,7 +35,7 @@ Treat changes here as cross-crate API changes.
## Technology Stack

- `serde` for Rust serialization.
- `uuid`, `dlt-core`, `walkdir`, and core Rust types for shared data models.
- `uuid`, `dlt-core`, and core Rust types for shared data models.

## Validation

Expand Down
22 changes: 0 additions & 22 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ log = "0.4"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = "1.0"
thiserror = "2.0"
lazy_static = "1.5"
memchr = "2.7"
bincode = "1.3"
bytecount = "0.6"
Expand Down Expand Up @@ -95,7 +94,6 @@ pcap-parser = "0.16"
tempfile = "3.27"
env_logger = "0.11"
log4rs = "1.4"
walkdir = "2.5"
anyhow = "1.0"
toml = "0.8"
blake3 = "1.8"
Expand Down
2 changes: 1 addition & 1 deletion crates/app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ chrono.workspace = true
chrono-tz.workspace = true
dirs.workspace = true
serialport.workspace = true
dlt-core.workspace = true
dlt-core = { workspace = true, features = ["statistics"] }
rustc-hash.workspace = true
regex.workspace = true
regex-syntax.workspace = true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Function to cleanup temp files and directories created by Chipmunk and haven't
//! Cleanup of temp files and directories created by Chipmunk that haven't
//! gotten cleaned up properly.

use std::{
Expand All @@ -7,71 +7,72 @@ use std::{
time::{Duration, SystemTime},
};

use crate::paths::{get_chipmunk_downloads_dir, get_streams_dir};
use log::error;
use semver::Version;
use session_core::paths::{get_chipmunk_downloads_dir, get_streams_dir};
use stypes::{NativeError, NativeErrorKind, Severity};

/// Cleanup temporary files that is generated by Chipmunk on previous sessions.
use crate::{common::app_info, host::service::update::version_dir_prefix};

/// Cleans up temporary files that were generated by Chipmunk on previous sessions.
///
/// If provided, `download_prefix` removes matching direct child directories in
/// the downloads directory in the same scan as age-based cleanup.
pub fn cleanup_temp_files(download_prefix: Option<&str>) -> Result<(), Vec<stypes::NativeError>> {
let res_tmp = cleanup_temp_dir();
let res_download = cleanup_download_dir(download_prefix);

let errs: Vec<_> = [res_tmp, res_download]
.into_iter()
.filter_map(|res| res.err())
.collect();

if errs.is_empty() { Ok(()) } else { Err(errs) }
/// Both cleanups are always attempted, failures are logged only since the caller
/// has no way to recover from them.
pub fn cleanup_temp_files() {
if let Err(err) = cleanup_temp_dir() {
error!("Error while cleaning up temporary files. Error: {err:?}");
}

if let Err(err) = cleanup_download_dir() {
error!("Error while cleaning up downloaded files. Error: {err:?}");
}
}

/// Iterates through chipmunk temporary directory and remove the entries older
/// than one months.
fn cleanup_temp_dir() -> Result<(), stypes::NativeError> {
fn cleanup_temp_dir() -> Result<(), NativeError> {
const ONE_MONTH: Duration = Duration::from_secs(30 * 24 * 60 * 60);

let tmp_dir = get_streams_dir()?;
cleanup_dir(&tmp_dir, stale_before(ONE_MONTH)?)?;

Ok(())
}

const ONE_MONTHS_SECONDS: u64 = 30 * 60 * 24 * 60;
let modified_limit = SystemTime::now()
.checked_sub(Duration::from_secs(ONE_MONTHS_SECONDS))
.ok_or_else(|| stypes::NativeError {
severity: stypes::Severity::ERROR,
kind: stypes::NativeErrorKind::Io,
/// Modification time before which an entry counts as stale.
fn stale_before(max_age: Duration) -> Result<SystemTime, NativeError> {
SystemTime::now()
.checked_sub(max_age)
.ok_or_else(|| NativeError {
severity: Severity::ERROR,
kind: NativeErrorKind::Io,
message: Some(String::from(
"Error while calculating modification time limit",
)),
})?;

cleanup_dir(&tmp_dir, modified_limit)?;

Ok(())
})
}

/// Iterates through chipmunk downloads directory and remove stale entries or
/// fresh matching download directories.
fn cleanup_download_dir(prefix: Option<&str>) -> Result<(), stypes::NativeError> {
/// Iterates through chipmunk downloads directory and remove stale entries alongside
/// with the staging directories of the running version.
fn cleanup_download_dir() -> Result<(), NativeError> {
const ONE_WEEK: Duration = Duration::from_secs(7 * 24 * 60 * 60);

let download_dir = get_chipmunk_downloads_dir()?;
if !download_dir.exists() {
return Ok(());
}

const ONE_WEEK_SECONDS: u64 = 7 * 60 * 24 * 60;
let modified_limit = SystemTime::now()
.checked_sub(Duration::from_secs(ONE_WEEK_SECONDS))
.ok_or_else(|| stypes::NativeError {
severity: stypes::Severity::ERROR,
kind: stypes::NativeErrorKind::Io,
message: Some(String::from(
"Error while calculating modification time limit",
)),
})?;

cleanup_download_entries(&download_dir, modified_limit, prefix)?;
// Staging directories of the running version can't belong to an install in
// progress because the app has just started.
let date_limit = stale_before(ONE_WEEK)?;
let current_version = app_info::current_version();
cleanup_download_entries(&download_dir, date_limit, current_version)?;

Ok(())
}

// Clean files and directory within the given path that have a modified time older than
// the given modified date limit
/// Clean files and directory within the given path that have a modified time older than
/// the given modified date limit
fn cleanup_dir(path: &Path, modified_date_limit: SystemTime) -> io::Result<()> {
if !path.exists() {
return Ok(());
Expand All @@ -84,35 +85,36 @@ fn cleanup_dir(path: &Path, modified_date_limit: SystemTime) -> io::Result<()> {
.try_for_each(remove_entry)
}

/// Clean stale entries within the given path, plus the staging directories of the
/// given version.
fn cleanup_download_entries(
path: &Path,
modified_date_limit: SystemTime,
prefix: Option<&str>,
version: &Version,
) -> io::Result<()> {
if !path.exists() {
return Ok(());
}

let prefix = version_dir_prefix(version);

fs::read_dir(path)?
.flat_map(Result::ok)
.filter(|entry| {
is_stale_entry(entry, modified_date_limit) || is_prefixed_dir(entry, prefix)
is_stale_entry(entry, modified_date_limit) || is_prefixed_dir(entry, &prefix)
})
.map(|entry| entry.path())
.try_for_each(remove_entry)
}

/// Returns `true` when the entry was last modified before the given limit.
fn is_stale_entry(entry: &fs::DirEntry, modified_date_limit: SystemTime) -> bool {
entry
.metadata()
.is_ok_and(|meta| meta.modified().is_ok_and(|date| date < modified_date_limit))
}

fn is_prefixed_dir(entry: &fs::DirEntry, prefix: Option<&str>) -> bool {
let Some(prefix) = prefix.filter(|prefix| !prefix.is_empty()) else {
return false;
};

fn is_prefixed_dir(entry: &fs::DirEntry, prefix: &str) -> bool {
entry.file_type().is_ok_and(|file_type| file_type.is_dir())
&& entry
.file_name()
Expand All @@ -138,6 +140,8 @@ mod tests {
time::{Duration, SystemTime},
};

use semver::Version;

use super::{cleanup_dir, cleanup_download_entries};

#[test]
Expand All @@ -159,29 +163,13 @@ mod tests {
let old_limit = SystemTime::now()
.checked_sub(Duration::from_secs(3600))
.unwrap();
cleanup_download_entries(temp_path, old_limit, Some("update_4.0.1_")).unwrap();
cleanup_download_entries(temp_path, old_limit, &Version::new(4, 0, 1)).unwrap();

assert!(!current_update.exists());
assert!(other_update.exists());
assert!(matching_file.exists());
}

#[test]
fn cleanup_download_entries_ignores_empty_prefix() {
let tempdir = tempfile::tempdir().unwrap();
let temp_path = tempdir.path();

let update_dir = temp_path.join("update_4.0.1_123");
fs::create_dir(&update_dir).unwrap();

let old_limit = SystemTime::now()
.checked_sub(Duration::from_secs(3600))
.unwrap();
cleanup_download_entries(temp_path, old_limit, Some("")).unwrap();

assert!(update_dir.exists());
}

#[test]
fn test_cleanup_dir() {
// Create temporary directory with some entries
Expand Down
Loading
Loading