1use std::collections::VecDeque;
10use std::time::{Duration, Instant};
11
12use futures_util::stream::BoxStream;
13use futures_util::StreamExt;
14use reqwest::Method;
15use serde_json::{json, Value};
16
17use crate::client::{Inner, SSE_TIMEOUT};
18use crate::error::{Result, WritError};
19use crate::models::{
20 AgentStatus, ApiKey, Automation, CancelOutcome, CrawlCancel, CrawlJob, CrawlList,
21 CrawlStartParams, DatasetFormat, DatasetList, DatasetMeta, DatasetSearchResult, Extractor,
22 Health, Monitor, MonitorHistory, Persona, RunCompleted, RunData, RunEvent, RunFeedItem,
23 RunOutcome, RunResults, RunStarted, SecretMeta, Selector, StoredFile, VaultStatus, Workflow,
24};
25use crate::page::Page;
26use crate::sse::SseParser;
27
28const DEFAULT_WAIT_TIMEOUT: Duration = Duration::from_secs(600);
30
31const POLL_INTERVAL: Duration = Duration::from_secs(1);
33
34pub type RunEventStream = BoxStream<'static, Result<RunEvent>>;
36
37#[derive(Debug, Clone, Default)]
42pub struct RunOptions {
43 pub inputs: Option<Value>,
45 pub persona_id: Option<i64>,
47 pub files: Option<Value>,
49 pub wait_timeout: Option<Duration>,
52 pub include_results: bool,
54}
55
56impl RunOptions {
57 fn body(&self, dry_run: bool) -> Value {
58 let mut body = serde_json::Map::new();
59 if let Some(inputs) = &self.inputs {
60 body.insert("inputs".into(), inputs.clone());
61 }
62 if let Some(persona_id) = self.persona_id {
63 body.insert("persona_id".into(), json!(persona_id));
64 }
65 if let Some(files) = &self.files {
66 body.insert("files".into(), files.clone());
67 }
68 if dry_run {
69 body.insert("dry_run".into(), json!(true));
70 }
71 Value::Object(body)
72 }
73}
74
75#[derive(Debug, Clone, Copy)]
77pub struct Agent<'a> {
78 pub(crate) c: &'a Inner,
79}
80
81impl Agent<'_> {
82 pub async fn status(&self) -> Result<AgentStatus> {
84 self.c.get_json("/v1/agent", &[]).await
85 }
86
87 pub async fn health(&self) -> Result<Health> {
89 self.c.get_json("/v1/health", &[]).await
90 }
91}
92
93#[derive(Debug, Clone, Copy)]
95pub struct Workflows<'a> {
96 pub(crate) c: &'a Inner,
97}
98
99impl Workflows<'_> {
100 pub async fn list(&self) -> Result<Page<Workflow>> {
102 self.list_with(&[]).await
103 }
104
105 pub async fn list_with(&self, query: &[(&str, &str)]) -> Result<Page<Workflow>> {
107 self.c.get_json("/v1/workflows", query).await
108 }
109
110 pub async fn create(&self, body: Value) -> Result<Workflow> {
112 self.c
113 .send_json(Method::POST, "/v1/workflows", &[], Some(&body))
114 .await
115 }
116
117 pub async fn get(&self, id: i64) -> Result<Workflow> {
119 self.c.get_json(&format!("/v1/workflows/{id}"), &[]).await
120 }
121
122 pub async fn update(&self, id: i64, patch: Value) -> Result<Workflow> {
124 self.c
125 .send_json(
126 Method::PATCH,
127 &format!("/v1/workflows/{id}"),
128 &[],
129 Some(&patch),
130 )
131 .await
132 }
133
134 pub async fn delete(&self, id: i64) -> Result<Value> {
136 self.c
137 .send_json(Method::DELETE, &format!("/v1/workflows/{id}"), &[], None)
138 .await
139 }
140
141 pub async fn run(&self, id: i64, opts: &RunOptions) -> Result<RunStarted> {
146 self.c
147 .send_json(
148 Method::POST,
149 &format!("/v1/workflows/{id}/run"),
150 &[],
151 Some(&opts.body(false)),
152 )
153 .await
154 }
155
156 pub async fn run_wait(
169 &self,
170 id: i64,
171 opts: &RunOptions,
172 timeout: Option<Duration>,
173 ) -> Result<RunCompleted> {
174 let secs = timeout.map(|d| d.as_secs().max(1).to_string());
175 let mut query: Vec<(&str, &str)> = vec![("wait", "true")];
176 if let Some(secs) = secs.as_deref() {
177 query.push(("timeout", secs));
178 }
179 let out: RunCompleted = self
183 .c
184 .send_json_allowing(
185 Method::POST,
186 &format!("/v1/workflows/{id}/run"),
187 &query,
188 Some(&opts.body(false)),
189 &[504],
190 )
191 .await?;
192 if !out.done {
193 return Err(WritError::RunTimeout {
194 run_id: out.run_id,
195 status_url: out.status_url,
196 events_url: out.events_url,
197 });
198 }
199 Ok(out)
200 }
201
202 pub async fn dry_run(&self, id: i64, opts: &RunOptions) -> Result<Value> {
205 self.c
206 .send_json(
207 Method::POST,
208 &format!("/v1/workflows/{id}/run"),
209 &[],
210 Some(&opts.body(true)),
211 )
212 .await
213 }
214
215 pub async fn cancel(&self, id: i64) -> Result<CancelOutcome> {
218 self.c
219 .send_json_allowing(
220 Method::POST,
221 &format!("/v1/workflows/{id}/cancel"),
222 &[],
223 None,
224 &[409],
225 )
226 .await
227 }
228
229 pub async fn session(&self, id: i64) -> Result<Value> {
231 self.c
232 .get_json(&format!("/v1/workflows/{id}/session"), &[])
233 .await
234 }
235
236 pub async fn clear_session(&self, id: i64) -> Result<Value> {
238 self.c
239 .send_json(
240 Method::DELETE,
241 &format!("/v1/workflows/{id}/session"),
242 &[],
243 None,
244 )
245 .await
246 }
247
248 pub async fn run_and_wait(&self, id: i64, opts: &RunOptions) -> Result<RunOutcome> {
256 let started = self.run(id, opts).await?;
257 let run_id = started.run_id;
258 let wait = opts.wait_timeout.unwrap_or(DEFAULT_WAIT_TIMEOUT);
259 let deadline = Instant::now() + wait;
260 let runs = Runs { c: self.c };
261
262 let timeout_err = || {
263 WritError::Connection(format!(
264 "run_and_wait: run {run_id} not terminal after {}s — the run was NOT cancelled \
265 and continues on the daemon",
266 wait.as_secs()
267 ))
268 };
269
270 let mut saw_terminal = false;
273 let remaining = deadline.saturating_duration_since(Instant::now());
274 if !remaining.is_zero() {
275 if let Ok(mut stream) = runs.events_with_timeout(run_id, remaining).await {
276 while let Some(item) = stream.next().await {
277 match item {
278 Ok(ev) if ev.is_terminal() => {
279 saw_terminal = true;
280 break;
281 }
282 Ok(_) => {
283 if Instant::now() >= deadline {
284 return Err(timeout_err());
285 }
286 }
287 Err(_) => break,
289 }
290 }
291 }
292 }
293
294 if !saw_terminal {
297 loop {
298 if Instant::now() >= deadline {
299 return Err(timeout_err());
300 }
301 let item = runs.get(run_id).await?;
302 if !item.is_running() {
303 break;
304 }
305 let nap = POLL_INTERVAL.min(deadline.saturating_duration_since(Instant::now()));
306 if nap.is_zero() {
307 return Err(timeout_err());
308 }
309 crate::util::sleep(nap).await;
310 }
311 }
312
313 let run = runs.get(run_id).await?;
315 let results = if opts.include_results {
316 Some(runs.results(run_id).await?)
317 } else {
318 None
319 };
320 Ok(RunOutcome { run, results })
321 }
322}
323
324#[derive(Debug, Clone, Copy)]
327pub struct Runs<'a> {
328 pub(crate) c: &'a Inner,
329}
330
331impl Runs<'_> {
332 pub async fn list(&self) -> Result<Page<RunFeedItem>> {
334 self.list_with(&[]).await
335 }
336
337 pub async fn list_with(&self, query: &[(&str, &str)]) -> Result<Page<RunFeedItem>> {
340 self.c.get_json("/v1/runs", query).await
341 }
342
343 pub async fn get(&self, run_id: i64) -> Result<RunFeedItem> {
345 self.c.get_json(&format!("/v1/runs/{run_id}"), &[]).await
346 }
347
348 pub async fn results(&self, run_id: i64) -> Result<RunResults> {
350 self.c
351 .get_json(&format!("/v1/runs/{run_id}/results"), &[])
352 .await
353 }
354
355 pub async fn data(&self, run_id: i64) -> Result<RunData> {
357 self.c
358 .get_json(&format!("/v1/runs/{run_id}/data"), &[])
359 .await
360 }
361
362 pub async fn data_csv(&self, run_id: i64) -> Result<String> {
364 self.c
365 .get_text(&format!("/v1/runs/{run_id}/data"), &[("format", "csv")])
366 .await
367 }
368
369 pub async fn cancel(&self, run_id: i64) -> Result<CancelOutcome> {
372 self.c
373 .send_json_allowing(
374 Method::POST,
375 &format!("/v1/runs/{run_id}/cancel"),
376 &[],
377 None,
378 &[409],
379 )
380 .await
381 }
382
383 pub async fn events(&self, run_id: i64) -> Result<RunEventStream> {
389 self.events_with_timeout(run_id, SSE_TIMEOUT).await
390 }
391
392 pub(crate) async fn events_with_timeout(
395 &self,
396 run_id: i64,
397 timeout: Duration,
398 ) -> Result<RunEventStream> {
399 let resp = self
400 .c
401 .get_stream(&format!("/v1/runs/{run_id}/events"), timeout)
402 .await?;
403
404 struct SseState {
405 body: BoxStream<'static, reqwest::Result<bytes::Bytes>>,
406 parser: SseParser,
407 pending: VecDeque<RunEvent>,
408 done: bool,
409 }
410
411 let state = SseState {
412 body: resp.bytes_stream().boxed(),
413 parser: SseParser::new(),
414 pending: VecDeque::new(),
415 done: false,
416 };
417
418 let stream = futures_util::stream::unfold(state, |mut st| async move {
419 loop {
420 if let Some(ev) = st.pending.pop_front() {
421 if ev.is_terminal() {
422 st.done = true;
424 st.pending.clear();
425 }
426 return Some((Ok(ev), st));
427 }
428 if st.done {
429 return None;
430 }
431 match st.body.next().await {
432 Some(Ok(chunk)) => {
433 let mut payloads = Vec::new();
434 st.parser.feed(&chunk, &mut payloads);
435 for p in payloads {
436 st.pending.push_back(RunEvent::parse(&p));
437 }
438 }
439 Some(Err(e)) => {
440 st.done = true;
441 return Some((Err(WritError::from(e)), st));
442 }
443 None => return None,
444 }
445 }
446 })
447 .boxed();
448 Ok(stream)
449 }
450}
451
452#[derive(Debug, Clone, Copy)]
454pub struct Monitors<'a> {
455 pub(crate) c: &'a Inner,
456}
457
458impl Monitors<'_> {
459 pub async fn list(&self) -> Result<Page<Monitor>> {
461 self.list_with(&[]).await
462 }
463
464 pub async fn list_with(&self, query: &[(&str, &str)]) -> Result<Page<Monitor>> {
466 self.c.get_json("/v1/monitors", query).await
467 }
468
469 pub async fn create(&self, body: Value) -> Result<Monitor> {
472 self.c
473 .send_json(Method::POST, "/v1/monitors", &[], Some(&body))
474 .await
475 }
476
477 pub async fn get(&self, id: i64) -> Result<Monitor> {
479 self.c.get_json(&format!("/v1/monitors/{id}"), &[]).await
480 }
481
482 pub async fn update(&self, id: i64, patch: Value) -> Result<Monitor> {
484 self.c
485 .send_json(
486 Method::PATCH,
487 &format!("/v1/monitors/{id}"),
488 &[],
489 Some(&patch),
490 )
491 .await
492 }
493
494 pub async fn delete(&self, id: i64) -> Result<Value> {
496 self.c
497 .send_json(Method::DELETE, &format!("/v1/monitors/{id}"), &[], None)
498 .await
499 }
500
501 pub async fn run(&self, id: i64) -> Result<Value> {
503 self.c
504 .send_json(Method::POST, &format!("/v1/monitors/{id}/run"), &[], None)
505 .await
506 }
507
508 pub async fn changes(&self, id: i64) -> Result<MonitorHistory> {
510 self.changes_with(id, &[]).await
511 }
512
513 pub async fn changes_with(&self, id: i64, query: &[(&str, &str)]) -> Result<MonitorHistory> {
515 self.c
516 .get_json(&format!("/v1/monitors/{id}/changes"), query)
517 .await
518 }
519
520 pub async fn capacity(&self) -> Result<Value> {
522 self.c.get_json("/v1/monitors/capacity", &[]).await
523 }
524
525 pub async fn recent_changes(&self) -> Result<Page<Value>> {
527 self.recent_changes_with(&[]).await
528 }
529
530 pub async fn recent_changes_with(&self, query: &[(&str, &str)]) -> Result<Page<Value>> {
532 self.c.get_json("/v1/changes/recent", query).await
533 }
534}
535
536#[derive(Debug, Clone, Copy)]
538pub struct Selectors<'a> {
539 pub(crate) c: &'a Inner,
540}
541
542impl Selectors<'_> {
543 pub async fn list(&self, monitor_id: i64) -> Result<Page<Selector>> {
545 self.c
546 .get_json(&format!("/v1/monitors/{monitor_id}/selectors"), &[])
547 .await
548 }
549
550 pub async fn create(&self, monitor_id: i64, body: Value) -> Result<Selector> {
552 self.c
553 .send_json(
554 Method::POST,
555 &format!("/v1/monitors/{monitor_id}/selectors"),
556 &[],
557 Some(&body),
558 )
559 .await
560 }
561
562 pub async fn get(&self, monitor_id: i64, selector_id: i64) -> Result<Selector> {
564 self.c
565 .get_json(
566 &format!("/v1/monitors/{monitor_id}/selectors/{selector_id}"),
567 &[],
568 )
569 .await
570 }
571
572 pub async fn update(
574 &self,
575 monitor_id: i64,
576 selector_id: i64,
577 patch: Value,
578 ) -> Result<Selector> {
579 self.c
580 .send_json(
581 Method::PATCH,
582 &format!("/v1/monitors/{monitor_id}/selectors/{selector_id}"),
583 &[],
584 Some(&patch),
585 )
586 .await
587 }
588
589 pub async fn delete(&self, monitor_id: i64, selector_id: i64) -> Result<Value> {
591 self.c
592 .send_json(
593 Method::DELETE,
594 &format!("/v1/monitors/{monitor_id}/selectors/{selector_id}"),
595 &[],
596 None,
597 )
598 .await
599 }
600
601 pub async fn toggle(&self, monitor_id: i64, selector_id: i64) -> Result<Value> {
603 self.c
604 .send_json(
605 Method::POST,
606 &format!("/v1/monitors/{monitor_id}/selectors/{selector_id}/toggle"),
607 &[],
608 None,
609 )
610 .await
611 }
612
613 pub async fn test(&self, monitor_id: i64, selector_id: i64) -> Result<Value> {
615 self.c
616 .send_json(
617 Method::POST,
618 &format!("/v1/monitors/{monitor_id}/selectors/{selector_id}/test"),
619 &[],
620 None,
621 )
622 .await
623 }
624
625 pub async fn set_baseline(&self, monitor_id: i64, selector_id: i64) -> Result<Value> {
627 self.c
628 .send_json(
629 Method::POST,
630 &format!("/v1/monitors/{monitor_id}/selectors/{selector_id}/set-baseline"),
631 &[],
632 None,
633 )
634 .await
635 }
636
637 pub async fn clear_baseline(&self, monitor_id: i64, selector_id: i64) -> Result<Value> {
639 self.c
640 .send_json(
641 Method::POST,
642 &format!("/v1/monitors/{monitor_id}/selectors/{selector_id}/clear-baseline"),
643 &[],
644 None,
645 )
646 .await
647 }
648}
649
650#[derive(Debug, Clone, Copy)]
652pub struct Extractors<'a> {
653 pub(crate) c: &'a Inner,
654}
655
656impl Extractors<'_> {
657 pub async fn list(&self, selector_id: i64) -> Result<Page<Extractor>> {
659 self.c
660 .get_json(&format!("/v1/selectors/{selector_id}/extractors"), &[])
661 .await
662 }
663
664 pub async fn create(&self, body: Value) -> Result<Extractor> {
666 self.c
667 .send_json(Method::POST, "/v1/extractors", &[], Some(&body))
668 .await
669 }
670
671 pub async fn get(&self, extractor_id: i64) -> Result<Extractor> {
673 self.c
674 .get_json(&format!("/v1/extractors/{extractor_id}"), &[])
675 .await
676 }
677
678 pub async fn update(&self, extractor_id: i64, patch: Value) -> Result<Extractor> {
680 self.c
681 .send_json(
682 Method::PATCH,
683 &format!("/v1/extractors/{extractor_id}"),
684 &[],
685 Some(&patch),
686 )
687 .await
688 }
689
690 pub async fn delete(&self, extractor_id: i64) -> Result<Value> {
692 self.c
693 .send_json(
694 Method::DELETE,
695 &format!("/v1/extractors/{extractor_id}"),
696 &[],
697 None,
698 )
699 .await
700 }
701
702 pub async fn toggle(&self, extractor_id: i64) -> Result<Value> {
704 self.c
705 .send_json(
706 Method::PATCH,
707 &format!("/v1/extractors/{extractor_id}/toggle"),
708 &[],
709 None,
710 )
711 .await
712 }
713
714 pub async fn test(&self, extractor_id: i64, body: Value) -> Result<Value> {
717 self.c
718 .send_json(
719 Method::POST,
720 &format!("/v1/extractors/{extractor_id}/test"),
721 &[],
722 Some(&body),
723 )
724 .await
725 }
726}
727
728#[derive(Debug, Clone, Copy)]
730pub struct Automations<'a> {
731 pub(crate) c: &'a Inner,
732}
733
734impl Automations<'_> {
735 pub async fn list(&self) -> Result<Page<Automation>> {
737 self.list_with(&[]).await
738 }
739
740 pub async fn list_with(&self, query: &[(&str, &str)]) -> Result<Page<Automation>> {
742 self.c.get_json("/v1/automations", query).await
743 }
744
745 pub async fn create(&self, body: Value) -> Result<Automation> {
747 self.c
748 .send_json(Method::POST, "/v1/automations", &[], Some(&body))
749 .await
750 }
751
752 pub async fn get(&self, id: i64) -> Result<Automation> {
754 self.c.get_json(&format!("/v1/automations/{id}"), &[]).await
755 }
756
757 pub async fn update(&self, id: i64, patch: Value) -> Result<Automation> {
759 self.c
760 .send_json(
761 Method::PATCH,
762 &format!("/v1/automations/{id}"),
763 &[],
764 Some(&patch),
765 )
766 .await
767 }
768
769 pub async fn delete(&self, id: i64) -> Result<Value> {
771 self.c
772 .send_json(Method::DELETE, &format!("/v1/automations/{id}"), &[], None)
773 .await
774 }
775
776 pub async fn enable(&self, id: i64, enabled: bool) -> Result<Automation> {
779 self.c
780 .send_json(
781 Method::POST,
782 &format!("/v1/automations/{id}/enable"),
783 &[],
784 Some(&json!({ "enabled": enabled })),
785 )
786 .await
787 }
788
789 pub async fn run(&self, id: i64, inputs: Option<Value>) -> Result<Value> {
792 let body = json!({ "inputs": inputs });
793 self.c
794 .send_json(
795 Method::POST,
796 &format!("/v1/automations/{id}/run"),
797 &[],
798 Some(&body),
799 )
800 .await
801 }
802}
803
804#[derive(Debug, Clone, Copy)]
806pub struct Personas<'a> {
807 pub(crate) c: &'a Inner,
808}
809
810impl Personas<'_> {
811 pub async fn list(&self) -> Result<Page<Persona>> {
813 self.list_with(&[]).await
814 }
815
816 pub async fn list_with(&self, query: &[(&str, &str)]) -> Result<Page<Persona>> {
818 self.c.get_json("/v1/personas", query).await
819 }
820
821 pub async fn create(&self, body: Value) -> Result<Persona> {
823 self.c
824 .send_json(Method::POST, "/v1/personas", &[], Some(&body))
825 .await
826 }
827
828 pub async fn get(&self, id: i64) -> Result<Persona> {
830 self.c.get_json(&format!("/v1/personas/{id}"), &[]).await
831 }
832
833 pub async fn update(&self, id: i64, patch: Value) -> Result<Persona> {
835 self.c
836 .send_json(
837 Method::PATCH,
838 &format!("/v1/personas/{id}"),
839 &[],
840 Some(&patch),
841 )
842 .await
843 }
844
845 pub async fn delete(&self, id: i64) -> Result<Value> {
847 self.c
848 .send_json(Method::DELETE, &format!("/v1/personas/{id}"), &[], None)
849 .await
850 }
851
852 pub async fn runs(&self, id: i64) -> Result<Page<Value>> {
854 self.c
855 .get_json(&format!("/v1/personas/{id}/runs"), &[])
856 .await
857 }
858
859 pub async fn validate_totp(&self, body: Value) -> Result<Value> {
863 self.c
864 .send_json(Method::POST, "/v1/personas/validate-totp", &[], Some(&body))
865 .await
866 }
867
868 pub async fn test_2fa(&self, id: i64) -> Result<Value> {
870 self.c
871 .send_json(
872 Method::POST,
873 &format!("/v1/personas/{id}/test-2fa"),
874 &[],
875 None,
876 )
877 .await
878 }
879}
880
881#[derive(Debug, Clone, Copy)]
883pub struct Secrets<'a> {
884 pub(crate) c: &'a Inner,
885}
886
887impl Secrets<'_> {
888 pub async fn list(&self) -> Result<Page<SecretMeta>> {
890 self.list_with(&[]).await
891 }
892
893 pub async fn list_with(&self, query: &[(&str, &str)]) -> Result<Page<SecretMeta>> {
895 self.c.get_json("/v1/secrets", query).await
896 }
897
898 pub async fn set(&self, key: &str, value: &str) -> Result<SecretMeta> {
901 self.create(json!({ "name": key, "value": value })).await
902 }
903
904 pub async fn create(&self, body: Value) -> Result<SecretMeta> {
908 self.c
909 .send_json(Method::POST, "/v1/secrets", &[], Some(&body))
910 .await
911 }
912
913 pub async fn get(&self, key: &str) -> Result<SecretMeta> {
915 self.c.get_json(&format!("/v1/secrets/{key}"), &[]).await
916 }
917
918 pub async fn delete(&self, key: &str) -> Result<Value> {
920 self.c
921 .send_json(Method::DELETE, &format!("/v1/secrets/{key}"), &[], None)
922 .await
923 }
924}
925
926#[derive(Debug, Clone, Copy)]
928pub struct Vault<'a> {
929 pub(crate) c: &'a Inner,
930}
931
932impl Vault<'_> {
933 pub async fn status(&self) -> Result<VaultStatus> {
935 self.c.get_json("/v1/vault/status", &[]).await
936 }
937
938 pub async fn lock(&self) -> Result<Value> {
940 self.c
941 .send_json(Method::POST, "/v1/vault/lock", &[], None)
942 .await
943 }
944
945 pub async fn unlock(&self, passphrase: &str) -> Result<Value> {
949 self.c
950 .send_json(
951 Method::POST,
952 "/v1/vault/unlock",
953 &[],
954 Some(&json!({ "passphrase": passphrase })),
955 )
956 .await
957 }
958}
959
960#[derive(Debug, Clone, Copy)]
962pub struct Files<'a> {
963 pub(crate) c: &'a Inner,
964}
965
966impl Files<'_> {
967 pub async fn list(&self) -> Result<Page<StoredFile>> {
969 self.list_with(&[]).await
970 }
971
972 pub async fn list_with(&self, query: &[(&str, &str)]) -> Result<Page<StoredFile>> {
974 self.c.get_json("/v1/files", query).await
975 }
976
977 pub async fn upload(
981 &self,
982 filename: &str,
983 bytes: impl Into<Vec<u8>>,
984 content_type: Option<&str>,
985 source: Option<&str>,
986 ) -> Result<StoredFile> {
987 let mut part =
988 reqwest::multipart::Part::bytes(bytes.into()).file_name(filename.to_string());
989 if let Some(ct) = content_type {
990 part = part
991 .mime_str(ct)
992 .map_err(|e| WritError::Connection(format!("invalid content type {ct:?}: {e}")))?;
993 }
994 let mut form = reqwest::multipart::Form::new().part("file", part);
995 if let Some(source) = source {
996 form = form.text("source", source.to_string());
997 }
998 self.c.post_multipart("/v1/files", form).await
999 }
1000
1001 pub async fn from_data(&self, body: Value) -> Result<StoredFile> {
1004 self.c
1005 .send_json(Method::POST, "/v1/files/from-data", &[], Some(&body))
1006 .await
1007 }
1008
1009 pub async fn get(&self, id: &str) -> Result<StoredFile> {
1011 self.c.get_json(&format!("/v1/files/{id}"), &[]).await
1012 }
1013
1014 pub async fn delete(&self, id: &str) -> Result<Value> {
1016 self.c
1017 .send_json(Method::DELETE, &format!("/v1/files/{id}"), &[], None)
1018 .await
1019 }
1020
1021 pub async fn content(&self, id: &str) -> Result<bytes::Bytes> {
1023 self.c
1024 .get_bytes(&format!("/v1/files/{id}/content"), &[])
1025 .await
1026 }
1027}
1028
1029#[derive(Debug, Clone, Copy)]
1032pub struct Data<'a> {
1033 pub(crate) c: &'a Inner,
1034}
1035
1036impl Data<'_> {
1037 pub async fn query(&self, query: &[(&str, &str)]) -> Result<Value> {
1039 self.c.get_json("/v1/data", query).await
1040 }
1041
1042 pub async fn workflow_data(&self, workflow_id: i64, query: &[(&str, &str)]) -> Result<Value> {
1045 self.c
1046 .get_json(&format!("/v1/workflows/{workflow_id}/data"), query)
1047 .await
1048 }
1049
1050 pub async fn delete_workflow_data(&self, workflow_id: i64) -> Result<Value> {
1052 self.c
1053 .send_json(
1054 Method::DELETE,
1055 &format!("/v1/workflows/{workflow_id}/data"),
1056 &[],
1057 None,
1058 )
1059 .await
1060 }
1061
1062 pub async fn facets(&self, workflow_id: i64) -> Result<Value> {
1064 self.c
1065 .get_json(&format!("/v1/workflows/{workflow_id}/data/facets"), &[])
1066 .await
1067 }
1068
1069 pub async fn export(&self, workflow_id: i64, query: &[(&str, &str)]) -> Result<bytes::Bytes> {
1072 self.c
1073 .get_bytes(&format!("/v1/workflows/{workflow_id}/data/export"), query)
1074 .await
1075 }
1076
1077 pub async fn data_runs(&self, workflow_id: i64) -> Result<Value> {
1079 self.c
1080 .get_json(&format!("/v1/workflows/{workflow_id}/data/runs"), &[])
1081 .await
1082 }
1083}
1084
1085#[derive(Debug, Clone, Copy)]
1087pub struct Keys<'a> {
1088 pub(crate) c: &'a Inner,
1089}
1090
1091impl Keys<'_> {
1092 pub async fn list(&self) -> Result<Page<ApiKey>> {
1094 self.c.get_json("/v1/keys", &[]).await
1095 }
1096
1097 pub async fn create(&self, name: &str, scopes: Option<&str>) -> Result<ApiKey> {
1101 let mut body = json!({ "name": name });
1102 if let Some(scopes) = scopes {
1103 body["scopes"] = Value::String(scopes.to_string());
1104 }
1105 self.c
1106 .send_json(Method::POST, "/v1/keys", &[], Some(&body))
1107 .await
1108 }
1109
1110 pub async fn get(&self, id: i64) -> Result<ApiKey> {
1112 self.c.get_json(&format!("/v1/keys/{id}"), &[]).await
1113 }
1114
1115 pub async fn delete(&self, id: i64) -> Result<Value> {
1117 self.c
1118 .send_json(Method::DELETE, &format!("/v1/keys/{id}"), &[], None)
1119 .await
1120 }
1121}
1122
1123#[derive(Debug, Clone, Copy)]
1127pub struct Crawl<'a> {
1128 pub(crate) c: &'a Inner,
1129}
1130
1131impl Crawl<'_> {
1132 pub async fn list(&self, limit: Option<i64>) -> Result<CrawlList> {
1136 let limit_str = limit.map(|n| n.to_string());
1137 let mut query: Vec<(&str, &str)> = Vec::new();
1138 if let Some(limit) = &limit_str {
1139 query.push(("limit", limit.as_str()));
1140 }
1141 self.c.get_json("/v1/crawl", &query).await
1142 }
1143
1144 pub async fn start(&self, params: CrawlStartParams) -> Result<CrawlJob> {
1149 let body = serde_json::to_value(¶ms)
1150 .map_err(|e| WritError::Connection(format!("serializing crawl params: {e}")))?;
1151 self.c
1152 .send_json(Method::POST, "/v1/crawl", &[], Some(&body))
1153 .await
1154 }
1155
1156 pub async fn get(&self, id: i64) -> Result<CrawlJob> {
1158 self.c.get_json(&format!("/v1/crawl/{id}"), &[]).await
1159 }
1160
1161 pub async fn cancel(&self, id: i64) -> Result<CrawlCancel> {
1165 self.c
1166 .send_json(Method::POST, &format!("/v1/crawl/{id}/cancel"), &[], None)
1167 .await
1168 }
1169}
1170
1171#[derive(Debug, Clone, Copy)]
1176pub struct Datasets<'a> {
1177 pub(crate) c: &'a Inner,
1178}
1179
1180impl Datasets<'_> {
1181 pub async fn list(&self) -> Result<DatasetList> {
1185 self.c.get_json("/v1/datasets", &[]).await
1186 }
1187
1188 pub async fn get(&self, id: i64) -> Result<DatasetMeta> {
1190 self.c.get_json(&format!("/v1/datasets/{id}"), &[]).await
1191 }
1192
1193 pub async fn records(&self, id: i64, query: &[(&str, &str)]) -> Result<Value> {
1198 self.c
1199 .get_json(&format!("/v1/datasets/{id}/records"), query)
1200 .await
1201 }
1202
1203 pub async fn export(&self, id: i64, query: &[(&str, &str)]) -> Result<String> {
1208 self.c
1209 .get_text(&format!("/v1/datasets/{id}/export"), query)
1210 .await
1211 }
1212
1213 pub async fn records_text(
1221 &self,
1222 id: i64,
1223 format: DatasetFormat,
1224 query: &[(&str, &str)],
1225 ) -> Result<String> {
1226 let mut q: Vec<(&str, &str)> = vec![("format", format.as_str())];
1227 q.extend_from_slice(query);
1228 self.c
1229 .get_text(&format!("/v1/datasets/{id}/records"), &q)
1230 .await
1231 }
1232
1233 pub async fn search_text(
1237 &self,
1238 q: &str,
1239 format: DatasetFormat,
1240 params: &[(&str, &str)],
1241 ) -> Result<String> {
1242 let mut query: Vec<(&str, &str)> = vec![("q", q), ("format", format.as_str())];
1243 query.extend_from_slice(params);
1244 self.c.get_text("/v1/datasets/search", &query).await
1245 }
1246
1247 pub async fn search_one_text(
1250 &self,
1251 id: i64,
1252 q: &str,
1253 format: DatasetFormat,
1254 params: &[(&str, &str)],
1255 ) -> Result<String> {
1256 let mut query: Vec<(&str, &str)> = vec![("q", q), ("format", format.as_str())];
1257 query.extend_from_slice(params);
1258 self.c
1259 .get_text(&format!("/v1/datasets/{id}/search"), &query)
1260 .await
1261 }
1262
1263 pub async fn search(&self, q: &str, params: &[(&str, &str)]) -> Result<DatasetSearchResult> {
1267 let mut query: Vec<(&str, &str)> = vec![("q", q)];
1268 query.extend_from_slice(params);
1269 self.c.get_json("/v1/datasets/search", &query).await
1270 }
1271
1272 pub async fn search_one(
1276 &self,
1277 id: i64,
1278 q: &str,
1279 params: &[(&str, &str)],
1280 ) -> Result<DatasetSearchResult> {
1281 let mut query: Vec<(&str, &str)> = vec![("q", q)];
1282 query.extend_from_slice(params);
1283 self.c
1284 .get_json(&format!("/v1/datasets/{id}/search"), &query)
1285 .await
1286 }
1287}