soulsync 1.0.7 → 1.0.9

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.
Files changed (3) hide show
  1. package/index.js +80 -35
  2. package/package.json +1 -1
  3. package/src/main.py +178 -2
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
- // 自动启动 Python 服务
11
- function startPythonService() {
12
- const pluginDir = path.dirname(__filename);
13
- const pythonScript = path.join(pluginDir, 'src', 'main.py');
14
- const pythonPath = process.env.PYTHON_PATH || 'python3';
15
-
16
- console.log('[SoulSync] Auto-starting Python service...');
17
-
18
- pythonProcess = spawn(pythonPath, [pythonScript], {
19
- cwd: pluginDir,
20
- env: {
21
- ...process.env,
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
- startPythonService();
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soulsync",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "SoulSync plugin for OpenClaw - cross-bot memory synchronization",
5
5
  "main": "index.js",
6
6
  "repository": {
package/src/main.py CHANGED
@@ -7,6 +7,8 @@ import json
7
7
  import os
8
8
  import sys
9
9
  import time
10
+ import getpass
11
+ import argparse
10
12
 
11
13
  # 获取插件根目录
12
14
  PLUGIN_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -111,8 +113,155 @@ class SoulSyncPlugin:
111
113
  with open(config_path, 'w', encoding='utf-8') as f:
112
114
  json.dump(config, f, indent=2, ensure_ascii=False)
113
115
 
114
- print("Auth info saved to config.json")
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)
136
+
137
+ def _interactive_login(self):
138
+ """交互式登录(带重试)"""
139
+ from client import OpenClawClient
140
+
141
+ max_retries = 5
142
+ retry_count = 0
143
+
144
+ while retry_count < max_retries:
145
+ print("\n--- Login / 登录 ---")
146
+ email = input("[SoulSync] Email / 邮箱: ").strip()
147
+ if not email:
148
+ print("[SoulSync] Email cannot be empty / 邮箱不能为空")
149
+ continue
150
+
151
+ password = getpass.getpass("[SoulSync] Password / 密码: ")
152
+ if not password:
153
+ print("[SoulSync] Password cannot be empty / 密码不能为空")
154
+ continue
155
+
156
+ try:
157
+ temp_client = OpenClawClient(self.config)
158
+ result = temp_client.authenticate(email, password)
159
+ if result:
160
+ print("\n[SoulSync] ✓ Login successful! / 登录成功!")
161
+ self._save_auth_to_config(result)
162
+ return True
163
+ except Exception as e:
164
+ retry_count += 1
165
+ remaining = max_retries - retry_count
166
+ error_msg = str(e)
167
+
168
+ if "429" in error_msg or "too many" in error_msg.lower():
169
+ print(f"\n[SoulSync] ❌ {e}")
170
+ print("\n[SoulSync] Too many failed attempts / 登录失败次数过多")
171
+ print("[SoulSync] Exiting... / 退出...")
172
+ sys.exit(0)
173
+
174
+ if remaining > 0:
175
+ print(f"\n[SoulSync] ❌ Login failed: {e} / 登录失败: {e}")
176
+ print(f"[SoulSync] Remaining attempts / 剩余尝试次数: {remaining}")
177
+ else:
178
+ print(f"\n[SoulSync] ❌ Login failed: {e} / 登录失败: {e}")
179
+
180
+ print("\n[SoulSync] ❌ Too many failed attempts. Please try again in 15 minutes. / 登录失败次数过多,请15分钟后再试")
181
+ print("[SoulSync] Exiting... / 退出...")
182
+ sys.exit(0)
183
+
184
+ def _interactive_register(self):
185
+ """交互式注册(带重试)"""
186
+ from client import OpenClawClient
187
+
188
+ max_retries = 5
189
+ retry_count = 0
190
+
191
+ while retry_count < max_retries:
192
+ print("\n--- Register / 注册 ---")
193
+ email = input("[SoulSync] Email / 邮箱: ").strip()
194
+ if not email or '@' not in email:
195
+ print("[SoulSync] Invalid email / 无效邮箱")
196
+ continue
197
+
198
+ password = getpass.getpass("[SoulSync] Password / 密码: ")
199
+ if len(password) < 6:
200
+ print("[SoulSync] Password must be at least 6 characters / 密码至少6位")
201
+ continue
202
+
203
+ password2 = getpass.getpass("[SoulSync] Confirm password / 确认密码: ")
204
+ if password != password2:
205
+ print("[SoulSync] Passwords do not match / 两次密码不一致")
206
+ continue
207
+
208
+ print(f"\n[SoulSync] Sending verification code to {email}...")
209
+ try:
210
+ temp_client = OpenClawClient(self.config)
211
+ temp_client.send_verification_code(email)
212
+ print("[SoulSync] ✓ Verification code sent! / 验证码已发送!")
213
+ except Exception as e:
214
+ print(f"[SoulSync] ❌ Failed to send code: {e}")
215
+ continue
216
+
217
+ code_retry = 0
218
+ while code_retry < max_retries:
219
+ code = input(f"[SoulSync] Enter code / 输入验证码 ({max_retries - code_retry} attempts left): ").strip()
220
+ if len(code) != 6 or not code.isdigit():
221
+ code_retry += 1
222
+ print("[SoulSync] Invalid code format / 验证码格式错误")
223
+ continue
224
+
225
+ try:
226
+ result = temp_client.register(email, password, code)
227
+ print("\n[SoulSync] ✓ Registration successful! / 注册成功!")
228
+ self._save_auth_to_config(result)
229
+ return True
230
+ except Exception as e:
231
+ code_retry += 1
232
+ remaining_code = max_retries - code_retry
233
+ if "invalid" in str(e).lower() or "expired" in str(e).lower():
234
+ if remaining_code > 0:
235
+ print(f"[SoulSync] ❌ Invalid or expired code: {e}")
236
+ print(f"[SoulSync] Remaining attempts / 剩余尝试: {remaining_code}")
237
+ else:
238
+ print("[SoulSync] ❌ Too many code attempts / 验证码错误次数过多")
239
+ break
240
+ else:
241
+ print(f"[SoulSync] ❌ Registration failed: {e}")
242
+ break
243
+
244
+ if code_retry >= max_retries:
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 / 退出")
249
+
250
+ sub_choice = input("[SoulSync] Choose / 选择 (1/2/3): ").strip()
251
+ if sub_choice == '1':
252
+ retry_count = 0
253
+ continue
254
+ elif sub_choice == '2':
255
+ retry_count = 0
256
+ break
257
+ else:
258
+ print("[SoulSync] Exiting... / 退出...")
259
+ sys.exit(0)
260
+
261
+ print("\n[SoulSync] ❌ Too many registration attempts / 注册尝试次数过多")
262
+ print("[SoulSync] Exiting... / 退出...")
263
+ sys.exit(0)
264
+
116
265
  def initialize(self):
117
266
  """初始化组件"""
118
267
  print("\n[SoulSync] ========================================")
@@ -297,7 +446,34 @@ class SoulSyncPlugin:
297
446
  print("Plugin shutdown complete")
298
447
  def main():
299
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
+
300
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()
301
477
  plugin.run()
302
478
  if __name__ == '__main__':
303
479
  main()