nia-sync 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
auth.py CHANGED
@@ -104,64 +104,64 @@ def login() -> bool:
104
104
  def _poll_for_api_key(session_id: str, user_code: str) -> str | None:
105
105
  """Poll the exchange endpoint until authentication completes."""
106
106
  with httpx.Client(timeout=30) as client:
107
- for attempt in range(MAX_POLL_ATTEMPTS):
108
- try:
109
- response = client.post(
110
- f"{API_BASE_URL}/public/mcp-device/exchange",
111
- json={
112
- "authorization_session_id": session_id,
113
- "user_code": user_code,
114
- }
115
- )
116
-
117
- if response.status_code == 200:
118
- data = response.json()
119
- console.print("[green]Authentication successful![/green]")
120
- return data.get("api_key")
121
-
122
- elif response.status_code == 400:
123
- # Not ready yet - still pending or authorized but not ready
124
- detail = response.json().get("detail", "")
125
- if "not yet authorized" in detail.lower() or "complete the setup" in detail.lower():
126
- # Still waiting for user to complete in browser
127
- _show_waiting_indicator(attempt)
128
- time.sleep(POLL_INTERVAL_SECONDS)
129
- continue
130
- else:
131
- console.print(f"[red]Error: {detail}[/red]")
107
+ with console.status("[dim]Waiting for browser authentication...[/dim]") as status:
108
+ for attempt in range(MAX_POLL_ATTEMPTS):
109
+ try:
110
+ response = client.post(
111
+ f"{API_BASE_URL}/public/mcp-device/exchange",
112
+ json={
113
+ "authorization_session_id": session_id,
114
+ "user_code": user_code,
115
+ }
116
+ )
117
+
118
+ if response.status_code == 200:
119
+ data = response.json()
120
+ status.stop()
121
+ console.print("[green]Authentication successful![/green]")
122
+ return data.get("api_key")
123
+
124
+ elif response.status_code == 400:
125
+ # Not ready yet - still pending or authorized but not ready
126
+ detail = response.json().get("detail", "")
127
+ if "not yet authorized" in detail.lower() or "complete the setup" in detail.lower():
128
+ # Still waiting for user to complete in browser
129
+ time.sleep(POLL_INTERVAL_SECONDS)
130
+ continue
131
+ else:
132
+ status.stop()
133
+ console.print(f"[red]Error: {detail}[/red]")
134
+ return None
135
+
136
+ elif response.status_code == 410:
137
+ status.stop()
138
+ console.print("[red]Session expired. Please try again.[/red]")
132
139
  return None
133
140
 
134
- elif response.status_code == 410:
135
- console.print("[red]Session expired. Please try again.[/red]")
136
- return None
137
-
138
- elif response.status_code == 409:
139
- console.print("[red]Session already used. Please try again.[/red]")
140
- return None
141
+ elif response.status_code == 409:
142
+ status.stop()
143
+ console.print("[red]Session already used. Please try again.[/red]")
144
+ return None
141
145
 
142
- elif response.status_code == 404:
143
- console.print("[red]Invalid session. Please try again.[/red]")
144
- return None
146
+ elif response.status_code == 404:
147
+ status.stop()
148
+ console.print("[red]Invalid session. Please try again.[/red]")
149
+ return None
145
150
 
146
- else:
147
- console.print(f"[red]Unexpected error: {response.status_code}[/red]")
148
- return None
151
+ else:
152
+ status.stop()
153
+ console.print(f"[red]Unexpected error: {response.status_code}[/red]")
154
+ return None
149
155
 
150
- except httpx.RequestError as e:
151
- console.print(f"[yellow]Network error, retrying... ({e})[/yellow]")
152
- time.sleep(POLL_INTERVAL_SECONDS)
153
- continue
156
+ except httpx.RequestError as e:
157
+ console.print(f"[yellow]Network error, retrying... ({e})[/yellow]")
158
+ time.sleep(POLL_INTERVAL_SECONDS)
159
+ continue
154
160
 
155
161
  console.print("[red]Timeout waiting for authentication. Please try again.[/red]")
156
162
  return None
157
163
 
158
164
 
159
- def _show_waiting_indicator(attempt: int):
160
- """Show a waiting indicator."""
161
- dots = "." * ((attempt % 3) + 1)
162
- console.print(f"\r[dim]Waiting for browser authentication{dots} [/dim]", end="")
163
-
164
-
165
165
  def logout():
166
166
  """Clear stored credentials."""
167
167
  clear_config()
config.py CHANGED
@@ -16,7 +16,7 @@ NIA_SYNC_DIR = Path.home() / ".nia-sync"
16
16
  CONFIG_FILE = NIA_SYNC_DIR / "config.json"
17
17
 
18
18
  # API configuration
19
- API_BASE_URL = os.getenv("NIA_API_URL", "https://api.trynia.ai")
19
+ API_BASE_URL = os.getenv("NIA_API_URL", "https://apigcp.trynia.ai")
20
20
 
21
21
  # Default directories to search for folders (no config needed)
22
22
  DEFAULT_WATCH_DIRS = [
extractor.py CHANGED
@@ -847,7 +847,7 @@ def _extract_folder(
847
847
  })
848
848
  extracted_count += 1
849
849
 
850
- except (PermissionError, IOError) as e:
850
+ except (PermissionError, IOError, OSError, UnicodeDecodeError) as e:
851
851
  logger.warning(f"Could not read {file_path}: {e}")
852
852
  continue
853
853
 
main.py CHANGED
@@ -261,8 +261,10 @@ def add(path: str = typer.Argument(..., help="Path to sync (folder or database)"
261
261
  result = add_source(path, detected_type)
262
262
 
263
263
  if result:
264
+ folder_id = result.get('local_folder_id', '')
265
+ short_id = folder_id[:8] if folder_id else 'unknown'
264
266
  console.print(f"[green]✓ Added:[/green] {result.get('display_name', path)}")
265
- console.print(f"[dim]ID: {result.get('local_folder_id')[:8]}[/dim]")
267
+ console.print(f"[dim]ID: {short_id}[/dim]")
266
268
  console.print("\n[dim]Run [cyan]nia[/cyan] to start syncing.[/dim]")
267
269
  else:
268
270
  console.print("[red]Failed to add source.[/red]")
@@ -478,8 +480,11 @@ def daemon(
478
480
  # Remove old watchers (source deleted from UI)
479
481
  for source_id in current_watching - new_source_ids:
480
482
  old_name = sources_by_id.get(source_id, {}).get("display_name", source_id[:8])
481
- watcher.unwatch(source_id)
482
- console.print(f" [dim]- Stopped watching {old_name}[/dim]")
483
+ try:
484
+ watcher.unwatch(source_id)
485
+ console.print(f" [dim]- Stopped watching {old_name}[/dim]")
486
+ except Exception as e:
487
+ logger.warning(f"Failed to unwatch {old_name}: {e}")
483
488
 
484
489
  sources_by_id = new_sources_by_id
485
490
  return resolved, newly_added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nia-sync
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Keep your local files in sync with Nia
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: typer>=0.9.0
@@ -0,0 +1,11 @@
1
+ auth.py,sha256=n0ezRqIbz3kZYcyIjH47_MDKgwNgAaVr0Y2NEfRxa50,5704
2
+ config.py,sha256=JWxdL8INKo23lam4F49ZbllxbW3yorqczm7aaqepQCc,7507
3
+ extractor.py,sha256=ViqOZQBtwrKUK_W0bOFTNU8Ar726mcZ6TJ6dS_PfVyk,28599
4
+ main.py,sha256=k_4KXmQb8sRI-Wf8r2MMKUZbIa1SLuGUVtpU30ej63E,22975
5
+ sync.py,sha256=W31mWjvo2qEszUaH_C9m7bQu0FMUsBFUaiw8QdcYDxs,5427
6
+ watcher.py,sha256=9RvHVpn1ozTBgxPeOe20kkW6x5gb8A1b7e33D_7JBc4,9598
7
+ nia_sync-0.1.1.dist-info/METADATA,sha256=3hhEQsrIu8LLcOm3uvUo_lyGSzQH5GmlSly8BXXRQ_o,240
8
+ nia_sync-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ nia_sync-0.1.1.dist-info/entry_points.txt,sha256=Fx8TIOgXqWdZzZEkEateDtcNfgnwuPW4jZTqlEUrHVs,33
10
+ nia_sync-0.1.1.dist-info/top_level.txt,sha256=_ZWBugSHWwSpLXYJAcF6TlWmzECu18k0y1-EX27jtBw,40
11
+ nia_sync-0.1.1.dist-info/RECORD,,
sync.py CHANGED
@@ -12,7 +12,7 @@ from pathlib import Path
12
12
  from typing import Any
13
13
  import httpx
14
14
 
15
- from config import API_BASE_URL, get_api_key, enable_source_sync
15
+ from config import API_BASE_URL, get_api_key
16
16
  from extractor import extract_incremental, detect_source_type
17
17
 
18
18
  logger = logging.getLogger(__name__)
@@ -69,11 +69,6 @@ def sync_source(source: dict[str, Any]) -> dict[str, Any]:
69
69
  "error": f"Path does not exist: {path}",
70
70
  }
71
71
 
72
- # Auto-enable sync if source exists locally but sync not enabled
73
- if not source.get("sync_enabled", False):
74
- logger.info(f"Auto-enabling sync for {path}")
75
- enable_source_sync(local_folder_id, path)
76
-
77
72
  # Auto-detect type if not specified
78
73
  if not detected_type:
79
74
  detected_type = detect_source_type(path)
watcher.py CHANGED
@@ -7,7 +7,7 @@ with debouncing to prevent rapid-fire updates.
7
7
  import os
8
8
  import threading
9
9
  import logging
10
- from typing import Callable
10
+ from typing import Any, Callable
11
11
  from pathlib import Path
12
12
 
13
13
  from watchdog.observers import Observer
@@ -148,7 +148,7 @@ class FileWatcher:
148
148
  self.debounce_sec = debounce_sec
149
149
  self.observer = Observer()
150
150
  self.handlers: dict[str, SyncEventHandler] = {}
151
- self._watches: dict[str, any] = {}
151
+ self._watches: dict[str, Any] = {}
152
152
  self._lock = threading.Lock()
153
153
  self._started = False
154
154
 
@@ -1,11 +0,0 @@
1
- auth.py,sha256=_Q-wzLLtinoy8qtTihZNqdRAECPEzVjC5NgCXOLftJ8,5487
2
- config.py,sha256=tJD3k3mBP9KORg4tX692uKk3Z92tEeYRI8jmgPjY5P0,7504
3
- extractor.py,sha256=GxKmBTq04AxCjhtimlHZ2Gh3auvEEUyMHT-vM4YqWzI,28570
4
- main.py,sha256=-ZWWs-5pQx_gR2QLwMrhbmuTKZo_8PQnVoExi85lqUU,22744
5
- sync.py,sha256=iH9N22NEr2Nt5O6GeDP_X--G6IXhr8Hx3Z2xfJWkXc0,5667
6
- watcher.py,sha256=BfdGwcfNDJiddUBMIPx61En2tSkLb3YnfxePKjSFQTc,9593
7
- nia_sync-0.1.0.dist-info/METADATA,sha256=Qm4BGkM9fO0tiZg1AgKmA8-S_8f9674DtZZXBY_u0hc,240
8
- nia_sync-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- nia_sync-0.1.0.dist-info/entry_points.txt,sha256=Fx8TIOgXqWdZzZEkEateDtcNfgnwuPW4jZTqlEUrHVs,33
10
- nia_sync-0.1.0.dist-info/top_level.txt,sha256=_ZWBugSHWwSpLXYJAcF6TlWmzECu18k0y1-EX27jtBw,40
11
- nia_sync-0.1.0.dist-info/RECORD,,