soulsync 1.0.12 → 1.0.13
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.
- package/config.json.example +2 -8
- package/index.js +421 -31
- package/openclaw.plugin.json +4 -4
- package/package.json +1 -1
- package/src/client.py +13 -44
- package/src/main.py +21 -49
- package/src/profiles.py +27 -49
- package/src/sync.py +67 -60
- package/src/interactive_auth.py +0 -283
- package/src/main_fixed.py +0 -434
- package/src/register.py +0 -131
package/src/main.py
CHANGED
|
@@ -61,7 +61,7 @@ class SoulSyncPlugin:
|
|
|
61
61
|
|
|
62
62
|
cloud_url = self.config.get('cloud_url', '').strip()
|
|
63
63
|
email = self.config.get('email', '').strip()
|
|
64
|
-
|
|
64
|
+
token = self.config.get('token', '').strip()
|
|
65
65
|
|
|
66
66
|
if not cloud_url:
|
|
67
67
|
self.config['cloud_url'] = 'https://soulsync.work'
|
|
@@ -98,11 +98,6 @@ class SoulSyncPlugin:
|
|
|
98
98
|
elif 'email' in auth_result:
|
|
99
99
|
config['email'] = auth_result.get('email', '')
|
|
100
100
|
|
|
101
|
-
# 保存 password(注册时输入的密码,需要保存以便自动登录)
|
|
102
|
-
# 注意:登录时我们只有从 config 读取的 password
|
|
103
|
-
if 'password' in auth_result:
|
|
104
|
-
config['password'] = auth_result.get('password', '')
|
|
105
|
-
|
|
106
101
|
# 保存 token
|
|
107
102
|
if 'token' in auth_result:
|
|
108
103
|
config['token'] = auth_result['token']
|
|
@@ -290,47 +285,32 @@ class SoulSyncPlugin:
|
|
|
290
285
|
profile = self.client.get_profile()
|
|
291
286
|
print(f"[SoulSync] Using existing token, user: {profile.get('email', 'unknown')}")
|
|
292
287
|
except Exception as e:
|
|
288
|
+
error_str = str(e)
|
|
289
|
+
if '401' in error_str or 'Unauthorized' in error_str or 'token' in error_str.lower():
|
|
290
|
+
print("[SoulSync] Token expired or invalid. Please re-login via chat: say 'login SoulSync'")
|
|
291
|
+
print("[SoulSync] Token 已过期,请在聊天中说 'login SoulSync' 重新登录")
|
|
292
|
+
return False
|
|
293
293
|
print(f"[SoulSync] Token invalid, re-authenticating: {e}")
|
|
294
294
|
token = None
|
|
295
295
|
|
|
296
296
|
if not token:
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
try:
|
|
302
|
-
result = self.client.authenticate(email, password)
|
|
303
|
-
if result:
|
|
304
|
-
result['email'] = email
|
|
305
|
-
result['password'] = password
|
|
306
|
-
print("[SoulSync] Login successful! / 登录成功!")
|
|
307
|
-
self._save_auth_to_config(result)
|
|
308
|
-
token = self.client.token
|
|
309
|
-
except Exception as e:
|
|
310
|
-
error_msg = str(e)
|
|
311
|
-
|
|
312
|
-
if "429" in error_msg or "too many" in error_msg.lower():
|
|
313
|
-
print("\n[SoulSync] ========================================")
|
|
314
|
-
print(f"[SoulSync] ❌ {e}")
|
|
315
|
-
print("[SoulSync] Too many failed attempts. Please try again later / 登录失败次数过多,请稍后再试")
|
|
316
|
-
print("[SoulSync] ========================================\n")
|
|
317
|
-
else:
|
|
318
|
-
print("\n[SoulSync] ========================================")
|
|
319
|
-
print(f"[SoulSync] ❌ {e}")
|
|
320
|
-
print("[SoulSync] Please run 'openclaw soulsync:setup' to reconfigure / 请运行 'openclaw soulsync:setup' 重新配置")
|
|
321
|
-
print("[SoulSync] ========================================\n")
|
|
322
|
-
|
|
323
|
-
sys.exit(0)
|
|
324
|
-
|
|
297
|
+
print("[SoulSync] Token expired or invalid. Please re-configure using 'openclaw soulsync:setup'")
|
|
298
|
+
print("[SoulSync] Token 已过期,请重新运行 'openclaw soulsync:setup' 配置")
|
|
299
|
+
return False
|
|
300
|
+
|
|
325
301
|
email = self.config.get('email')
|
|
326
|
-
|
|
327
|
-
|
|
302
|
+
|
|
328
303
|
try:
|
|
329
304
|
profile = self.client.get_profile()
|
|
330
305
|
print(f"\n[SoulSync] Logged in as: {profile.get('email')}")
|
|
331
306
|
subscription = profile.get('subscription', {})
|
|
332
307
|
print(f"[SoulSync] Subscription: {subscription.get('status')} (days remaining: {subscription.get('daysRemaining', 0)})\n")
|
|
333
308
|
except Exception as e:
|
|
309
|
+
error_str = str(e)
|
|
310
|
+
if '401' in error_str or 'Unauthorized' in error_str or 'token' in error_str.lower():
|
|
311
|
+
print("[SoulSync] Token expired or invalid. Please re-login via chat: say 'login SoulSync'")
|
|
312
|
+
print("[SoulSync] Token 已过期,请在聊天中说 'login SoulSync' 重新登录")
|
|
313
|
+
return False
|
|
334
314
|
print(f"[SoulSync] Warning: Could not get profile: {e}")
|
|
335
315
|
|
|
336
316
|
# 版本管理器
|
|
@@ -391,20 +371,12 @@ class SoulSyncPlugin:
|
|
|
391
371
|
"""WebSocket 消息回调"""
|
|
392
372
|
event = data.get('event')
|
|
393
373
|
|
|
394
|
-
if event == '
|
|
395
|
-
|
|
374
|
+
if event == 'profile:updated':
|
|
375
|
+
user_id = data.get('user_id')
|
|
396
376
|
version = data.get('version')
|
|
397
|
-
print(f"\n[SoulSync] [WebSocket]
|
|
398
|
-
try:
|
|
399
|
-
self.profile_sync.on_remote_change(file_path, version)
|
|
400
|
-
except Exception as e:
|
|
401
|
-
print(f"[SoulSync] Sync error: {e}")
|
|
402
|
-
|
|
403
|
-
elif event == 'new_memory':
|
|
404
|
-
print(f"\n[SoulSync] [WebSocket] New memory available!")
|
|
377
|
+
print(f"\n[SoulSync] [WebSocket] Profile updated (v{version})")
|
|
405
378
|
try:
|
|
406
379
|
self.profile_sync.pull_all()
|
|
407
|
-
print("[SoulSync] Memory synced from remote")
|
|
408
380
|
except Exception as e:
|
|
409
381
|
print(f"[SoulSync] Sync error: {e}")
|
|
410
382
|
|
|
@@ -489,9 +461,9 @@ def main():
|
|
|
489
461
|
plugin.load_config()
|
|
490
462
|
|
|
491
463
|
email = plugin.config.get('email', '').strip()
|
|
492
|
-
|
|
464
|
+
token = plugin.config.get('token', '').strip()
|
|
493
465
|
|
|
494
|
-
if not email or not
|
|
466
|
+
if not email or not token:
|
|
495
467
|
print("\n[SoulSync] ========================================")
|
|
496
468
|
print("[SoulSync] Not configured. Run 'openclaw soulsync:setup' first.")
|
|
497
469
|
print("[SoulSync] 尚未配置,请先运行 'openclaw soulsync:setup'")
|
package/src/profiles.py
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import requests
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
|
6
|
+
from src.client import TLSAdapter
|
|
2
7
|
|
|
3
8
|
|
|
4
9
|
class ProfilesClient:
|
|
5
|
-
"""Profiles API 客户端"""
|
|
10
|
+
"""Profiles API 客户端 - 统一同步接口"""
|
|
6
11
|
|
|
7
12
|
def __init__(self, cloud_url: str, token: str = None):
|
|
8
13
|
self.cloud_url = cloud_url.rstrip('/')
|
|
9
14
|
self.token = token
|
|
15
|
+
self.session = requests.Session()
|
|
16
|
+
self.session.mount('https://', TLSAdapter())
|
|
10
17
|
|
|
11
18
|
def _get_headers(self) -> dict:
|
|
12
19
|
"""获取请求头"""
|
|
@@ -19,57 +26,50 @@ class ProfilesClient:
|
|
|
19
26
|
"""设置 token"""
|
|
20
27
|
self.token = token
|
|
21
28
|
|
|
22
|
-
def get_profiles(self
|
|
23
|
-
"""
|
|
29
|
+
def get_profiles(self) -> dict:
|
|
30
|
+
"""获取用户的完整 profiles
|
|
24
31
|
|
|
25
|
-
Args:
|
|
26
|
-
path: 可选的文件路径
|
|
27
|
-
|
|
28
32
|
Returns:
|
|
29
|
-
包含
|
|
33
|
+
包含 content (dict), version, updated_at 的字典
|
|
34
|
+
示例: {"content": {"SOUL.md": "...", "USER.md": "..."}, "version": 3, "updated_at": "..."}
|
|
30
35
|
"""
|
|
31
36
|
url = f"{self.cloud_url}/api/profiles"
|
|
32
|
-
if path:
|
|
33
|
-
url += f"?path={path}"
|
|
34
37
|
|
|
35
|
-
response =
|
|
38
|
+
response = self.session.get(url, headers=self._get_headers())
|
|
36
39
|
|
|
37
40
|
if response.status_code == 200:
|
|
38
41
|
return response.json()
|
|
39
|
-
elif response.status_code == 404:
|
|
40
|
-
return {'files': []}
|
|
41
42
|
else:
|
|
42
43
|
error = response.json().get('error', 'Unknown error')
|
|
43
44
|
raise Exception(f"Get profiles failed: {error}")
|
|
44
45
|
|
|
45
|
-
def
|
|
46
|
-
"""
|
|
46
|
+
def upload_profiles(self, content: dict, version: int) -> dict:
|
|
47
|
+
"""整体替换用户的 profiles
|
|
47
48
|
|
|
48
49
|
Args:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
version:
|
|
50
|
+
content: 包含所有文件的 dict,key 是文件名,value 是内容
|
|
51
|
+
示例: {"SOUL.md": "...", "USER.md": "...", "MEMORY.md": "..."}
|
|
52
|
+
version: 客户端当前持有的版本号
|
|
52
53
|
|
|
53
54
|
Returns:
|
|
54
|
-
成功时返回 {
|
|
55
|
-
|
|
55
|
+
成功时返回 {"content": {...}, "version": N, "updated_at": "..."}
|
|
56
|
+
冲突时抛出 ConflictError
|
|
56
57
|
"""
|
|
57
58
|
url = f"{self.cloud_url}/api/profiles"
|
|
58
59
|
data = {
|
|
59
|
-
'file_path': file_path,
|
|
60
60
|
'content': content,
|
|
61
61
|
'version': version
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
response =
|
|
64
|
+
response = self.session.put(url, json=data, headers=self._get_headers())
|
|
65
65
|
|
|
66
66
|
if response.status_code == 200:
|
|
67
67
|
return response.json()
|
|
68
68
|
elif response.status_code == 409:
|
|
69
69
|
result = response.json()
|
|
70
70
|
raise ConflictError(
|
|
71
|
-
result.get('
|
|
72
|
-
result.get('
|
|
71
|
+
server_content=result.get('server_content', {}),
|
|
72
|
+
server_version=result.get('server_version', 0)
|
|
73
73
|
)
|
|
74
74
|
elif response.status_code == 403:
|
|
75
75
|
error = response.json().get('error', 'Subscription required')
|
|
@@ -77,34 +77,12 @@ class ProfilesClient:
|
|
|
77
77
|
else:
|
|
78
78
|
error = response.json().get('error', 'Unknown error')
|
|
79
79
|
raise Exception(f"Upload failed: {error}")
|
|
80
|
-
|
|
81
|
-
def sync_profiles(self, since: str = '0') -> dict:
|
|
82
|
-
"""增量同步 profiles
|
|
83
|
-
|
|
84
|
-
Args:
|
|
85
|
-
since: 时间戳
|
|
86
|
-
|
|
87
|
-
Returns:
|
|
88
|
-
包含 files 列表和 server_time 的字典
|
|
89
|
-
"""
|
|
90
|
-
url = f"{self.cloud_url}/api/profiles/sync?since={since}"
|
|
91
|
-
|
|
92
|
-
response = requests.get(url, headers=self._get_headers())
|
|
93
|
-
|
|
94
|
-
if response.status_code == 200:
|
|
95
|
-
return response.json()
|
|
96
|
-
elif response.status_code == 403:
|
|
97
|
-
error = response.json().get('error', 'Subscription required')
|
|
98
|
-
raise Exception(f"Sync failed: {error}")
|
|
99
|
-
else:
|
|
100
|
-
error = response.json().get('error', 'Unknown error')
|
|
101
|
-
raise Exception(f"Sync failed: {error}")
|
|
102
80
|
|
|
103
81
|
|
|
104
82
|
class ConflictError(Exception):
|
|
105
83
|
"""版本冲突异常"""
|
|
106
84
|
|
|
107
|
-
def __init__(self,
|
|
108
|
-
self.
|
|
109
|
-
self.
|
|
110
|
-
super().__init__(f"Version conflict:
|
|
85
|
+
def __init__(self, server_content: dict, server_version: int):
|
|
86
|
+
self.server_content = server_content
|
|
87
|
+
self.server_version = server_version
|
|
88
|
+
super().__init__(f"Version conflict: server version is {server_version}")
|
package/src/sync.py
CHANGED
|
@@ -42,7 +42,7 @@ class ProfileSync:
|
|
|
42
42
|
with open(absolute_path, 'r', encoding='utf-8') as f:
|
|
43
43
|
return f.read()
|
|
44
44
|
except Exception as e:
|
|
45
|
-
print(f"[
|
|
45
|
+
print(f"[SoulSync] Error reading local file {file_path}: {e}")
|
|
46
46
|
return None
|
|
47
47
|
return None
|
|
48
48
|
|
|
@@ -57,7 +57,7 @@ class ProfileSync:
|
|
|
57
57
|
with open(absolute_path, 'w', encoding='utf-8') as f:
|
|
58
58
|
f.write(content)
|
|
59
59
|
except Exception as e:
|
|
60
|
-
print(f"[
|
|
60
|
+
print(f"[SoulSync] Error writing local file {file_path}: {e}")
|
|
61
61
|
|
|
62
62
|
def _create_conflict_backup(self, file_path: str, local_content: str, server_content: str):
|
|
63
63
|
"""创建冲突备份文件"""
|
|
@@ -71,64 +71,51 @@ class ProfileSync:
|
|
|
71
71
|
f.write(local_content or "(empty)")
|
|
72
72
|
f.write("\n\n========== SERVER VERSION ==========\n")
|
|
73
73
|
f.write(server_content or "(empty)")
|
|
74
|
-
print(f"[
|
|
74
|
+
print(f"[SoulSync] Conflict backup created: {conflict_path}")
|
|
75
75
|
except Exception as e:
|
|
76
|
-
print(f"[
|
|
76
|
+
print(f"[SoulSync] Error creating conflict backup: {e}")
|
|
77
77
|
|
|
78
78
|
def pull_all(self):
|
|
79
79
|
"""Pull all profiles from cloud"""
|
|
80
|
-
print("[
|
|
80
|
+
print("[SoulSync] Pulling all profiles from cloud...")
|
|
81
81
|
|
|
82
82
|
try:
|
|
83
83
|
result = self.client.get_profiles()
|
|
84
|
-
|
|
84
|
+
cloud_content = result.get('content', {})
|
|
85
|
+
cloud_version = result.get('version', 0)
|
|
85
86
|
except Exception as e:
|
|
86
|
-
print(f"[
|
|
87
|
+
print(f"[SoulSync] Error fetching cloud profiles: {e}")
|
|
87
88
|
return
|
|
88
89
|
|
|
89
|
-
if not
|
|
90
|
-
print("[
|
|
90
|
+
if not cloud_content:
|
|
91
|
+
print("[SoulSync] No profiles on cloud")
|
|
91
92
|
return
|
|
92
93
|
|
|
94
|
+
local_files = ['SOUL.md', 'USER.md', 'MEMORY.md']
|
|
93
95
|
pulled_count = 0
|
|
94
|
-
pushed_count = 0
|
|
95
96
|
skipped_count = 0
|
|
96
97
|
|
|
97
|
-
for
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if not file_path:
|
|
103
|
-
continue
|
|
104
|
-
|
|
105
|
-
local_content = self._read_local_file(file_path)
|
|
106
|
-
local_version = self.version_manager.get_version(file_path)
|
|
98
|
+
for file_name in local_files:
|
|
99
|
+
cloud_file_content = cloud_content.get(file_name, '')
|
|
100
|
+
local_content = self._read_local_file(file_name)
|
|
101
|
+
local_version = self.version_manager.get_version(file_name)
|
|
107
102
|
|
|
108
103
|
if cloud_version > local_version:
|
|
109
|
-
self._mark_syncing(
|
|
104
|
+
self._mark_syncing(file_name)
|
|
110
105
|
try:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
106
|
+
if cloud_file_content:
|
|
107
|
+
self._write_local_file(file_name, cloud_file_content)
|
|
108
|
+
self.version_manager.set_version(file_name, cloud_version)
|
|
109
|
+
pulled_count += 1
|
|
110
|
+
print(f"[SoulSync] Pulled: {file_name} (v{cloud_version})")
|
|
115
111
|
finally:
|
|
116
|
-
self._unmark_syncing(
|
|
117
|
-
elif local_version > cloud_version and local_content is not None:
|
|
118
|
-
try:
|
|
119
|
-
result = self.client.upload_profile(file_path, local_content, local_version)
|
|
120
|
-
self.version_manager.set_version(file_path, result.get('version', local_version))
|
|
121
|
-
pushed_count += 1
|
|
122
|
-
print(f"[Sync] Pushed: {file_path} (v{local_version})")
|
|
123
|
-
except ConflictError as e:
|
|
124
|
-
self._handle_conflict(file_path, local_content, e.server_content, e.server_version)
|
|
125
|
-
pushed_count += 1
|
|
126
|
-
except Exception as e:
|
|
127
|
-
print(f"[Sync] Error pushing {file_path}: {e}")
|
|
112
|
+
self._unmark_syncing(file_name)
|
|
128
113
|
else:
|
|
129
114
|
skipped_count += 1
|
|
130
115
|
|
|
131
|
-
|
|
116
|
+
self.version_manager.set_version('__profiles__', cloud_version)
|
|
117
|
+
|
|
118
|
+
print(f"[SoulSync] Sync complete: {pulled_count} pulled, {skipped_count} skipped")
|
|
132
119
|
|
|
133
120
|
def push_file(self, file_path: str):
|
|
134
121
|
"""Push a file to cloud"""
|
|
@@ -140,64 +127,84 @@ class ProfileSync:
|
|
|
140
127
|
try:
|
|
141
128
|
local_content = self._read_local_file(file_path)
|
|
142
129
|
if local_content is None:
|
|
143
|
-
print(f"[
|
|
130
|
+
print(f"[SoulSync] File not found locally: {file_path}")
|
|
144
131
|
return
|
|
145
132
|
|
|
146
133
|
local_version = self.version_manager.get_version(file_path)
|
|
147
134
|
|
|
135
|
+
profiles_version = self.version_manager.get_version('__profiles__')
|
|
136
|
+
|
|
137
|
+
current_profiles = {}
|
|
148
138
|
try:
|
|
149
|
-
result = self.client.
|
|
150
|
-
|
|
139
|
+
result = self.client.get_profiles()
|
|
140
|
+
current_profiles = result.get('content', {})
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
current_profiles[file_path] = local_content
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
result = self.client.upload_profiles(current_profiles, profiles_version)
|
|
148
|
+
new_version = result.get('version', profiles_version + 1)
|
|
149
|
+
self.version_manager.set_version('__profiles__', new_version)
|
|
151
150
|
self.version_manager.set_version(file_path, new_version)
|
|
152
|
-
print(f"[
|
|
151
|
+
print(f"[SoulSync] Pushed: {file_path} (v{new_version})")
|
|
153
152
|
except ConflictError as e:
|
|
154
153
|
self._handle_conflict(file_path, local_content, e.server_content, e.server_version)
|
|
155
154
|
except Exception as e:
|
|
156
|
-
print(f"[
|
|
155
|
+
print(f"[SoulSync] Error pushing {file_path}: {e}")
|
|
157
156
|
finally:
|
|
158
157
|
self._unmark_syncing(file_path)
|
|
159
158
|
|
|
160
159
|
def on_remote_change(self, file_path: str, version: int):
|
|
161
160
|
"""Handle remote file change"""
|
|
162
|
-
local_version = self.version_manager.get_version(
|
|
161
|
+
local_version = self.version_manager.get_version('__profiles__')
|
|
163
162
|
|
|
164
163
|
if version <= local_version:
|
|
165
|
-
print(f"[
|
|
164
|
+
print(f"[SoulSync] Remote version not newer, skipping: {file_path} (local: v{local_version}, remote: v{version})")
|
|
166
165
|
return
|
|
167
166
|
|
|
168
167
|
self._mark_syncing(file_path)
|
|
169
168
|
|
|
170
169
|
try:
|
|
171
|
-
result = self.client.get_profiles(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
170
|
+
result = self.client.get_profiles()
|
|
171
|
+
cloud_content = result.get('content', {})
|
|
172
|
+
cloud_version = result.get('version', 0)
|
|
173
|
+
|
|
174
|
+
if file_path not in cloud_content:
|
|
175
|
+
print(f"[SoulSync] File not found on cloud: {file_path}")
|
|
175
176
|
return
|
|
176
177
|
|
|
177
|
-
|
|
178
|
-
cloud_content = cloud_file.get('content', '')
|
|
178
|
+
cloud_file_content = cloud_content.get(file_path, '')
|
|
179
179
|
|
|
180
180
|
local_content = self._read_local_file(file_path)
|
|
181
181
|
|
|
182
|
-
if local_content is not None and local_content !=
|
|
183
|
-
self._create_conflict_backup(file_path, local_content,
|
|
182
|
+
if local_content is not None and local_content != cloud_file_content:
|
|
183
|
+
self._create_conflict_backup(file_path, local_content, cloud_file_content)
|
|
184
184
|
|
|
185
|
-
self._write_local_file(file_path,
|
|
185
|
+
self._write_local_file(file_path, cloud_file_content)
|
|
186
|
+
self.version_manager.set_version('__profiles__', cloud_version)
|
|
186
187
|
self.version_manager.set_version(file_path, version)
|
|
187
|
-
print(f"[
|
|
188
|
+
print(f"[SoulSync] Pulled remote change: {file_path} (v{version})")
|
|
188
189
|
except Exception as e:
|
|
189
|
-
print(f"[
|
|
190
|
+
print(f"[SoulSync] Error handling remote change for {file_path}: {e}")
|
|
190
191
|
finally:
|
|
191
192
|
self._unmark_syncing(file_path)
|
|
192
193
|
|
|
193
194
|
def _handle_conflict(self, file_path: str, local_content: str, server_content: str, server_version: int):
|
|
194
195
|
"""Handle conflict when pushing file"""
|
|
195
|
-
print(f"[
|
|
196
|
+
print(f"[SoulSync] CONFLICT detected for {file_path}")
|
|
196
197
|
|
|
197
198
|
self._create_conflict_backup(file_path, local_content, server_content)
|
|
198
199
|
|
|
199
|
-
|
|
200
|
+
if isinstance(server_content, dict):
|
|
201
|
+
server_file_content = server_content.get(file_path, '')
|
|
202
|
+
else:
|
|
203
|
+
server_file_content = server_content
|
|
204
|
+
|
|
205
|
+
self._write_local_file(file_path, server_file_content)
|
|
206
|
+
self.version_manager.set_version('__profiles__', server_version)
|
|
200
207
|
self.version_manager.set_version(file_path, server_version)
|
|
201
208
|
|
|
202
|
-
print(f"[
|
|
203
|
-
print(f"[
|
|
209
|
+
print(f"[SoulSync] Conflict resolved: server version used for {file_path}")
|
|
210
|
+
print(f"[SoulSync] Please manually merge the conflict backup file")
|