soulsync 1.0.22 → 1.2.0
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/index.js +41 -43
- package/package.json +5 -3
- package/src/api-client.js +84 -0
- package/src/daemon.js +94 -0
- package/src/sync-engine.js +745 -0
- package/debug.py +0 -221
- package/requirements.txt +0 -3
- package/setup.sh +0 -91
- package/src/__init__.py +0 -1
- package/src/client.py +0 -300
- package/src/main.py +0 -479
- package/src/profiles.py +0 -88
- package/src/sync.py +0 -210
- package/src/version_manager.py +0 -61
- package/src/watcher.py +0 -133
- package/test_ssl_fix.py +0 -58
package/src/sync.py
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import time
|
|
3
|
-
import shutil
|
|
4
|
-
import threading
|
|
5
|
-
from profiles import ProfilesClient, ConflictError
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ProfileSync:
|
|
9
|
-
"""多文件同步逻辑"""
|
|
10
|
-
|
|
11
|
-
def __init__(self, client: ProfilesClient, version_manager, workspace: str):
|
|
12
|
-
self.client = client
|
|
13
|
-
self.version_manager = version_manager
|
|
14
|
-
self.workspace = workspace
|
|
15
|
-
self._syncing_files = set()
|
|
16
|
-
self._sync_lock = threading.Lock()
|
|
17
|
-
|
|
18
|
-
def _get_absolute_path(self, relative_path: str) -> str:
|
|
19
|
-
"""获取文件的绝对路径"""
|
|
20
|
-
return os.path.normpath(os.path.join(self.workspace, relative_path))
|
|
21
|
-
|
|
22
|
-
def _is_syncing(self, file_path: str) -> bool:
|
|
23
|
-
"""检查文件是否正在同步"""
|
|
24
|
-
return file_path in self._syncing_files
|
|
25
|
-
|
|
26
|
-
def _mark_syncing(self, file_path: str):
|
|
27
|
-
"""标记文件正在同步"""
|
|
28
|
-
self._syncing_files.add(file_path)
|
|
29
|
-
|
|
30
|
-
def _unmark_syncing(self, file_path: str):
|
|
31
|
-
"""延迟取消同步标记"""
|
|
32
|
-
def remove():
|
|
33
|
-
time.sleep(2)
|
|
34
|
-
self._syncing_files.discard(file_path)
|
|
35
|
-
threading.Thread(target=remove, daemon=True).start()
|
|
36
|
-
|
|
37
|
-
def _read_local_file(self, file_path: str) -> str | None:
|
|
38
|
-
"""读取本地文件内容"""
|
|
39
|
-
absolute_path = self._get_absolute_path(file_path)
|
|
40
|
-
if os.path.exists(absolute_path):
|
|
41
|
-
try:
|
|
42
|
-
with open(absolute_path, 'r', encoding='utf-8') as f:
|
|
43
|
-
return f.read()
|
|
44
|
-
except Exception as e:
|
|
45
|
-
print(f"[SoulSync] Error reading local file {file_path}: {e}")
|
|
46
|
-
return None
|
|
47
|
-
return None
|
|
48
|
-
|
|
49
|
-
def _write_local_file(self, file_path: str, content: str):
|
|
50
|
-
"""写入本地文件"""
|
|
51
|
-
absolute_path = self._get_absolute_path(file_path)
|
|
52
|
-
try:
|
|
53
|
-
directory = os.path.dirname(absolute_path)
|
|
54
|
-
if directory and not os.path.exists(directory):
|
|
55
|
-
os.makedirs(directory, exist_ok=True)
|
|
56
|
-
|
|
57
|
-
with open(absolute_path, 'w', encoding='utf-8') as f:
|
|
58
|
-
f.write(content)
|
|
59
|
-
except Exception as e:
|
|
60
|
-
print(f"[SoulSync] Error writing local file {file_path}: {e}")
|
|
61
|
-
|
|
62
|
-
def _create_conflict_backup(self, file_path: str, local_content: str, server_content: str):
|
|
63
|
-
"""创建冲突备份文件"""
|
|
64
|
-
absolute_path = self._get_absolute_path(file_path)
|
|
65
|
-
conflict_path = absolute_path + '.conflict'
|
|
66
|
-
try:
|
|
67
|
-
with open(conflict_path, 'w', encoding='utf-8') as f:
|
|
68
|
-
f.write(f"# Conflict: {file_path}\n")
|
|
69
|
-
f.write(f"# Created at: {time.strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
|
70
|
-
f.write("========== LOCAL VERSION ==========\n")
|
|
71
|
-
f.write(local_content or "(empty)")
|
|
72
|
-
f.write("\n\n========== SERVER VERSION ==========\n")
|
|
73
|
-
f.write(server_content or "(empty)")
|
|
74
|
-
print(f"[SoulSync] Conflict backup created: {conflict_path}")
|
|
75
|
-
except Exception as e:
|
|
76
|
-
print(f"[SoulSync] Error creating conflict backup: {e}")
|
|
77
|
-
|
|
78
|
-
def pull_all(self):
|
|
79
|
-
"""Pull all profiles from cloud"""
|
|
80
|
-
print("[SoulSync] Pulling all profiles from cloud...")
|
|
81
|
-
|
|
82
|
-
try:
|
|
83
|
-
result = self.client.get_profiles()
|
|
84
|
-
cloud_content = result.get('content', {})
|
|
85
|
-
cloud_version = result.get('version', 0)
|
|
86
|
-
except Exception as e:
|
|
87
|
-
print(f"[SoulSync] Error fetching cloud profiles: {e}")
|
|
88
|
-
return
|
|
89
|
-
|
|
90
|
-
if not cloud_content:
|
|
91
|
-
print("[SoulSync] No profiles on cloud")
|
|
92
|
-
return
|
|
93
|
-
|
|
94
|
-
local_files = ['SOUL.md', 'USER.md', 'MEMORY.md']
|
|
95
|
-
pulled_count = 0
|
|
96
|
-
skipped_count = 0
|
|
97
|
-
|
|
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)
|
|
102
|
-
|
|
103
|
-
if cloud_version > local_version:
|
|
104
|
-
self._mark_syncing(file_name)
|
|
105
|
-
try:
|
|
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})")
|
|
111
|
-
finally:
|
|
112
|
-
self._unmark_syncing(file_name)
|
|
113
|
-
else:
|
|
114
|
-
skipped_count += 1
|
|
115
|
-
|
|
116
|
-
self.version_manager.set_version('__profiles__', cloud_version)
|
|
117
|
-
|
|
118
|
-
print(f"[SoulSync] Sync complete: {pulled_count} pulled, {skipped_count} skipped")
|
|
119
|
-
|
|
120
|
-
def push_file(self, file_path: str):
|
|
121
|
-
"""Push a file to cloud"""
|
|
122
|
-
if self._is_syncing(file_path):
|
|
123
|
-
return
|
|
124
|
-
|
|
125
|
-
self._mark_syncing(file_path)
|
|
126
|
-
|
|
127
|
-
try:
|
|
128
|
-
local_content = self._read_local_file(file_path)
|
|
129
|
-
if local_content is None:
|
|
130
|
-
print(f"[SoulSync] File not found locally: {file_path}")
|
|
131
|
-
return
|
|
132
|
-
|
|
133
|
-
local_version = self.version_manager.get_version(file_path)
|
|
134
|
-
|
|
135
|
-
profiles_version = self.version_manager.get_version('__profiles__')
|
|
136
|
-
|
|
137
|
-
current_profiles = {}
|
|
138
|
-
try:
|
|
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)
|
|
150
|
-
self.version_manager.set_version(file_path, new_version)
|
|
151
|
-
print(f"[SoulSync] Pushed: {file_path} (v{new_version})")
|
|
152
|
-
except ConflictError as e:
|
|
153
|
-
self._handle_conflict(file_path, local_content, e.server_content, e.server_version)
|
|
154
|
-
except Exception as e:
|
|
155
|
-
print(f"[SoulSync] Error pushing {file_path}: {e}")
|
|
156
|
-
finally:
|
|
157
|
-
self._unmark_syncing(file_path)
|
|
158
|
-
|
|
159
|
-
def on_remote_change(self, file_path: str, version: int):
|
|
160
|
-
"""Handle remote file change"""
|
|
161
|
-
local_version = self.version_manager.get_version('__profiles__')
|
|
162
|
-
|
|
163
|
-
if version <= local_version:
|
|
164
|
-
print(f"[SoulSync] Remote version not newer, skipping: {file_path} (local: v{local_version}, remote: v{version})")
|
|
165
|
-
return
|
|
166
|
-
|
|
167
|
-
self._mark_syncing(file_path)
|
|
168
|
-
|
|
169
|
-
try:
|
|
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}")
|
|
176
|
-
return
|
|
177
|
-
|
|
178
|
-
cloud_file_content = cloud_content.get(file_path, '')
|
|
179
|
-
|
|
180
|
-
local_content = self._read_local_file(file_path)
|
|
181
|
-
|
|
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
|
-
|
|
185
|
-
self._write_local_file(file_path, cloud_file_content)
|
|
186
|
-
self.version_manager.set_version('__profiles__', cloud_version)
|
|
187
|
-
self.version_manager.set_version(file_path, version)
|
|
188
|
-
print(f"[SoulSync] Pulled remote change: {file_path} (v{version})")
|
|
189
|
-
except Exception as e:
|
|
190
|
-
print(f"[SoulSync] Error handling remote change for {file_path}: {e}")
|
|
191
|
-
finally:
|
|
192
|
-
self._unmark_syncing(file_path)
|
|
193
|
-
|
|
194
|
-
def _handle_conflict(self, file_path: str, local_content: str, server_content: str, server_version: int):
|
|
195
|
-
"""Handle conflict when pushing file"""
|
|
196
|
-
print(f"[SoulSync] CONFLICT detected for {file_path}")
|
|
197
|
-
|
|
198
|
-
self._create_conflict_backup(file_path, local_content, server_content)
|
|
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)
|
|
207
|
-
self.version_manager.set_version(file_path, server_version)
|
|
208
|
-
|
|
209
|
-
print(f"[SoulSync] Conflict resolved: server version used for {file_path}")
|
|
210
|
-
print(f"[SoulSync] Please manually merge the conflict backup file")
|
package/src/version_manager.py
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import os
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class VersionManager:
|
|
6
|
-
"""本地版本管理"""
|
|
7
|
-
|
|
8
|
-
def __init__(self, versions_file: str):
|
|
9
|
-
self.versions_file = versions_file
|
|
10
|
-
self.versions = {}
|
|
11
|
-
self.load()
|
|
12
|
-
|
|
13
|
-
def load(self):
|
|
14
|
-
"""加载版本文件"""
|
|
15
|
-
if os.path.exists(self.versions_file):
|
|
16
|
-
try:
|
|
17
|
-
with open(self.versions_file, 'r', encoding='utf-8') as f:
|
|
18
|
-
self.versions = json.load(f)
|
|
19
|
-
except Exception as e:
|
|
20
|
-
print(f"Failed to load versions file: {e}")
|
|
21
|
-
self.versions = {}
|
|
22
|
-
else:
|
|
23
|
-
self.versions = {}
|
|
24
|
-
|
|
25
|
-
def save(self):
|
|
26
|
-
"""保存版本文件"""
|
|
27
|
-
try:
|
|
28
|
-
directory = os.path.dirname(self.versions_file)
|
|
29
|
-
if directory and not os.path.exists(directory):
|
|
30
|
-
os.makedirs(directory, exist_ok=True)
|
|
31
|
-
|
|
32
|
-
with open(self.versions_file, 'w', encoding='utf-8') as f:
|
|
33
|
-
json.dump(self.versions, f, indent=2, ensure_ascii=False)
|
|
34
|
-
except Exception as e:
|
|
35
|
-
print(f"Failed to save versions file: {e}")
|
|
36
|
-
|
|
37
|
-
def get_version(self, file_path: str) -> int:
|
|
38
|
-
"""获取文件版本"""
|
|
39
|
-
return self.versions.get(file_path, 0)
|
|
40
|
-
|
|
41
|
-
def set_version(self, file_path: str, version: int):
|
|
42
|
-
"""设置文件版本"""
|
|
43
|
-
self.versions[file_path] = version
|
|
44
|
-
self.save()
|
|
45
|
-
|
|
46
|
-
def increment_version(self, file_path: str) -> int:
|
|
47
|
-
"""递增版本"""
|
|
48
|
-
current = self.get_version(file_path)
|
|
49
|
-
new_version = current + 1
|
|
50
|
-
self.set_version(file_path, new_version)
|
|
51
|
-
return new_version
|
|
52
|
-
|
|
53
|
-
def update_versions(self, updates: dict):
|
|
54
|
-
"""批量更新版本"""
|
|
55
|
-
for file_path, version in updates.items():
|
|
56
|
-
self.versions[file_path] = version
|
|
57
|
-
self.save()
|
|
58
|
-
|
|
59
|
-
def get_all_versions(self) -> dict:
|
|
60
|
-
"""获取所有版本"""
|
|
61
|
-
return self.versions.copy()
|
package/src/watcher.py
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import time
|
|
3
|
-
from watchdog.observers import Observer
|
|
4
|
-
from watchdog.events import FileSystemEventHandler, FileSystemEvent
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class OpenClawMultiWatcher(FileSystemEventHandler):
|
|
8
|
-
"""OpenClaw 多文件/目录监听器"""
|
|
9
|
-
|
|
10
|
-
def __init__(self, workspace: str, watch_paths: list, callback):
|
|
11
|
-
self.workspace = os.path.abspath(workspace)
|
|
12
|
-
self.watch_paths = watch_paths
|
|
13
|
-
self.callback = callback
|
|
14
|
-
self.observer = None
|
|
15
|
-
self.last_events = {}
|
|
16
|
-
self.debounce_seconds = 1
|
|
17
|
-
self.ignore_patterns = ['.tmp', '.swp', '.bak', '~']
|
|
18
|
-
|
|
19
|
-
def start(self):
|
|
20
|
-
"""开始监听"""
|
|
21
|
-
if not os.path.exists(self.workspace):
|
|
22
|
-
os.makedirs(self.workspace, exist_ok=True)
|
|
23
|
-
|
|
24
|
-
self.observer = Observer()
|
|
25
|
-
|
|
26
|
-
for watch_path in self.watch_paths:
|
|
27
|
-
full_path = os.path.join(self.workspace, watch_path)
|
|
28
|
-
|
|
29
|
-
if watch_path.endswith('/'):
|
|
30
|
-
dir_name = watch_path.rstrip('/')
|
|
31
|
-
if not os.path.exists(full_path):
|
|
32
|
-
os.makedirs(full_path, exist_ok=True)
|
|
33
|
-
print(f"Created directory: {full_path}")
|
|
34
|
-
|
|
35
|
-
self.observer.schedule(self, full_path, recursive=True)
|
|
36
|
-
print(f"Watching directory: {watch_path}")
|
|
37
|
-
else:
|
|
38
|
-
directory = os.path.dirname(full_path)
|
|
39
|
-
if directory and not os.path.exists(directory):
|
|
40
|
-
os.makedirs(directory, exist_ok=True)
|
|
41
|
-
|
|
42
|
-
if not os.path.exists(full_path):
|
|
43
|
-
with open(full_path, 'w', encoding='utf-8') as f:
|
|
44
|
-
pass
|
|
45
|
-
print(f"Created file: {watch_path}")
|
|
46
|
-
|
|
47
|
-
self.observer.schedule(self, os.path.dirname(full_path), recursive=False)
|
|
48
|
-
print(f"Watching file: {watch_path}")
|
|
49
|
-
|
|
50
|
-
self.observer.start()
|
|
51
|
-
print(f"Started watching {len(self.watch_paths)} paths in {self.workspace}")
|
|
52
|
-
|
|
53
|
-
def stop(self):
|
|
54
|
-
"""停止监听"""
|
|
55
|
-
if self.observer:
|
|
56
|
-
self.observer.stop()
|
|
57
|
-
self.observer.join()
|
|
58
|
-
print("Stopped watching")
|
|
59
|
-
|
|
60
|
-
def _should_ignore(self, path: str) -> bool:
|
|
61
|
-
"""检查是否应该忽略"""
|
|
62
|
-
basename = os.path.basename(path)
|
|
63
|
-
for pattern in self.ignore_patterns:
|
|
64
|
-
if pattern in basename:
|
|
65
|
-
return True
|
|
66
|
-
return False
|
|
67
|
-
|
|
68
|
-
def _get_relative_path(self, absolute_path: str) -> str:
|
|
69
|
-
"""获取相对路径"""
|
|
70
|
-
if absolute_path.startswith(self.workspace):
|
|
71
|
-
relative = absolute_path[len(self.workspace):].lstrip('/')
|
|
72
|
-
return relative
|
|
73
|
-
return absolute_path
|
|
74
|
-
|
|
75
|
-
def _debounce_check(self, file_path: str) -> bool:
|
|
76
|
-
"""防抖检查"""
|
|
77
|
-
current_time = time.time()
|
|
78
|
-
last_time = self.last_events.get(file_path, 0)
|
|
79
|
-
|
|
80
|
-
if current_time - last_time < self.debounce_seconds:
|
|
81
|
-
return False
|
|
82
|
-
|
|
83
|
-
self.last_events[file_path] = current_time
|
|
84
|
-
return True
|
|
85
|
-
|
|
86
|
-
def on_modified(self, event):
|
|
87
|
-
"""文件被修改"""
|
|
88
|
-
if event.is_directory:
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
event_path = os.path.abspath(event.src_path)
|
|
92
|
-
|
|
93
|
-
if self._should_ignore(event_path):
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
if self._debounce_check(event_path):
|
|
97
|
-
relative_path = self._get_relative_path(event_path)
|
|
98
|
-
print(f"[Watcher] File modified: {relative_path}")
|
|
99
|
-
self._trigger_callback('modified', relative_path, event_path)
|
|
100
|
-
|
|
101
|
-
def on_created(self, event):
|
|
102
|
-
"""文件被创建"""
|
|
103
|
-
if event.is_directory:
|
|
104
|
-
return
|
|
105
|
-
|
|
106
|
-
event_path = os.path.abspath(event.src_path)
|
|
107
|
-
|
|
108
|
-
if self._should_ignore(event_path):
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
if self._debounce_check(event_path):
|
|
112
|
-
relative_path = self._get_relative_path(event_path)
|
|
113
|
-
print(f"[Watcher] File created: {relative_path}")
|
|
114
|
-
self._trigger_callback('created', relative_path, event_path)
|
|
115
|
-
|
|
116
|
-
def on_deleted(self, event):
|
|
117
|
-
"""文件被删除"""
|
|
118
|
-
if event.is_directory:
|
|
119
|
-
return
|
|
120
|
-
|
|
121
|
-
event_path = os.path.abspath(event.src_path)
|
|
122
|
-
|
|
123
|
-
if self._should_ignore(event_path):
|
|
124
|
-
return
|
|
125
|
-
|
|
126
|
-
relative_path = self._get_relative_path(event_path)
|
|
127
|
-
print(f"[Watcher] File deleted: {relative_path}")
|
|
128
|
-
self._trigger_callback('deleted', relative_path, event_path)
|
|
129
|
-
|
|
130
|
-
def _trigger_callback(self, event_type: str, relative_path: str, absolute_path: str = None):
|
|
131
|
-
"""触发回调函数"""
|
|
132
|
-
if self.callback:
|
|
133
|
-
self.callback(event_type, relative_path, absolute_path)
|
package/test_ssl_fix.py
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
测试 SSL 修复是否有效
|
|
4
|
-
"""
|
|
5
|
-
import ssl
|
|
6
|
-
import sys
|
|
7
|
-
|
|
8
|
-
print(f"Python 版本: {sys.version}")
|
|
9
|
-
print(f"SSL 版本: {ssl.OPENSSL_VERSION}")
|
|
10
|
-
print()
|
|
11
|
-
|
|
12
|
-
# 测试 1: 使用 ssl.create_default_context()
|
|
13
|
-
print("测试 1: 使用 ssl.create_default_context()")
|
|
14
|
-
try:
|
|
15
|
-
import urllib.request
|
|
16
|
-
ctx = ssl.create_default_context()
|
|
17
|
-
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
18
|
-
req = urllib.request.Request('https://soulsync.work/api/health')
|
|
19
|
-
response = urllib.request.urlopen(req, context=ctx)
|
|
20
|
-
print(f"✅ 成功: {response.read().decode()}")
|
|
21
|
-
except Exception as e:
|
|
22
|
-
print(f"❌ 失败: {e}")
|
|
23
|
-
|
|
24
|
-
print()
|
|
25
|
-
|
|
26
|
-
# 测试 2: 使用 requests + TLSAdapter
|
|
27
|
-
print("测试 2: 使用 requests + 自定义 TLSAdapter")
|
|
28
|
-
try:
|
|
29
|
-
import requests
|
|
30
|
-
from requests.adapters import HTTPAdapter
|
|
31
|
-
|
|
32
|
-
class TLSAdapter(HTTPAdapter):
|
|
33
|
-
def init_poolmanager(self, *args, **kwargs):
|
|
34
|
-
ctx = ssl.create_default_context()
|
|
35
|
-
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
36
|
-
kwargs['ssl_context'] = ctx
|
|
37
|
-
return super().init_poolmanager(*args, **kwargs)
|
|
38
|
-
|
|
39
|
-
session = requests.Session()
|
|
40
|
-
session.mount('https://', TLSAdapter())
|
|
41
|
-
response = session.get('https://soulsync.work/api/health')
|
|
42
|
-
print(f"✅ 成功: {response.text}")
|
|
43
|
-
except Exception as e:
|
|
44
|
-
print(f"❌ 失败: {e}")
|
|
45
|
-
|
|
46
|
-
print()
|
|
47
|
-
|
|
48
|
-
# 测试 3: 直接 requests(无适配器)
|
|
49
|
-
print("测试 3: 直接 requests(无适配器)")
|
|
50
|
-
try:
|
|
51
|
-
import requests
|
|
52
|
-
response = requests.get('https://soulsync.work/api/health')
|
|
53
|
-
print(f"✅ 成功: {response.text}")
|
|
54
|
-
except Exception as e:
|
|
55
|
-
print(f"❌ 失败: {e}")
|
|
56
|
-
|
|
57
|
-
print()
|
|
58
|
-
print("测试完成")
|