soulsync 1.0.6 → 1.0.8
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/index.js +80 -35
- package/package.json +1 -1
- package/src/main.py +163 -135
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/index.js
CHANGED
|
@@ -1,59 +1,106 @@
|
|
|
1
1
|
const { spawn } = require('child_process');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
3
4
|
|
|
4
5
|
let pythonProcess = null;
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
function getPluginDir() {
|
|
8
|
+
return path.dirname(__filename);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function checkConfigExists(pluginDir) {
|
|
12
|
+
const configPath = path.join(pluginDir, 'config.json');
|
|
13
|
+
if (!fs.existsSync(configPath)) {
|
|
14
|
+
const examplePath = path.join(pluginDir, 'config.json.example');
|
|
15
|
+
if (fs.existsSync(examplePath)) {
|
|
16
|
+
fs.copyFileSync(examplePath, configPath);
|
|
17
|
+
console.log('[SoulSync] Created config.json from template');
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
24
|
+
const email = (config.email || '').trim();
|
|
25
|
+
const password = (config.password || '').trim();
|
|
26
|
+
return email && password && email !== 'your-email@example.com' && password !== 'your-password';
|
|
27
|
+
} catch (e) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function startPythonService(mode = '--start') {
|
|
33
|
+
const pluginDir = getPluginDir();
|
|
34
|
+
const pythonScript = path.join(pluginDir, 'src', 'main.py');
|
|
35
|
+
const pythonPath = process.env.PYTHON_PATH || 'python3';
|
|
36
|
+
|
|
37
|
+
console.log(`[SoulSync] Starting Python service (${mode})...`);
|
|
38
|
+
|
|
39
|
+
pythonProcess = spawn(pythonPath, [pythonScript, mode], {
|
|
40
|
+
cwd: pluginDir,
|
|
41
|
+
env: {
|
|
42
|
+
...process.env,
|
|
43
|
+
OPENCLAW_PLUGIN: 'true',
|
|
44
|
+
PLUGIN_DIR: pluginDir
|
|
45
|
+
},
|
|
46
|
+
stdio: 'inherit'
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
pythonProcess.on('close', (code) => {
|
|
50
|
+
console.log(`[SoulSync] Python process exited with code ${code}`);
|
|
51
|
+
pythonProcess = null;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
pythonProcess.on('error', (err) => {
|
|
55
|
+
console.error(`[SoulSync] Failed to start Python process: ${err}`);
|
|
56
|
+
pythonProcess = null;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return pythonProcess;
|
|
60
|
+
}
|
|
61
|
+
|
|
7
62
|
module.exports = function register(api) {
|
|
8
63
|
console.log('[SoulSync] Registering plugin...');
|
|
9
64
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
OPENCLAW_PLUGIN: 'true',
|
|
23
|
-
PLUGIN_DIR: pluginDir
|
|
24
|
-
},
|
|
25
|
-
stdio: 'inherit'
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
pythonProcess.on('close', (code) => {
|
|
29
|
-
console.log(`[SoulSync] Python process exited with code ${code}`);
|
|
30
|
-
pythonProcess = null;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
pythonProcess.on('error', (err) => {
|
|
34
|
-
console.error(`[SoulSync] Failed to start Python process: ${err}`);
|
|
35
|
-
pythonProcess = null;
|
|
36
|
-
});
|
|
37
|
-
}
|
|
65
|
+
api.registerCli(
|
|
66
|
+
({ program }) => {
|
|
67
|
+
program
|
|
68
|
+
.command('soulsync:setup')
|
|
69
|
+
.description('首次配置:注册或登录 SoulSync 账号')
|
|
70
|
+
.action(() => {
|
|
71
|
+
console.log('[SoulSync] Starting setup...');
|
|
72
|
+
startPythonService('--setup');
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
{ commands: ['soulsync:setup'] }
|
|
76
|
+
);
|
|
38
77
|
|
|
39
|
-
// 注册CLI命令:启动 SoulSync
|
|
40
78
|
api.registerCli(
|
|
41
79
|
({ program }) => {
|
|
42
80
|
program
|
|
43
81
|
.command('soulsync:start')
|
|
44
82
|
.description('启动 SoulSync 同步服务')
|
|
45
83
|
.action(() => {
|
|
84
|
+
const pluginDir = getPluginDir();
|
|
85
|
+
const configPath = path.join(pluginDir, 'config.json');
|
|
86
|
+
|
|
87
|
+
if (!checkConfigExists(pluginDir)) {
|
|
88
|
+
console.log('[SoulSync] Not configured. Starting setup...');
|
|
89
|
+
startPythonService('--setup');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
46
93
|
if (pythonProcess) {
|
|
47
94
|
console.log('[SoulSync] Service already running');
|
|
48
95
|
return;
|
|
49
96
|
}
|
|
50
|
-
|
|
97
|
+
|
|
98
|
+
startPythonService('--start');
|
|
51
99
|
});
|
|
52
100
|
},
|
|
53
101
|
{ commands: ['soulsync:start'] }
|
|
54
102
|
);
|
|
55
103
|
|
|
56
|
-
// 注册CLI命令:停止 SoulSync
|
|
57
104
|
api.registerCli(
|
|
58
105
|
({ program }) => {
|
|
59
106
|
program
|
|
@@ -72,8 +119,6 @@ module.exports = function register(api) {
|
|
|
72
119
|
{ commands: ['soulsync:stop'] }
|
|
73
120
|
);
|
|
74
121
|
|
|
75
|
-
|
|
76
|
-
startPythonService();
|
|
77
|
-
|
|
122
|
+
console.log('[SoulSync] Plugin loaded. Run "openclaw soulsync:start" to begin.');
|
|
78
123
|
console.log('[SoulSync] Plugin registered successfully');
|
|
79
124
|
};
|
package/package.json
CHANGED
package/src/main.py
CHANGED
|
@@ -8,6 +8,7 @@ import os
|
|
|
8
8
|
import sys
|
|
9
9
|
import time
|
|
10
10
|
import getpass
|
|
11
|
+
import argparse
|
|
11
12
|
|
|
12
13
|
# 获取插件根目录
|
|
13
14
|
PLUGIN_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
@@ -22,8 +23,6 @@ from watcher import OpenClawMultiWatcher
|
|
|
22
23
|
from version_manager import VersionManager
|
|
23
24
|
from profiles import ProfilesClient
|
|
24
25
|
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
26
|
|
|
28
27
|
|
|
29
28
|
class SoulSyncPlugin:
|
|
@@ -43,14 +42,14 @@ class SoulSyncPlugin:
|
|
|
43
42
|
config_path = os.path.normpath(os.path.join(PLUGIN_DIR, 'config.json'))
|
|
44
43
|
config_example_path = os.path.normpath(os.path.join(PLUGIN_DIR, 'config.json.example'))
|
|
45
44
|
|
|
46
|
-
print(f"Looking for config at: {config_path}")
|
|
45
|
+
print(f"[SoulSync] Looking for config at: {config_path}")
|
|
47
46
|
|
|
48
47
|
if not os.path.exists(config_path):
|
|
49
48
|
if os.path.exists(config_example_path):
|
|
50
|
-
print("Config file not found, copying from config.json.example...")
|
|
49
|
+
print("[SoulSync] Config file not found, copying from config.json.example...")
|
|
51
50
|
import shutil
|
|
52
51
|
shutil.copy(config_example_path, config_path)
|
|
53
|
-
print(
|
|
52
|
+
print("[SoulSync] Created config.json from template")
|
|
54
53
|
else:
|
|
55
54
|
raise FileNotFoundError(f"Config file not found: {config_path}")
|
|
56
55
|
|
|
@@ -60,25 +59,24 @@ class SoulSyncPlugin:
|
|
|
60
59
|
except json.JSONDecodeError as e:
|
|
61
60
|
raise ValueError(f"Invalid JSON in config.json: {e}")
|
|
62
61
|
|
|
63
|
-
# 检查必要配置
|
|
64
62
|
cloud_url = self.config.get('cloud_url', '').strip()
|
|
65
63
|
email = self.config.get('email', '').strip()
|
|
66
64
|
password = self.config.get('password', '').strip()
|
|
67
65
|
|
|
68
|
-
# 如果 cloud_url 为空,设置为默认值
|
|
69
66
|
if not cloud_url:
|
|
70
67
|
self.config['cloud_url'] = 'https://soulsync.work'
|
|
71
|
-
print("Cloud URL not set, using default: https://soulsync.work")
|
|
68
|
+
print("[SoulSync] Cloud URL not set, using default: https://soulsync.work")
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
print("
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
if not email or not password or email == 'your-email@example.com' or password == 'your-password':
|
|
71
|
+
print("\n[SoulSync] ========================================")
|
|
72
|
+
print("[SoulSync] Please configure your account first / 请先配置账号")
|
|
73
|
+
print("[SoulSync] ========================================")
|
|
74
|
+
print("[SoulSync] Edit config file / 编辑配置文件:")
|
|
75
|
+
print(f" {config_path}")
|
|
76
|
+
print("[SoulSync] Set your email and password, then restart / 填写邮箱和密码后重启")
|
|
77
|
+
print("[SoulSync] ========================================\n")
|
|
78
|
+
sys.exit(0)
|
|
80
79
|
|
|
81
|
-
# 处理 workspace 路径
|
|
82
80
|
workspace = self.config.get('workspace', './workspace')
|
|
83
81
|
if workspace.startswith('./'):
|
|
84
82
|
workspace = workspace[2:]
|
|
@@ -89,62 +87,77 @@ class SoulSyncPlugin:
|
|
|
89
87
|
self.config['workspace'] = workspace
|
|
90
88
|
self.config['watch_files'] = watch_files
|
|
91
89
|
|
|
92
|
-
print(f"Config loaded:")
|
|
93
|
-
print(f"
|
|
94
|
-
print(f"
|
|
95
|
-
print(f"
|
|
96
|
-
|
|
97
|
-
def
|
|
98
|
-
"""
|
|
99
|
-
|
|
100
|
-
from interactive_auth import interactive_setup
|
|
90
|
+
print(f"[SoulSync] Config loaded:")
|
|
91
|
+
print(f" Cloud URL: {self.config.get('cloud_url')}")
|
|
92
|
+
print(f" Workspace: {workspace}")
|
|
93
|
+
print(f" Watch files: {watch_files}")
|
|
94
|
+
|
|
95
|
+
def _save_auth_to_config(self, auth_result):
|
|
96
|
+
"""保存认证结果到 config.json"""
|
|
97
|
+
config_path = os.path.normpath(os.path.join(PLUGIN_DIR, 'config.json'))
|
|
101
98
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
99
|
+
try:
|
|
100
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
101
|
+
config = json.load(f)
|
|
102
|
+
except:
|
|
103
|
+
config = {}
|
|
104
|
+
|
|
105
|
+
# 保存 email 和 password(如果 auth_result 包含)
|
|
106
|
+
if 'user' in auth_result:
|
|
107
|
+
config['email'] = auth_result['user'].get('email', '')
|
|
108
|
+
|
|
109
|
+
# 保存 token
|
|
110
|
+
if 'token' in auth_result:
|
|
111
|
+
config['token'] = auth_result['token']
|
|
112
|
+
|
|
113
|
+
with open(config_path, 'w', encoding='utf-8') as f:
|
|
114
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
115
|
+
|
|
116
|
+
print("[SoulSync] Auth info saved to config.json")
|
|
117
|
+
|
|
118
|
+
def run_setup(self):
|
|
119
|
+
"""交互式设置:注册或登录"""
|
|
120
|
+
print("\n[SoulSync] ========================================")
|
|
121
|
+
print("[SoulSync] Welcome to SoulSync! / 欢迎使用 SoulSync!")
|
|
122
|
+
print("[SoulSync] ========================================")
|
|
123
|
+
print("[SoulSync] 1. Register / 注册")
|
|
124
|
+
print("[SoulSync] 2. Login / 登录")
|
|
125
|
+
print("[SoulSync] ========================================")
|
|
126
|
+
|
|
127
|
+
choice = input("[SoulSync] Choose / 选择 (1/2): ").strip()
|
|
128
|
+
|
|
129
|
+
if choice == '1':
|
|
130
|
+
return self._interactive_register()
|
|
131
|
+
elif choice == '2':
|
|
132
|
+
return self._interactive_login()
|
|
133
|
+
else:
|
|
134
|
+
print("[SoulSync] Invalid choice / 无效选择")
|
|
135
|
+
sys.exit(0)
|
|
125
136
|
|
|
126
137
|
def _interactive_login(self):
|
|
127
138
|
"""交互式登录(带重试)"""
|
|
139
|
+
from client import OpenClawClient
|
|
140
|
+
|
|
128
141
|
max_retries = 5
|
|
129
142
|
retry_count = 0
|
|
130
143
|
|
|
131
144
|
while retry_count < max_retries:
|
|
132
145
|
print("\n--- Login / 登录 ---")
|
|
133
|
-
email = input("Email / 邮箱: ").strip()
|
|
146
|
+
email = input("[SoulSync] Email / 邮箱: ").strip()
|
|
134
147
|
if not email:
|
|
135
|
-
print("Email cannot be empty / 邮箱不能为空")
|
|
148
|
+
print("[SoulSync] Email cannot be empty / 邮箱不能为空")
|
|
136
149
|
continue
|
|
137
150
|
|
|
138
|
-
password = getpass.getpass("Password / 密码: ")
|
|
151
|
+
password = getpass.getpass("[SoulSync] Password / 密码: ")
|
|
139
152
|
if not password:
|
|
140
|
-
print("Password cannot be empty / 密码不能为空")
|
|
153
|
+
print("[SoulSync] Password cannot be empty / 密码不能为空")
|
|
141
154
|
continue
|
|
142
155
|
|
|
143
156
|
try:
|
|
144
157
|
temp_client = OpenClawClient(self.config)
|
|
145
158
|
result = temp_client.authenticate(email, password)
|
|
146
159
|
if result:
|
|
147
|
-
print("\n
|
|
160
|
+
print("\n[SoulSync] ✓ Login successful! / 登录成功!")
|
|
148
161
|
self._save_auth_to_config(result)
|
|
149
162
|
return True
|
|
150
163
|
except Exception as e:
|
|
@@ -153,67 +166,65 @@ class SoulSyncPlugin:
|
|
|
153
166
|
error_msg = str(e)
|
|
154
167
|
|
|
155
168
|
if "429" in error_msg or "too many" in error_msg.lower():
|
|
156
|
-
print(f"\n❌ {e}")
|
|
157
|
-
print("\
|
|
158
|
-
print("Exiting... / 退出...")
|
|
169
|
+
print(f"\n[SoulSync] ❌ {e}")
|
|
170
|
+
print("\n[SoulSync] Too many failed attempts / 登录失败次数过多")
|
|
171
|
+
print("[SoulSync] Exiting... / 退出...")
|
|
159
172
|
sys.exit(0)
|
|
160
173
|
|
|
161
174
|
if remaining > 0:
|
|
162
|
-
print(f"\n❌ Login failed: {e} / 登录失败: {e}")
|
|
163
|
-
print(f"Remaining attempts / 剩余尝试次数: {remaining}")
|
|
175
|
+
print(f"\n[SoulSync] ❌ Login failed: {e} / 登录失败: {e}")
|
|
176
|
+
print(f"[SoulSync] Remaining attempts / 剩余尝试次数: {remaining}")
|
|
164
177
|
else:
|
|
165
|
-
print(f"\n❌ Login failed: {e} / 登录失败: {e}")
|
|
178
|
+
print(f"\n[SoulSync] ❌ Login failed: {e} / 登录失败: {e}")
|
|
166
179
|
|
|
167
|
-
print("\n❌ Too many failed attempts. Please try again in 15 minutes. / 登录失败次数过多,请15分钟后再试")
|
|
168
|
-
print("Exiting... / 退出...")
|
|
180
|
+
print("\n[SoulSync] ❌ Too many failed attempts. Please try again in 15 minutes. / 登录失败次数过多,请15分钟后再试")
|
|
181
|
+
print("[SoulSync] Exiting... / 退出...")
|
|
169
182
|
sys.exit(0)
|
|
170
183
|
|
|
171
184
|
def _interactive_register(self):
|
|
172
185
|
"""交互式注册(带重试)"""
|
|
173
|
-
from
|
|
186
|
+
from client import OpenClawClient
|
|
174
187
|
|
|
175
188
|
max_retries = 5
|
|
176
189
|
retry_count = 0
|
|
177
190
|
|
|
178
191
|
while retry_count < max_retries:
|
|
179
192
|
print("\n--- Register / 注册 ---")
|
|
180
|
-
email = input("Email / 邮箱: ").strip()
|
|
193
|
+
email = input("[SoulSync] Email / 邮箱: ").strip()
|
|
181
194
|
if not email or '@' not in email:
|
|
182
|
-
print("Invalid email / 无效邮箱")
|
|
195
|
+
print("[SoulSync] Invalid email / 无效邮箱")
|
|
183
196
|
continue
|
|
184
197
|
|
|
185
|
-
password = getpass.getpass("Password / 密码: ")
|
|
198
|
+
password = getpass.getpass("[SoulSync] Password / 密码: ")
|
|
186
199
|
if len(password) < 6:
|
|
187
|
-
print("Password must be at least 6 characters / 密码至少6位")
|
|
200
|
+
print("[SoulSync] Password must be at least 6 characters / 密码至少6位")
|
|
188
201
|
continue
|
|
189
202
|
|
|
190
|
-
password2 = getpass.getpass("Confirm password / 确认密码: ")
|
|
203
|
+
password2 = getpass.getpass("[SoulSync] Confirm password / 确认密码: ")
|
|
191
204
|
if password != password2:
|
|
192
|
-
print("Passwords do not match / 两次密码不一致")
|
|
205
|
+
print("[SoulSync] Passwords do not match / 两次密码不一致")
|
|
193
206
|
continue
|
|
194
207
|
|
|
195
|
-
|
|
196
|
-
print(f"\nSending verification code to {email}...")
|
|
208
|
+
print(f"\n[SoulSync] Sending verification code to {email}...")
|
|
197
209
|
try:
|
|
198
210
|
temp_client = OpenClawClient(self.config)
|
|
199
211
|
temp_client.send_verification_code(email)
|
|
200
|
-
print("
|
|
212
|
+
print("[SoulSync] ✓ Verification code sent! / 验证码已发送!")
|
|
201
213
|
except Exception as e:
|
|
202
|
-
print(f"❌ Failed to send code: {e}")
|
|
214
|
+
print(f"[SoulSync] ❌ Failed to send code: {e}")
|
|
203
215
|
continue
|
|
204
216
|
|
|
205
|
-
# 验证码输入(带重试)
|
|
206
217
|
code_retry = 0
|
|
207
218
|
while code_retry < max_retries:
|
|
208
|
-
code = input(f"Enter
|
|
219
|
+
code = input(f"[SoulSync] Enter code / 输入验证码 ({max_retries - code_retry} attempts left): ").strip()
|
|
209
220
|
if len(code) != 6 or not code.isdigit():
|
|
210
221
|
code_retry += 1
|
|
211
|
-
print("Invalid code format / 验证码格式错误")
|
|
222
|
+
print("[SoulSync] Invalid code format / 验证码格式错误")
|
|
212
223
|
continue
|
|
213
224
|
|
|
214
225
|
try:
|
|
215
226
|
result = temp_client.register(email, password, code)
|
|
216
|
-
print("\n
|
|
227
|
+
print("\n[SoulSync] ✓ Registration successful! / 注册成功!")
|
|
217
228
|
self._save_auth_to_config(result)
|
|
218
229
|
return True
|
|
219
230
|
except Exception as e:
|
|
@@ -221,62 +232,41 @@ class SoulSyncPlugin:
|
|
|
221
232
|
remaining_code = max_retries - code_retry
|
|
222
233
|
if "invalid" in str(e).lower() or "expired" in str(e).lower():
|
|
223
234
|
if remaining_code > 0:
|
|
224
|
-
print(f"❌ Invalid or expired code: {e}")
|
|
225
|
-
print(f"Remaining attempts / 剩余尝试: {remaining_code}")
|
|
235
|
+
print(f"[SoulSync] ❌ Invalid or expired code: {e}")
|
|
236
|
+
print(f"[SoulSync] Remaining attempts / 剩余尝试: {remaining_code}")
|
|
226
237
|
else:
|
|
227
|
-
print("❌ Too many code attempts / 验证码错误次数过多")
|
|
238
|
+
print("[SoulSync] ❌ Too many code attempts / 验证码错误次数过多")
|
|
228
239
|
break
|
|
229
240
|
else:
|
|
230
|
-
print(f"❌ Registration failed: {e}")
|
|
241
|
+
print(f"[SoulSync] ❌ Registration failed: {e}")
|
|
231
242
|
break
|
|
232
243
|
|
|
233
244
|
if code_retry >= max_retries:
|
|
234
|
-
print("\
|
|
235
|
-
print("1. Resend code / 重新发送验证码")
|
|
236
|
-
print("2. Start over / 重新开始")
|
|
237
|
-
print("3. Exit / 退出")
|
|
245
|
+
print("\n[SoulSync] Too many code verification failures.")
|
|
246
|
+
print("[SoulSync] 1. Resend code / 重新发送验证码")
|
|
247
|
+
print("[SoulSync] 2. Start over / 重新开始")
|
|
248
|
+
print("[SoulSync] 3. Exit / 退出")
|
|
238
249
|
|
|
239
|
-
sub_choice = input("Choose / 选择 (1/2/3): ").strip()
|
|
250
|
+
sub_choice = input("[SoulSync] Choose / 选择 (1/2/3): ").strip()
|
|
240
251
|
if sub_choice == '1':
|
|
241
|
-
retry_count = 0
|
|
252
|
+
retry_count = 0
|
|
242
253
|
continue
|
|
243
254
|
elif sub_choice == '2':
|
|
244
255
|
retry_count = 0
|
|
245
|
-
break
|
|
256
|
+
break
|
|
246
257
|
else:
|
|
247
|
-
print("Exiting... / 退出...")
|
|
258
|
+
print("[SoulSync] Exiting... / 退出...")
|
|
248
259
|
sys.exit(0)
|
|
249
260
|
|
|
250
|
-
print("\n❌ Too many registration attempts / 注册尝试次数过多")
|
|
251
|
-
|
|
252
|
-
|
|
261
|
+
print("\n[SoulSync] ❌ Too many registration attempts / 注册尝试次数过多")
|
|
262
|
+
print("[SoulSync] Exiting... / 退出...")
|
|
263
|
+
sys.exit(0)
|
|
253
264
|
|
|
254
|
-
def _save_auth_to_config(self, auth_result):
|
|
255
|
-
"""保存认证结果到 config.json"""
|
|
256
|
-
config_path = os.path.normpath(os.path.join(PLUGIN_DIR, 'config.json'))
|
|
257
|
-
|
|
258
|
-
try:
|
|
259
|
-
with open(config_path, 'r', encoding='utf-8') as f:
|
|
260
|
-
config = json.load(f)
|
|
261
|
-
except:
|
|
262
|
-
config = {}
|
|
263
|
-
|
|
264
|
-
# 保存 email 和 password(如果 auth_result 包含)
|
|
265
|
-
if 'user' in auth_result:
|
|
266
|
-
config['email'] = auth_result['user'].get('email', '')
|
|
267
|
-
|
|
268
|
-
# 保存 token
|
|
269
|
-
if 'token' in auth_result:
|
|
270
|
-
config['token'] = auth_result['token']
|
|
271
|
-
|
|
272
|
-
with open(config_path, 'w', encoding='utf-8') as f:
|
|
273
|
-
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
274
|
-
|
|
275
|
-
print("Auth info saved to config.json")
|
|
276
|
-
|
|
277
265
|
def initialize(self):
|
|
278
266
|
"""初始化组件"""
|
|
279
|
-
print("\n
|
|
267
|
+
print("\n[SoulSync] ========================================")
|
|
268
|
+
print("[SoulSync] Initializing SoulSync Plugin")
|
|
269
|
+
print("[SoulSync] ========================================\n")
|
|
280
270
|
|
|
281
271
|
self.client = OpenClawClient(self.config)
|
|
282
272
|
|
|
@@ -284,38 +274,49 @@ class SoulSyncPlugin:
|
|
|
284
274
|
if token:
|
|
285
275
|
try:
|
|
286
276
|
profile = self.client.get_profile()
|
|
287
|
-
print(f"Using existing token, user: {profile.get('email', 'unknown')}")
|
|
277
|
+
print(f"[SoulSync] Using existing token, user: {profile.get('email', 'unknown')}")
|
|
288
278
|
except Exception as e:
|
|
289
|
-
print(f"Token invalid
|
|
279
|
+
print(f"[SoulSync] Token invalid, re-authenticating: {e}")
|
|
290
280
|
token = None
|
|
291
281
|
|
|
292
282
|
if not token:
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
283
|
+
email = self.config.get('email', '').strip()
|
|
284
|
+
password = self.config.get('password', '').strip()
|
|
285
|
+
|
|
286
|
+
print("[SoulSync] No valid token, attempting auto-login...")
|
|
287
|
+
try:
|
|
288
|
+
result = self.client.authenticate(email, password)
|
|
289
|
+
if result:
|
|
290
|
+
print("[SoulSync] Login successful! / 登录成功!")
|
|
291
|
+
self._save_auth_to_config(result)
|
|
292
|
+
token = self.client.token
|
|
293
|
+
except Exception as e:
|
|
294
|
+
error_msg = str(e)
|
|
295
|
+
print(f"[SoulSync] Login failed: {e}")
|
|
296
|
+
|
|
297
|
+
if "429" in error_msg or "too many" in error_msg.lower():
|
|
298
|
+
print("\n[SoulSync] ========================================")
|
|
299
|
+
print("[SoulSync] Too many failed attempts. Please try again later / 登录失败次数过多,请稍后再试")
|
|
300
|
+
print("[SoulSync] ========================================\n")
|
|
301
|
+
else:
|
|
302
|
+
print("\n[SoulSync] ========================================")
|
|
303
|
+
print("[SoulSync] Login failed: invalid email or password / 登录失败:邮箱或密码错误")
|
|
304
|
+
print("[SoulSync] Please check your config file / 请检查配置文件:")
|
|
305
|
+
print(f" {os.path.normpath(os.path.join(PLUGIN_DIR, 'config.json'))}")
|
|
306
|
+
print("[SoulSync] ========================================\n")
|
|
307
|
+
|
|
308
|
+
sys.exit(0)
|
|
308
309
|
|
|
309
310
|
email = self.config.get('email')
|
|
310
311
|
password = self.config.get('password')
|
|
311
312
|
|
|
312
313
|
try:
|
|
313
314
|
profile = self.client.get_profile()
|
|
314
|
-
print(f"\
|
|
315
|
+
print(f"\n[SoulSync] Logged in as: {profile.get('email')}")
|
|
315
316
|
subscription = profile.get('subscription', {})
|
|
316
|
-
print(f"Subscription: {subscription.get('status')} (days remaining: {subscription.get('daysRemaining', 0)})\n")
|
|
317
|
+
print(f"[SoulSync] Subscription: {subscription.get('status')} (days remaining: {subscription.get('daysRemaining', 0)})\n")
|
|
317
318
|
except Exception as e:
|
|
318
|
-
print(f"Warning: Could not get profile: {e}")
|
|
319
|
+
print(f"[SoulSync] Warning: Could not get profile: {e}")
|
|
319
320
|
|
|
320
321
|
# 版本管理器
|
|
321
322
|
versions_file = os.path.normpath(os.path.join(PLUGIN_DIR, 'versions.json'))
|
|
@@ -445,7 +446,34 @@ class SoulSyncPlugin:
|
|
|
445
446
|
print("Plugin shutdown complete")
|
|
446
447
|
def main():
|
|
447
448
|
"""主函数"""
|
|
449
|
+
parser = argparse.ArgumentParser(description='SoulSync Plugin')
|
|
450
|
+
parser.add_argument('--setup', action='store_true', help='Run interactive setup (register/login)')
|
|
451
|
+
parser.add_argument('--start', action='store_true', help='Start sync service (auto-login from config)')
|
|
452
|
+
|
|
453
|
+
args = parser.parse_args()
|
|
454
|
+
|
|
448
455
|
plugin = SoulSyncPlugin()
|
|
456
|
+
|
|
457
|
+
if args.setup:
|
|
458
|
+
plugin.load_config()
|
|
459
|
+
plugin.run_setup()
|
|
460
|
+
print("\n[SoulSync] ✓ Setup complete! Run 'openclaw soulsync:start' to begin syncing.")
|
|
461
|
+
print("[SoulSync] 设置完成!运行 'openclaw soulsync:start' 开始同步。")
|
|
462
|
+
sys.exit(0)
|
|
463
|
+
|
|
464
|
+
plugin.load_config()
|
|
465
|
+
|
|
466
|
+
email = plugin.config.get('email', '').strip()
|
|
467
|
+
password = plugin.config.get('password', '').strip()
|
|
468
|
+
|
|
469
|
+
if not email or not password:
|
|
470
|
+
print("\n[SoulSync] ========================================")
|
|
471
|
+
print("[SoulSync] Not configured. Run 'openclaw soulsync:setup' first.")
|
|
472
|
+
print("[SoulSync] 尚未配置,请先运行 'openclaw soulsync:setup'")
|
|
473
|
+
print("[SoulSync] ========================================\n")
|
|
474
|
+
sys.exit(0)
|
|
475
|
+
|
|
476
|
+
plugin.initialize()
|
|
449
477
|
plugin.run()
|
|
450
478
|
if __name__ == '__main__':
|
|
451
479
|
main()
|