Skip to main content

comindly_api/
repos.rs

1//! Repositories, indexing and jobs (`/api/v1/repos*`, `/index*`, `/jobs*`).
2
3use serde::{Deserialize, Serialize};
4
5pub use crate::events::Event;
6pub use crate::hosting::{Provider, RemoteRepo};
7pub use crate::jobs::{JobRecord, Status};
8
9/// `GET /api/v1/repos`: the list `comindly link` indexes, and what the index has besides.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Repos {
12    pub repos: Vec<RepoRow>,
13    pub unlisted: Vec<UnlistedRepo>,
14    /// The team's repositories with no folder here.
15    pub team_missing: Vec<String>,
16    pub root: Option<String>,
17}
18
19/// A listed repository and what the index holds of it.
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct RepoRow {
22    pub path: String,
23    pub name: String,
24    pub exists: bool,
25    pub branch: Option<String>,
26    /// A repository comindly clones and keeps itself.
27    pub managed: bool,
28    pub cloned: bool,
29    pub your_branch: Option<String>,
30    pub remote: Option<String>,
31    pub symbols: Option<usize>,
32    pub indexed_commit: Option<String>,
33}
34
35/// A repository in the index with no folder in the list.
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct UnlistedRepo {
38    pub name: String,
39    pub symbols: Option<usize>,
40    pub commit: Option<String>,
41    pub remote: Option<String>,
42}
43
44/// `POST /api/v1/repos/scan`: git repositories under a folder, `depth` 1 to 4 (2).
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct ScanRequest {
47    pub dir: String,
48    #[serde(default, skip_serializing_if = "Option::is_none")]
49    pub depth: Option<usize>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct Scan {
54    pub ok: bool,
55    pub found: Vec<ScanHit>,
56}
57
58/// A repository found under the folder, marked when already listed.
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct ScanHit {
61    pub name: String,
62    pub listed: bool,
63    pub remote: Option<String>,
64    pub path: String,
65}
66
67/// `POST /api/v1/repos`: folders to add and remove.
68#[derive(Debug, Clone, Default, Serialize, Deserialize)]
69pub struct RepoChange {
70    #[serde(default)]
71    pub add: Vec<String>,
72    #[serde(default)]
73    pub remove: Vec<String>,
74    /// Teams to label the added repositories with.
75    #[serde(default)]
76    pub teams: Vec<String>,
77}
78
79/// `POST /api/v1/repos/remote/list`: a GitHub organisation or GitLab group, with a token if needed.
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct RemoteListRequest {
82    pub url: String,
83    #[serde(default, skip_serializing_if = "Option::is_none")]
84    pub username: Option<String>,
85    #[serde(default, skip_serializing_if = "Option::is_none")]
86    pub token: Option<String>,
87}
88
89/// The host's repositories, the ones already here marked.
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct RemoteList {
92    pub ok: bool,
93    pub provider: Provider,
94    pub host: String,
95    pub owner: String,
96    pub repos: Vec<RemoteRow>,
97    pub warning: Option<String>,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct RemoteRow {
102    #[serde(flatten)]
103    pub repo: RemoteRepo,
104    pub listed: bool,
105}
106
107/// `POST /api/v1/repos/remote/add`: clone URLs, optionally shared with the team.
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct RemoteAddRequest {
110    pub urls: Vec<String>,
111    #[serde(default)]
112    pub share: bool,
113    /// Teams to label the added repositories with.
114    #[serde(default)]
115    pub teams: Vec<String>,
116}
117
118/// `POST /api/v1/index`: queue indexing of the listed repositories.
119#[derive(Debug, Clone, Default, Serialize, Deserialize)]
120pub struct IndexRequest {
121    #[serde(default)]
122    pub llm: bool,
123    /// Index without the repositories that have no folder here, dropping them from the index.
124    #[serde(default)]
125    pub drop_unlisted: bool,
126    /// LLM tasks to write again from scratch, with the current model and prompt; needs `llm`.
127    #[serde(default)]
128    pub rewrite: Vec<crate::prompts::Task>,
129    /// Folders to index as they are instead of the list; only an engine on this machine takes them.
130    #[serde(default, skip_serializing_if = "Vec::is_empty")]
131    pub paths: Vec<String>,
132    /// The LLM steps to run, whatever the team settings say; none: the team settings decide.
133    #[serde(default, skip_serializing_if = "Vec::is_empty")]
134    pub steps: Vec<crate::prompts::Task>,
135}
136
137/// The job that runs it; `position` when it waits, 1 for the next to run.
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct IndexStarted {
140    pub ok: bool,
141    pub message: String,
142    pub id: String,
143    pub status: Status,
144    pub position: Option<usize>,
145}
146
147/// `GET /api/v1/index`: the latest job with its events, or none yet; and the queue.
148#[derive(Debug, Clone, Serialize, Deserialize)]
149#[serde(untagged)]
150pub enum IndexState {
151    Latest(LatestJob),
152    Idle {
153        running: bool,
154        events: Vec<Event>,
155        queued: Vec<QueuedJob>,
156    },
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct LatestJob {
161    pub id: String,
162    pub running: bool,
163    pub cancelled: bool,
164    /// Whether it succeeded, once it ended.
165    pub ok: Option<bool>,
166    pub status: Status,
167    pub events: Vec<Event>,
168    pub started: String,
169    pub finished: Option<String>,
170    pub queued: Vec<QueuedJob>,
171}
172
173/// A job waiting its turn.
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct QueuedJob {
176    pub id: String,
177    pub kind: String,
178    pub position: usize,
179    pub started_by: Option<String>,
180    pub created: String,
181}
182
183/// `GET /api/v1/index/watch`: whether automatic re-indexing is on and what it last found.
184#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct IndexWatch {
186    pub enabled: bool,
187    /// Minutes between checks.
188    pub minutes: u64,
189    pub last: WatchResult,
190}
191
192#[derive(Debug, Clone, Default, Serialize, Deserialize)]
193pub struct WatchResult {
194    pub checked: Option<String>,
195    pub result: String,
196}
197
198/// `GET /api/v1/jobs`: most recent first.
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct Jobs {
201    pub jobs: Vec<JobRow>,
202}
203
204/// A job; a waiting one carries its `position`, 1 for the next to run.
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct JobRow {
207    #[serde(flatten)]
208    pub job: JobRecord,
209    #[serde(default, skip_serializing_if = "Option::is_none")]
210    pub position: Option<usize>,
211}
212
213/// `GET /api/v1/jobs/{id}`: the job, its events so far and its place in the queue.
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct JobDetail {
216    pub job: JobRecord,
217    pub events: Vec<Event>,
218    pub position: Option<usize>,
219}