autoglm-gui 1.0.2__py3-none-any.whl → 1.2.0__py3-none-any.whl
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.
- AutoGLM_GUI/adb_plus/__init__.py +12 -1
- AutoGLM_GUI/adb_plus/mdns.py +192 -0
- AutoGLM_GUI/adb_plus/pair.py +60 -0
- AutoGLM_GUI/adb_plus/qr_pair.py +372 -0
- AutoGLM_GUI/adb_plus/serial.py +61 -2
- AutoGLM_GUI/adb_plus/version.py +81 -0
- AutoGLM_GUI/api/__init__.py +16 -1
- AutoGLM_GUI/api/agents.py +329 -94
- AutoGLM_GUI/api/devices.py +304 -100
- AutoGLM_GUI/api/workflows.py +70 -0
- AutoGLM_GUI/device_manager.py +760 -0
- AutoGLM_GUI/exceptions.py +18 -0
- AutoGLM_GUI/phone_agent_manager.py +549 -0
- AutoGLM_GUI/phone_agent_patches.py +146 -0
- AutoGLM_GUI/schemas.py +380 -2
- AutoGLM_GUI/state.py +21 -0
- AutoGLM_GUI/static/assets/{about-BOnRPlKQ.js → about-PcGX7dIG.js} +1 -1
- AutoGLM_GUI/static/assets/chat-B0FKL2ne.js +124 -0
- AutoGLM_GUI/static/assets/dialog-BSNX0L1i.js +45 -0
- AutoGLM_GUI/static/assets/index-BjYIY--m.css +1 -0
- AutoGLM_GUI/static/assets/index-CnEYDOXp.js +11 -0
- AutoGLM_GUI/static/assets/index-DOt5XNhh.js +1 -0
- AutoGLM_GUI/static/assets/logo-Cyfm06Ym.png +0 -0
- AutoGLM_GUI/static/assets/workflows-B1hgBC_O.js +1 -0
- AutoGLM_GUI/static/favicon.ico +0 -0
- AutoGLM_GUI/static/index.html +9 -2
- AutoGLM_GUI/static/logo-192.png +0 -0
- AutoGLM_GUI/static/logo-512.png +0 -0
- AutoGLM_GUI/workflow_manager.py +181 -0
- {autoglm_gui-1.0.2.dist-info → autoglm_gui-1.2.0.dist-info}/METADATA +80 -35
- {autoglm_gui-1.0.2.dist-info → autoglm_gui-1.2.0.dist-info}/RECORD +34 -19
- AutoGLM_GUI/static/assets/chat-CGW6uMKB.js +0 -149
- AutoGLM_GUI/static/assets/index-CRFVU0eu.js +0 -1
- AutoGLM_GUI/static/assets/index-DH-Dl4tK.js +0 -10
- AutoGLM_GUI/static/assets/index-DzUQ89YC.css +0 -1
- {autoglm_gui-1.0.2.dist-info → autoglm_gui-1.2.0.dist-info}/WHEEL +0 -0
- {autoglm_gui-1.0.2.dist-info → autoglm_gui-1.2.0.dist-info}/entry_points.txt +0 -0
- {autoglm_gui-1.0.2.dist-info → autoglm_gui-1.2.0.dist-info}/licenses/LICENSE +0 -0
AutoGLM_GUI/api/devices.py
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
"""Device discovery routes."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
3
7
|
from fastapi import APIRouter
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from AutoGLM_GUI.device_manager import ManagedDevice
|
|
11
|
+
from AutoGLM_GUI.phone_agent_manager import PhoneAgentManager
|
|
12
|
+
|
|
13
|
+
from AutoGLM_GUI.adb_plus.qr_pair import qr_pairing_manager
|
|
14
|
+
from AutoGLM_GUI.logger import logger
|
|
6
15
|
|
|
7
16
|
from AutoGLM_GUI.schemas import (
|
|
8
17
|
DeviceListResponse,
|
|
@@ -12,112 +21,134 @@ from AutoGLM_GUI.schemas import (
|
|
|
12
21
|
WiFiDisconnectResponse,
|
|
13
22
|
WiFiManualConnectRequest,
|
|
14
23
|
WiFiManualConnectResponse,
|
|
24
|
+
WiFiPairRequest,
|
|
25
|
+
WiFiPairResponse,
|
|
26
|
+
MdnsDiscoverResponse,
|
|
27
|
+
MdnsDeviceResponse,
|
|
28
|
+
QRPairGenerateResponse,
|
|
29
|
+
QRPairStatusResponse,
|
|
30
|
+
QRPairCancelResponse,
|
|
15
31
|
)
|
|
16
|
-
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _build_device_response_with_agent(
|
|
35
|
+
device: ManagedDevice, agent_manager: PhoneAgentManager
|
|
36
|
+
) -> dict:
|
|
37
|
+
"""组合设备信息和 Agent 状态(API 层职责)。
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
device: ManagedDevice 实例
|
|
41
|
+
agent_manager: PhoneAgentManager 实例
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
dict: 完整的设备响应,匹配 DeviceResponse schema
|
|
45
|
+
"""
|
|
46
|
+
# 获取纯设备信息
|
|
47
|
+
response = device.to_dict()
|
|
48
|
+
|
|
49
|
+
# 通过 serial 查找 Agent(支持连接切换)
|
|
50
|
+
agent_device_id = agent_manager.find_agent_by_serial(device.serial)
|
|
51
|
+
|
|
52
|
+
if agent_device_id:
|
|
53
|
+
metadata = agent_manager.get_metadata(agent_device_id)
|
|
54
|
+
|
|
55
|
+
if metadata:
|
|
56
|
+
response["agent"] = {
|
|
57
|
+
"state": metadata.state.value,
|
|
58
|
+
"created_at": metadata.created_at,
|
|
59
|
+
"last_used": metadata.last_used,
|
|
60
|
+
"error_message": metadata.error_message,
|
|
61
|
+
"model_name": metadata.model_config.model_name,
|
|
62
|
+
}
|
|
63
|
+
else:
|
|
64
|
+
response["agent"] = None
|
|
65
|
+
else:
|
|
66
|
+
response["agent"] = None
|
|
67
|
+
|
|
68
|
+
return response
|
|
69
|
+
|
|
17
70
|
|
|
18
71
|
router = APIRouter()
|
|
19
72
|
|
|
20
73
|
|
|
21
74
|
@router.get("/api/devices", response_model=DeviceListResponse)
|
|
22
75
|
def list_devices() -> DeviceListResponse:
|
|
23
|
-
"""列出所有 ADB
|
|
24
|
-
from
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
devices_with_serial = []
|
|
30
|
-
for d in adb_devices:
|
|
31
|
-
# 使用 adb_plus 的 get_device_serial 获取真实序列号
|
|
32
|
-
serial = get_device_serial(d.device_id, conn.adb_path)
|
|
33
|
-
|
|
34
|
-
devices_with_serial.append(
|
|
35
|
-
{
|
|
36
|
-
"id": d.device_id,
|
|
37
|
-
"model": d.model or "Unknown",
|
|
38
|
-
"status": d.status,
|
|
39
|
-
"connection_type": d.connection_type.value,
|
|
40
|
-
"is_initialized": d.device_id in agents,
|
|
41
|
-
"serial": serial, # 真实序列号
|
|
42
|
-
}
|
|
43
|
-
)
|
|
76
|
+
"""列出所有 ADB 设备及 Agent 状态."""
|
|
77
|
+
from AutoGLM_GUI.device_manager import DeviceManager
|
|
78
|
+
from AutoGLM_GUI.phone_agent_manager import PhoneAgentManager
|
|
79
|
+
|
|
80
|
+
device_manager = DeviceManager.get_instance()
|
|
81
|
+
agent_manager = PhoneAgentManager.get_instance()
|
|
44
82
|
|
|
45
|
-
|
|
83
|
+
# Fallback: 如果轮询未启动,执行同步获取
|
|
84
|
+
if not device_manager._poll_thread or not device_manager._poll_thread.is_alive():
|
|
85
|
+
logger.warning("Polling not started, performing synchronous device fetch")
|
|
86
|
+
device_manager.force_refresh()
|
|
87
|
+
|
|
88
|
+
managed_devices = device_manager.get_devices()
|
|
89
|
+
|
|
90
|
+
# API 层负责聚合设备信息和 Agent 状态
|
|
91
|
+
devices_with_agents = [
|
|
92
|
+
_build_device_response_with_agent(d, agent_manager) for d in managed_devices
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
return DeviceListResponse(devices=devices_with_agents)
|
|
46
96
|
|
|
47
97
|
|
|
48
98
|
@router.post("/api/devices/connect_wifi", response_model=WiFiConnectResponse)
|
|
49
99
|
def connect_wifi(request: WiFiConnectRequest) -> WiFiConnectResponse:
|
|
50
100
|
"""从 USB 启用 TCP/IP 并连接到 WiFi。"""
|
|
51
|
-
from
|
|
101
|
+
from AutoGLM_GUI.device_manager import DeviceManager
|
|
52
102
|
|
|
53
|
-
|
|
103
|
+
device_manager = DeviceManager.get_instance()
|
|
104
|
+
success, message, wifi_id = device_manager.connect_wifi(
|
|
105
|
+
device_id=request.device_id,
|
|
106
|
+
port=request.port,
|
|
107
|
+
)
|
|
54
108
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return WiFiConnectResponse(
|
|
59
|
-
success=False,
|
|
60
|
-
message="No connected device found",
|
|
61
|
-
error="device_not_found",
|
|
62
|
-
)
|
|
109
|
+
if success:
|
|
110
|
+
# Immediately refresh device list to show new WiFi device
|
|
111
|
+
device_manager.force_refresh()
|
|
63
112
|
|
|
64
|
-
# 已经是 WiFi 连接则直接返回
|
|
65
|
-
if device_info.connection_type == ConnectionType.REMOTE:
|
|
66
|
-
address = device_info.device_id
|
|
67
113
|
return WiFiConnectResponse(
|
|
68
114
|
success=True,
|
|
69
|
-
message=
|
|
70
|
-
device_id=
|
|
71
|
-
address=
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
# 1) 启用 tcpip
|
|
75
|
-
ok, msg = conn.enable_tcpip(port=request.port, device_id=device_info.device_id)
|
|
76
|
-
if not ok:
|
|
77
|
-
return WiFiConnectResponse(
|
|
78
|
-
success=False, message=msg or "Failed to enable tcpip", error="tcpip"
|
|
115
|
+
message=message,
|
|
116
|
+
device_id=wifi_id,
|
|
117
|
+
address=wifi_id,
|
|
79
118
|
)
|
|
119
|
+
else:
|
|
120
|
+
# Determine error type from message
|
|
121
|
+
error_type = "connect"
|
|
122
|
+
if "not found" in message.lower():
|
|
123
|
+
error_type = "device_not_found"
|
|
124
|
+
elif "tcpip" in message.lower():
|
|
125
|
+
error_type = "tcpip"
|
|
126
|
+
elif "ip" in message.lower():
|
|
127
|
+
error_type = "ip"
|
|
80
128
|
|
|
81
|
-
# 2) 读取设备 IP:先用本地 adb_plus 的 WiFi 优先逻辑,失败再回退上游接口
|
|
82
|
-
ip = get_wifi_ip(conn.adb_path, device_info.device_id) or conn.get_device_ip(
|
|
83
|
-
device_info.device_id
|
|
84
|
-
)
|
|
85
|
-
if not ip:
|
|
86
|
-
return WiFiConnectResponse(
|
|
87
|
-
success=False, message="Failed to get device IP", error="ip"
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
address = f"{ip}:{request.port}"
|
|
91
|
-
|
|
92
|
-
# 3) 连接 WiFi
|
|
93
|
-
ok, msg = conn.connect(address)
|
|
94
|
-
if not ok:
|
|
95
129
|
return WiFiConnectResponse(
|
|
96
130
|
success=False,
|
|
97
|
-
message=
|
|
98
|
-
error=
|
|
131
|
+
message=message,
|
|
132
|
+
error=error_type,
|
|
99
133
|
)
|
|
100
134
|
|
|
101
|
-
return WiFiConnectResponse(
|
|
102
|
-
success=True,
|
|
103
|
-
message="Switched to WiFi successfully",
|
|
104
|
-
device_id=address,
|
|
105
|
-
address=address,
|
|
106
|
-
)
|
|
107
|
-
|
|
108
135
|
|
|
109
136
|
@router.post("/api/devices/disconnect_wifi", response_model=WiFiDisconnectResponse)
|
|
110
137
|
def disconnect_wifi(request: WiFiDisconnectRequest) -> WiFiDisconnectResponse:
|
|
111
138
|
"""断开 WiFi 连接。"""
|
|
112
|
-
from
|
|
139
|
+
from AutoGLM_GUI.device_manager import DeviceManager
|
|
140
|
+
|
|
141
|
+
device_manager = DeviceManager.get_instance()
|
|
142
|
+
success, message = device_manager.disconnect_wifi(request.device_id)
|
|
113
143
|
|
|
114
|
-
|
|
115
|
-
|
|
144
|
+
if success:
|
|
145
|
+
# Refresh device list to update status
|
|
146
|
+
device_manager.force_refresh()
|
|
116
147
|
|
|
117
148
|
return WiFiDisconnectResponse(
|
|
118
|
-
success=
|
|
119
|
-
message=
|
|
120
|
-
error=None if
|
|
149
|
+
success=success,
|
|
150
|
+
message=message,
|
|
151
|
+
error=None if success else "disconnect_failed",
|
|
121
152
|
)
|
|
122
153
|
|
|
123
154
|
|
|
@@ -128,41 +159,214 @@ def connect_wifi_manual(
|
|
|
128
159
|
request: WiFiManualConnectRequest,
|
|
129
160
|
) -> WiFiManualConnectResponse:
|
|
130
161
|
"""手动连接到 WiFi 设备 (直接连接,无需 USB)."""
|
|
131
|
-
import
|
|
162
|
+
from AutoGLM_GUI.device_manager import DeviceManager
|
|
132
163
|
|
|
133
|
-
|
|
164
|
+
device_manager = DeviceManager.get_instance()
|
|
165
|
+
success, message, device_id = device_manager.connect_wifi_manual(
|
|
166
|
+
ip=request.ip,
|
|
167
|
+
port=request.port,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if success:
|
|
171
|
+
# Refresh device list to show new device
|
|
172
|
+
device_manager.force_refresh()
|
|
134
173
|
|
|
135
|
-
# IP 格式验证
|
|
136
|
-
ip_pattern = r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$"
|
|
137
|
-
if not re.match(ip_pattern, request.ip):
|
|
138
174
|
return WiFiManualConnectResponse(
|
|
139
|
-
success=
|
|
140
|
-
message=
|
|
141
|
-
|
|
175
|
+
success=True,
|
|
176
|
+
message=message,
|
|
177
|
+
device_id=device_id,
|
|
142
178
|
)
|
|
179
|
+
else:
|
|
180
|
+
# Determine error type from message
|
|
181
|
+
error_type = "connect_failed"
|
|
182
|
+
if "Invalid IP" in message:
|
|
183
|
+
error_type = "invalid_ip"
|
|
184
|
+
elif "Port must be" in message:
|
|
185
|
+
error_type = "invalid_port"
|
|
143
186
|
|
|
144
|
-
# 端口范围验证
|
|
145
|
-
if not (1 <= request.port <= 65535):
|
|
146
187
|
return WiFiManualConnectResponse(
|
|
147
188
|
success=False,
|
|
148
|
-
message=
|
|
149
|
-
error=
|
|
189
|
+
message=message,
|
|
190
|
+
error=error_type,
|
|
150
191
|
)
|
|
151
192
|
|
|
152
|
-
conn = ADBConnection()
|
|
153
|
-
address = f"{request.ip}:{request.port}"
|
|
154
193
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
194
|
+
@router.post("/api/devices/pair_wifi", response_model=WiFiPairResponse)
|
|
195
|
+
def pair_wifi(request: WiFiPairRequest) -> WiFiPairResponse:
|
|
196
|
+
"""使用无线调试配对并连接到 WiFi 设备 (Android 11+)."""
|
|
197
|
+
from AutoGLM_GUI.device_manager import DeviceManager
|
|
198
|
+
|
|
199
|
+
device_manager = DeviceManager.get_instance()
|
|
200
|
+
success, message, device_id = device_manager.pair_wifi(
|
|
201
|
+
ip=request.ip,
|
|
202
|
+
pairing_port=request.pairing_port,
|
|
203
|
+
pairing_code=request.pairing_code,
|
|
204
|
+
connection_port=request.connection_port,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
if success:
|
|
208
|
+
# Refresh device list to show newly paired device
|
|
209
|
+
device_manager.force_refresh()
|
|
210
|
+
|
|
211
|
+
return WiFiPairResponse(
|
|
212
|
+
success=True,
|
|
213
|
+
message=message,
|
|
214
|
+
device_id=device_id,
|
|
215
|
+
)
|
|
216
|
+
else:
|
|
217
|
+
# Determine error type from message
|
|
218
|
+
error_type = "connect_failed"
|
|
219
|
+
if "Invalid IP" in message:
|
|
220
|
+
error_type = "invalid_ip"
|
|
221
|
+
elif "port must be" in message.lower():
|
|
222
|
+
error_type = "invalid_port"
|
|
223
|
+
elif "Pairing code must be" in message:
|
|
224
|
+
error_type = "invalid_pairing_code"
|
|
225
|
+
elif "connection failed" not in message.lower():
|
|
226
|
+
error_type = "pair_failed"
|
|
227
|
+
|
|
228
|
+
return WiFiPairResponse(
|
|
159
229
|
success=False,
|
|
160
|
-
message=
|
|
161
|
-
error=
|
|
230
|
+
message=message,
|
|
231
|
+
error=error_type,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@router.get("/api/devices/discover_mdns", response_model=MdnsDiscoverResponse)
|
|
236
|
+
def discover_mdns() -> MdnsDiscoverResponse:
|
|
237
|
+
"""Discover wireless ADB devices via mDNS."""
|
|
238
|
+
from phone_agent.adb import ADBConnection
|
|
239
|
+
from AutoGLM_GUI.adb_plus import discover_mdns_devices
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
conn = ADBConnection()
|
|
243
|
+
devices = discover_mdns_devices(conn.adb_path)
|
|
244
|
+
|
|
245
|
+
device_responses = [
|
|
246
|
+
MdnsDeviceResponse(
|
|
247
|
+
name=dev.name,
|
|
248
|
+
ip=dev.ip,
|
|
249
|
+
port=dev.port,
|
|
250
|
+
has_pairing=dev.has_pairing,
|
|
251
|
+
service_type=dev.service_type,
|
|
252
|
+
pairing_port=dev.pairing_port,
|
|
253
|
+
)
|
|
254
|
+
for dev in devices
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
return MdnsDiscoverResponse(
|
|
258
|
+
success=True,
|
|
259
|
+
devices=device_responses,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
except Exception as e:
|
|
263
|
+
return MdnsDiscoverResponse(
|
|
264
|
+
success=False,
|
|
265
|
+
devices=[],
|
|
266
|
+
error=str(e),
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
# QR Code Pairing Routes
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
@router.post("/api/devices/qr_pair/generate", response_model=QRPairGenerateResponse)
|
|
274
|
+
def generate_qr_pairing(timeout: int = 90) -> QRPairGenerateResponse:
|
|
275
|
+
"""Generate QR code for wireless pairing and start mDNS listener.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
timeout: Session timeout in seconds (default 90)
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
QR code payload and session information
|
|
282
|
+
"""
|
|
283
|
+
try:
|
|
284
|
+
from phone_agent.adb import ADBConnection
|
|
285
|
+
|
|
286
|
+
conn = ADBConnection()
|
|
287
|
+
session = qr_pairing_manager.create_session(
|
|
288
|
+
timeout=timeout, adb_path=conn.adb_path
|
|
162
289
|
)
|
|
163
290
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
291
|
+
return QRPairGenerateResponse(
|
|
292
|
+
success=True,
|
|
293
|
+
qr_payload=session.qr_payload,
|
|
294
|
+
session_id=session.session_id,
|
|
295
|
+
expires_at=session.expires_at,
|
|
296
|
+
message="QR code generated, listening for devices...",
|
|
297
|
+
)
|
|
298
|
+
except Exception as e:
|
|
299
|
+
return QRPairGenerateResponse(
|
|
300
|
+
success=False,
|
|
301
|
+
message=f"Failed to generate QR pairing: {str(e)}",
|
|
302
|
+
error="generation_failed",
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _get_status_message(status: str) -> str:
|
|
307
|
+
"""Get user-friendly message for status code."""
|
|
308
|
+
messages = {
|
|
309
|
+
"listening": "等待手机扫描二维码...",
|
|
310
|
+
"pairing": "正在配对设备...",
|
|
311
|
+
"paired": "配对成功,正在连接...",
|
|
312
|
+
"connecting": "正在建立连接...",
|
|
313
|
+
"connected": "连接成功!",
|
|
314
|
+
"timeout": "超时:未检测到设备扫码",
|
|
315
|
+
"error": "配对失败",
|
|
316
|
+
}
|
|
317
|
+
return messages.get(status, "未知状态")
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
@router.get(
|
|
321
|
+
"/api/devices/qr_pair/status/{session_id}", response_model=QRPairStatusResponse
|
|
322
|
+
)
|
|
323
|
+
def get_qr_pairing_status(session_id: str) -> QRPairStatusResponse:
|
|
324
|
+
"""Get current status of a QR pairing session.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
session_id: Session UUID
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
Current session status and device information if connected
|
|
331
|
+
"""
|
|
332
|
+
session = qr_pairing_manager.get_session(session_id)
|
|
333
|
+
|
|
334
|
+
if not session:
|
|
335
|
+
return QRPairStatusResponse(
|
|
336
|
+
session_id=session_id,
|
|
337
|
+
status="error",
|
|
338
|
+
message="Session not found or expired",
|
|
339
|
+
error="session_not_found",
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
return QRPairStatusResponse(
|
|
343
|
+
session_id=session.session_id,
|
|
344
|
+
status=session.status,
|
|
345
|
+
device_id=session.device_id,
|
|
346
|
+
message=_get_status_message(session.status),
|
|
347
|
+
error=session.error_message,
|
|
168
348
|
)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
@router.delete("/api/devices/qr_pair/{session_id}", response_model=QRPairCancelResponse)
|
|
352
|
+
def cancel_qr_pairing(session_id: str) -> QRPairCancelResponse:
|
|
353
|
+
"""Cancel an active QR pairing session.
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
session_id: Session UUID to cancel
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Success status
|
|
360
|
+
"""
|
|
361
|
+
success = qr_pairing_manager.cancel_session(session_id)
|
|
362
|
+
|
|
363
|
+
if success:
|
|
364
|
+
return QRPairCancelResponse(
|
|
365
|
+
success=True,
|
|
366
|
+
message="Pairing session cancelled",
|
|
367
|
+
)
|
|
368
|
+
else:
|
|
369
|
+
return QRPairCancelResponse(
|
|
370
|
+
success=False,
|
|
371
|
+
message="Session not found or already completed",
|
|
372
|
+
)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Workflow API 路由."""
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, HTTPException
|
|
4
|
+
|
|
5
|
+
from AutoGLM_GUI.schemas import (
|
|
6
|
+
WorkflowCreate,
|
|
7
|
+
WorkflowListResponse,
|
|
8
|
+
WorkflowResponse,
|
|
9
|
+
WorkflowUpdate,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
router = APIRouter()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@router.get("/api/workflows", response_model=WorkflowListResponse)
|
|
16
|
+
def list_workflows() -> WorkflowListResponse:
|
|
17
|
+
"""获取所有 workflows."""
|
|
18
|
+
from AutoGLM_GUI.workflow_manager import workflow_manager
|
|
19
|
+
|
|
20
|
+
workflows = workflow_manager.list_workflows()
|
|
21
|
+
return WorkflowListResponse(workflows=workflows)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@router.get("/api/workflows/{workflow_uuid}", response_model=WorkflowResponse)
|
|
25
|
+
def get_workflow(workflow_uuid: str) -> WorkflowResponse:
|
|
26
|
+
"""获取单个 workflow."""
|
|
27
|
+
from AutoGLM_GUI.workflow_manager import workflow_manager
|
|
28
|
+
|
|
29
|
+
workflow = workflow_manager.get_workflow(workflow_uuid)
|
|
30
|
+
if not workflow:
|
|
31
|
+
raise HTTPException(status_code=404, detail="Workflow not found")
|
|
32
|
+
return WorkflowResponse(**workflow)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@router.post("/api/workflows", response_model=WorkflowResponse)
|
|
36
|
+
def create_workflow(request: WorkflowCreate) -> WorkflowResponse:
|
|
37
|
+
"""创建新 workflow."""
|
|
38
|
+
from AutoGLM_GUI.workflow_manager import workflow_manager
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
workflow = workflow_manager.create_workflow(
|
|
42
|
+
name=request.name, text=request.text
|
|
43
|
+
)
|
|
44
|
+
return WorkflowResponse(**workflow)
|
|
45
|
+
except Exception as e:
|
|
46
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@router.put("/api/workflows/{workflow_uuid}", response_model=WorkflowResponse)
|
|
50
|
+
def update_workflow(workflow_uuid: str, request: WorkflowUpdate) -> WorkflowResponse:
|
|
51
|
+
"""更新 workflow."""
|
|
52
|
+
from AutoGLM_GUI.workflow_manager import workflow_manager
|
|
53
|
+
|
|
54
|
+
workflow = workflow_manager.update_workflow(
|
|
55
|
+
uuid=workflow_uuid, name=request.name, text=request.text
|
|
56
|
+
)
|
|
57
|
+
if not workflow:
|
|
58
|
+
raise HTTPException(status_code=404, detail="Workflow not found")
|
|
59
|
+
return WorkflowResponse(**workflow)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@router.delete("/api/workflows/{workflow_uuid}")
|
|
63
|
+
def delete_workflow(workflow_uuid: str) -> dict:
|
|
64
|
+
"""删除 workflow."""
|
|
65
|
+
from AutoGLM_GUI.workflow_manager import workflow_manager
|
|
66
|
+
|
|
67
|
+
success = workflow_manager.delete_workflow(workflow_uuid)
|
|
68
|
+
if not success:
|
|
69
|
+
raise HTTPException(status_code=404, detail="Workflow not found")
|
|
70
|
+
return {"success": True, "message": "Workflow deleted"}
|