Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions backend/database/memories.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import copy

Check warning on line 1 in backend/database/memories.py

View workflow job for this annotation

GitHub Actions / Hygiene

Large changed file

backend/database/memories.py is 1060 lines; consider splitting files over 800 lines.

Check warning on line 1 in backend/database/memories.py

View workflow job for this annotation

GitHub Actions / Desktop Swift Build & Tests

Large changed file

backend/database/memories.py is 1060 lines; consider splitting files over 800 lines.

Check warning on line 1 in backend/database/memories.py

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Large changed file

backend/database/memories.py is 1060 lines; consider splitting files over 800 lines.

Check warning on line 1 in backend/database/memories.py

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Large changed file

backend/database/memories.py is 1060 lines; consider splitting files over 800 lines.

Check warning on line 1 in backend/database/memories.py

View workflow job for this annotation

GitHub Actions / Desktop Swift Build & Tests

Large changed file

backend/database/memories.py is 1060 lines; consider splitting files over 800 lines.
import hashlib
import json
from datetime import datetime, timezone
Expand Down Expand Up @@ -522,12 +522,6 @@
memory_ref.update(update_payload)


def recompute_evidence(uid: str, memory_id: str, *, firestore_client: Any = None) -> List[Dict[str, Any]]:
"""Placeholder hook for later veracity/tombstone recomputation tickets."""
memory = get_memory(uid, memory_id, firestore_client=firestore_client)
return (memory or {}).get('evidence', [])


def edit_memory(uid: str, memory_id: str, value: str, *, firestore_client: Any = None) -> Optional[Dict[str, Any]]:
database = _get_db(firestore_client)
user_ref = database.collection(users_collection).document(uid)
Expand Down
109 changes: 77 additions & 32 deletions desktop/macos/Backend-Rust/src/routes/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ async fn get_agent_status(
);
// Update Firestore to reflect stopped state
let now = chrono::Utc::now().to_rfc3339();
let _ = state
if let Err(error) = state
.firestore
.set_agent_vm(
&user.uid,
Expand All @@ -225,7 +225,15 @@ async fn get_agent_status(
&vm.auth_token,
&now,
)
.await;
.await
{
tracing::error!(
"Failed to mark stopped VM {} provisioning: {}",
vm.vm_name,
error
);
return Err(StatusCode::INTERNAL_SERVER_ERROR);
}

// Start the VM in the background
let firestore = state.firestore.clone();
Expand All @@ -241,7 +249,7 @@ async fn get_agent_status(
Ok(ip) => {
tracing::info!("VM {} restarted with IP {}", vm_name, ip);
let now = chrono::Utc::now().to_rfc3339();
let _ = firestore
if let Err(error) = firestore
.set_agent_vm(
&uid,
&vm_name,
Expand All @@ -251,12 +259,19 @@ async fn get_agent_status(
&auth_token,
&now,
)
.await;
.await
{
tracing::error!(
"Failed to mark restarted VM {} ready: {}",
vm_name,
error
);
}
}
Err(e) => {
tracing::error!("Failed to restart VM {}: {}", vm_name, e);
let now = chrono::Utc::now().to_rfc3339();
let _ = firestore
if let Err(error) = firestore
.set_agent_vm(
&uid,
&vm_name,
Expand All @@ -266,7 +281,14 @@ async fn get_agent_status(
&auth_token,
&now,
)
.await;
.await
{
tracing::error!(
"Failed to mark restart failure for VM {}: {}",
vm_name,
error
);
}
}
}
});
Expand All @@ -283,7 +305,14 @@ async fn get_agent_status(
"VM {} no longer exists in GCP — clearing Firestore record",
vm.vm_name
);
let _ = state.firestore.delete_agent_vm(&user.uid).await;
if let Err(error) = state.firestore.delete_agent_vm(&user.uid).await {
tracing::error!(
"Failed to clear missing VM {} from Firestore: {}",
vm.vm_name,
error
);
return Err(StatusCode::INTERNAL_SERVER_ERROR);
}
return Ok(Json(None));
}
Ok(gce_status)
Expand Down Expand Up @@ -311,31 +340,47 @@ async fn get_agent_status(
"https://compute.googleapis.com/compute/v1/projects/{}/zones/{}/instances/{}",
project, zone, vm_name
);
if let Ok(resp) = firestore
.build_compute_request(reqwest::Method::GET, &instance_url)
.await
{
if let Ok(resp) = resp.send().await {
if let Ok(instance) = resp.json::<serde_json::Value>().await {
let ip = instance["networkInterfaces"][0]["accessConfigs"]
[0]["natIP"]
.as_str()
.unwrap_or("unknown")
.to_string();
let now = chrono::Utc::now().to_rfc3339();
let _ = firestore
.set_agent_vm(
&uid,
&vm_name,
&zone,
Some(&ip),
AgentVmStatus::Ready,
&auth_token,
&now,
)
.await;
tracing::info!("VM {} recovered — ip={}", vm_name, ip);
}
let recovery = async {
let response = firestore
.build_compute_request(reqwest::Method::GET, &instance_url)
.await
.map_err(|error| error.to_string())?;
let instance = response
.send()
.await
.map_err(|error| error.to_string())?
.error_for_status()
.map_err(|error| error.to_string())?
.json::<serde_json::Value>()
.await
.map_err(|error| error.to_string())?;
let ip = instance["networkInterfaces"][0]["accessConfigs"][0]
["natIP"]
.as_str()
.filter(|ip| !ip.is_empty())
.ok_or_else(|| {
"recovered VM response has no external IP".to_owned()
})?;
let now = chrono::Utc::now().to_rfc3339();
firestore
.set_agent_vm(
&uid,
&vm_name,
&zone,
Some(ip),
AgentVmStatus::Ready,
&auth_token,
&now,
)
.await
.map_err(|error| error.to_string())?;
Ok::<_, String>(())
}
.await;
match recovery {
Ok(()) => tracing::info!("VM {} recovered", vm_name),
Err(error) => {
tracing::error!("Failed to recover VM {}: {}", vm_name, error)
}
}
});
Expand Down
24 changes: 15 additions & 9 deletions desktop/macos/Backend-Rust/src/routes/auth.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// OAuth Authentication Routes

Check warning on line 1 in desktop/macos/Backend-Rust/src/routes/auth.rs

View workflow job for this annotation

GitHub Actions / Hygiene

Large changed file

desktop/macos/Backend-Rust/src/routes/auth.rs is 993 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Backend-Rust/src/routes/auth.rs

View workflow job for this annotation

GitHub Actions / Desktop Swift Build & Tests

Large changed file

desktop/macos/Backend-Rust/src/routes/auth.rs is 993 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Backend-Rust/src/routes/auth.rs

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Large changed file

desktop/macos/Backend-Rust/src/routes/auth.rs is 993 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Backend-Rust/src/routes/auth.rs

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Large changed file

desktop/macos/Backend-Rust/src/routes/auth.rs is 993 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Backend-Rust/src/routes/auth.rs

View workflow job for this annotation

GitHub Actions / Desktop Swift Build & Tests

Large changed file

desktop/macos/Backend-Rust/src/routes/auth.rs is 993 lines; consider splitting files over 800 lines.
// Port from Python backend (main.py)

use axum::{
Expand Down Expand Up @@ -492,7 +492,13 @@
if form.use_custom_token {
match generate_custom_token(&state, &credentials).await {
Ok(token) => response.custom_token = Some(token),
Err(e) => tracing::warn!("Failed to generate custom token: {}", e),
Err(e) => {
tracing::error!("Failed to generate custom token: {}", e);
return Err(ErrorResponse {
error: "custom_token_unavailable".to_string(),
message: e.to_string(),
});
}
}
}

Expand Down Expand Up @@ -774,14 +780,14 @@

tracing::info!("Firebase sign-in successful, UID: {}", firebase_uid);

// For custom token generation, we need Firebase Admin SDK
// In Rust, we'd need to use the service account to create a custom token
// For now, return an error indicating this needs server-side implementation
// The Python version uses firebase_admin.auth.create_custom_token()

// TODO: Implement custom token generation using service account
// This requires signing a JWT with the service account private key
Err("Custom token generation requires Firebase Admin SDK - not yet implemented in Rust".into())
// Custom-token minting needs Firebase Admin JWT signing with the service
// account private key (Python: firebase_admin.auth.create_custom_token).
// Do not fail open with a partial token response — callers that set
// use_custom_token=true require the custom token or a hard error.
Err(format!(
"custom token minting is not implemented for uid {firebase_uid}; use id_token exchange instead"
)
.into())
}

/// Escape a value for safe interpolation into a double-quoted JavaScript string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,10 @@ extension APIClient {
if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
return json["is_setup_completed"] as? Bool ?? false
}
} catch {}
log("APIClient: app setup status response was not JSON for \(fullUrl.absoluteString)")
} catch {
logError("APIClient: failed to check app setup status", error: error)
}
return false
}

Expand Down
4 changes: 3 additions & 1 deletion desktop/windows/src/main/integrations/tokenStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ export function loadRefreshToken(): { refreshToken: string; email?: string } | n
if (!raw.refreshToken) return null
const dec = safeStorage.decryptString(Buffer.from(raw.refreshToken, 'base64'))
return { refreshToken: dec, email: raw.email }
} catch {
} catch (error) {
// Corrupt/unreadable secure storage must not look like "never connected".
console.warn('[google] failed to load refresh token from secure storage:', error)
return null
}
}
Expand Down
5 changes: 4 additions & 1 deletion desktop/windows/src/main/ipc/screen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ const READ_TIMEOUT_MS = 4500
// used when Rewind has no frame yet (capture just enabled / disabled).
async function desktopCapturerOcr(): Promise<string> {
try {
const primaryId = await getPrimarySourceId().catch(() => null)
const primaryId = await getPrimarySourceId().catch((error: unknown) => {
console.warn('[screen:readNow] primary source lookup failed; falling back to first screen:', error)
return null
})
const sources = await desktopCapturer.getSources({
types: ['screen'],
thumbnailSize: { width: 1920, height: 1080 }
Expand Down
10 changes: 9 additions & 1 deletion desktop/windows/src/main/rewind/retentionRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ export async function pruneRewindOnce(): Promise<number> {
const cutoff = retentionCutoff(Date.now(), retentionDays)
const removed = deleteRewindFramesOlderThan(cutoff)
await Promise.all(
removed.map((f) => unlink(f.imagePath).catch(() => undefined)) // file may already be gone
removed.map((f) =>
unlink(f.imagePath).catch((error: NodeJS.ErrnoException) => {
// ENOENT is idempotent (frame already gone). Other failures need a log
// so retention cannot silently leave disk growth undiagnosed.
if (error?.code !== 'ENOENT') {
console.warn('[rewind] failed to delete pruned frame:', f.imagePath, error)
}
})
)
)
return removed.length
}
Expand Down
Loading