@agentunion/kite 1.4.0 → 1.5.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/CHANGELOG.md +102 -0
- package/cli.js +44 -5
- package/core/dependency_checker.py +250 -0
- package/core/env_checker.py +490 -0
- package/dependencies_lock.json +128 -0
- package/extensions/agents/assistant/server.py +33 -17
- package/extensions/channels/acp_channel/server.py +33 -17
- package/extensions/services/backup/entry.py +23 -16
- package/extensions/services/evol/auth_manager.py +443 -0
- package/extensions/services/evol/config.yaml +149 -0
- package/extensions/services/evol/config_loader.py +117 -0
- package/extensions/services/evol/entry.py +406 -0
- package/extensions/services/evol/evol_api.py +173 -0
- package/extensions/services/evol/evol_config.json5 +29 -0
- package/extensions/services/evol/migrate_tokens.py +122 -0
- package/extensions/services/evol/module.md +32 -0
- package/extensions/services/evol/pairing.py +250 -0
- package/extensions/services/evol/pairing_codes.jsonl +1 -0
- package/extensions/services/evol/relay.py +682 -0
- package/extensions/services/evol/relay_config.json5 +67 -0
- package/extensions/services/evol/routes/__init__.py +1 -0
- package/extensions/services/evol/routes/routes_management_ws.py +127 -0
- package/extensions/services/evol/routes/routes_rpc.py +89 -0
- package/extensions/services/evol/routes/routes_test.py +61 -0
- package/extensions/services/evol/server.py +875 -0
- package/extensions/services/evol/static/css/style.css +1200 -0
- package/extensions/services/evol/static/index.html +781 -0
- package/extensions/services/evol/static/index_evol.html +14 -0
- package/extensions/services/evol/static/js/app.js +6304 -0
- package/extensions/services/evol/static/js/auth.js +326 -0
- package/extensions/services/evol/static/js/dialog.js +285 -0
- package/extensions/services/evol/static/js/evol-app-fixed.js +50 -0
- package/extensions/services/evol/static/js/evol-app.js +1949 -0
- package/extensions/services/evol/static/js/evol-app.js.bak +1800 -0
- package/extensions/services/evol/static/js/kernel-client-example.js +228 -0
- package/extensions/services/evol/static/js/kernel-client.js +396 -0
- package/extensions/services/evol/static/js/main.js +141 -0
- package/extensions/services/evol/static/js/registry-tests.js +585 -0
- package/extensions/services/evol/static/js/stats.js +217 -0
- package/extensions/services/evol/static/js/token-manager.js +175 -0
- package/extensions/services/evol/static/pairing.html +248 -0
- package/extensions/services/evol/static/test_registry.html +262 -0
- package/extensions/services/evol/static/test_relay.html +462 -0
- package/extensions/services/evol/stats_manager.py +240 -0
- package/extensions/services/model_service/entry.py +23 -1
- package/extensions/services/proxy/.claude/settings.local.json +13 -0
- package/extensions/services/proxy/CHANGELOG_20260308.md +258 -0
- package/extensions/services/proxy/_fix_prints.py +133 -0
- package/extensions/services/proxy/_fix_prints2.py +87 -0
- package/extensions/services/proxy/agentcp/LICENCE +178 -0
- package/extensions/services/proxy/agentcp/README copy.md +85 -0
- package/extensions/services/proxy/agentcp/README.md +260 -0
- package/extensions/services/proxy/agentcp/__init__.py +16 -0
- package/extensions/services/proxy/agentcp/agent.py +4 -0
- package/extensions/services/proxy/agentcp/agentcp.py +2494 -0
- package/extensions/services/proxy/agentcp/agentprofile.json +89 -0
- package/extensions/services/proxy/agentcp/ap/__init__.py +16 -0
- package/extensions/services/proxy/agentcp/ap/ap_client.py +316 -0
- package/extensions/services/proxy/agentcp/assets/images/wechat_qr.png +0 -0
- package/extensions/services/proxy/agentcp/backup/metrics.json +31 -0
- package/extensions/services/proxy/agentcp/base/__init__.py +20 -0
- package/extensions/services/proxy/agentcp/base/auth_client.py +257 -0
- package/extensions/services/proxy/agentcp/base/client.py +112 -0
- package/extensions/services/proxy/agentcp/base/env.py +34 -0
- package/extensions/services/proxy/agentcp/base/html_util.py +336 -0
- package/extensions/services/proxy/agentcp/base/log.py +98 -0
- package/extensions/services/proxy/agentcp/ca/__init__.py +17 -0
- package/extensions/services/proxy/agentcp/ca/ca_client.py +414 -0
- package/extensions/services/proxy/agentcp/ca/ca_root.py +74 -0
- package/extensions/services/proxy/agentcp/context/__init__.py +20 -0
- package/extensions/services/proxy/agentcp/context/context.py +73 -0
- package/extensions/services/proxy/agentcp/context/exceptions.py +114 -0
- package/extensions/services/proxy/agentcp/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/create_profile_weather.py +125 -0
- package/extensions/services/proxy/agentcp/db/__init__.py +15 -0
- package/extensions/services/proxy/agentcp/db/db_mananger.py +550 -0
- package/extensions/services/proxy/agentcp/docs/UDP_HEARTBEAT_FIX_REPORT.md +265 -0
- package/extensions/services/proxy/agentcp/docs/heartbeat_issue_analysis.md +291 -0
- package/extensions/services/proxy/agentcp/file/__init__.py +16 -0
- package/extensions/services/proxy/agentcp/file/file_client.py +141 -0
- package/extensions/services/proxy/agentcp/file/wss_binary_message.py +137 -0
- package/extensions/services/proxy/agentcp/hcp.py +299 -0
- package/extensions/services/proxy/agentcp/heartbeat/__init__.py +16 -0
- package/extensions/services/proxy/agentcp/heartbeat/heartbeat_client.py +360 -0
- package/extensions/services/proxy/agentcp/improved_scheduler.py +498 -0
- package/extensions/services/proxy/agentcp/llm_agent_utils.py +249 -0
- package/extensions/services/proxy/agentcp/llm_server.py +172 -0
- package/extensions/services/proxy/agentcp/mermaid.py +210 -0
- package/extensions/services/proxy/agentcp/message.py +149 -0
- package/extensions/services/proxy/agentcp/metrics.py +256 -0
- package/extensions/services/proxy/agentcp/monitoring/__init__.py +20 -0
- package/extensions/services/proxy/agentcp/monitoring/global_monitor.py +27 -0
- package/extensions/services/proxy/agentcp/monitoring/metrics_store.py +325 -0
- package/extensions/services/proxy/agentcp/monitoring/monitoring_service.py +269 -0
- package/extensions/services/proxy/agentcp/monitoring/sliding_window.py +222 -0
- package/extensions/services/proxy/agentcp/monitoring/standalone_reader.py +224 -0
- package/extensions/services/proxy/agentcp/msg/__init__.py +21 -0
- package/extensions/services/proxy/agentcp/msg/connection_manager.py +456 -0
- package/extensions/services/proxy/agentcp/msg/message_client.py +2058 -0
- package/extensions/services/proxy/agentcp/msg/message_serialize.py +263 -0
- package/extensions/services/proxy/agentcp/msg/open_ai_message.py +88 -0
- package/extensions/services/proxy/agentcp/msg/session_manager.py +1062 -0
- package/extensions/services/proxy/agentcp/msg/stream_client.py +267 -0
- package/extensions/services/proxy/agentcp/msg/websocket_file_receiver.py +89 -0
- package/extensions/services/proxy/agentcp/msg/ws_logger.py +685 -0
- package/extensions/services/proxy/agentcp/msg/wss_binary_message.py +137 -0
- package/extensions/services/proxy/agentcp/requirements.txt +7 -0
- package/extensions/services/proxy/agentcp/samples/agent_graph/README.md +37 -0
- package/extensions/services/proxy/agentcp/samples/agent_graph/agentprofile.json +89 -0
- package/extensions/services/proxy/agentcp/samples/agent_graph/create_profile.py +138 -0
- package/extensions/services/proxy/agentcp/samples/agent_graph/main.py +164 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/create_profile.py +123 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/llm/create_profile.py +129 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/llm/env.json +5 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/llm/main.py +146 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/main.py +123 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/readme.md +379 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/search/create_profile.py +129 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/search/main.py +28 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/tool/create_profile.py +129 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/tool/main.py +20 -0
- package/extensions/services/proxy/agentcp/samples/ali_amap/README.md +97 -0
- package/extensions/services/proxy/agentcp/samples/ali_amap/amap_agent.py +88 -0
- package/extensions/services/proxy/agentcp/samples/ali_amap/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/compute_agent/agent/powershell.py +228 -0
- package/extensions/services/proxy/agentcp/samples/compute_agent/agent/software.py +63 -0
- package/extensions/services/proxy/agentcp/samples/compute_agent/agent/tools.py +36 -0
- package/extensions/services/proxy/agentcp/samples/compute_agent/browser_user.py +41 -0
- package/extensions/services/proxy/agentcp/samples/deepseek/README.md +79 -0
- package/extensions/services/proxy/agentcp/samples/deepseek/create_profile.py +126 -0
- package/extensions/services/proxy/agentcp/samples/deepseek/deepseek.py +42 -0
- package/extensions/services/proxy/agentcp/samples/dify_chat/README.md +78 -0
- package/extensions/services/proxy/agentcp/samples/dify_chat/create_profile.py +126 -0
- package/extensions/services/proxy/agentcp/samples/dify_chat/dify_chat.py +47 -0
- package/extensions/services/proxy/agentcp/samples/dify_workflow/README.md +78 -0
- package/extensions/services/proxy/agentcp/samples/dify_workflow/create_profile.py +126 -0
- package/extensions/services/proxy/agentcp/samples/dify_workflow/dify_workflow.py +46 -0
- package/extensions/services/proxy/agentcp/samples/executor/README.md +44 -0
- package/extensions/services/proxy/agentcp/samples/executor/agentprofile.json +89 -0
- package/extensions/services/proxy/agentcp/samples/executor/create_profile.py +139 -0
- package/extensions/services/proxy/agentcp/samples/executor/main.py +160 -0
- package/extensions/services/proxy/agentcp/samples/filereader/README.md +45 -0
- package/extensions/services/proxy/agentcp/samples/filereader/agentprofile.json +90 -0
- package/extensions/services/proxy/agentcp/samples/filereader/create_profile.py +137 -0
- package/extensions/services/proxy/agentcp/samples/filereader/main.py +253 -0
- package/extensions/services/proxy/agentcp/samples/filewriter/README.md +38 -0
- package/extensions/services/proxy/agentcp/samples/filewriter/agentprofile.json +91 -0
- package/extensions/services/proxy/agentcp/samples/filewriter/create_profile.py +138 -0
- package/extensions/services/proxy/agentcp/samples/filewriter/main.py +289 -0
- package/extensions/services/proxy/agentcp/samples/hcp/README.md +85 -0
- package/extensions/services/proxy/agentcp/samples/hcp/acp_weather_agent.zip +0 -0
- package/extensions/services/proxy/agentcp/samples/hcp/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/hcp/hcp.py +237 -0
- package/extensions/services/proxy/agentcp/samples/helloworld/README.md +68 -0
- package/extensions/services/proxy/agentcp/samples/helloworld/hello_world.py +40 -0
- package/extensions/services/proxy/agentcp/samples/llm_agent/MEADME.md +117 -0
- package/extensions/services/proxy/agentcp/samples/llm_agent/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/llm_agent/qwen_agent.py +136 -0
- package/extensions/services/proxy/agentcp/samples/local_llm_agent/README.md +90 -0
- package/extensions/services/proxy/agentcp/samples/local_llm_agent/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/local_llm_agent/main.py +49 -0
- package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/README.md +55 -0
- package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/main.py +23 -0
- package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/README.md +103 -0
- package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/main.py +69 -0
- package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/README.md +58 -0
- package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/main.py +25 -0
- package/extensions/services/proxy/agentcp/samples/qwen3/README.md +71 -0
- package/extensions/services/proxy/agentcp/samples/qwen3/create_profile.py +126 -0
- package/extensions/services/proxy/agentcp/samples/qwen3/qwen3.py +37 -0
- package/extensions/services/proxy/agentcp/samples/qwen3_tools/README.md +133 -0
- package/extensions/services/proxy/agentcp/samples/qwen3_tools/create_profile.py +126 -0
- package/extensions/services/proxy/agentcp/samples/qwen3_tools/qwen3_tools.py +98 -0
- package/extensions/services/proxy/agentcp/samples/search/create_profile_qwen.py +125 -0
- package/extensions/services/proxy/agentcp/samples/search/create_profile_search.py +125 -0
- package/extensions/services/proxy/agentcp/samples/search/qwen_agent.py +136 -0
- package/extensions/services/proxy/agentcp/samples/search/search_agent.py +170 -0
- package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/README.md +89 -0
- package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/main.py +44 -0
- package/extensions/services/proxy/agentcp/utils/__init__.py +15 -0
- package/extensions/services/proxy/agentcp/utils/file_util.py +117 -0
- package/extensions/services/proxy/agentcp/utils/proxy_bypass.py +99 -0
- package/extensions/services/proxy/agentcp/workflow.py +203 -0
- package/extensions/services/proxy/console_auth.py +109 -0
- package/extensions/services/proxy/evol/__init__.py +1 -0
- package/extensions/services/proxy/evol/config.py +37 -0
- package/extensions/services/proxy/evol/http/__init__.py +1 -0
- package/extensions/services/proxy/evol/http/async_http.py +551 -0
- package/extensions/services/proxy/evol/log.py +28 -0
- package/extensions/services/proxy/evol/presenter/__init__.py +2 -0
- package/extensions/services/proxy/evol/presenter/agentIdPresenter.py +1031 -0
- package/extensions/services/proxy/evol/presenter/apikeyPresenter.py +106 -0
- package/extensions/services/proxy/evol/presenter/configPresenter.py +1281 -0
- package/extensions/services/proxy/evol/presenter/userPresenter.py +477 -0
- package/extensions/services/proxy/evol/server/__init__.py +1 -0
- package/extensions/services/proxy/evol/server/claude_proxy_async.py +3430 -0
- package/extensions/services/proxy/evol/server/openclaw_proxy.py +1861 -0
- package/extensions/services/proxy/evol/server/proxy_config.py +15 -0
- package/extensions/services/proxy/evol/server/proxy_engine.py +501 -0
- package/extensions/services/proxy/evol/version.py +24 -0
- package/extensions/services/proxy/logs/websocket.log +260 -0
- package/extensions/services/proxy/main.py +240 -0
- package/extensions/services/proxy/requirements.txt +13 -0
- package/extensions/services/proxy/server.py +271 -0
- package/extensions/services/watchdog/entry.py +42 -16
- package/extensions/services/watchdog/module.md +1 -0
- package/extensions/services/watchdog/monitor.py +34 -4
- package/extensions/services/web/module.md +1 -1
- package/extensions/services/web/server.py +30 -18
- package/extensions/services/web/static/js/token-manager.js +10 -10
- package/kernel/entry.py +1 -1
- package/kernel/module.md +25 -1
- package/kernel/registry_store.py +2 -26
- package/kernel/rpc_router.py +36 -10
- package/kernel/server.py +106 -17
- package/kite_cli/commands/deps_install.py +67 -0
- package/kite_cli/commands/env_check.py +45 -0
- package/kite_cli/commands/prepare.py +49 -0
- package/kite_cli/commands/venv_setup.py +56 -0
- package/kite_cli/main.py +29 -1
- package/launcher/entry.py +306 -21
- package/launcher/module.md +9 -0
- package/launcher/module_scanner.py +11 -1
- package/main.py +4 -1
- package/package.json +8 -1
- package/python_version.json +4 -0
- package/requirements.txt +38 -0
- package/scripts/env-manager.js +328 -0
- package/scripts/python-env.js +79 -0
- package/scripts/scan_dependencies.py +461 -0
- package/scripts/setup-python-env.js +191 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright 2025 AgentUnion Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
import json
|
|
16
|
+
import asyncio
|
|
17
|
+
import queue
|
|
18
|
+
import ssl
|
|
19
|
+
import threading
|
|
20
|
+
import time
|
|
21
|
+
import urllib.parse
|
|
22
|
+
from typing import Optional
|
|
23
|
+
|
|
24
|
+
import websocket
|
|
25
|
+
|
|
26
|
+
from agentcp.base.log import log_debug, log_error, log_exception, log_info
|
|
27
|
+
from agentcp.msg.wss_binary_message import *
|
|
28
|
+
from agentcp.utils.proxy_bypass import ensure_no_proxy_for_local_env, without_proxy_env
|
|
29
|
+
|
|
30
|
+
from ..context import ErrorContext, exceptions
|
|
31
|
+
|
|
32
|
+
ensure_no_proxy_for_local_env()
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class FileChunk:
|
|
36
|
+
offset: int
|
|
37
|
+
chunk_size: int
|
|
38
|
+
chunk: bytes
|
|
39
|
+
|
|
40
|
+
class StreamClient:
|
|
41
|
+
def __init__(self, agent_id: str, session_id, server_url: str, signature: str):
|
|
42
|
+
"""消息客户端类
|
|
43
|
+
Args:
|
|
44
|
+
agent_id: 代理ID
|
|
45
|
+
server_url: 服务器URL
|
|
46
|
+
"""
|
|
47
|
+
self.session_id = session_id
|
|
48
|
+
self.agent_id = agent_id
|
|
49
|
+
self.server_url = server_url
|
|
50
|
+
self.signature = signature
|
|
51
|
+
self.connected_event = threading.Event()
|
|
52
|
+
self.ws = None
|
|
53
|
+
self.ws_url = ""
|
|
54
|
+
self.ws_thread: Optional[threading.Thread] = None
|
|
55
|
+
self.ws_is_running = False
|
|
56
|
+
self.ws_chunks = ""
|
|
57
|
+
self.file_stream_chunk_queue = queue.Queue()
|
|
58
|
+
self.file_stream_push_cache_left_space = 65536
|
|
59
|
+
|
|
60
|
+
def set_message_handler(self, message_handler):
|
|
61
|
+
"""设置消息处理器"""
|
|
62
|
+
self.message_handler = message_handler
|
|
63
|
+
|
|
64
|
+
async def start_websocket_client(self):
|
|
65
|
+
if self.connected_event.is_set():
|
|
66
|
+
return True
|
|
67
|
+
|
|
68
|
+
# 确保URL格式正确
|
|
69
|
+
ws_url = self.server_url.rstrip("/") # 移除末尾斜杠
|
|
70
|
+
ws_url = ws_url.replace("https://", "wss://").replace("http://", "ws://")
|
|
71
|
+
ws_url = ws_url + f"&agent_id={self.agent_id}"
|
|
72
|
+
|
|
73
|
+
log_debug(f"message Connecting to WebSocket URL: {ws_url}") # 调试日志
|
|
74
|
+
self.ws_url = ws_url
|
|
75
|
+
self.ws_thread = threading.Thread(target=self.__ws_handler, daemon=True)
|
|
76
|
+
self.ws_thread.start()
|
|
77
|
+
wait = 0
|
|
78
|
+
while not self.connected_event.is_set() and wait < 5:
|
|
79
|
+
log_debug("WebSocket client is reconnect...")
|
|
80
|
+
await asyncio.sleep(0.01) # 使用 asyncio.sleep 避免阻塞事件循环
|
|
81
|
+
wait += 0.01
|
|
82
|
+
return self.connected_event.is_set()
|
|
83
|
+
|
|
84
|
+
def send_wss_message(self, msg):
|
|
85
|
+
"""发送WebSocket消息,如果连接断开则尝试重连"""
|
|
86
|
+
# 检查连接状态,如果断开则尝试重连
|
|
87
|
+
if not self.ws or not self.ws.sock or not self.ws.sock.connected:
|
|
88
|
+
try:
|
|
89
|
+
# 使用事件循环在另一个线程中运行异步重连
|
|
90
|
+
loop = asyncio.new_event_loop()
|
|
91
|
+
def reconnect_sync():
|
|
92
|
+
asyncio.set_event_loop(loop)
|
|
93
|
+
try:
|
|
94
|
+
loop.run_until_complete(self.start_websocket_client())
|
|
95
|
+
finally:
|
|
96
|
+
loop.close()
|
|
97
|
+
|
|
98
|
+
# 在新线程中执行重连
|
|
99
|
+
reconnect_thread = threading.Thread(target=reconnect_sync, daemon=True)
|
|
100
|
+
reconnect_thread.start()
|
|
101
|
+
reconnect_thread.join(timeout=5.0)
|
|
102
|
+
|
|
103
|
+
if not self.connected_event.is_set():
|
|
104
|
+
log_error("[StreamClient] WebSocket重连超时")
|
|
105
|
+
return False
|
|
106
|
+
except Exception as e:
|
|
107
|
+
log_error(f"[StreamClient] WebSocket重连失败: {e}")
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
# 发送消息
|
|
111
|
+
self.__send_wss_message_p(msg)
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
def __send_wss_message_p(self, msg):
|
|
115
|
+
if self.ws.sock and self.ws.sock.connected:
|
|
116
|
+
bytes_msg = encode_wss_binary_message(msg)
|
|
117
|
+
self.ws.send(bytes_msg, websocket.ABNF.OPCODE_BINARY)
|
|
118
|
+
|
|
119
|
+
def stop_websocket_client(self):
|
|
120
|
+
self.connected_event.clear() # 清除连接事件
|
|
121
|
+
if self.ws:
|
|
122
|
+
self.ws.close()
|
|
123
|
+
self.ws_thread.join()
|
|
124
|
+
self.ws = None
|
|
125
|
+
self.ws_thread = None
|
|
126
|
+
|
|
127
|
+
def send_msg(self, msg):
|
|
128
|
+
try:
|
|
129
|
+
self.ws.send(msg)
|
|
130
|
+
return True
|
|
131
|
+
except Exception as e:
|
|
132
|
+
log_exception(f"send message: {msg}")
|
|
133
|
+
trace_id = msg.get("trace_id", "") if isinstance(msg, dict) else ""
|
|
134
|
+
ErrorContext.publish(exceptions.SendMsgError(f"发送消息时发生错误: {e}", trace_id=trace_id))
|
|
135
|
+
|
|
136
|
+
def send_chunk_to_stream(self, chunk, type="text/event-stream"):
|
|
137
|
+
|
|
138
|
+
if self.ws is None or self.ws.sock is None or not self.ws.sock.connected:
|
|
139
|
+
log_error("WebSocket connection is not established @send_chunk_to_stream.")
|
|
140
|
+
self.ws_chunks += chunk
|
|
141
|
+
return False
|
|
142
|
+
try:
|
|
143
|
+
if type=="text/event-stream":
|
|
144
|
+
chunk_quote = urllib.parse.quote_plus(chunk)
|
|
145
|
+
data = {
|
|
146
|
+
"cmd": "push_text_stream_req",
|
|
147
|
+
"data": {
|
|
148
|
+
"chunk": f"{chunk_quote}",
|
|
149
|
+
},
|
|
150
|
+
}
|
|
151
|
+
msg = json.dumps(data)
|
|
152
|
+
self.send_msg(msg) # 发送消息到 WebSocket 服务器
|
|
153
|
+
return True
|
|
154
|
+
return False
|
|
155
|
+
except Exception as e:
|
|
156
|
+
import traceback
|
|
157
|
+
log_error(f"发送消息时发生错误: {str(e)}\n{traceback.format_exc()}")
|
|
158
|
+
ErrorContext.publish(exceptions.SendChunkToStreamError(f"发送消息时发生错误: {e}"))
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
def send_chunk_to_file_stream(self, offset: int, chunk: bytes) -> bool:
|
|
162
|
+
try:
|
|
163
|
+
if self.ws is None or self.ws.sock is None or not self.ws.sock.connected:
|
|
164
|
+
file_chunk = FileChunk()
|
|
165
|
+
file_chunk.offset = offset
|
|
166
|
+
file_chunk.size = len(chunk)
|
|
167
|
+
file_chunk.chunk = chunk
|
|
168
|
+
self.file_stream_chunk_queue.put(file_chunk)
|
|
169
|
+
time.sleep(1)
|
|
170
|
+
return False
|
|
171
|
+
msg = WssBinaryMessage()
|
|
172
|
+
msg.magic_byte1 = ord('M')
|
|
173
|
+
msg.magic_byte2 = ord('U')
|
|
174
|
+
msg.version = 0x101
|
|
175
|
+
msg.flags = 0
|
|
176
|
+
msg.msg_type = 0x5
|
|
177
|
+
msg.msg_seq = 0
|
|
178
|
+
msg.content_type = 0x5
|
|
179
|
+
msg.compressed = 0
|
|
180
|
+
msg.reserved = offset
|
|
181
|
+
if self.ws and self.ws.sock and self.ws.sock.connected: # 检查WebSocket连接状态是否正常
|
|
182
|
+
bytes_msg = encode_wss_binary_buffer(chunk, msg)
|
|
183
|
+
self.ws.send(bytes_msg, websocket.ABNF.OPCODE_BINARY)
|
|
184
|
+
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送文件数据: {offset} + {len(chunk)}")
|
|
185
|
+
self.file_stream_push_cache_left_space -= len(chunk)
|
|
186
|
+
return self.file_stream_push_cache_left_space >= 16384
|
|
187
|
+
except Exception as e:
|
|
188
|
+
print(f"发送文件数据时发生错误: {str(e)}")
|
|
189
|
+
return False
|
|
190
|
+
|
|
191
|
+
def __ws_handler(self):
|
|
192
|
+
"""
|
|
193
|
+
WebSocket客户端定时发送消息函数
|
|
194
|
+
:param url: WebSocket服务器URL(ws://或wss://开头)
|
|
195
|
+
:param message: 要定时发送的消息内容
|
|
196
|
+
:param interval: 发送间隔时间(秒),默认5秒
|
|
197
|
+
"""
|
|
198
|
+
try:
|
|
199
|
+
|
|
200
|
+
def on_message(ws, message):
|
|
201
|
+
"""接收到服务器消息时的处理函数"""
|
|
202
|
+
if isinstance(message, bytes):
|
|
203
|
+
txt_msg = decode_wss_binary_message(message)
|
|
204
|
+
if txt_msg is not None and len(txt_msg) > 0:
|
|
205
|
+
log_info(f"Text_Stream收到消息收到二进制消息: {txt_msg}")
|
|
206
|
+
on_message(ws, txt_msg)
|
|
207
|
+
elif isinstance(message, str):
|
|
208
|
+
log_info(f"Text_Stream收到消息: {self.agent_id}\t{message}")
|
|
209
|
+
|
|
210
|
+
def on_error(ws, error):
|
|
211
|
+
"""连接发生错误时的处理函数"""
|
|
212
|
+
log_error(f"Text_Stream连接错误: {error}")
|
|
213
|
+
self.connected_event.clear() # 清除连接事件,允许重连
|
|
214
|
+
ErrorContext.publish(exceptions.SDKError(f"websocket on_error: {error}"))
|
|
215
|
+
|
|
216
|
+
def on_close(ws, close_status_code, close_msg):
|
|
217
|
+
"""连接关闭时的处理函数"""
|
|
218
|
+
log_info("Text_Stream WebSocket 连接已关闭")
|
|
219
|
+
self.connected_event.clear() # 清除连接事件,允许重连
|
|
220
|
+
|
|
221
|
+
def on_open(ws):
|
|
222
|
+
"""连接建立后的处理函数,用于发送初始消息"""
|
|
223
|
+
log_info("Text_Stream WebSocket 连接已建立")
|
|
224
|
+
self.connected_event.set() # 设置连接事件
|
|
225
|
+
# if self.ws_chunks is not None and len(self.ws_chunks) > 0:
|
|
226
|
+
# self.send_chunk_to_stream(self.ws_chunks)
|
|
227
|
+
# self.ws_chunks = ""
|
|
228
|
+
|
|
229
|
+
# 创建 WebSocket 客户端实例
|
|
230
|
+
self.ws = websocket.WebSocketApp(
|
|
231
|
+
self.ws_url,
|
|
232
|
+
on_open=on_open,
|
|
233
|
+
on_message=on_message,
|
|
234
|
+
on_error=on_error,
|
|
235
|
+
on_close=on_close,
|
|
236
|
+
)
|
|
237
|
+
# 启动WebSocket连接(阻塞当前线程)
|
|
238
|
+
with without_proxy_env(enabled=True):
|
|
239
|
+
self.ws.run_forever(
|
|
240
|
+
sslopt={
|
|
241
|
+
"cert_reqs": ssl.CERT_NONE, # 禁用证书验证
|
|
242
|
+
"check_hostname": False, # 忽略主机名不匹配
|
|
243
|
+
},
|
|
244
|
+
)
|
|
245
|
+
except Exception as e:
|
|
246
|
+
log_exception(f"WebSocket连接失败: {e}")
|
|
247
|
+
ErrorContext.publish(exceptions.SDKError(f"WebSocket连接失败: {e}"))
|
|
248
|
+
|
|
249
|
+
def close_stream(self, stream_url: str):
|
|
250
|
+
if self.ws and self.ws.sock and self.ws.sock.connected: # 检查WebSocket连接状态是否正常
|
|
251
|
+
data = {
|
|
252
|
+
"cmd": "close_stream_req",
|
|
253
|
+
}
|
|
254
|
+
msg = json.dumps(data)
|
|
255
|
+
self.ws.send(msg)
|
|
256
|
+
# self.__send_wss_message(self.ws, msg) # 发送消息到 WebSocket 服务器
|
|
257
|
+
log_info(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送消息: {msg}")
|
|
258
|
+
|
|
259
|
+
if self.ws_is_running:
|
|
260
|
+
self.ws_is_running = False
|
|
261
|
+
self.ws.close()
|
|
262
|
+
self.ws = None
|
|
263
|
+
|
|
264
|
+
def __send_wss_message(self, msg):
|
|
265
|
+
if self.ws.sock and self.ws.sock.connected:
|
|
266
|
+
bytes_msg = encode_wss_binary_message(msg)
|
|
267
|
+
self.ws.send(bytes_msg, websocket.ABNF.OPCODE_BINARY)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright 2025 AgentUnion Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
import websocket
|
|
16
|
+
import os
|
|
17
|
+
import ssl
|
|
18
|
+
from agentcp.msg.wss_binary_message import decode_wss_binary_buffer
|
|
19
|
+
from agentcp.utils.proxy_bypass import ensure_no_proxy_for_local_env, without_proxy_env
|
|
20
|
+
|
|
21
|
+
ensure_no_proxy_for_local_env()
|
|
22
|
+
def download_file_via_websocket(wss_url, save_path):
|
|
23
|
+
"""
|
|
24
|
+
通过WebSocket下载文件并保存到指定路径
|
|
25
|
+
:param wss_url: WebSocket连接地址
|
|
26
|
+
:param file_extension: 文件扩展名
|
|
27
|
+
:param save_path: 文件保存路径
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
os.makedirs(os.path.dirname(save_path), exist_ok=True)
|
|
31
|
+
file_stream_pulled_file = open(save_path, "wb")
|
|
32
|
+
print(f"文件保存路径:{save_path}")
|
|
33
|
+
def on_message(ws, message):
|
|
34
|
+
try:
|
|
35
|
+
if isinstance(message, bytes):
|
|
36
|
+
bin_msg = decode_wss_binary_buffer(message)
|
|
37
|
+
if bin_msg is None:
|
|
38
|
+
return
|
|
39
|
+
if bin_msg.msg_type == 0x5 and bin_msg.content_type == 0x5:
|
|
40
|
+
print(f"收到文件流:offset={bin_msg.reserved}, size={bin_msg.payload_length}, crc32={bin_msg.crc32}")
|
|
41
|
+
if file_stream_pulled_file is not None:
|
|
42
|
+
file_stream_pulled_file.write(bin_msg.payload)
|
|
43
|
+
else:
|
|
44
|
+
txt_msg = bin_msg.payload.decode()
|
|
45
|
+
if txt_msg is not None and len(txt_msg) > 0:
|
|
46
|
+
print(f"File_Stream pull handler 收到消息收到二进制消息: {txt_msg}")
|
|
47
|
+
on_message(ws, txt_msg)
|
|
48
|
+
elif isinstance(message, str):
|
|
49
|
+
import json
|
|
50
|
+
js = json.loads(message)
|
|
51
|
+
if "cmd" not in js or "data" not in js:
|
|
52
|
+
return
|
|
53
|
+
cmd = js["cmd"]
|
|
54
|
+
data = js["data"]
|
|
55
|
+
if cmd == "close_file_stream_req":
|
|
56
|
+
if file_stream_pulled_file is not None:
|
|
57
|
+
file_stream_pulled_file.close()
|
|
58
|
+
file_stream_pulled_file = None
|
|
59
|
+
ws.close()
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
print(f"file_stream_pull_handler在处理收到的消息时发生异常 {str(e)}")
|
|
63
|
+
|
|
64
|
+
def on_error(ws, error):
|
|
65
|
+
print(f"发生错误: {error}")
|
|
66
|
+
ws.close() # 发生错误时主动关闭连接
|
|
67
|
+
|
|
68
|
+
def on_close(ws, close_status_code, close_msg):
|
|
69
|
+
print("连接已关闭")
|
|
70
|
+
ws = None # 确保引用被清除
|
|
71
|
+
|
|
72
|
+
def on_open(ws):
|
|
73
|
+
print("连接已建立,等待文件数据...")
|
|
74
|
+
|
|
75
|
+
# 创建WebSocket连接,禁用SSL验证
|
|
76
|
+
ws = websocket.WebSocketApp(wss_url,
|
|
77
|
+
on_open=on_open,
|
|
78
|
+
on_message=on_message,
|
|
79
|
+
on_error=on_error,
|
|
80
|
+
on_close=on_close)
|
|
81
|
+
|
|
82
|
+
# 运行时不验证SSL证书
|
|
83
|
+
with without_proxy_env(enabled=True):
|
|
84
|
+
ws.run_forever(
|
|
85
|
+
sslopt={
|
|
86
|
+
"cert_reqs": ssl.CERT_NONE, # 禁用证书验证
|
|
87
|
+
"check_hostname": False, # 忽略主机名不匹配
|
|
88
|
+
},
|
|
89
|
+
)
|