Skip to content

Commit d328854

Browse files
authored
Merge pull request #2987 from spacedriveapp/cursor/device-proxy-pairing-651c
Proxy pairing
2 parents bdfd858 + 5d9a691 commit d328854

70 files changed

Lines changed: 6399 additions & 502 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,15 @@ jobs:
259259
with:
260260
token: ${{ secrets.GITHUB_TOKEN }}
261261

262+
- name: Verify native deps were downloaded
263+
if: runner.os == 'Linux'
264+
run: |
265+
echo "Checking apps/.deps directory:"
266+
ls -la apps/.deps/ || echo "Directory doesn't exist"
267+
echo "Checking apps/.deps/lib directory:"
268+
ls -la apps/.deps/lib/ || echo "lib/ subdirectory doesn't exist - creating empty dir"
269+
mkdir -p apps/.deps/lib
270+
262271
- name: Build
263272
working-directory: apps/tauri
264273
run: |

TODO

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Journey to v2.0.0-pre.1:
3232
✔ Fix volume reactivity @done(26-01-20 14:59)
3333
☐ Merge ephemeral results with indexed results to enable showing hidden files on demand
3434
☐ Mobile explorer
35-
search
35+
search @done(26-01-22 12:21)
3636
☐ inspector
3737
☐ context menu
3838
☐ view settings

apps/cli/src/domains/events/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,26 @@ fn summarize_event(event: &Event) -> String {
441441
format!("Sync error: {}", message)
442442
}
443443

444+
// Proxy pairing events
445+
Event::ProxyPairingConfirmationRequired {
446+
vouchee_device_name,
447+
voucher_device_name,
448+
..
449+
} => {
450+
format!(
451+
"Proxy pairing confirmation required: {} vouched by {}",
452+
vouchee_device_name, voucher_device_name
453+
)
454+
}
455+
Event::ProxyPairingVouchingReady {
456+
vouchee_device_id, ..
457+
} => {
458+
format!("Proxy pairing vouching ready for device {}", vouchee_device_id)
459+
}
460+
461+
// Config events
462+
Event::ConfigChanged { .. } => "Configuration changed".to_string(),
463+
444464
// Custom events
445465
Event::Custom { event_type, data } => {
446466
format!("Custom event: {} - {:?}", event_type, data)
@@ -452,3 +472,4 @@ fn summarize_event(event: &Event) -> String {
452472
}
453473
}
454474
}
475+
}

apps/cli/src/domains/sync/mod.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,14 @@ pub enum SyncCmd {
2424

2525
/// Export sync event log
2626
Events(SyncEventsArgs),
27+
28+
/// Show computed sync partners for this library
29+
Partners,
2730
}
2831

2932
pub async fn run(ctx: &Context, cmd: SyncCmd) -> Result<()> {
3033
match cmd {
34+
SyncCmd::Partners => show_partners(ctx).await?,
3135
SyncCmd::Events(args) => export_events(ctx, args).await?,
3236
SyncCmd::Metrics(args) => {
3337
// Parse time filters
@@ -590,3 +594,95 @@ fn format_events_markdown(
590594

591595
output
592596
}
597+
598+
async fn show_partners(ctx: &Context) -> Result<()> {
599+
let library_id = ctx.library_id.ok_or_else(|| {
600+
anyhow::anyhow!("No library selected. Use 'sd library switch' to select a library first.")
601+
})?;
602+
603+
// Create input for the operation
604+
let input = sd_core::ops::sync::get_sync_partners::GetSyncPartnersInput {};
605+
606+
let json_response = ctx.core.query(&input, Some(library_id)).await?;
607+
let output: sd_core::ops::sync::get_sync_partners::GetSyncPartnersOutput =
608+
serde_json::from_value(json_response)?;
609+
610+
println!("\n{}", "Sync Partners for Current Library".bold());
611+
println!("{}", "═".repeat(60));
612+
println!();
613+
614+
if output.partners.is_empty() {
615+
println!(" {} No connected sync partners found", "●".red());
616+
println!();
617+
println!(" Possible reasons:");
618+
println!(" - No other devices paired with this device");
619+
println!(" - Paired devices are not in this library's devices table");
620+
println!(" - Paired devices do not have sync_enabled=true");
621+
println!();
622+
} else {
623+
println!(" {} {} sync partner(s) available", "●".green(), output.partners.len());
624+
println!();
625+
626+
let mut table = Table::new();
627+
table
628+
.load_preset(UTF8_FULL)
629+
.set_content_arrangement(ContentArrangement::Dynamic)
630+
.set_header(Row::from(vec![
631+
Cell::new("Device UUID").add_attribute(Attribute::Bold),
632+
Cell::new("Name").add_attribute(Attribute::Bold),
633+
Cell::new("Status").add_attribute(Attribute::Bold),
634+
]));
635+
636+
for partner in &output.partners {
637+
table.add_row(vec![
638+
partner.device_uuid.to_string(),
639+
partner.device_name.clone(),
640+
if partner.is_paired {
641+
"✓ Paired".green().to_string()
642+
} else {
643+
"○ Not Paired".dark_grey().to_string()
644+
},
645+
]);
646+
}
647+
648+
println!("{}", table);
649+
println!();
650+
}
651+
652+
// Show debug info
653+
println!("{}", "Library Membership Debug".dark_grey().bold());
654+
println!("{}", "─".repeat(60).dark_grey());
655+
println!();
656+
println!(" Total devices in library: {}", output.debug_info.total_devices);
657+
println!(" Devices with sync_enabled: {}", output.debug_info.sync_enabled_devices);
658+
println!(" Devices with NodeId mapping: {}", output.debug_info.paired_devices);
659+
println!(" Final sync partners: {}", output.debug_info.final_sync_partners);
660+
println!();
661+
662+
if !output.debug_info.device_details.is_empty() {
663+
let mut debug_table = Table::new();
664+
debug_table
665+
.load_preset(UTF8_FULL)
666+
.set_content_arrangement(ContentArrangement::Dynamic)
667+
.set_header(Row::from(vec![
668+
Cell::new("Device").add_attribute(Attribute::Bold).fg(Color::DarkGrey),
669+
Cell::new("Sync Enabled").add_attribute(Attribute::Bold).fg(Color::DarkGrey),
670+
Cell::new("Has NodeId").add_attribute(Attribute::Bold).fg(Color::DarkGrey),
671+
Cell::new("NodeId").add_attribute(Attribute::Bold).fg(Color::DarkGrey),
672+
]));
673+
674+
for device in &output.debug_info.device_details {
675+
debug_table.add_row(vec![
676+
Cell::new(&device.name).fg(Color::DarkGrey),
677+
Cell::new(if device.sync_enabled { "✓" } else { "✗" }).fg(Color::DarkGrey),
678+
Cell::new(if device.has_node_id { "✓" } else { "✗" }).fg(Color::DarkGrey),
679+
Cell::new(device.node_id.as_deref().unwrap_or("-")).fg(Color::DarkGrey),
680+
]);
681+
}
682+
683+
println!("{}", debug_table);
684+
println!();
685+
}
686+
687+
Ok(())
688+
}

apps/mobile/src/screens/overview/components/DevicePanel.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { useVolumeIndexingStore } from "../../../stores";
2020

2121
// Temporary type extension
2222
type DeviceWithConnection = Device & {
23-
connection_method?: "Direct" | "Relay" | "Mixed" | null;
23+
connection_method?: "LocalNetwork" | "DirectInternet" | "RelayProxy" | null;
2424
};
2525

2626
function formatBytes(bytes: number): string {
@@ -194,14 +194,14 @@ export function DevicePanel({ onLocationSelect }: DevicePanelProps = {}) {
194194
}
195195

196196
interface ConnectionBadgeProps {
197-
method: "Direct" | "Relay" | "Mixed";
197+
method: "LocalNetwork" | "DirectInternet" | "RelayProxy";
198198
}
199199

200200
function ConnectionBadge({ method }: ConnectionBadgeProps) {
201201
const labels = {
202-
Direct: "Local",
203-
Relay: "Relay",
204-
Mixed: "Mixed",
202+
LocalNetwork: "Local",
203+
DirectInternet: "Direct",
204+
RelayProxy: "Relay",
205205
};
206206

207207
return (

core/examples/library_demo.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
9797
created_at: Set(device.created_at),
9898
updated_at: Set(device.updated_at),
9999
sync_enabled: Set(false),
100-
last_sync_at: Set(None),
101100
};
102101
let inserted_device = device_model.insert(db.conn()).await?;
103102
println!(" ✓ Device registered");

core/src/bin/daemon.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ struct Args {
3232
}
3333

3434
#[tokio::main]
35-
async fn main() -> Result<(), Box<dyn std::error::Error>> {
35+
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
3636
let args = Args::parse();
3737

3838
// Resolve base data directory

core/src/config/app_config.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ pub struct AppConfig {
3737
/// Daemon logging configuration with multi-stream support
3838
#[serde(default)]
3939
pub logging: LoggingConfig,
40+
41+
/// Proxy pairing configuration
42+
#[serde(default)]
43+
pub proxy_pairing: ProxyPairingConfig,
4044
}
4145

4246
/// Configuration for core services
@@ -134,6 +138,33 @@ pub struct LoggingConfig {
134138
pub streams: Vec<LogStreamConfig>,
135139
}
136140

141+
/// Proxy pairing configuration
142+
#[derive(Debug, Clone, Serialize, Deserialize)]
143+
pub struct ProxyPairingConfig {
144+
/// Automatically accept vouches from trusted devices
145+
pub auto_accept_vouched: bool,
146+
/// Automatically vouch new devices to all paired devices
147+
pub auto_vouch_to_all: bool,
148+
/// Maximum age of vouch signatures in seconds
149+
pub vouch_signature_max_age: u64,
150+
/// Timeout for proxy confirmation in seconds
151+
pub vouch_response_timeout: u64,
152+
/// Maximum retries for queued vouches
153+
pub vouch_queue_retry_limit: u32,
154+
}
155+
156+
impl Default for ProxyPairingConfig {
157+
fn default() -> Self {
158+
Self {
159+
auto_accept_vouched: true,
160+
auto_vouch_to_all: false,
161+
vouch_signature_max_age: 300,
162+
vouch_response_timeout: 60,
163+
vouch_queue_retry_limit: 5,
164+
}
165+
}
166+
}
167+
137168
impl Default for LoggingConfig {
138169
fn default() -> Self {
139170
Self {
@@ -210,6 +241,7 @@ impl AppConfig {
210241
job_logging: JobLoggingConfig::default(),
211242
services: ServiceConfig::default(),
212243
logging: LoggingConfig::default(),
244+
proxy_pairing: ProxyPairingConfig::default(),
213245
}
214246
}
215247

@@ -273,7 +305,7 @@ impl Migrate for AppConfig {
273305
}
274306

275307
fn target_version() -> u32 {
276-
4 // Updated schema version for multi-stream logging
308+
5 // Added proxy pairing configuration
277309
}
278310

279311
fn migrate(&mut self) -> Result<()> {
@@ -301,7 +333,13 @@ impl Migrate for AppConfig {
301333
self.version = 4;
302334
Ok(())
303335
}
304-
4 => Ok(()), // Already at target version
336+
4 => {
337+
// Migration from v4 to v5: Add proxy pairing configuration
338+
self.proxy_pairing = ProxyPairingConfig::default();
339+
self.version = 5;
340+
Ok(())
341+
}
342+
5 => Ok(()), // Already at target version
305343
v => Err(anyhow!("Unknown config version: {}", v)),
306344
}
307345
}

core/src/device/manager.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,6 @@ impl DeviceManager {
345345
is_online: true,
346346
last_seen_at: chrono::Utc::now(),
347347
sync_enabled: true,
348-
last_sync_at: None,
349348
created_at: chrono::Utc::now(),
350349
updated_at: chrono::Utc::now(),
351350
// Ephemeral fields

0 commit comments

Comments
 (0)