chasm_cli/integrations/
system.rs

1// Copyright (c) 2024-2026 Nervosys LLC
2// SPDX-License-Identifier: Apache-2.0
3//! System Integrations
4//!
5//! Shell, Clipboard, Filesystem, Notifications, System Info
6
7use super::IntegrationResult;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::path::PathBuf;
11
12// =============================================================================
13// Shell / Terminal
14// =============================================================================
15
16/// Shell command result
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct CommandResult {
19    pub command: String,
20    pub stdout: String,
21    pub stderr: String,
22    pub exit_code: i32,
23    pub duration_ms: u64,
24    pub started_at: String,
25}
26
27/// Shell environment
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct ShellEnvironment {
30    pub shell: String,
31    pub cwd: PathBuf,
32    pub env_vars: HashMap<String, String>,
33}
34
35/// Shell provider trait
36#[async_trait::async_trait]
37pub trait ShellProvider: Send + Sync {
38    /// Run a command and wait for result
39    async fn run_command(&self, command: &str, cwd: Option<&str>) -> IntegrationResult;
40
41    /// Run a command with custom environment
42    async fn run_with_env(&self, command: &str, env: HashMap<String, String>) -> IntegrationResult;
43
44    /// Run a command in the background
45    async fn run_background(&self, command: &str) -> IntegrationResult;
46
47    /// Kill a background process
48    async fn kill_process(&self, pid: u32) -> IntegrationResult;
49
50    /// Get current shell environment
51    async fn get_environment(&self) -> IntegrationResult;
52
53    /// Set environment variable
54    async fn set_env_var(&self, name: &str, value: &str) -> IntegrationResult;
55
56    /// Source a shell script
57    async fn source_script(&self, path: &str) -> IntegrationResult;
58}
59
60// =============================================================================
61// Clipboard
62// =============================================================================
63
64/// Clipboard content types
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub enum ClipboardContent {
67    Text(String),
68    Html(String),
69    Image(Vec<u8>),
70    Files(Vec<PathBuf>),
71    RichText { text: String, rtf: String },
72}
73
74/// Clipboard history entry
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct ClipboardEntry {
77    pub content: ClipboardContent,
78    pub timestamp: String,
79    pub source_app: Option<String>,
80}
81
82/// Clipboard provider trait
83#[async_trait::async_trait]
84pub trait ClipboardProvider: Send + Sync {
85    /// Get current clipboard content
86    async fn get(&self) -> IntegrationResult;
87
88    /// Set clipboard content
89    async fn set_text(&self, text: &str) -> IntegrationResult;
90
91    /// Set clipboard HTML
92    async fn set_html(&self, html: &str) -> IntegrationResult;
93
94    /// Set clipboard image
95    async fn set_image(&self, image_data: Vec<u8>) -> IntegrationResult;
96
97    /// Get clipboard history (requires clipboard manager)
98    async fn get_history(&self, limit: u32) -> IntegrationResult;
99
100    /// Clear clipboard
101    async fn clear(&self) -> IntegrationResult;
102}
103
104// =============================================================================
105// Filesystem
106// =============================================================================
107
108/// File info
109#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct FileInfo {
111    pub path: PathBuf,
112    pub name: String,
113    pub is_dir: bool,
114    pub is_file: bool,
115    pub is_symlink: bool,
116    pub size_bytes: u64,
117    pub created: Option<String>,
118    pub modified: Option<String>,
119    pub accessed: Option<String>,
120    pub permissions: Option<u32>,
121}
122
123/// File search options
124#[derive(Debug, Clone, Default, Serialize, Deserialize)]
125pub struct SearchOptions {
126    pub pattern: String,
127    pub recursive: bool,
128    pub include_hidden: bool,
129    pub file_types: Option<Vec<String>>,
130    pub max_depth: Option<u32>,
131    pub max_results: Option<u32>,
132}
133
134/// Watch event types
135#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
136#[serde(rename_all = "snake_case")]
137pub enum WatchEvent {
138    Created,
139    Modified,
140    Deleted,
141    Renamed,
142    Accessed,
143}
144
145/// Filesystem provider trait
146#[async_trait::async_trait]
147pub trait FilesystemProvider: Send + Sync {
148    // Reading
149    async fn read_file(&self, path: &str) -> IntegrationResult;
150    async fn read_file_bytes(&self, path: &str) -> IntegrationResult;
151    async fn read_json(&self, path: &str) -> IntegrationResult;
152    async fn list_dir(&self, path: &str) -> IntegrationResult;
153    async fn get_file_info(&self, path: &str) -> IntegrationResult;
154
155    // Writing
156    async fn write_file(&self, path: &str, content: &str) -> IntegrationResult;
157    async fn write_file_bytes(&self, path: &str, content: Vec<u8>) -> IntegrationResult;
158    async fn append_file(&self, path: &str, content: &str) -> IntegrationResult;
159
160    // Operations
161    async fn create_dir(&self, path: &str, recursive: bool) -> IntegrationResult;
162    async fn copy(&self, src: &str, dst: &str) -> IntegrationResult;
163    async fn move_path(&self, src: &str, dst: &str) -> IntegrationResult;
164    async fn delete(&self, path: &str, recursive: bool) -> IntegrationResult;
165    async fn exists(&self, path: &str) -> IntegrationResult;
166
167    // Search
168    async fn search(&self, base_path: &str, options: SearchOptions) -> IntegrationResult;
169    async fn glob(&self, pattern: &str) -> IntegrationResult;
170
171    // Watch
172    async fn watch(&self, path: &str, events: Vec<WatchEvent>) -> IntegrationResult;
173    async fn unwatch(&self, watch_id: &str) -> IntegrationResult;
174}
175
176// =============================================================================
177// System Notifications
178// =============================================================================
179
180/// Notification priority
181#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
182#[serde(rename_all = "snake_case")]
183pub enum NotificationPriority {
184    Low,
185    Normal,
186    High,
187    Urgent,
188}
189
190/// Notification action button
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct NotificationAction {
193    pub id: String,
194    pub label: String,
195    pub is_destructive: bool,
196}
197
198/// Notification options
199#[derive(Debug, Clone, Default, Serialize, Deserialize)]
200pub struct NotificationOptions {
201    pub title: String,
202    pub body: String,
203    pub subtitle: Option<String>,
204    pub icon: Option<String>,
205    pub image: Option<String>,
206    pub sound: Option<String>,
207    pub priority: Option<NotificationPriority>,
208    pub actions: Vec<NotificationAction>,
209    pub timeout_ms: Option<u32>,
210    pub silent: bool,
211}
212
213/// System notifications provider trait
214#[async_trait::async_trait]
215pub trait SystemNotificationProvider: Send + Sync {
216    /// Send a notification
217    async fn notify(&self, options: NotificationOptions) -> IntegrationResult;
218
219    /// Schedule a notification
220    async fn schedule(&self, options: NotificationOptions, at: &str) -> IntegrationResult;
221
222    /// Cancel a scheduled notification
223    async fn cancel(&self, notification_id: &str) -> IntegrationResult;
224
225    /// List pending notifications
226    async fn list_pending(&self) -> IntegrationResult;
227
228    /// Request notification permission
229    async fn request_permission(&self) -> IntegrationResult;
230}
231
232// =============================================================================
233// System Info
234// =============================================================================
235
236/// CPU info
237#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct CpuInfo {
239    pub model: String,
240    pub cores: u32,
241    pub threads: u32,
242    pub usage_percent: f32,
243    pub frequency_mhz: u64,
244}
245
246/// Memory info
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct MemoryInfo {
249    pub total_bytes: u64,
250    pub used_bytes: u64,
251    pub free_bytes: u64,
252    pub available_bytes: u64,
253    pub usage_percent: f32,
254}
255
256/// Disk info
257#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct DiskInfo {
259    pub name: String,
260    pub mount_point: String,
261    pub fs_type: String,
262    pub total_bytes: u64,
263    pub used_bytes: u64,
264    pub free_bytes: u64,
265    pub usage_percent: f32,
266}
267
268/// Network interface info
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct NetworkInfo {
271    pub name: String,
272    pub ip_address: Option<String>,
273    pub mac_address: Option<String>,
274    pub is_up: bool,
275    pub bytes_sent: u64,
276    pub bytes_received: u64,
277}
278
279/// Battery info
280#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct BatteryInfo {
282    pub charge_percent: u8,
283    pub is_charging: bool,
284    pub is_plugged_in: bool,
285    pub time_to_full_mins: Option<u32>,
286    pub time_to_empty_mins: Option<u32>,
287    pub health_percent: Option<u8>,
288}
289
290/// System info provider trait
291#[async_trait::async_trait]
292pub trait SystemInfoProvider: Send + Sync {
293    async fn get_os_info(&self) -> IntegrationResult;
294    async fn get_cpu_info(&self) -> IntegrationResult;
295    async fn get_memory_info(&self) -> IntegrationResult;
296    async fn get_disk_info(&self) -> IntegrationResult;
297    async fn get_network_info(&self) -> IntegrationResult;
298    async fn get_battery_info(&self) -> IntegrationResult;
299    async fn get_uptime(&self) -> IntegrationResult;
300    async fn get_load_average(&self) -> IntegrationResult;
301    async fn get_processes(&self, limit: Option<u32>) -> IntegrationResult;
302}
303
304// =============================================================================
305// Application Control
306// =============================================================================
307
308/// Application info
309#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct AppInfo {
311    pub name: String,
312    pub bundle_id: Option<String>,
313    pub path: PathBuf,
314    pub version: Option<String>,
315    pub is_running: bool,
316    pub pid: Option<u32>,
317}
318
319/// Window info
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct WindowInfo {
322    pub id: u64,
323    pub title: String,
324    pub app_name: String,
325    pub is_focused: bool,
326    pub is_visible: bool,
327    pub position: (i32, i32),
328    pub size: (u32, u32),
329}
330
331/// Application control provider trait
332#[async_trait::async_trait]
333pub trait AppControlProvider: Send + Sync {
334    // Apps
335    async fn list_installed_apps(&self) -> IntegrationResult;
336    async fn list_running_apps(&self) -> IntegrationResult;
337    async fn launch_app(&self, app_id: &str) -> IntegrationResult;
338    async fn quit_app(&self, app_id: &str) -> IntegrationResult;
339    async fn focus_app(&self, app_id: &str) -> IntegrationResult;
340
341    // Windows
342    async fn list_windows(&self) -> IntegrationResult;
343    async fn focus_window(&self, window_id: u64) -> IntegrationResult;
344    async fn close_window(&self, window_id: u64) -> IntegrationResult;
345    async fn minimize_window(&self, window_id: u64) -> IntegrationResult;
346    async fn maximize_window(&self, window_id: u64) -> IntegrationResult;
347    async fn move_window(&self, window_id: u64, x: i32, y: i32) -> IntegrationResult;
348    async fn resize_window(&self, window_id: u64, width: u32, height: u32) -> IntegrationResult;
349}
350
351// =============================================================================
352// Keyboard / Input Simulation
353// =============================================================================
354
355/// Key modifier
356#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
357#[serde(rename_all = "snake_case")]
358pub enum KeyModifier {
359    Shift,
360    Ctrl,
361    Alt,
362    Meta,
363    Cmd,
364}
365
366/// Input provider trait
367#[async_trait::async_trait]
368pub trait InputProvider: Send + Sync {
369    /// Type text
370    async fn type_text(&self, text: &str, delay_ms: Option<u32>) -> IntegrationResult;
371
372    /// Press a key
373    async fn press_key(&self, key: &str, modifiers: Vec<KeyModifier>) -> IntegrationResult;
374
375    /// Press a key combination
376    async fn key_combo(&self, keys: Vec<&str>) -> IntegrationResult;
377
378    /// Move mouse
379    async fn move_mouse(&self, x: i32, y: i32) -> IntegrationResult;
380
381    /// Click mouse
382    async fn click(&self, button: &str) -> IntegrationResult;
383
384    /// Double click
385    async fn double_click(&self) -> IntegrationResult;
386
387    /// Scroll
388    async fn scroll(&self, dx: i32, dy: i32) -> IntegrationResult;
389}
390
391// =============================================================================
392// Audio Control
393// =============================================================================
394
395/// Audio device
396#[derive(Debug, Clone, Serialize, Deserialize)]
397pub struct AudioDevice {
398    pub id: String,
399    pub name: String,
400    pub device_type: AudioDeviceType,
401    pub is_default: bool,
402    pub volume: u8,
403    pub is_muted: bool,
404}
405
406#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
407#[serde(rename_all = "snake_case")]
408pub enum AudioDeviceType {
409    Output,
410    Input,
411}
412
413/// Audio control provider trait
414#[async_trait::async_trait]
415pub trait AudioProvider: Send + Sync {
416    async fn list_devices(&self) -> IntegrationResult;
417    async fn get_volume(&self) -> IntegrationResult;
418    async fn set_volume(&self, volume: u8) -> IntegrationResult;
419    async fn mute(&self) -> IntegrationResult;
420    async fn unmute(&self) -> IntegrationResult;
421    async fn set_default_device(&self, device_id: &str) -> IntegrationResult;
422}
423
424// =============================================================================
425// Display Control
426// =============================================================================
427
428/// Display info
429#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct DisplayInfo {
431    pub id: String,
432    pub name: String,
433    pub width: u32,
434    pub height: u32,
435    pub refresh_rate: u32,
436    pub is_primary: bool,
437    pub brightness: Option<u8>,
438    pub scale_factor: f32,
439}
440
441/// Display control provider trait
442#[async_trait::async_trait]
443pub trait DisplayProvider: Send + Sync {
444    async fn list_displays(&self) -> IntegrationResult;
445    async fn get_brightness(&self, display_id: &str) -> IntegrationResult;
446    async fn set_brightness(&self, display_id: &str, brightness: u8) -> IntegrationResult;
447    async fn take_screenshot(&self, display_id: Option<&str>) -> IntegrationResult;
448    async fn take_screenshot_region(
449        &self,
450        x: i32,
451        y: i32,
452        width: u32,
453        height: u32,
454    ) -> IntegrationResult;
455}
456
457// =============================================================================
458// Power Management
459// =============================================================================
460
461/// Power action
462#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
463#[serde(rename_all = "snake_case")]
464pub enum PowerAction {
465    Shutdown,
466    Restart,
467    Sleep,
468    Hibernate,
469    Lock,
470    LogOut,
471}
472
473/// Power management provider trait
474#[async_trait::async_trait]
475pub trait PowerProvider: Send + Sync {
476    async fn perform_action(&self, action: PowerAction) -> IntegrationResult;
477    async fn schedule_action(&self, action: PowerAction, at: &str) -> IntegrationResult;
478    async fn cancel_scheduled(&self) -> IntegrationResult;
479    async fn prevent_sleep(&self, reason: &str) -> IntegrationResult;
480    async fn allow_sleep(&self) -> IntegrationResult;
481}
482
483// =============================================================================
484// Cron / Scheduled Tasks
485// =============================================================================
486
487/// Scheduled task
488#[derive(Debug, Clone, Serialize, Deserialize)]
489pub struct ScheduledTask {
490    pub id: String,
491    pub name: String,
492    pub command: String,
493    pub schedule: String, // cron expression
494    pub enabled: bool,
495    pub last_run: Option<String>,
496    pub next_run: Option<String>,
497}
498
499/// Scheduler provider trait
500#[async_trait::async_trait]
501pub trait SchedulerProvider: Send + Sync {
502    async fn list_tasks(&self) -> IntegrationResult;
503    async fn create_task(&self, name: &str, command: &str, schedule: &str) -> IntegrationResult;
504    async fn update_task(
505        &self,
506        task_id: &str,
507        command: Option<&str>,
508        schedule: Option<&str>,
509    ) -> IntegrationResult;
510    async fn delete_task(&self, task_id: &str) -> IntegrationResult;
511    async fn enable_task(&self, task_id: &str) -> IntegrationResult;
512    async fn disable_task(&self, task_id: &str) -> IntegrationResult;
513    async fn run_task_now(&self, task_id: &str) -> IntegrationResult;
514}