atmos/os_lib/aura/builtins/
cloud.rs1use super::*;
4
5pub(crate) fn builtin_drive_ls(env: &mut Env, args: &[AST]) -> Result<Value, String> {
7 let vals = eval_args(env, args)?;
8 let folder_id = vals.first().map(unquote);
9 let auth = crate::os_lib::cloud::load_auth();
10 if !auth.is_logged_in() {
11 return Err(String::from(
12 "Not logged in to Google Drive. Log in via the drive app first.",
13 ));
14 }
15 match crate::os_lib::cloud::drive_list_files_in_folder(
16 &auth.access_token,
17 folder_id.as_deref(),
18 ) {
19 Ok(files) => {
20 let mut out = alloc::format!("Drive listing ({} items):\n", files.len());
21 for f in &files {
22 let tag = if f.mime_type == crate::os_lib::cloud::MIME_FOLDER {
23 "[D]"
24 } else if f.mime_type == crate::os_lib::cloud::MIME_GOOGLE_DOC {
25 "[G]"
26 } else {
27 " "
28 };
29 out.push_str(&alloc::format!("{} {} ({})\n", tag, f.name, f.id));
30 }
31 Ok(Value::Str(out))
32 }
33 Err(e) => Err(alloc::format!("drive ls error: {}", e)),
34 }
35}
36
37pub(crate) fn builtin_drive_cat(env: &mut Env, args: &[AST]) -> Result<Value, String> {
39 let vals = eval_args(env, args)?;
40 if vals.is_empty() {
41 return Err(String::from("USAGE: drive.cat(\"file_id\")"));
42 }
43 let file_id = unquote(&vals[0]);
44 let auth = crate::os_lib::cloud::load_auth();
45 if !auth.is_logged_in() {
46 return Err(String::from("Not logged in to Google Drive."));
47 }
48 match crate::os_lib::cloud::drive_cat(&auth.access_token, &file_id) {
49 Ok(text) => Ok(Value::Str(text)),
50 Err(e) => Err(alloc::format!("drive cat error: {}", e)),
51 }
52}
53
54pub(crate) fn builtin_drive_download(env: &mut Env, args: &[AST]) -> Result<Value, String> {
57 let vals = eval_args(env, args)?;
58 if vals.len() < 2 {
59 return Err(String::from("USAGE: drive.download(\"file_id\" \"local_name\")"));
60 }
61 let file_id = unquote(&vals[0]);
62 let local_name = unquote(&vals[1]);
63 let auth = crate::os_lib::cloud::load_auth();
64 if !auth.is_logged_in() {
65 return Err(String::from("Not logged in to Google Drive."));
66 }
67 match crate::os_lib::cloud::drive_download_binary(&auth.access_token, &file_id) {
68 Ok(bytes) => {
69 let len = bytes.len();
70 let fs = crate::kernel::fs::get_fs();
71 match fs.save_file(&local_name, &bytes, "downloaded") {
72 Ok(()) => Ok(Value::Str(alloc::format!(
73 "Downloaded {} bytes to '{}'.",
74 len, local_name
75 ))),
76 Err(e) => Err(alloc::format!("drive download save error: {}", e)),
77 }
78 }
79 Err(e) => Err(alloc::format!("drive download error: {}", e)),
80 }
81}
82
83pub(crate) fn builtin_drive_upload(env: &mut Env, args: &[AST]) -> Result<Value, String> {
85 let vals = eval_args(env, args)?;
86 if vals.len() < 2 {
87 return Err(String::from("USAGE: drive.upload(\"name\" \"content\")"));
88 }
89 let name = unquote(&vals[0]);
90 let content = unquote(&vals[1]);
91 let auth = crate::os_lib::cloud::load_auth();
92 if !auth.is_logged_in() {
93 return Err(String::from("Not logged in to Google Drive."));
94 }
95 match crate::os_lib::cloud::drive_create_text(&auth.access_token, &name, &content) {
96 Ok(id) => Ok(Value::Str(alloc::format!(
97 "Uploaded '{}' (id={})",
98 name, id
99 ))),
100 Err(e) => Err(alloc::format!("drive upload error: {}", e)),
101 }
102}
103
104pub(crate) fn builtin_drive_rm(env: &mut Env, args: &[AST]) -> Result<Value, String> {
106 let vals = eval_args(env, args)?;
107 if vals.is_empty() {
108 return Err(String::from("USAGE: drive.rm(\"file_id\")"));
109 }
110 let file_id = unquote(&vals[0]);
111 let auth = crate::os_lib::cloud::load_auth();
112 if !auth.is_logged_in() {
113 return Err(String::from("Not logged in to Google Drive."));
114 }
115 match crate::os_lib::cloud::drive_delete(&auth.access_token, &file_id) {
116 Ok(()) => Ok(Value::Str(alloc::format!("Deleted {}", file_id))),
117 Err(e) => Err(alloc::format!("drive rm error: {}", e)),
118 }
119}
120
121pub(crate) fn builtin_gh_login(env: &mut Env, args: &[AST]) -> Result<Value, String> {
128 let vals = eval_args(env, args)?;
129 if vals.is_empty() {
130 return Err(String::from("USAGE: gh.login(\"client_id\")"));
131 }
132 let client_id = unquote(&vals[0]);
133 match crate::os_lib::cloud::github_request_device_code(&client_id) {
134 Ok(dc) if !dc.user_code.is_empty() => {
135 let mut auth = crate::os_lib::cloud::load_gh_auth();
136 auth.client_id = client_id;
137 auth.device_code = dc.device_code;
138 auth.access_token = String::new();
139 crate::os_lib::cloud::save_gh_auth(&auth)
143 .map_err(|e| alloc::format!("gh login: failed to save device code: {}", e))?;
144 Ok(Value::Str(alloc::format!(
145 "Open {} and enter code: {}\nThen run gh.poll() to complete login.",
146 dc.verification_url, dc.user_code
147 )))
148 }
149 Ok(_) => Err(String::from(
150 "Device code request returned empty (check client_id/scope).",
151 )),
152 Err(e) => Err(alloc::format!("gh login error: {}", e)),
153 }
154}
155
156pub(crate) fn builtin_gh_poll(_env: &mut Env, _args: &[AST]) -> Result<Value, String> {
158 let mut auth = crate::os_lib::cloud::load_gh_auth();
159 if auth.device_code.is_empty() {
160 return Err(String::from("No pending login. Run gh.login(client_id) first."));
161 }
162 match crate::os_lib::cloud::github_poll_token(&auth.client_id, &auth.device_code) {
163 Ok(tok) if !tok.access_token.is_empty() => {
164 auth.access_token = tok.access_token;
165 auth.device_code = String::new();
166 if let Err(e) = crate::os_lib::cloud::save_gh_auth(&auth) {
169 return Err(alloc::format!(
170 "Logged in, but the token could not be saved ({}). It will be lost on reboot.",
171 e
172 ));
173 }
174 Ok(Value::Str(String::from("Logged in to GitHub.")))
175 }
176 Ok(tok) if tok.error == "authorization_pending" => Ok(Value::Str(String::from(
177 "Still pending. Authorize in the browser, then run gh.poll() again.",
178 ))),
179 Ok(tok) if tok.error == "slow_down" => Ok(Value::Str(String::from(
180 "Slow down: wait a few seconds, then run gh.poll() again.",
181 ))),
182 Ok(tok) if !tok.error.is_empty() => Err(alloc::format!("gh poll error: {}", tok.error)),
183 Ok(_) => Ok(Value::Str(String::from("No token yet. Run gh.poll() again."))),
184 Err(e) => Err(alloc::format!("gh poll error: {}", e)),
185 }
186}
187
188pub(crate) fn builtin_gh_user(_env: &mut Env, _args: &[AST]) -> Result<Value, String> {
190 let auth = crate::os_lib::cloud::load_gh_auth();
191 if !auth.is_logged_in() {
192 return Err(String::from(
193 "Not logged in to GitHub. Run gh.login(client_id) first.",
194 ));
195 }
196 match crate::os_lib::cloud::github_get_user(&auth.access_token) {
197 Ok(u) => Ok(Value::Str(alloc::format!(
198 "login={} name={} id={}",
199 u.login, u.name, u.id
200 ))),
201 Err(e) => Err(alloc::format!("gh user error: {}", e)),
202 }
203}
204
205pub(crate) fn builtin_gh_repos(_env: &mut Env, _args: &[AST]) -> Result<Value, String> {
207 let auth = crate::os_lib::cloud::load_gh_auth();
208 if !auth.is_logged_in() {
209 return Err(String::from(
210 "Not logged in to GitHub. Run gh.login(client_id) first.",
211 ));
212 }
213 match crate::os_lib::cloud::github_list_repos(&auth.access_token) {
214 Ok(repos) => {
215 let mut out = alloc::format!("GitHub repos ({}):\n", repos.len());
216 for r in &repos {
217 let vis = if r.private { "private" } else { "public" };
218 out.push_str(&alloc::format!("{} ({})\n", r.full_name, vis));
219 }
220 Ok(Value::Str(out))
221 }
222 Err(e) => Err(alloc::format!("gh repos error: {}", e)),
223 }
224}
225
226pub(crate) fn builtin_gh_logout(_env: &mut Env, _args: &[AST]) -> Result<Value, String> {
228 let mut auth = crate::os_lib::cloud::load_gh_auth();
229 auth.access_token = String::new();
230 auth.device_code = String::new();
231 match crate::os_lib::cloud::save_gh_auth(&auth) {
232 Ok(()) => Ok(Value::Str(String::from("Logged out of GitHub."))),
233 Err(e) => Err(alloc::format!("gh logout error: {}", e)),
234 }
235}
236