claudesync 0.2.6__tar.gz → 0.2.7__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: claudesync
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: A tool to synchronize local files with Claude.ai projects
5
5
  Author-email: Jahziah Wagner <jahziah.wagner+pypi@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/jahwag/claudesync
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "claudesync"
7
- version = "0.2.6"
7
+ version = "0.2.7"
8
8
  authors = [
9
9
  {name = "Jahziah Wagner", email = "jahziah.wagner+pypi@gmail.com"},
10
10
  ]
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  import mimetypes
3
+ import time
3
4
  from watchdog.events import FileSystemEventHandler
4
5
  from .debounce import DebounceHandler
5
6
  from .gitignore_utils import load_gitignore, should_ignore
@@ -21,6 +22,10 @@ class FileUploadHandler(FileSystemEventHandler):
21
22
  self.gitignore = load_gitignore(self.base_path)
22
23
  self.log_callback = None
23
24
  self.max_file_size = max_file_size
25
+ self.backoff_time = 1
26
+ self.max_backoff_time = 60
27
+ self.session_expiration_time = 180 # 3 minutes
28
+ self.session_expiration_start = None
24
29
 
25
30
  def log(self, message):
26
31
  if self.log_callback:
@@ -57,37 +62,52 @@ class FileUploadHandler(FileSystemEventHandler):
57
62
  return should_ignore(self.gitignore, file_path, self.base_path)
58
63
 
59
64
  def api_request(self, method, url, **kwargs):
60
- try:
61
- response = requests.request(method, url, headers=self.headers, cookies=self.cookies, **kwargs)
62
- response.raise_for_status()
63
- return response.json() if response.text else None
64
- except requests.RequestException as e:
65
- print(f"API request error: {str(e)}")
66
- return None
65
+ while True:
66
+ try:
67
+ response = requests.request(method, url, headers=self.headers, cookies=self.cookies, **kwargs)
68
+ if response.status_code == 403:
69
+ if self.session_expiration_start is None:
70
+ self.session_expiration_start = time.time()
71
+ elif time.time() - self.session_expiration_start > self.session_expiration_time:
72
+ self.log("Session key has likely expired. Please restart ClaudeSync with a new session key.")
73
+ raise SystemExit(1)
74
+
75
+ self.log(f"Received 403 error. Backing off for {self.backoff_time} seconds.")
76
+ time.sleep(self.backoff_time)
77
+ self.backoff_time = min(self.backoff_time * 2, self.max_backoff_time)
78
+ else:
79
+ self.session_expiration_start = None
80
+ self.backoff_time = 1
81
+ response.raise_for_status()
82
+ return response.json() if response.text else None
83
+ except requests.RequestException as e:
84
+ if response.status_code != 403:
85
+ self.log(f"API request error: {str(e)}")
86
+ return None
67
87
 
68
88
  def get_documents(self):
69
89
  return self.api_request('GET', self.api_endpoint) or []
70
90
 
71
91
  def delete_document(self, uuid):
72
92
  if self.api_request('DELETE', f"{self.api_endpoint}/{uuid}"):
73
- print(f"Deleted document: {uuid}")
93
+ self.log(f"Deleted document: {uuid}")
74
94
 
75
95
  def delete_all_documents(self):
76
96
  for doc in self.get_documents():
77
97
  self.delete_document(doc['uuid'])
78
- print("All documents deleted.")
98
+ self.log("All documents deleted.")
79
99
 
80
100
  def upload_file(self, file_path):
81
101
  if not os.path.isfile(file_path) or self.should_ignore_file(file_path):
82
102
  return
83
103
  if os.path.getsize(file_path) == 0:
84
- print(f"Skipping empty file: {file_path}")
104
+ self.log(f"Skipping empty file: {file_path}")
85
105
  return
86
106
  try:
87
107
  with open(file_path, 'r', encoding='utf-8') as file:
88
108
  content = file.read()
89
109
  if not content.strip():
90
- print(f"Skipping file with only whitespace: {file_path}")
110
+ self.log(f"Skipping file with only whitespace: {file_path}")
91
111
  return
92
112
 
93
113
  rel_path = os.path.relpath(file_path, self.base_path)
@@ -100,4 +120,4 @@ class FileUploadHandler(FileSystemEventHandler):
100
120
  if self.api_request('POST', self.api_endpoint, json=payload):
101
121
  self.log(f"Uploaded: {file_name}")
102
122
  except Exception as e:
103
- print(f"Error processing file {file_path}: {str(e)}")
123
+ self.log(f"Error processing file {file_path}: {str(e)}")
@@ -21,8 +21,9 @@ class ClaudeSyncTUI:
21
21
  self.observer = None
22
22
  self.handler = None
23
23
  self.scroll_position = 0
24
- self.max_log_lines = 1000 # Increased for more history
25
- self.auto_scroll = True # New flag for automatic scrolling
24
+ self.max_log_lines = 1000
25
+ self.auto_scroll = True
26
+ self.is_syncing = False
26
27
 
27
28
  def setup(self):
28
29
  if not self.user_id:
@@ -39,14 +40,16 @@ class ClaudeSyncTUI:
39
40
  self.observer.schedule(self.handler, self.watch_dir, recursive=True)
40
41
 
41
42
  def initial_sync(self):
42
- print("Please wait ...")
43
- self.add_log_message("Performing initial synchronization...")
43
+ self.is_syncing = True
44
+ self.add_log_message("Starting initial synchronization...")
44
45
  for root, dirs, files in os.walk(self.watch_dir):
45
46
  for file in files:
46
47
  file_path = os.path.join(root, file)
47
48
  if not self.handler.should_ignore_file(file_path):
48
49
  self.handler.upload_file(file_path)
50
+ self.draw() # Redraw the TUI after each file upload
49
51
  self.add_log_message("Initial synchronization completed.")
52
+ self.is_syncing = False
50
53
 
51
54
  def add_log_message(self, message):
52
55
  self.log_messages.append(message)
@@ -68,6 +71,9 @@ class ClaudeSyncTUI:
68
71
  print(f"Project ID: {self.project_id}")
69
72
  print(f"Auto-scroll: {'ON' if self.auto_scroll else 'OFF'}")
70
73
 
74
+ if self.is_syncing:
75
+ print(self.term.move_y(7) + self.term.red("Initial sync in progress..."))
76
+
71
77
  print(self.term.move_y(8) + self.term.black_on_skyblue(self.term.center('Recent Activity')))
72
78
 
73
79
  log_height = self.term.height - 12
@@ -79,6 +85,7 @@ class ClaudeSyncTUI:
79
85
 
80
86
  def run(self):
81
87
  with self.term.cbreak(), self.term.hidden_cursor():
88
+ self.draw() # Initial draw before starting sync
82
89
  self.initial_sync()
83
90
  self.observer.start()
84
91
  while True:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: claudesync
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: A tool to synchronize local files with Claude.ai projects
5
5
  Author-email: Jahziah Wagner <jahziah.wagner+pypi@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/jahwag/claudesync
File without changes
File without changes
File without changes
File without changes