soulsync 1.0.6 → 1.0.7
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 +4 -3
- package/package.json +1 -1
- package/src/main.py +51 -199
package/config.json.example
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"cloud_url": "https://soulsync.work",
|
|
3
|
-
"email": "
|
|
4
|
-
"password": "
|
|
3
|
+
"email": "",
|
|
4
|
+
"password": "",
|
|
5
5
|
"workspace": "./workspace",
|
|
6
6
|
"memory_file": "MEMORY.md",
|
|
7
7
|
"watch_files": [
|
|
@@ -13,5 +13,6 @@
|
|
|
13
13
|
"skills.json",
|
|
14
14
|
"memory/",
|
|
15
15
|
"MEMORY.md"
|
|
16
|
-
]
|
|
16
|
+
],
|
|
17
|
+
"_comment": "Register at https://soulsync.work or use curl to call /api/auth/register endpoint"
|
|
17
18
|
}
|
package/package.json
CHANGED
package/src/main.py
CHANGED
|
@@ -7,7 +7,6 @@ import json
|
|
|
7
7
|
import os
|
|
8
8
|
import sys
|
|
9
9
|
import time
|
|
10
|
-
import getpass
|
|
11
10
|
|
|
12
11
|
# 获取插件根目录
|
|
13
12
|
PLUGIN_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
@@ -22,8 +21,6 @@ from watcher import OpenClawMultiWatcher
|
|
|
22
21
|
from version_manager import VersionManager
|
|
23
22
|
from profiles import ProfilesClient
|
|
24
23
|
from sync import ProfileSync
|
|
25
|
-
from register import Register, Login
|
|
26
|
-
from interactive_auth import prompt_for_missing_config, interactive_setup, check_existing_config
|
|
27
24
|
|
|
28
25
|
|
|
29
26
|
class SoulSyncPlugin:
|
|
@@ -43,14 +40,14 @@ class SoulSyncPlugin:
|
|
|
43
40
|
config_path = os.path.normpath(os.path.join(PLUGIN_DIR, 'config.json'))
|
|
44
41
|
config_example_path = os.path.normpath(os.path.join(PLUGIN_DIR, 'config.json.example'))
|
|
45
42
|
|
|
46
|
-
print(f"Looking for config at: {config_path}")
|
|
43
|
+
print(f"[SoulSync] Looking for config at: {config_path}")
|
|
47
44
|
|
|
48
45
|
if not os.path.exists(config_path):
|
|
49
46
|
if os.path.exists(config_example_path):
|
|
50
|
-
print("Config file not found, copying from config.json.example...")
|
|
47
|
+
print("[SoulSync] Config file not found, copying from config.json.example...")
|
|
51
48
|
import shutil
|
|
52
49
|
shutil.copy(config_example_path, config_path)
|
|
53
|
-
print(
|
|
50
|
+
print("[SoulSync] Created config.json from template")
|
|
54
51
|
else:
|
|
55
52
|
raise FileNotFoundError(f"Config file not found: {config_path}")
|
|
56
53
|
|
|
@@ -60,25 +57,24 @@ class SoulSyncPlugin:
|
|
|
60
57
|
except json.JSONDecodeError as e:
|
|
61
58
|
raise ValueError(f"Invalid JSON in config.json: {e}")
|
|
62
59
|
|
|
63
|
-
# 检查必要配置
|
|
64
60
|
cloud_url = self.config.get('cloud_url', '').strip()
|
|
65
61
|
email = self.config.get('email', '').strip()
|
|
66
62
|
password = self.config.get('password', '').strip()
|
|
67
63
|
|
|
68
|
-
# 如果 cloud_url 为空,设置为默认值
|
|
69
64
|
if not cloud_url:
|
|
70
65
|
self.config['cloud_url'] = 'https://soulsync.work'
|
|
71
|
-
print("Cloud URL not set, using default: https://soulsync.work")
|
|
66
|
+
print("[SoulSync] Cloud URL not set, using default: https://soulsync.work")
|
|
72
67
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
print("
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
if not email or not password or email == 'your-email@example.com' or password == 'your-password':
|
|
69
|
+
print("\n[SoulSync] ========================================")
|
|
70
|
+
print("[SoulSync] Please configure your account first / 请先配置账号")
|
|
71
|
+
print("[SoulSync] ========================================")
|
|
72
|
+
print("[SoulSync] Edit config file / 编辑配置文件:")
|
|
73
|
+
print(f" {config_path}")
|
|
74
|
+
print("[SoulSync] Set your email and password, then restart / 填写邮箱和密码后重启")
|
|
75
|
+
print("[SoulSync] ========================================\n")
|
|
76
|
+
sys.exit(0)
|
|
80
77
|
|
|
81
|
-
# 处理 workspace 路径
|
|
82
78
|
workspace = self.config.get('workspace', './workspace')
|
|
83
79
|
if workspace.startswith('./'):
|
|
84
80
|
workspace = workspace[2:]
|
|
@@ -89,167 +85,10 @@ class SoulSyncPlugin:
|
|
|
89
85
|
self.config['workspace'] = workspace
|
|
90
86
|
self.config['watch_files'] = watch_files
|
|
91
87
|
|
|
92
|
-
print(f"Config loaded:")
|
|
93
|
-
print(f"
|
|
94
|
-
print(f"
|
|
95
|
-
print(f"
|
|
96
|
-
|
|
97
|
-
def _interactive_setup(self):
|
|
98
|
-
"""交互式设置:引导用户登录或注册"""
|
|
99
|
-
from register import Register, Login
|
|
100
|
-
from interactive_auth import interactive_setup
|
|
101
|
-
|
|
102
|
-
while True:
|
|
103
|
-
print("\n" + "=" * 50)
|
|
104
|
-
print("Welcome / 欢迎使用 SoulSync")
|
|
105
|
-
print("=" * 50)
|
|
106
|
-
print("1. Login / 登录(已有账号)")
|
|
107
|
-
print("2. Register / 注册(新用户)")
|
|
108
|
-
print("3. Exit / 退出")
|
|
109
|
-
|
|
110
|
-
choice = input("Choose / 选择 (1/2/3): ").strip()
|
|
111
|
-
|
|
112
|
-
if choice == '1':
|
|
113
|
-
success = self._interactive_login()
|
|
114
|
-
if success:
|
|
115
|
-
return True
|
|
116
|
-
elif choice == '2':
|
|
117
|
-
success = self._interactive_register()
|
|
118
|
-
if success:
|
|
119
|
-
return True
|
|
120
|
-
elif choice == '3':
|
|
121
|
-
print("Exiting... / 退出...")
|
|
122
|
-
sys.exit(0)
|
|
123
|
-
else:
|
|
124
|
-
print("Invalid choice / 无效选择")
|
|
125
|
-
|
|
126
|
-
def _interactive_login(self):
|
|
127
|
-
"""交互式登录(带重试)"""
|
|
128
|
-
max_retries = 5
|
|
129
|
-
retry_count = 0
|
|
130
|
-
|
|
131
|
-
while retry_count < max_retries:
|
|
132
|
-
print("\n--- Login / 登录 ---")
|
|
133
|
-
email = input("Email / 邮箱: ").strip()
|
|
134
|
-
if not email:
|
|
135
|
-
print("Email cannot be empty / 邮箱不能为空")
|
|
136
|
-
continue
|
|
137
|
-
|
|
138
|
-
password = getpass.getpass("Password / 密码: ")
|
|
139
|
-
if not password:
|
|
140
|
-
print("Password cannot be empty / 密码不能为空")
|
|
141
|
-
continue
|
|
142
|
-
|
|
143
|
-
try:
|
|
144
|
-
temp_client = OpenClawClient(self.config)
|
|
145
|
-
result = temp_client.authenticate(email, password)
|
|
146
|
-
if result:
|
|
147
|
-
print("\n✅ Login successful! / 登录成功!")
|
|
148
|
-
self._save_auth_to_config(result)
|
|
149
|
-
return True
|
|
150
|
-
except Exception as e:
|
|
151
|
-
retry_count += 1
|
|
152
|
-
remaining = max_retries - retry_count
|
|
153
|
-
error_msg = str(e)
|
|
154
|
-
|
|
155
|
-
if "429" in error_msg or "too many" in error_msg.lower():
|
|
156
|
-
print(f"\n❌ {e}")
|
|
157
|
-
print("\nToo many failed attempts / 登录失败次数过多")
|
|
158
|
-
print("Exiting... / 退出...")
|
|
159
|
-
sys.exit(0)
|
|
160
|
-
|
|
161
|
-
if remaining > 0:
|
|
162
|
-
print(f"\n❌ Login failed: {e} / 登录失败: {e}")
|
|
163
|
-
print(f"Remaining attempts / 剩余尝试次数: {remaining}")
|
|
164
|
-
else:
|
|
165
|
-
print(f"\n❌ Login failed: {e} / 登录失败: {e}")
|
|
166
|
-
|
|
167
|
-
print("\n❌ Too many failed attempts. Please try again in 15 minutes. / 登录失败次数过多,请15分钟后再试")
|
|
168
|
-
print("Exiting... / 退出...")
|
|
169
|
-
sys.exit(0)
|
|
170
|
-
|
|
171
|
-
def _interactive_register(self):
|
|
172
|
-
"""交互式注册(带重试)"""
|
|
173
|
-
from register import Register
|
|
174
|
-
|
|
175
|
-
max_retries = 5
|
|
176
|
-
retry_count = 0
|
|
177
|
-
|
|
178
|
-
while retry_count < max_retries:
|
|
179
|
-
print("\n--- Register / 注册 ---")
|
|
180
|
-
email = input("Email / 邮箱: ").strip()
|
|
181
|
-
if not email or '@' not in email:
|
|
182
|
-
print("Invalid email / 无效邮箱")
|
|
183
|
-
continue
|
|
184
|
-
|
|
185
|
-
password = getpass.getpass("Password / 密码: ")
|
|
186
|
-
if len(password) < 6:
|
|
187
|
-
print("Password must be at least 6 characters / 密码至少6位")
|
|
188
|
-
continue
|
|
189
|
-
|
|
190
|
-
password2 = getpass.getpass("Confirm password / 确认密码: ")
|
|
191
|
-
if password != password2:
|
|
192
|
-
print("Passwords do not match / 两次密码不一致")
|
|
193
|
-
continue
|
|
194
|
-
|
|
195
|
-
# 发送验证码
|
|
196
|
-
print(f"\nSending verification code to {email}...")
|
|
197
|
-
try:
|
|
198
|
-
temp_client = OpenClawClient(self.config)
|
|
199
|
-
temp_client.send_verification_code(email)
|
|
200
|
-
print("✅ Verification code sent! / 验证码已发送!")
|
|
201
|
-
except Exception as e:
|
|
202
|
-
print(f"❌ Failed to send code: {e}")
|
|
203
|
-
continue
|
|
204
|
-
|
|
205
|
-
# 验证码输入(带重试)
|
|
206
|
-
code_retry = 0
|
|
207
|
-
while code_retry < max_retries:
|
|
208
|
-
code = input(f"Enter verification code / 请输入验证码 ({max_retries - code_retry} attempts left): ").strip()
|
|
209
|
-
if len(code) != 6 or not code.isdigit():
|
|
210
|
-
code_retry += 1
|
|
211
|
-
print("Invalid code format / 验证码格式错误")
|
|
212
|
-
continue
|
|
213
|
-
|
|
214
|
-
try:
|
|
215
|
-
result = temp_client.register(email, password, code)
|
|
216
|
-
print("\n✅ Registration successful! / 注册成功!")
|
|
217
|
-
self._save_auth_to_config(result)
|
|
218
|
-
return True
|
|
219
|
-
except Exception as e:
|
|
220
|
-
code_retry += 1
|
|
221
|
-
remaining_code = max_retries - code_retry
|
|
222
|
-
if "invalid" in str(e).lower() or "expired" in str(e).lower():
|
|
223
|
-
if remaining_code > 0:
|
|
224
|
-
print(f"❌ Invalid or expired code: {e}")
|
|
225
|
-
print(f"Remaining attempts / 剩余尝试: {remaining_code}")
|
|
226
|
-
else:
|
|
227
|
-
print("❌ Too many code attempts / 验证码错误次数过多")
|
|
228
|
-
break
|
|
229
|
-
else:
|
|
230
|
-
print(f"❌ Registration failed: {e}")
|
|
231
|
-
break
|
|
232
|
-
|
|
233
|
-
if code_retry >= max_retries:
|
|
234
|
-
print("\nToo many code verification failures. Would you like to:")
|
|
235
|
-
print("1. Resend code / 重新发送验证码")
|
|
236
|
-
print("2. Start over / 重新开始")
|
|
237
|
-
print("3. Exit / 退出")
|
|
238
|
-
|
|
239
|
-
sub_choice = input("Choose / 选择 (1/2/3): ").strip()
|
|
240
|
-
if sub_choice == '1':
|
|
241
|
-
retry_count = 0 # 重置主重试计数
|
|
242
|
-
continue
|
|
243
|
-
elif sub_choice == '2':
|
|
244
|
-
retry_count = 0
|
|
245
|
-
break # 跳出内层循环,继续外层循环
|
|
246
|
-
else:
|
|
247
|
-
print("Exiting... / 退出...")
|
|
248
|
-
sys.exit(0)
|
|
249
|
-
|
|
250
|
-
print("\n❌ Too many registration attempts / 注册尝试次数过多")
|
|
251
|
-
print("Exiting... / 退出...")
|
|
252
|
-
sys.exit(0)
|
|
88
|
+
print(f"[SoulSync] Config loaded:")
|
|
89
|
+
print(f" Cloud URL: {self.config.get('cloud_url')}")
|
|
90
|
+
print(f" Workspace: {workspace}")
|
|
91
|
+
print(f" Watch files: {watch_files}")
|
|
253
92
|
|
|
254
93
|
def _save_auth_to_config(self, auth_result):
|
|
255
94
|
"""保存认证结果到 config.json"""
|
|
@@ -276,7 +115,9 @@ class SoulSyncPlugin:
|
|
|
276
115
|
|
|
277
116
|
def initialize(self):
|
|
278
117
|
"""初始化组件"""
|
|
279
|
-
print("\n
|
|
118
|
+
print("\n[SoulSync] ========================================")
|
|
119
|
+
print("[SoulSync] Initializing SoulSync Plugin")
|
|
120
|
+
print("[SoulSync] ========================================\n")
|
|
280
121
|
|
|
281
122
|
self.client = OpenClawClient(self.config)
|
|
282
123
|
|
|
@@ -284,38 +125,49 @@ class SoulSyncPlugin:
|
|
|
284
125
|
if token:
|
|
285
126
|
try:
|
|
286
127
|
profile = self.client.get_profile()
|
|
287
|
-
print(f"Using existing token, user: {profile.get('email', 'unknown')}")
|
|
128
|
+
print(f"[SoulSync] Using existing token, user: {profile.get('email', 'unknown')}")
|
|
288
129
|
except Exception as e:
|
|
289
|
-
print(f"Token invalid
|
|
130
|
+
print(f"[SoulSync] Token invalid, re-authenticating: {e}")
|
|
290
131
|
token = None
|
|
291
132
|
|
|
292
133
|
if not token:
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
134
|
+
email = self.config.get('email', '').strip()
|
|
135
|
+
password = self.config.get('password', '').strip()
|
|
136
|
+
|
|
137
|
+
print("[SoulSync] No valid token, attempting auto-login...")
|
|
138
|
+
try:
|
|
139
|
+
result = self.client.authenticate(email, password)
|
|
140
|
+
if result:
|
|
141
|
+
print("[SoulSync] Login successful! / 登录成功!")
|
|
142
|
+
self._save_auth_to_config(result)
|
|
143
|
+
token = self.client.token
|
|
144
|
+
except Exception as e:
|
|
145
|
+
error_msg = str(e)
|
|
146
|
+
print(f"[SoulSync] Login failed: {e}")
|
|
147
|
+
|
|
148
|
+
if "429" in error_msg or "too many" in error_msg.lower():
|
|
149
|
+
print("\n[SoulSync] ========================================")
|
|
150
|
+
print("[SoulSync] Too many failed attempts. Please try again later / 登录失败次数过多,请稍后再试")
|
|
151
|
+
print("[SoulSync] ========================================\n")
|
|
152
|
+
else:
|
|
153
|
+
print("\n[SoulSync] ========================================")
|
|
154
|
+
print("[SoulSync] Login failed: invalid email or password / 登录失败:邮箱或密码错误")
|
|
155
|
+
print("[SoulSync] Please check your config file / 请检查配置文件:")
|
|
156
|
+
print(f" {os.path.normpath(os.path.join(PLUGIN_DIR, 'config.json'))}")
|
|
157
|
+
print("[SoulSync] ========================================\n")
|
|
158
|
+
|
|
159
|
+
sys.exit(0)
|
|
308
160
|
|
|
309
161
|
email = self.config.get('email')
|
|
310
162
|
password = self.config.get('password')
|
|
311
163
|
|
|
312
164
|
try:
|
|
313
165
|
profile = self.client.get_profile()
|
|
314
|
-
print(f"\
|
|
166
|
+
print(f"\n[SoulSync] Logged in as: {profile.get('email')}")
|
|
315
167
|
subscription = profile.get('subscription', {})
|
|
316
|
-
print(f"Subscription: {subscription.get('status')} (days remaining: {subscription.get('daysRemaining', 0)})\n")
|
|
168
|
+
print(f"[SoulSync] Subscription: {subscription.get('status')} (days remaining: {subscription.get('daysRemaining', 0)})\n")
|
|
317
169
|
except Exception as e:
|
|
318
|
-
print(f"Warning: Could not get profile: {e}")
|
|
170
|
+
print(f"[SoulSync] Warning: Could not get profile: {e}")
|
|
319
171
|
|
|
320
172
|
# 版本管理器
|
|
321
173
|
versions_file = os.path.normpath(os.path.join(PLUGIN_DIR, 'versions.json'))
|