Leon4gr45 commited on
Commit
82d8767
·
verified ·
1 Parent(s): f0b07c1

Deploy Agent-Zero with HF adaptations (main branch) and security fixes

Browse files
Files changed (3) hide show
  1. helpers/api.py +193 -29
  2. helpers/ui_server.py +302 -0
  3. start_hf.sh +8 -14
helpers/api.py CHANGED
@@ -2,62 +2,226 @@ from abc import abstractmethod
2
  import json
3
  import threading
4
  import os
5
- from typing import Union, TypedDict, Dict, Any
6
- from flask import Request, Response, Flask, request
7
- from python.helpers.print_style import PrintStyle
8
- from python.helpers.errors import format_error
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  Input = dict
11
- Output = Union[Dict[str, Any], Response, TypedDict]
 
12
 
13
  class ApiHandler:
14
- def __init__(self, app: Flask, thread_lock: threading.Lock):
15
  self.app = app
16
  self.thread_lock = thread_lock
17
 
18
  @classmethod
19
- def requires_loopback(cls) -> bool: return False
 
 
20
  @classmethod
21
- def requires_api_key(cls) -> bool: return False
 
 
22
  @classmethod
23
  def requires_auth(cls) -> bool:
24
- return False if os.getenv("HF_SPACE") == "true" else True
 
25
  @classmethod
26
- def get_methods(cls) -> list[str]: return ["POST", "GET"]
 
 
27
  @classmethod
28
  def requires_csrf(cls) -> bool:
29
- return False if os.getenv("HF_SPACE") == "true" else cls.requires_auth()
30
 
31
  @abstractmethod
32
- async def process(self, input: Input, request: Request) -> Output: pass
 
33
 
34
  async def handle_request(self, request: Request) -> Response:
35
  try:
36
  input_data: Input = {}
37
  if request.is_json:
38
- if request.data: input_data = request.get_json()
 
 
 
 
 
39
  else:
40
- input_data = request.args.to_dict()
41
- if request.method == "POST":
42
- input_data.update({"data": request.get_data(as_text=True)})
43
 
44
  output = await self.process(input_data, request)
45
 
46
- if isinstance(output, Response): return output
47
- return Response(response=json.dumps(output), status=200, mimetype="application/json")
 
 
 
 
 
48
  except Exception as e:
49
  error = format_error(e)
50
  PrintStyle.error(f"API error: {error}")
51
  return Response(response=error, status=500, mimetype="text/plain")
52
 
53
- def get_context(self, ctxid: str):
54
- from agent import AgentContext
55
- from initialize import initialize_agent
56
- with self.thread_lock:
57
- if not ctxid:
58
- first = AgentContext.first()
59
- if first: return first
60
- return AgentContext(config=initialize_agent())
61
- got = AgentContext.get(ctxid)
62
- if got: return got
63
- return AgentContext(config=initialize_agent(), id=ctxid)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import json
3
  import threading
4
  import os
5
+ from functools import wraps
6
+ from pathlib import Path
7
+ from typing import Union, Dict, Any
8
+ from flask import (
9
+ Request,
10
+ Response,
11
+ jsonify,
12
+ Flask,
13
+ session,
14
+ request,
15
+ send_file,
16
+ redirect,
17
+ url_for,
18
+ )
19
+ from werkzeug.wrappers.response import Response as BaseResponse
20
+ from agent import AgentContext
21
+ from helpers.print_style import PrintStyle
22
+ from helpers.errors import format_error
23
+ from helpers import files, cache
24
+
25
+ ThreadLockType = Union[threading.Lock, threading.RLock]
26
+
27
+ CACHE_AREA = "api_handlers(api)"
28
 
29
  Input = dict
30
+ Output = Union[Dict[str, Any], Response]
31
+
32
 
33
  class ApiHandler:
34
+ def __init__(self, app: Flask, thread_lock: ThreadLockType):
35
  self.app = app
36
  self.thread_lock = thread_lock
37
 
38
  @classmethod
39
+ def requires_loopback(cls) -> bool:
40
+ return False
41
+
42
  @classmethod
43
+ def requires_api_key(cls) -> bool:
44
+ return False
45
+
46
  @classmethod
47
  def requires_auth(cls) -> bool:
48
+ return True
49
+
50
  @classmethod
51
+ def get_methods(cls) -> list[str]:
52
+ return ["POST"]
53
+
54
  @classmethod
55
  def requires_csrf(cls) -> bool:
56
+ return cls.requires_auth()
57
 
58
  @abstractmethod
59
+ async def process(self, input: Input, request: Request) -> Output:
60
+ pass
61
 
62
  async def handle_request(self, request: Request) -> Response:
63
  try:
64
  input_data: Input = {}
65
  if request.is_json:
66
+ try:
67
+ if request.data:
68
+ input_data = request.get_json()
69
+ except Exception as e:
70
+ PrintStyle().print(f"Error parsing JSON: {str(e)}")
71
+ input_data = {}
72
  else:
73
+ input_data = {}
 
 
74
 
75
  output = await self.process(input_data, request)
76
 
77
+ if isinstance(output, Response):
78
+ return output
79
+ else:
80
+ response_json = json.dumps(output)
81
+ return Response(
82
+ response=response_json, status=200, mimetype="application/json"
83
+ )
84
  except Exception as e:
85
  error = format_error(e)
86
  PrintStyle.error(f"API error: {error}")
87
  return Response(response=error, status=500, mimetype="text/plain")
88
 
89
+ def use_context(self, ctxid: str, create_if_not_exists: bool = True):
90
+ from helpers.context_utils import use_context as _use_context
91
+ return _use_context(self.thread_lock, ctxid, create_if_not_exists)
92
+
93
+
94
+ from helpers.network import is_loopback_address
95
+
96
+
97
+ def requires_api_key(f):
98
+ @wraps(f)
99
+ async def decorated(*args, **kwargs):
100
+ if os.getenv("HF_SPACE") == "true":
101
+ return await f(*args, **kwargs)
102
+
103
+ from helpers.settings import get_settings
104
+ valid_api_key = get_settings()["mcp_server_token"]
105
+
106
+ if api_key := request.headers.get("X-API-KEY"):
107
+ if api_key != valid_api_key:
108
+ return Response("Invalid API key", 401)
109
+ elif request.json and request.json.get("api_key"):
110
+ api_key = request.json.get("api_key")
111
+ if api_key != valid_api_key:
112
+ return Response("Invalid API key", 401)
113
+ else:
114
+ return Response("API key required", 401)
115
+ return await f(*args, **kwargs)
116
+
117
+ return decorated
118
+
119
+
120
+ def requires_loopback(f):
121
+ @wraps(f)
122
+ async def decorated(*args, **kwargs):
123
+ if os.getenv("HF_SPACE") == "true":
124
+ return await f(*args, **kwargs)
125
+
126
+ if not is_loopback_address(str(request.remote_addr)):
127
+ return Response("Access denied.", 403, {})
128
+ return await f(*args, **kwargs)
129
+
130
+ return decorated
131
+
132
+
133
+ def requires_auth(f):
134
+ @wraps(f)
135
+ async def decorated(*args, **kwargs):
136
+ if os.getenv("HF_SPACE") == "true":
137
+ return await f(*args, **kwargs)
138
+
139
+ from helpers import login
140
+ user_pass_hash = login.get_credentials_hash()
141
+ if not user_pass_hash:
142
+ return await f(*args, **kwargs)
143
+ if session.get("authentication") != user_pass_hash:
144
+ return redirect(url_for("login_handler"))
145
+ return await f(*args, **kwargs)
146
+
147
+ return decorated
148
+
149
+
150
+ def csrf_protect(f):
151
+ @wraps(f)
152
+ async def decorated(*args, **kwargs):
153
+ if os.getenv("HF_SPACE") == "true":
154
+ return await f(*args, **kwargs)
155
+
156
+ from helpers import runtime
157
+ token = session.get("csrf_token")
158
+ header = request.headers.get("X-CSRF-Token")
159
+ cookie = request.cookies.get("csrf_token_" + runtime.get_runtime_id())
160
+ sent = header or cookie
161
+ if not token or not sent or token != sent:
162
+ return Response("CSRF token missing or invalid", 403)
163
+ return await f(*args, **kwargs)
164
+
165
+ return decorated
166
+
167
+
168
+ def register_api_route(app: Flask, lock: ThreadLockType) -> None:
169
+ from helpers.modules import load_classes_from_file
170
+ from helpers import plugins
171
+
172
+ async def _dispatch(path: str) -> BaseResponse:
173
+ cached = cache.get(CACHE_AREA, path)
174
+ if cached is not None:
175
+ return await cached()
176
+
177
+ handler_cls: type[ApiHandler] | None = None
178
+
179
+ builtin_file = files.get_abs_path(f"api/{path}.py")
180
+ if files.is_in_dir(builtin_file, files.get_abs_path("api")) and files.exists(
181
+ builtin_file
182
+ ):
183
+ classes = load_classes_from_file(builtin_file, ApiHandler)
184
+ if classes:
185
+ handler_cls = classes[0]
186
+
187
+ if handler_cls is None and path.startswith("plugins/"):
188
+ parts = path.split("/", 2)
189
+ if len(parts) == 3:
190
+ _, plugin_name, handler_name = parts
191
+ plugin_dir = plugins.find_plugin_dir(plugin_name)
192
+ if plugin_dir:
193
+ plugin_file = Path(plugin_dir) / "api" / f"{handler_name}.py"
194
+ if plugin_file.is_file():
195
+ classes = load_classes_from_file(str(plugin_file), ApiHandler)
196
+ if classes:
197
+ handler_cls = classes[0]
198
+
199
+ if handler_cls is None:
200
+ return Response(f"API endpoint not found: {path}", 404)
201
+
202
+ if request.method not in handler_cls.get_methods():
203
+ return Response(f"Method {request.method} not allowed for: {path}", 405)
204
+
205
+ async def call_handler() -> BaseResponse:
206
+ instance = handler_cls(app, lock)
207
+ return await instance.handle_request(request=request)
208
+
209
+ handler_fn = call_handler
210
+ if handler_cls.requires_csrf():
211
+ handler_fn = csrf_protect(handler_fn)
212
+ if handler_cls.requires_api_key():
213
+ handler_fn = requires_api_key(handler_fn)
214
+ if handler_cls.requires_auth():
215
+ handler_fn = requires_auth(handler_fn)
216
+ if handler_cls.requires_loopback():
217
+ handler_fn = requires_loopback(handler_fn)
218
+
219
+ cache.add(CACHE_AREA, path, handler_fn)
220
+ return await handler_fn()
221
+
222
+ app.add_url_rule(
223
+ "/api/<path:path>",
224
+ "api_dispatch",
225
+ _dispatch,
226
+ methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
227
+ )
helpers/ui_server.py ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass, field
2
+ from datetime import timedelta
3
+ import asyncio
4
+ import logging
5
+ import os
6
+ import secrets
7
+ import threading
8
+ import time
9
+ from typing import Any
10
+
11
+ from flask import (
12
+ Flask,
13
+ Response,
14
+ redirect,
15
+ render_template_string,
16
+ request,
17
+ send_file,
18
+ session,
19
+ url_for,
20
+ )
21
+ from socketio import ASGIApp
22
+ from starlette.applications import Starlette
23
+ from starlette.routing import Mount, Route
24
+ from starlette.responses import JSONResponse
25
+ from uvicorn.middleware.wsgi import WSGIMiddleware
26
+ from werkzeug.wrappers.request import Request as WerkzeugRequest
27
+ import socketio # type: ignore[import-untyped]
28
+
29
+ from helpers import dotenv, fasta2a_server, files, git, login, mcp_server, runtime
30
+ from helpers.api import register_api_route, requires_auth
31
+ from helpers.extension import extensible
32
+ from helpers.files import get_abs_path
33
+ from helpers.print_style import PrintStyle
34
+ from helpers.server_startup import StartupMonitor
35
+ from helpers import settings as settings_helper
36
+ from helpers.ws import register_ws_namespace, validate_ws_origin
37
+ from helpers.ws_manager import WsManager, set_shared_ws_manager
38
+
39
+
40
+ UPLOAD_LIMIT_BYTES = 5 * 1024 * 1024 * 1024
41
+
42
+
43
+ def configure_process_environment() -> None:
44
+ logging.getLogger().setLevel(logging.WARNING)
45
+ os.environ["TZ"] = "UTC"
46
+ os.environ["TOKENIZERS_PARALLELISM"] = "false"
47
+ if hasattr(time, "tzset"):
48
+ time.tzset()
49
+
50
+
51
+ @dataclass
52
+ class UiServerRuntime:
53
+ webapp: Flask
54
+ socketio_server: socketio.AsyncServer
55
+ ws_manager: WsManager
56
+ lock: threading.RLock
57
+ settings_snapshot: dict[str, Any]
58
+ _routes_registered: bool = False
59
+ _transport_registered: bool = False
60
+ _route_handlers: "UiRouteHandlers | None" = field(default=None, init=False)
61
+
62
+ @classmethod
63
+ def create(cls) -> "UiServerRuntime":
64
+ webapp = Flask("app", static_folder=get_abs_path("./webui"), static_url_path="/")
65
+ webapp.secret_key = os.getenv("FLASK_SECRET_KEY") or secrets.token_hex(32)
66
+
67
+ if os.getenv("HF_SPACE") == "true":
68
+ from werkzeug.middleware.proxy_fix import ProxyFix
69
+ webapp.wsgi_app = ProxyFix(webapp.wsgi_app, x_for=1, x_proto=1, x_host=1)
70
+
71
+ WerkzeugRequest.max_form_memory_size = UPLOAD_LIMIT_BYTES
72
+ webapp.config.update(
73
+ JSON_SORT_KEYS=False,
74
+ SESSION_COOKIE_NAME="session_" + runtime.get_runtime_id(),
75
+ SESSION_COOKIE_SAMESITE="Lax",
76
+ SESSION_PERMANENT=True,
77
+ PERMANENT_SESSION_LIFETIME=timedelta(days=1),
78
+ MAX_CONTENT_LENGTH=int(
79
+ os.getenv("FLASK_MAX_CONTENT_LENGTH", str(UPLOAD_LIMIT_BYTES))
80
+ ),
81
+ MAX_FORM_MEMORY_SIZE=int(
82
+ os.getenv("FLASK_MAX_FORM_MEMORY_SIZE", str(UPLOAD_LIMIT_BYTES))
83
+ ),
84
+ )
85
+
86
+ lock = threading.RLock()
87
+
88
+ cors_allowed = "*" if os.getenv("HF_SPACE") == "true" else (lambda _origin, environ: validate_ws_origin(environ)[0])
89
+
90
+ socketio_server = socketio.AsyncServer(
91
+ async_mode="asgi",
92
+ namespaces="*",
93
+ cors_allowed_origins=cors_allowed,
94
+ logger=False,
95
+ engineio_logger=False,
96
+ ping_interval=25,
97
+ ping_timeout=30,
98
+ max_http_buffer_size=50 * 1024 * 1024,
99
+ )
100
+
101
+ ws_manager = WsManager(socketio_server, lock)
102
+ set_shared_ws_manager(ws_manager)
103
+
104
+ server_runtime = cls(
105
+ webapp=webapp,
106
+ socketio_server=socketio_server,
107
+ ws_manager=ws_manager,
108
+ lock=lock,
109
+ settings_snapshot={},
110
+ )
111
+ server_runtime.refresh_runtime_settings()
112
+ return server_runtime
113
+
114
+ def refresh_runtime_settings(self) -> None:
115
+ self.settings_snapshot = settings_helper.get_settings()
116
+ settings_helper.set_runtime_settings_snapshot(self.settings_snapshot)
117
+ self.ws_manager.set_server_restart_broadcast(
118
+ self.settings_snapshot.get("websocket_server_restart_enabled", True)
119
+ )
120
+
121
+ def register_http_routes(self) -> None:
122
+ if self._routes_registered:
123
+ return
124
+
125
+ handlers = UiRouteHandlers(self)
126
+ self._route_handlers = handlers
127
+ self.webapp.add_url_rule(
128
+ "/login",
129
+ "login_handler",
130
+ handlers.login_handler,
131
+ methods=["GET", "POST"],
132
+ )
133
+ self.webapp.add_url_rule(
134
+ "/logout",
135
+ "logout_handler",
136
+ handlers.logout_handler,
137
+ methods=["GET"],
138
+ )
139
+ self.webapp.add_url_rule(
140
+ "/",
141
+ "serve_index",
142
+ handlers.serve_index,
143
+ methods=["GET"],
144
+ )
145
+ self.webapp.add_url_rule(
146
+ "/plugins/<plugin_name>/<path:asset_path>",
147
+ "serve_builtin_plugin_asset",
148
+ handlers.serve_builtin_plugin_asset,
149
+ methods=["GET"],
150
+ )
151
+ self.webapp.add_url_rule(
152
+ "/usr/plugins/<plugin_name>/<path:asset_path>",
153
+ "serve_plugin_asset",
154
+ handlers.serve_plugin_asset,
155
+ methods=["GET"],
156
+ )
157
+ self.webapp.add_url_rule(
158
+ "/extensions/webui/<path:asset_path>",
159
+ "serve_extension_asset",
160
+ handlers.serve_extension_asset,
161
+ methods=["GET"],
162
+ )
163
+ self._routes_registered = True
164
+
165
+ def register_transport_handlers(self) -> None:
166
+ if self._transport_registered:
167
+ return
168
+ register_api_route(self.webapp, self.lock)
169
+ register_ws_namespace(
170
+ self.socketio_server,
171
+ self.webapp,
172
+ self.lock,
173
+ manager=self.ws_manager,
174
+ )
175
+ self._transport_registered = True
176
+
177
+ def build_asgi_app(self, startup_monitor: StartupMonitor):
178
+ with startup_monitor.stage("wsgi.middleware.create"):
179
+ wsgi_app = WSGIMiddleware(self.webapp)
180
+
181
+ with startup_monitor.stage("mcp.proxy.init"):
182
+ mcp_app = mcp_server.DynamicMcpProxy.get_instance()
183
+
184
+ with startup_monitor.stage("a2a.proxy.init"):
185
+ a2a_app = fasta2a_server.DynamicA2AProxy.get_instance()
186
+
187
+ async def health_endpoint(request):
188
+ return JSONResponse({"status": "ok", "service": "agent-zero"})
189
+
190
+ async def api_docs_endpoint(request):
191
+ from api.api_docs import ApiDocs
192
+ handler = ApiDocs(self.webapp, self.lock)
193
+ result = await handler.process({}, None)
194
+ return JSONResponse(result)
195
+
196
+ with startup_monitor.stage("starlette.app.create"):
197
+ starlette_app = Starlette(
198
+ routes=[
199
+ Route("/health", endpoint=health_endpoint, methods=["GET"]),
200
+ Route("/api-docs", endpoint=api_docs_endpoint, methods=["GET"]),
201
+ Mount("/mcp", app=mcp_app),
202
+ Mount("/a2a", app=a2a_app),
203
+ Mount("/", app=wsgi_app),
204
+ ],
205
+ lifespan=startup_monitor.lifespan(),
206
+ )
207
+
208
+ with startup_monitor.stage("socketio.asgi.create"):
209
+ return ASGIApp(self.socketio_server, other_asgi_app=starlette_app)
210
+
211
+ def access_log_enabled(self) -> bool:
212
+ return self.settings_snapshot.get("uvicorn_access_logs_enabled", False)
213
+
214
+
215
+ class UiRouteHandlers:
216
+ def __init__(self, runtime_state: UiServerRuntime) -> None:
217
+ self.runtime = runtime_state
218
+
219
+ @extensible
220
+ async def login_handler(self):
221
+ error = None
222
+ if request.method == "POST":
223
+ user = dotenv.get_dotenv_value("AUTH_LOGIN")
224
+ password = dotenv.get_dotenv_value("AUTH_PASSWORD")
225
+
226
+ if request.form["username"] == user and request.form["password"] == password:
227
+ session["authentication"] = login.get_credentials_hash()
228
+ return redirect(url_for("serve_index"))
229
+ else:
230
+ await asyncio.sleep(1)
231
+ error = "Invalid Credentials. Please try again."
232
+
233
+ login_page_content = files.read_file("webui/login.html")
234
+ return render_template_string(login_page_content, error=error)
235
+
236
+ @extensible
237
+ async def logout_handler(self):
238
+ session.pop("authentication", None)
239
+ return redirect(url_for("login_handler"))
240
+
241
+ @requires_auth
242
+ @extensible
243
+ async def serve_index(self):
244
+ try:
245
+ gitinfo = git.get_git_info()
246
+ except Exception:
247
+ gitinfo = {
248
+ "version": "unknown",
249
+ "commit_time": "unknown",
250
+ }
251
+
252
+ index = files.read_file("webui/index.html")
253
+ return files.replace_placeholders_text(
254
+ _content=index,
255
+ version_no=gitinfo["version"],
256
+ version_time=gitinfo["commit_time"],
257
+ runtime_id=runtime.get_runtime_id(),
258
+ runtime_is_development=("true" if runtime.is_development() else "false"),
259
+ logged_in=("true" if login.get_credentials_hash() else "false"),
260
+ )
261
+
262
+ @requires_auth
263
+ async def serve_builtin_plugin_asset(self, plugin_name, asset_path):
264
+ return await self._serve_plugin_asset(plugin_name, asset_path)
265
+
266
+ @requires_auth
267
+ async def serve_plugin_asset(self, plugin_name, asset_path):
268
+ return await self._serve_plugin_asset(plugin_name, asset_path)
269
+
270
+ @requires_auth
271
+ async def serve_extension_asset(self, asset_path):
272
+ exts = files.get_abs_path("extensions/webui")
273
+ path = files.get_abs_path(exts, asset_path)
274
+ if not files.is_in_dir(path, exts):
275
+ return Response("Access denied", 403)
276
+ return send_file(path)
277
+
278
+ @extensible
279
+ async def _serve_plugin_asset(self, plugin_name, asset_path):
280
+ from helpers import plugins
281
+
282
+ plugin_dir = plugins.find_plugin_dir(plugin_name)
283
+ if not plugin_dir:
284
+ return Response("Plugin not found", 404)
285
+
286
+ try:
287
+ asset_file = files.get_abs_path(plugin_dir, asset_path)
288
+ webui_dir = files.get_abs_path(plugin_dir, "webui")
289
+ webui_extensions_dir = files.get_abs_path(plugin_dir, "extensions/webui")
290
+
291
+ if not files.is_in_dir(str(asset_file), str(webui_dir)) and not files.is_in_dir(
292
+ str(asset_file), str(webui_extensions_dir)
293
+ ):
294
+ return Response("Access denied", 403)
295
+
296
+ if not files.is_file(asset_file):
297
+ return Response("Asset not found", 404)
298
+
299
+ return send_file(str(asset_file))
300
+ except Exception as e:
301
+ PrintStyle.error(f"Error serving plugin asset: {e}")
302
+ return Response("Error serving asset", 500)
start_hf.sh CHANGED
@@ -5,33 +5,27 @@ REPO_URL="https://github.com/JsonLord/agent-zero.git"
5
  CLONE_DIR="/home/user/app/agent-zero-clone"
6
  APP_DIR="/home/user/app"
7
 
8
- echo "Cloning Agent-Zero (fix-a2a-auth-15797037665282663574 branch)..."
9
  if [ -d "$CLONE_DIR" ]; then
10
  rm -rf "$CLONE_DIR"
11
  fi
12
 
13
- git clone --branch fix-a2a-auth-15797037665282663574 "$REPO_URL" "$CLONE_DIR"
14
 
15
  echo "Applying adaptations..."
16
  cp -rn "$CLONE_DIR"/* "$APP_DIR/" 2>/dev/null || true
17
 
18
- cp -f /home/user/app/helpers/api.py "$APP_DIR/python/helpers/api.py"
19
- cp -f /home/user/app/helpers/runtime.py "$APP_DIR/python/helpers/runtime.py"
20
- cp -f /home/user/app/helpers/settings.py "$APP_DIR/python/helpers/settings.py"
21
- cp -f /home/user/app/run_ui.py "$APP_DIR/run_ui.py"
22
- cp -f /home/user/app/api/api_docs.py "$APP_DIR/python/api/api_docs.py"
23
-
24
- if [ -f "/home/user/app/helpers/ws.py" ]; then
25
- cp -f /home/user/app/helpers/ws.py "$APP_DIR/python/helpers/ws.py"
26
- fi
27
 
28
  echo "Installing dependencies..."
29
  if command -v uv > /dev/null; then
30
  uv pip install --system --no-cache -r "$APP_DIR/requirements.txt"
31
- uv pip install --system flask-socketio eventlet
32
  else
33
  pip install --no-cache-dir -r "$APP_DIR/requirements.txt"
34
- pip install flask-socketio eventlet
35
  fi
36
 
37
  export HF_SPACE=true
@@ -39,4 +33,4 @@ export PORT=7860
39
  export HOST=0.0.0.0
40
 
41
  echo "Starting Agent-Zero..."
42
- python run_ui.py --host 0.0.0.0 --port 7860 --dockerized=true
 
5
  CLONE_DIR="/home/user/app/agent-zero-clone"
6
  APP_DIR="/home/user/app"
7
 
8
+ echo "Cloning Agent-Zero (main branch)..."
9
  if [ -d "$CLONE_DIR" ]; then
10
  rm -rf "$CLONE_DIR"
11
  fi
12
 
13
+ git clone "$REPO_URL" "$CLONE_DIR"
14
 
15
  echo "Applying adaptations..."
16
  cp -rn "$CLONE_DIR"/* "$APP_DIR/" 2>/dev/null || true
17
 
18
+ cp -f /home/user/app/helpers/api.py "$APP_DIR/helpers/api.py"
19
+ cp -f /home/user/app/helpers/runtime.py "$APP_DIR/helpers/runtime.py"
20
+ cp -f /home/user/app/helpers/settings.py "$APP_DIR/helpers/settings.py"
21
+ cp -f /home/user/app/helpers/ui_server.py "$APP_DIR/helpers/ui_server.py"
22
+ cp -f /home/user/app/api/api_docs.py "$APP_DIR/api/api_docs.py"
 
 
 
 
23
 
24
  echo "Installing dependencies..."
25
  if command -v uv > /dev/null; then
26
  uv pip install --system --no-cache -r "$APP_DIR/requirements.txt"
 
27
  else
28
  pip install --no-cache-dir -r "$APP_DIR/requirements.txt"
 
29
  fi
30
 
31
  export HF_SPACE=true
 
33
  export HOST=0.0.0.0
34
 
35
  echo "Starting Agent-Zero..."
36
+ python run_ui.py