Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:32:04.796Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

job_debug_queue.rs

237 lines · 7.4 KB · rust
1use std::{collections::VecDeque, time::Instant};
2
3use gpui::SharedString;
4
5use super::JobId;
6
7pub struct GitJobDebugQueue {
8    pending: VecDeque<PendingJob>,
9    running: VecDeque<RunningJob>,
10    completed: VecDeque<CompletedJob>,
11}
12
13const MAX_COMPLETED_JOBS: usize = 500;
14
15#[derive(Clone, Debug)]
16pub struct PendingJob {
17    pub id: JobId,
18    pub description: SharedString,
19    pub key: Option<SharedString>,
20    pub enqueued_at: Instant,
21}
22
23#[derive(Clone, Debug)]
24pub struct RunningJob {
25    pub id: JobId,
26    pub description: SharedString,
27    pub key: Option<SharedString>,
28    pub enqueued_at: Instant,
29    pub started_at: Instant,
30}
31
32#[derive(Clone, Debug)]
33pub struct CompletedJob {
34    pub id: JobId,
35    pub description: SharedString,
36    pub key: Option<SharedString>,
37    pub enqueued_at: Instant,
38    pub started_at: Option<Instant>,
39    pub completed_at: Instant,
40    pub status: CompletedJobStatus,
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub enum CompletedJobStatus {
45    Finished,
46    Skipped,
47}
48
49impl GitJobDebugQueue {
50    pub fn new() -> Self {
51        Self {
52            pending: VecDeque::new(),
53            running: VecDeque::new(),
54            completed: VecDeque::new(),
55        }
56    }
57
58    pub fn add(&mut self, id: JobId, description: &'static str, key: Option<SharedString>) {
59        self.pending.push_back(PendingJob {
60            id,
61            description: description.into(),
62            key,
63            enqueued_at: Instant::now(),
64        });
65    }
66
67    pub fn mark_running(&mut self, id: JobId) {
68        let Some(index) = self.pending.iter().position(|job| job.id == id) else {
69            return;
70        };
71        // Safe to unwrap: `index` was just found by `position()`, so it's in bounds.
72        let pending = self.pending.remove(index).unwrap();
73
74        self.running.push_back(RunningJob {
75            id: pending.id,
76            description: pending.description,
77            key: pending.key,
78            enqueued_at: pending.enqueued_at,
79            started_at: Instant::now(),
80        });
81    }
82
83    pub fn mark_unfinished_complete(&mut self, status: CompletedJobStatus) {
84        let ids = self
85            .pending
86            .iter()
87            .map(|job| job.id)
88            .chain(self.running.iter().map(|job| job.id))
89            .collect::<Vec<_>>();
90        for id in ids {
91            self.mark_complete(id, status);
92        }
93    }
94
95    pub fn mark_complete(&mut self, id: JobId, status: CompletedJobStatus) {
96        let (enqueued_at, started_at, description, key) =
97            if let Some(index) = self.running.iter().position(|job| job.id == id) {
98                let running = self.running.remove(index).unwrap();
99                (
100                    running.enqueued_at,
101                    Some(running.started_at),
102                    running.description,
103                    running.key,
104                )
105            } else if let Some(index) = self.pending.iter().position(|job| job.id == id) {
106                let pending = self.pending.remove(index).unwrap();
107                (pending.enqueued_at, None, pending.description, pending.key)
108            } else {
109                return;
110            };
111
112        self.completed.push_back(CompletedJob {
113            id,
114            description,
115            key,
116            enqueued_at,
117            started_at,
118            completed_at: Instant::now(),
119            status,
120        });
121
122        while self.completed.len() > MAX_COMPLETED_JOBS {
123            self.completed.pop_front();
124        }
125    }
126
127    pub fn to_debug_string(&self) -> String {
128        serde_json::to_string_pretty(&self.to_debug_value()).unwrap_or_default()
129    }
130
131    pub fn to_debug_value(&self) -> serde_json::Value {
132        let mut entries = Vec::new();
133
134        let mut pending_count = 0u64;
135        let mut running_count = 0u64;
136        let mut finished_count = 0u64;
137        let mut skipped_count = 0u64;
138
139        for job in &self.pending {
140            pending_count += 1;
141            entries.push((job.enqueued_at, self.format_pending(job)));
142        }
143        for job in &self.running {
144            running_count += 1;
145            entries.push((job.enqueued_at, self.format_running(job)));
146        }
147        for job in &self.completed {
148            match job.status {
149                CompletedJobStatus::Finished => finished_count += 1,
150                CompletedJobStatus::Skipped => skipped_count += 1,
151            }
152            entries.push((job.enqueued_at, self.format_completed(job)));
153        }
154
155        entries.sort_by_key(|(enqueued_at, _)| *enqueued_at);
156
157        let json_entries: Vec<serde_json::Value> =
158            entries.into_iter().map(|(_, json)| json).collect();
159
160        serde_json::json!({
161            "summary": {
162                "pending": pending_count,
163                "running": running_count,
164                "finished": finished_count,
165                "skipped": skipped_count,
166            },
167            "entries": json_entries,
168        })
169    }
170
171    fn format_pending(&self, job: &PendingJob) -> serde_json::Value {
172        serde_json::json!({
173            "id": job.id,
174            "description": job.description.as_ref(),
175            "key": job.key.as_ref().map(|k| k.as_ref()),
176            "status": "Pending",
177            "enqueued": format!("{} ago", format_duration(job.enqueued_at.elapsed())),
178        })
179    }
180
181    fn format_running(&self, job: &RunningJob) -> serde_json::Value {
182        serde_json::json!({
183            "id": job.id,
184            "description": job.description.as_ref(),
185            "key": job.key.as_ref().map(|k| k.as_ref()),
186            "status": "Running",
187            "enqueued": format!("{} ago", format_duration(job.enqueued_at.elapsed())),
188            "wait_time": format_duration(job.started_at.duration_since(job.enqueued_at)),
189            "run_time": format!("{} (still running)", format_duration(job.started_at.elapsed())),
190        })
191    }
192
193    fn format_completed(&self, job: &CompletedJob) -> serde_json::Value {
194        let status = match job.status {
195            CompletedJobStatus::Finished => "Finished",
196            CompletedJobStatus::Skipped => "Skipped",
197        };
198
199        let (wait_time, run_time) = if let Some(started) = job.started_at {
200            let wait = format_duration(started.duration_since(job.enqueued_at));
201            let run = format_duration(job.completed_at.duration_since(started));
202            (wait, Some(run))
203        } else {
204            let wait = format!(
205                "{} (skipped)",
206                format_duration(job.completed_at.duration_since(job.enqueued_at))
207            );
208            (wait, None)
209        };
210
211        serde_json::json!({
212            "id": job.id,
213            "description": job.description.as_ref(),
214            "key": job.key.as_ref().map(|k| k.as_ref()),
215            "status": status,
216            "enqueued": format!("{} ago", format_duration(job.enqueued_at.elapsed())),
217            "wait_time": wait_time,
218            "run_time": run_time,
219        })
220    }
221}
222
223fn format_duration(duration: std::time::Duration) -> String {
224    let secs = duration.as_secs_f64();
225    if secs < 0.001 {
226        format!("{:.0}us", secs * 1_000_000.0)
227    } else if secs < 1.0 {
228        format!("{:.0}ms", secs * 1000.0)
229    } else if secs < 60.0 {
230        format!("{:.0}s", secs)
231    } else if secs < 3600.0 {
232        format!("{:.0}m", secs / 60.0)
233    } else {
234        format!("{:.0}h", secs / 3600.0)
235    }
236}
237
Served at tenant.openagents/omega Member data and write actions are omitted.