Rafael Uzarowski commited on
Commit
1529dc0
·
unverified ·
1 Parent(s): de76de9

feat: External API Endpoints

Browse files
python/api/api_files_get.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import os
3
+ from python.helpers.api import ApiHandler, Request, Response
4
+ from python.helpers import files
5
+ from python.helpers.print_style import PrintStyle
6
+ import json
7
+
8
+
9
+ class ApiFilesGet(ApiHandler):
10
+ @classmethod
11
+ def requires_auth(cls) -> bool:
12
+ return False
13
+
14
+ @classmethod
15
+ def requires_csrf(cls) -> bool:
16
+ return False
17
+
18
+ @classmethod
19
+ def requires_api_key(cls) -> bool:
20
+ return True
21
+
22
+ @classmethod
23
+ def get_methods(cls) -> list[str]:
24
+ return ["POST"]
25
+
26
+ async def process(self, input: dict, request: Request) -> dict | Response:
27
+ try:
28
+ # Get paths from input
29
+ paths = input.get("paths", [])
30
+
31
+ if not paths:
32
+ return Response(
33
+ '{"error": "paths array is required"}',
34
+ status=400,
35
+ mimetype="application/json"
36
+ )
37
+
38
+ if not isinstance(paths, list):
39
+ return Response(
40
+ '{"error": "paths must be an array"}',
41
+ status=400,
42
+ mimetype="application/json"
43
+ )
44
+
45
+ result = {}
46
+
47
+ for path in paths:
48
+ try:
49
+ # Convert internal paths to external paths
50
+ if path.startswith("/a0/tmp/uploads/"):
51
+ # Internal path - convert to external
52
+ filename = path.replace("/a0/tmp/uploads/", "")
53
+ external_path = files.get_abs_path("tmp/uploads", filename)
54
+ filename = os.path.basename(external_path)
55
+ elif path.startswith("/a0/"):
56
+ # Other internal Agent Zero paths
57
+ relative_path = path.replace("/a0/", "")
58
+ external_path = files.get_abs_path(relative_path)
59
+ filename = os.path.basename(external_path)
60
+ else:
61
+ # Assume it's already an external/absolute path
62
+ external_path = path
63
+ filename = os.path.basename(path)
64
+
65
+ # Check if file exists
66
+ if not os.path.exists(external_path):
67
+ PrintStyle.warning(f"File not found: {path}")
68
+ continue
69
+
70
+ # Read and encode file
71
+ with open(external_path, "rb") as f:
72
+ file_content = f.read()
73
+ base64_content = base64.b64encode(file_content).decode('utf-8')
74
+ result[filename] = base64_content
75
+
76
+ PrintStyle().print(f"Retrieved file: {filename} ({len(file_content)} bytes)")
77
+
78
+ except Exception as e:
79
+ PrintStyle.error(f"Failed to read file {path}: {str(e)}")
80
+ continue
81
+
82
+ # Log the retrieval
83
+ PrintStyle(
84
+ background_color="#2ECC71", font_color="white", bold=True, padding=True
85
+ ).print(f"API Files retrieved: {len(result)} files")
86
+
87
+ return result
88
+
89
+ except Exception as e:
90
+ PrintStyle.error(f"API files get error: {str(e)}")
91
+ return Response(
92
+ json.dumps({"error": f"Internal server error: {str(e)}"}),
93
+ status=500,
94
+ mimetype="application/json"
95
+ )
python/api/api_log_get.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from agent import AgentContext
2
+ from python.helpers.api import ApiHandler, Request, Response
3
+
4
+
5
+ class ApiLogGet(ApiHandler):
6
+ @classmethod
7
+ def get_methods(cls) -> list[str]:
8
+ return ["GET", "POST"]
9
+
10
+ @classmethod
11
+ def requires_auth(cls) -> bool:
12
+ return False # No web auth required
13
+
14
+ @classmethod
15
+ def requires_csrf(cls) -> bool:
16
+ return False # No CSRF required
17
+
18
+ @classmethod
19
+ def requires_api_key(cls) -> bool:
20
+ return True # Require API key
21
+
22
+ async def process(self, input: dict, request: Request) -> dict | Response:
23
+ # Extract parameters (support both query params for GET and body for POST)
24
+ if request.method == "GET":
25
+ context_id = request.args.get("context_id", "")
26
+ length = int(request.args.get("length", 100))
27
+ else:
28
+ context_id = input.get("context_id", "")
29
+ length = input.get("length", 100)
30
+
31
+ if not context_id:
32
+ return Response('{"error": "context_id is required"}', status=400, mimetype="application/json")
33
+
34
+ # Get context
35
+ context = AgentContext.get(context_id)
36
+ if not context:
37
+ return Response('{"error": "Context not found"}', status=404, mimetype="application/json")
38
+
39
+ try:
40
+ # Get total number of log items
41
+ total_items = len(context.log.logs)
42
+
43
+ # Calculate start position (from newest, so we work backwards)
44
+ start_pos = max(0, total_items - length)
45
+
46
+ # Get log items from the calculated start position
47
+ log_items = context.log.output(start=start_pos)
48
+
49
+ # Return log data with metadata
50
+ return {
51
+ "context_id": context_id,
52
+ "log": {
53
+ "guid": context.log.guid,
54
+ "total_items": total_items,
55
+ "returned_items": len(log_items),
56
+ "start_position": start_pos,
57
+ "progress": context.log.progress,
58
+ "progress_active": context.log.progress_active,
59
+ "items": log_items
60
+ }
61
+ }
62
+
63
+ except Exception as e:
64
+ return Response(f'{{"error": "{str(e)}"}}', status=500, mimetype="application/json")
python/api/api_message.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import os
3
+ from datetime import datetime, timedelta
4
+ from agent import AgentContext, UserMessage, AgentContextType
5
+ from python.helpers.api import ApiHandler, Request, Response
6
+ from python.helpers import files
7
+ from python.helpers.print_style import PrintStyle
8
+ from werkzeug.utils import secure_filename
9
+ from initialize import initialize_agent
10
+ import threading
11
+
12
+
13
+ class ApiMessage(ApiHandler):
14
+ # Track chat lifetimes for cleanup
15
+ _chat_lifetimes = {}
16
+ _cleanup_lock = threading.Lock()
17
+
18
+ @classmethod
19
+ def requires_auth(cls) -> bool:
20
+ return False # No web auth required
21
+
22
+ @classmethod
23
+ def requires_csrf(cls) -> bool:
24
+ return False # No CSRF required
25
+
26
+ @classmethod
27
+ def requires_api_key(cls) -> bool:
28
+ return True # Require API key
29
+
30
+ async def process(self, input: dict, request: Request) -> dict | Response:
31
+ # Extract parameters
32
+ context_id = input.get("context_id", "")
33
+ message = input.get("message", "")
34
+ attachments = input.get("attachments", [])
35
+ lifetime_hours = input.get("lifetime_hours", 24) # Default 24 hours
36
+
37
+ if not message:
38
+ return Response('{"error": "Message is required"}', status=400, mimetype="application/json")
39
+
40
+ # Handle attachments (base64 encoded)
41
+ attachment_paths = []
42
+ if attachments:
43
+ upload_folder_int = "/a0/tmp/uploads"
44
+ upload_folder_ext = files.get_abs_path("tmp/uploads")
45
+ os.makedirs(upload_folder_ext, exist_ok=True)
46
+
47
+ for attachment in attachments:
48
+ if not isinstance(attachment, dict) or "filename" not in attachment or "base64" not in attachment:
49
+ continue
50
+
51
+ try:
52
+ filename = secure_filename(attachment["filename"])
53
+ if not filename:
54
+ continue
55
+
56
+ # Decode base64 content
57
+ file_content = base64.b64decode(attachment["base64"])
58
+
59
+ # Save to temp file
60
+ save_path = os.path.join(upload_folder_ext, filename)
61
+ with open(save_path, "wb") as f:
62
+ f.write(file_content)
63
+
64
+ attachment_paths.append(os.path.join(upload_folder_int, filename))
65
+ except Exception as e:
66
+ PrintStyle.error(f"Failed to process attachment {attachment.get('filename', 'unknown')}: {e}")
67
+ continue
68
+
69
+ # Get or create context
70
+ if context_id:
71
+ context = AgentContext.get(context_id)
72
+ if not context:
73
+ return Response('{"error": "Context not found"}', status=404, mimetype="application/json")
74
+ else:
75
+ config = initialize_agent()
76
+ context = AgentContext(config=config, type=AgentContextType.USER)
77
+ context_id = context.id
78
+
79
+ # Update chat lifetime
80
+ with self._cleanup_lock:
81
+ self._chat_lifetimes[context_id] = datetime.now() + timedelta(hours=lifetime_hours)
82
+
83
+ # Process message
84
+ try:
85
+ # Log the message
86
+ attachment_filenames = [os.path.basename(path) for path in attachment_paths] if attachment_paths else []
87
+
88
+ PrintStyle(
89
+ background_color="#6C3483", font_color="white", bold=True, padding=True
90
+ ).print("External API message:")
91
+ PrintStyle(font_color="white", padding=False).print(f"> {message}")
92
+ if attachment_filenames:
93
+ PrintStyle(font_color="white", padding=False).print("Attachments:")
94
+ for filename in attachment_filenames:
95
+ PrintStyle(font_color="white", padding=False).print(f"- {filename}")
96
+
97
+ # Add user message to chat history so it's visible in the UI
98
+ context.log.log(
99
+ type="user",
100
+ heading="User message",
101
+ content=message,
102
+ kvps={"attachments": attachment_filenames},
103
+ )
104
+
105
+ # Send message to agent
106
+ task = context.communicate(UserMessage(message, attachment_paths))
107
+ result = await task.result()
108
+
109
+ # Clean up expired chats
110
+ self._cleanup_expired_chats()
111
+
112
+ return {
113
+ "context_id": context_id,
114
+ "response": result
115
+ }
116
+
117
+ except Exception as e:
118
+ PrintStyle.error(f"External API error: {e}")
119
+ return Response(f'{{"error": "{str(e)}"}}', status=500, mimetype="application/json")
120
+
121
+ @classmethod
122
+ def _cleanup_expired_chats(cls):
123
+ """Clean up expired chats"""
124
+ with cls._cleanup_lock:
125
+ now = datetime.now()
126
+ expired_contexts = [
127
+ context_id for context_id, expiry in cls._chat_lifetimes.items()
128
+ if now > expiry
129
+ ]
130
+
131
+ for context_id in expired_contexts:
132
+ try:
133
+ context = AgentContext.get(context_id)
134
+ if context:
135
+ context.reset()
136
+ AgentContext.remove(context_id)
137
+ del cls._chat_lifetimes[context_id]
138
+ PrintStyle().print(f"Cleaned up expired chat: {context_id}")
139
+ except Exception as e:
140
+ PrintStyle.error(f"Failed to cleanup chat {context_id}: {e}")
python/api/api_reset_chat.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from agent import AgentContext
2
+ from python.helpers.api import ApiHandler, Request, Response
3
+ from python.helpers.print_style import PrintStyle
4
+ from python.helpers import persist_chat
5
+ import json
6
+
7
+
8
+ class ApiResetChat(ApiHandler):
9
+ @classmethod
10
+ def requires_auth(cls) -> bool:
11
+ return False
12
+
13
+ @classmethod
14
+ def requires_csrf(cls) -> bool:
15
+ return False
16
+
17
+ @classmethod
18
+ def requires_api_key(cls) -> bool:
19
+ return True
20
+
21
+ @classmethod
22
+ def get_methods(cls) -> list[str]:
23
+ return ["POST"]
24
+
25
+ async def process(self, input: dict, request: Request) -> dict | Response:
26
+ try:
27
+ # Get context_id from input
28
+ context_id = input.get("context_id")
29
+
30
+ if not context_id:
31
+ return Response(
32
+ '{"error": "context_id is required"}',
33
+ status=400,
34
+ mimetype="application/json"
35
+ )
36
+
37
+ # Check if context exists
38
+ context = AgentContext.get(context_id)
39
+ if not context:
40
+ return Response(
41
+ '{"error": "Chat context not found"}',
42
+ status=404,
43
+ mimetype="application/json"
44
+ )
45
+
46
+ # Reset the chat context (clears history but keeps context alive)
47
+ context.reset()
48
+ # Save the reset context to persist the changes
49
+ persist_chat.save_tmp_chat(context)
50
+
51
+ # Log the reset
52
+ PrintStyle(
53
+ background_color="#3498DB", font_color="white", bold=True, padding=True
54
+ ).print(f"API Chat reset: {context_id}")
55
+
56
+ # Return success response
57
+ return {
58
+ "success": True,
59
+ "message": "Chat reset successfully",
60
+ "context_id": context_id
61
+ }
62
+
63
+ except Exception as e:
64
+ PrintStyle.error(f"API reset chat error: {str(e)}")
65
+ return Response(
66
+ json.dumps({"error": f"Internal server error: {str(e)}"}),
67
+ status=500,
68
+ mimetype="application/json"
69
+ )
python/api/api_terminate_chat.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from agent import AgentContext
2
+ from python.helpers.api import ApiHandler, Request, Response
3
+ from python.helpers.persist_chat import remove_chat
4
+ from python.helpers.print_style import PrintStyle
5
+ import json
6
+
7
+
8
+ class ApiTerminateChat(ApiHandler):
9
+ @classmethod
10
+ def requires_auth(cls) -> bool:
11
+ return False
12
+
13
+ @classmethod
14
+ def requires_csrf(cls) -> bool:
15
+ return False
16
+
17
+ @classmethod
18
+ def requires_api_key(cls) -> bool:
19
+ return True
20
+
21
+ @classmethod
22
+ def get_methods(cls) -> list[str]:
23
+ return ["POST"]
24
+
25
+ async def process(self, input: dict, request: Request) -> dict | Response:
26
+ try:
27
+ # Get context_id from input
28
+ context_id = input.get("context_id")
29
+
30
+ if not context_id:
31
+ return Response(
32
+ '{"error": "context_id is required"}',
33
+ status=400,
34
+ mimetype="application/json"
35
+ )
36
+
37
+ # Check if context exists
38
+ context = AgentContext.get(context_id)
39
+ if not context:
40
+ return Response(
41
+ '{"error": "Chat context not found"}',
42
+ status=404,
43
+ mimetype="application/json"
44
+ )
45
+
46
+ # Delete the chat context
47
+ AgentContext.remove(context.id)
48
+ remove_chat(context.id)
49
+
50
+ # Log the deletion
51
+ PrintStyle(
52
+ background_color="#E74C3C", font_color="white", bold=True, padding=True
53
+ ).print(f"API Chat deleted: {context_id}")
54
+
55
+ # Return success response
56
+ return {
57
+ "success": True,
58
+ "message": "Chat deleted successfully",
59
+ "context_id": context_id
60
+ }
61
+
62
+ except Exception as e:
63
+ PrintStyle.error(f"API terminate chat error: {str(e)}")
64
+ return Response(
65
+ json.dumps({"error": f"Internal server error: {str(e)}"}),
66
+ status=500,
67
+ mimetype="application/json"
68
+ )
python/helpers/settings.py CHANGED
@@ -67,7 +67,7 @@ class Settings(TypedDict):
67
  memory_memorize_enabled: bool
68
  memory_memorize_consolidation: bool
69
  memory_memorize_replace_threshold: float
70
-
71
 
72
  api_keys: dict[str, str]
73
 
@@ -880,7 +880,7 @@ def convert_out(settings: Settings) -> SettingsOutput:
880
 
881
  # TTS fields
882
  tts_fields: list[SettingsField] = []
883
-
884
  tts_fields.append(
885
  {
886
  "id": "tts_kokoro",
@@ -982,6 +982,28 @@ def convert_out(settings: Settings) -> SettingsOutput:
982
  "tab": "mcp",
983
  }
984
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
985
  # Backup & Restore section
986
  backup_fields: list[SettingsField] = []
987
 
@@ -1030,6 +1052,7 @@ def convert_out(settings: Settings) -> SettingsOutput:
1030
  auth_section,
1031
  mcp_client_section,
1032
  mcp_server_section,
 
1033
  backup_section,
1034
  dev_section,
1035
  ]
 
67
  memory_memorize_enabled: bool
68
  memory_memorize_consolidation: bool
69
  memory_memorize_replace_threshold: float
70
+
71
 
72
  api_keys: dict[str, str]
73
 
 
880
 
881
  # TTS fields
882
  tts_fields: list[SettingsField] = []
883
+
884
  tts_fields.append(
885
  {
886
  "id": "tts_kokoro",
 
982
  "tab": "mcp",
983
  }
984
 
985
+ # External API section
986
+ external_api_fields: list[SettingsField] = []
987
+
988
+ external_api_fields.append(
989
+ {
990
+ "id": "external_api_examples",
991
+ "title": "API Examples",
992
+ "description": "View examples for using Agent Zero's external API endpoints with API key authentication.",
993
+ "type": "button",
994
+ "value": "Show API Examples",
995
+ }
996
+ )
997
+
998
+ external_api_section: SettingsSection = {
999
+ "id": "external_api",
1000
+ "title": "External API",
1001
+ "description": "Agent Zero provides external API endpoints for integration with other applications. "
1002
+ "These endpoints use API key authentication and support text messages and file attachments.",
1003
+ "fields": external_api_fields,
1004
+ "tab": "external",
1005
+ }
1006
+
1007
  # Backup & Restore section
1008
  backup_fields: list[SettingsField] = []
1009
 
 
1052
  auth_section,
1053
  mcp_client_section,
1054
  mcp_server_section,
1055
+ external_api_section,
1056
  backup_section,
1057
  dev_section,
1058
  ]
run_ui.py CHANGED
@@ -1,18 +1,15 @@
1
  from datetime import timedelta
2
  import os
3
  import secrets
4
- import sys
5
  import time
6
  import socket
7
  import struct
8
  from functools import wraps
9
  import threading
10
- import signal
11
- from typing import override
12
  from flask import Flask, request, Response, session
13
  from flask_basicauth import BasicAuth
14
  import initialize
15
- from python.helpers import errors, files, git, mcp_server
16
  from python.helpers.files import get_abs_path
17
  from python.helpers import runtime, dotenv, process
18
  from python.helpers.extract_tools import load_classes_from_folder
@@ -81,14 +78,17 @@ def is_loopback_address(address):
81
  def requires_api_key(f):
82
  @wraps(f)
83
  async def decorated(*args, **kwargs):
84
- valid_api_key = dotenv.get_dotenv_value("API_KEY")
 
 
 
85
  if api_key := request.headers.get("X-API-KEY"):
86
  if api_key != valid_api_key:
87
- return Response("API key required", 401)
88
  elif request.json and request.json.get("api_key"):
89
  api_key = request.json.get("api_key")
90
  if api_key != valid_api_key:
91
- return Response("API key required", 401)
92
  else:
93
  return Response("API key required", 401)
94
  return await f(*args, **kwargs)
@@ -171,7 +171,7 @@ def run():
171
  from werkzeug.serving import WSGIRequestHandler
172
  from werkzeug.serving import make_server
173
  from werkzeug.middleware.dispatcher import DispatcherMiddleware
174
- from a2wsgi import ASGIMiddleware, WSGIMiddleware
175
 
176
  PrintStyle().print("Starting server...")
177
 
 
1
  from datetime import timedelta
2
  import os
3
  import secrets
 
4
  import time
5
  import socket
6
  import struct
7
  from functools import wraps
8
  import threading
 
 
9
  from flask import Flask, request, Response, session
10
  from flask_basicauth import BasicAuth
11
  import initialize
12
+ from python.helpers import files, git, mcp_server
13
  from python.helpers.files import get_abs_path
14
  from python.helpers import runtime, dotenv, process
15
  from python.helpers.extract_tools import load_classes_from_folder
 
78
  def requires_api_key(f):
79
  @wraps(f)
80
  async def decorated(*args, **kwargs):
81
+ # Use the auth token from settings (same as MCP server)
82
+ from python.helpers.settings import get_settings
83
+ valid_api_key = get_settings()["mcp_server_token"]
84
+
85
  if api_key := request.headers.get("X-API-KEY"):
86
  if api_key != valid_api_key:
87
+ return Response("Invalid API key", 401)
88
  elif request.json and request.json.get("api_key"):
89
  api_key = request.json.get("api_key")
90
  if api_key != valid_api_key:
91
+ return Response("Invalid API key", 401)
92
  else:
93
  return Response("API key required", 401)
94
  return await f(*args, **kwargs)
 
171
  from werkzeug.serving import WSGIRequestHandler
172
  from werkzeug.serving import make_server
173
  from werkzeug.middleware.dispatcher import DispatcherMiddleware
174
+ from a2wsgi import ASGIMiddleware
175
 
176
  PrintStyle().print("Starting server...")
177
 
webui/components/settings/external/api-examples.html ADDED
@@ -0,0 +1,678 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <html>
2
+
3
+ <head>
4
+ <title>Agent Zero External API Examples</title>
5
+ </head>
6
+
7
+ <body>
8
+ <div x-data>
9
+ <p>Agent Zero provides external API endpoints for integration with other applications.</p>
10
+ <p>These endpoints use API key authentication and support text messages and file attachments.</p>
11
+
12
+ <!-- API Token Information -->
13
+ <div style="background-color: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: 6px; padding: 12px; margin: 16px 0;">
14
+ <h4 style="margin: 0 0 8px 0; color: var(--color-text-primary);">API Token Information</h4>
15
+ <p style="margin: 0; color: var(--color-text-secondary); font-size: 14px;">
16
+ The API token is automatically generated from your username and password.
17
+ This same token is used for both MCP server connections and external API endpoints.
18
+ The token changes when you update your credentials.
19
+ </p>
20
+ </div>
21
+
22
+ <!-- Section 1: api_message Endpoint -->
23
+ <div class="api-section">
24
+ <h2>POST /api_message</h2>
25
+ <p style="margin: 0 0 16px 0; color: var(--color-text-secondary); font-size: 14px;">
26
+ Send messages to Agent Zero and receive responses. Supports text messages, file attachments, and conversation continuity.
27
+ </p>
28
+
29
+ <!-- API Reference -->
30
+ <h3 style="margin: 20px 0 12px 0; color: var(--color-text-primary);">API Reference</h3>
31
+ <div style="background-color: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: 6px; padding: 12px; margin: 0 0 20px 0;">
32
+ <p style="margin: 0 0 8px 0; color: var(--color-text-secondary); font-size: 14px;">
33
+ <strong>Parameters:</strong><br>
34
+ • <code>context_id</code> (string, optional): Existing chat context ID<br>
35
+ • <code>message</code> (string, required): The message to send<br>
36
+ • <code>attachments</code> (array, optional): Array of {filename, base64} objects<br>
37
+ • <code>lifetime_hours</code> (number, optional): Chat lifetime in hours (default: 24)
38
+ </p>
39
+ <p style="margin: 0; color: var(--color-text-secondary); font-size: 14px;">
40
+ <strong>Headers:</strong> <code>X-API-KEY</code> (required), <code>Content-Type: application/json</code>
41
+ </p>
42
+ </div>
43
+
44
+ <!-- JavaScript Examples -->
45
+ <h3 style="margin: 20px 0 12px 0; color: var(--color-text-primary);">JavaScript Examples</h3>
46
+
47
+ <h4>Basic Usage Example</h4>
48
+ <div id="api-basic-example"></div>
49
+
50
+ <h4>Conversation Continuation Example</h4>
51
+ <div id="api-continuation-example"></div>
52
+
53
+ <h4>File Attachment Example</h4>
54
+ <div id="api-attachment-example"></div>
55
+ </div>
56
+
57
+ <!-- Section 2: api_log_get Endpoint -->
58
+ <div class="api-section">
59
+ <h2>GET/POST /api_log_get</h2>
60
+ <p style="margin: 0 0 16px 0; color: var(--color-text-secondary); font-size: 14px;">
61
+ Retrieve log data by context ID, limited to a specified number of entries from the newest.
62
+ </p>
63
+
64
+ <!-- API Reference -->
65
+ <h3 style="margin: 20px 0 12px 0; color: var(--color-text-primary);">API Reference</h3>
66
+ <div style="background-color: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: 6px; padding: 12px; margin: 0 0 20px 0;">
67
+ <p style="margin: 0 0 8px 0; color: var(--color-text-secondary); font-size: 14px;">
68
+ <strong>Parameters:</strong><br>
69
+ • <code>context_id</code> (string, required): Context ID to get logs from<br>
70
+ • <code>length</code> (integer, optional): Number of log items to return from newest (default: 100)
71
+ </p>
72
+ <p style="margin: 0; color: var(--color-text-secondary); font-size: 14px;">
73
+ <strong>Headers:</strong> <code>X-API-KEY</code> (required), <code>Content-Type: application/json</code> (for POST)
74
+ </p>
75
+ </div>
76
+
77
+ <!-- JavaScript Examples -->
78
+ <h3 style="margin: 20px 0 12px 0; color: var(--color-text-primary);">JavaScript Examples</h3>
79
+
80
+ <h4>GET Request Example</h4>
81
+ <div id="api-log-get-example"></div>
82
+
83
+ <h4>POST Request Example</h4>
84
+ <div id="api-log-post-example"></div>
85
+ </div>
86
+
87
+ <!-- Section 3: api_terminate_chat Endpoint -->
88
+ <div class="api-section">
89
+ <h2>POST /api_terminate_chat</h2>
90
+ <p style="margin: 0 0 16px 0; color: var(--color-text-secondary); font-size: 14px;">
91
+ Terminate and remove a chat context to free up resources. Similar to the MCP finish_chat function.
92
+ </p>
93
+
94
+ <!-- API Reference -->
95
+ <h3 style="margin: 20px 0 12px 0; color: var(--color-text-primary);">API Reference</h3>
96
+ <div style="background-color: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: 6px; padding: 12px; margin: 0 0 20px 0;">
97
+ <p style="margin: 0 0 8px 0; color: var(--color-text-secondary); font-size: 14px;">
98
+ <strong>Parameters:</strong><br>
99
+ • <code>context_id</code> (string, required): Context ID of the chat to terminate
100
+ </p>
101
+ <p style="margin: 0; color: var(--color-text-secondary); font-size: 14px;">
102
+ <strong>Headers:</strong> <code>X-API-KEY</code> (required), <code>Content-Type: application/json</code>
103
+ </p>
104
+ </div>
105
+
106
+ <!-- JavaScript Examples -->
107
+ <h3 style="margin: 20px 0 12px 0; color: var(--color-text-primary);">JavaScript Examples</h3>
108
+ <h4>Basic Termination Examples</h4>
109
+ <div id="api-terminate-example"></div>
110
+ </div>
111
+
112
+ <!-- Section 4: api_reset_chat Endpoint -->
113
+ <div class="api-section">
114
+ <h2>POST /api_reset_chat</h2>
115
+ <p style="margin: 0 0 16px 0; color: var(--color-text-secondary); font-size: 14px;">
116
+ Reset a chat context to clear conversation history while keeping the context_id alive for continued use.
117
+ </p>
118
+
119
+ <!-- API Reference -->
120
+ <h3 style="margin: 20px 0 12px 0; color: var(--color-text-primary);">API Reference</h3>
121
+ <div style="background-color: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: 6px; padding: 12px; margin: 0 0 20px 0;">
122
+ <p style="margin: 0 0 8px 0; color: var(--color-text-secondary); font-size: 14px;">
123
+ <strong>Parameters:</strong><br>
124
+ • <code>context_id</code> (string, required): Context ID of the chat to reset
125
+ </p>
126
+ <p style="margin: 0; color: var(--color-text-secondary); font-size: 14px;">
127
+ <strong>Headers:</strong> <code>X-API-KEY</code> (required), <code>Content-Type: application/json</code>
128
+ </p>
129
+ </div>
130
+
131
+ <!-- JavaScript Examples -->
132
+ <h3 style="margin: 20px 0 12px 0; color: var(--color-text-primary);">JavaScript Examples</h3>
133
+ <h4>Basic Reset Examples</h4>
134
+ <div id="api-reset-example"></div>
135
+ </div>
136
+
137
+ <!-- Section 5: api_files_get Endpoint -->
138
+ <div class="api-section">
139
+ <h2>POST /api_files_get</h2>
140
+ <p style="margin: 0 0 16px 0; color: var(--color-text-secondary); font-size: 14px;">
141
+ Retrieve file contents by paths, returning files as base64 encoded data. Useful for retrieving uploaded attachments.
142
+ </p>
143
+
144
+ <!-- API Reference -->
145
+ <h3 style="margin: 20px 0 12px 0; color: var(--color-text-primary);">API Reference</h3>
146
+ <div style="background-color: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: 6px; padding: 12px; margin: 0 0 20px 0;">
147
+ <p style="margin: 0 0 8px 0; color: var(--color-text-secondary); font-size: 14px;">
148
+ <strong>Parameters:</strong><br>
149
+ • <code>paths</code> (array, required): Array of file paths to retrieve (e.g., ["/a0/tmp/uploads/file.txt"])
150
+ </p>
151
+ <p style="margin: 0; color: var(--color-text-secondary); font-size: 14px;">
152
+ <strong>Headers:</strong> <code>X-API-KEY</code> (required), <code>Content-Type: application/json</code>
153
+ </p>
154
+ </div>
155
+
156
+ <!-- JavaScript Examples -->
157
+ <h3 style="margin: 20px 0 12px 0; color: var(--color-text-primary);">JavaScript Examples</h3>
158
+ <h4>File Retrieval Examples</h4>
159
+ <div id="api-files-get-example"></div>
160
+ </div>
161
+
162
+ <!-- Section 6: Additional Endpoints -->
163
+ <!--
164
+ Example template for new endpoint sections:
165
+
166
+ <div class="api-section">
167
+ <h2>POST /endpoint_name</h2>
168
+ <p style="margin: 0 0 16px 0; color: var(--color-text-secondary); font-size: 14px;">
169
+ Description of what this endpoint does...
170
+ </p>
171
+
172
+ <h3 style="margin: 20px 0 12px 0; color: var(--color-text-primary);">API Reference</h3>
173
+ <div style="background-color: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: 6px; padding: 12px; margin: 0 0 20px 0;">
174
+ <p style="margin: 0 0 8px 0; color: var(--color-text-secondary); font-size: 14px;">
175
+ <strong>Parameters:</strong><br>
176
+ • <code>param_name</code> (type, required/optional): Description
177
+ </p>
178
+ <p style="margin: 0; color: var(--color-text-secondary); font-size: 14px;">
179
+ <strong>Headers:</strong> <code>X-API-KEY</code> (required), <code>Content-Type: application/json</code>
180
+ </p>
181
+ </div>
182
+
183
+ <h3 style="margin: 20px 0 12px 0; color: var(--color-text-primary);">JavaScript Examples</h3>
184
+ <h4>Example Title</h4>
185
+ <div id="example-id"></div>
186
+ </div>
187
+ -->
188
+
189
+ <script>
190
+ setTimeout(() => {
191
+ const url = window.location.origin;
192
+ const token = settingsModalProxy.settings.sections.filter(x => x.id == "mcp_server")[0].fields.filter(x => x.id == "mcp_server_token")[0].value;
193
+
194
+ // Basic usage example
195
+ const basicExample = `// Basic message example
196
+ async function sendMessage() {
197
+ try {
198
+ const response = await fetch('${url}/api_message', {
199
+ method: 'POST',
200
+ headers: {
201
+ 'Content-Type': 'application/json',
202
+ 'X-API-KEY': '${token}'
203
+ },
204
+ body: JSON.stringify({
205
+ message: "Hello, how can you help me?",
206
+ lifetime_hours: 24
207
+ })
208
+ });
209
+
210
+ const data = await response.json();
211
+
212
+ if (response.ok) {
213
+ console.log('✅ Success!');
214
+ console.log('Response:', data.response);
215
+ console.log('Context ID:', data.context_id);
216
+ return data;
217
+ } else {
218
+ console.error('❌ Error:', data.error);
219
+ return null;
220
+ }
221
+ } catch (error) {
222
+ console.error('❌ Request failed:', error);
223
+ return null;
224
+ }
225
+ }
226
+
227
+ // Call the function
228
+ sendMessage().then(result => {
229
+ if (result) {
230
+ console.log('Message sent successfully!');
231
+ }
232
+ });`;
233
+
234
+ // Continuation example
235
+ const continuationExample = `// Continue conversation example
236
+ async function continueConversation(contextId) {
237
+ try {
238
+ const response = await fetch('${url}/api_message', {
239
+ method: 'POST',
240
+ headers: {
241
+ 'Content-Type': 'application/json',
242
+ 'X-API-KEY': '${token}'
243
+ },
244
+ body: JSON.stringify({
245
+ context_id: contextId,
246
+ message: "Can you tell me more about that?",
247
+ lifetime_hours: 24
248
+ })
249
+ });
250
+
251
+ const data = await response.json();
252
+
253
+ if (response.ok) {
254
+ console.log('✅ Continuation Success!');
255
+ console.log('Response:', data.response);
256
+ return data;
257
+ } else {
258
+ console.error('❌ Error:', data.error);
259
+ return null;
260
+ }
261
+ } catch (error) {
262
+ console.error('❌ Request failed:', error);
263
+ return null;
264
+ }
265
+ }
266
+
267
+ // Example: First send a message, then continue the conversation
268
+ async function fullConversationExample() {
269
+ const firstResult = await sendMessage();
270
+ if (firstResult && firstResult.context_id) {
271
+ await continueConversation(firstResult.context_id);
272
+ }
273
+ }
274
+
275
+ fullConversationExample();`;
276
+
277
+ // Attachment example
278
+ const attachmentExample = `// File attachment example
279
+ async function sendWithAttachment() {
280
+ try {
281
+ // Example with text content (convert to base64)
282
+ const textContent = "Hello World from attachment!";
283
+ const base64Content = btoa(textContent);
284
+
285
+ const response = await fetch('${url}/api_message', {
286
+ method: 'POST',
287
+ headers: {
288
+ 'Content-Type': 'application/json',
289
+ 'X-API-KEY': '${token}'
290
+ },
291
+ body: JSON.stringify({
292
+ message: "Please analyze this file:",
293
+ attachments: [
294
+ {
295
+ filename: "document.txt",
296
+ base64: base64Content
297
+ }
298
+ ],
299
+ lifetime_hours: 12
300
+ })
301
+ });
302
+
303
+ const data = await response.json();
304
+
305
+ if (response.ok) {
306
+ console.log('✅ File sent successfully!');
307
+ console.log('Response:', data.response);
308
+ return data;
309
+ } else {
310
+ console.error('❌ Error:', data.error);
311
+ return null;
312
+ }
313
+ } catch (error) {
314
+ console.error('❌ Request failed:', error);
315
+ return null;
316
+ }
317
+ }
318
+
319
+ // Call the function
320
+ sendWithAttachment();`;
321
+
322
+ // Log GET example
323
+ const logGetExample = `// Get logs using GET request
324
+ async function getLogsGET(contextId, length = 50) {
325
+ try {
326
+ const params = new URLSearchParams({
327
+ context_id: contextId,
328
+ length: length.toString()
329
+ });
330
+
331
+ const response = await fetch('${url}/api_log_get?' + params, {
332
+ method: 'GET',
333
+ headers: {
334
+ 'X-API-KEY': '${token}'
335
+ }
336
+ });
337
+
338
+ const data = await response.json();
339
+
340
+ if (response.ok) {
341
+ console.log('✅ Logs retrieved successfully!');
342
+ console.log('Total items:', data.log.total_items);
343
+ console.log('Returned items:', data.log.returned_items);
344
+ console.log('Log items:', data.log.items);
345
+ return data;
346
+ } else {
347
+ console.error('❌ Error:', data.error);
348
+ return null;
349
+ }
350
+ } catch (error) {
351
+ console.error('❌ Request failed:', error);
352
+ return null;
353
+ }
354
+ }
355
+
356
+ // Example usage
357
+ getLogsGET('ctx_abc123', 20);`;
358
+
359
+ // Log POST example
360
+ const logPostExample = `// Get logs using POST request
361
+ async function getLogsPOST(contextId, length = 50) {
362
+ try {
363
+ const response = await fetch('${url}/api_log_get', {
364
+ method: 'POST',
365
+ headers: {
366
+ 'Content-Type': 'application/json',
367
+ 'X-API-KEY': '${token}'
368
+ },
369
+ body: JSON.stringify({
370
+ context_id: contextId,
371
+ length: length
372
+ })
373
+ });
374
+
375
+ const data = await response.json();
376
+
377
+ if (response.ok) {
378
+ console.log('✅ Logs retrieved successfully!');
379
+ console.log('Context ID:', data.context_id);
380
+ console.log('Log GUID:', data.log.guid);
381
+ console.log('Total items:', data.log.total_items);
382
+ console.log('Returned items:', data.log.returned_items);
383
+ console.log('Start position:', data.log.start_position);
384
+ console.log('Progress:', data.log.progress);
385
+ console.log('Log items:', data.log.items);
386
+ return data;
387
+ } else {
388
+ console.error('❌ Error:', data.error);
389
+ return null;
390
+ }
391
+ } catch (error) {
392
+ console.error('❌ Request failed:', error);
393
+ return null;
394
+ }
395
+ }
396
+
397
+ // Example usage - get latest 10 log entries
398
+ getLogsPOST('ctx_abc123', 10);`;
399
+
400
+ // Terminate chat example
401
+ const terminateExample = `// Basic terminate chat function
402
+ async function terminateChat(contextId) {
403
+ try {
404
+ const response = await fetch('${url}/api_terminate_chat', {
405
+ method: 'POST',
406
+ headers: {
407
+ 'Content-Type': 'application/json',
408
+ 'X-API-KEY': '${token}'
409
+ },
410
+ body: JSON.stringify({
411
+ context_id: contextId
412
+ })
413
+ });
414
+
415
+ const data = await response.json();
416
+
417
+ if (response.ok) {
418
+ console.log('✅ Chat deleted successfully!');
419
+ console.log('Message:', data.message);
420
+ return data;
421
+ } else {
422
+ console.error('❌ Error:', data.error);
423
+ return null;
424
+ }
425
+ } catch (error) {
426
+ console.error('❌ Request failed:', error);
427
+ return null;
428
+ }
429
+ }
430
+
431
+ // Example 1: Terminate a specific chat
432
+ terminateChat('ctx_abc123');
433
+
434
+ // Example 2: Complete workflow - send message, then terminate
435
+ async function simpleWorkflow() {
436
+ // Send a message
437
+ const result = await sendMessage();
438
+
439
+ if (result && result.context_id) {
440
+ console.log('Chat created:', result.context_id);
441
+
442
+ // Do some work with the chat...
443
+ // await continueConversation(result.context_id);
444
+
445
+ // Clean up when done
446
+ await terminateChat(result.context_id);
447
+ console.log('Chat cleaned up');
448
+ }
449
+ }
450
+
451
+ // Run the workflow
452
+ simpleWorkflow();`;
453
+
454
+ // Reset chat example
455
+ const resetExample = `// Basic reset chat function
456
+ async function resetChat(contextId) {
457
+ try {
458
+ const response = await fetch('${url}/api_reset_chat', {
459
+ method: 'POST',
460
+ headers: {
461
+ 'Content-Type': 'application/json',
462
+ 'X-API-KEY': '${token}'
463
+ },
464
+ body: JSON.stringify({
465
+ context_id: contextId
466
+ })
467
+ });
468
+
469
+ const data = await response.json();
470
+
471
+ if (response.ok) {
472
+ console.log('✅ Chat reset successfully!');
473
+ console.log('Message:', data.message);
474
+ console.log('Context ID:', data.context_id);
475
+ return data;
476
+ } else {
477
+ console.error('❌ Error:', data.error);
478
+ return null;
479
+ }
480
+ } catch (error) {
481
+ console.error('❌ Request failed:', error);
482
+ return null;
483
+ }
484
+ }
485
+
486
+ // Example 1: Reset a specific chat
487
+ resetChat('ctx_abc123');
488
+
489
+ // Example 2: Reset and continue conversation
490
+ async function resetAndContinue() {
491
+ const contextId = 'ctx_abc123';
492
+
493
+ // Reset the chat to clear history
494
+ const resetResult = await resetChat(contextId);
495
+
496
+ if (resetResult) {
497
+ console.log('Chat reset, starting fresh conversation...');
498
+
499
+ // Continue with same context_id but fresh history
500
+ const response = await fetch('${url}/api_message', {
501
+ method: 'POST',
502
+ headers: {
503
+ 'Content-Type': 'application/json',
504
+ 'X-API-KEY': '${token}'
505
+ },
506
+ body: JSON.stringify({
507
+ context_id: contextId, // Same context ID
508
+ message: "Hello, this is a fresh start!",
509
+ lifetime_hours: 24
510
+ })
511
+ });
512
+
513
+ const data = await response.json();
514
+ console.log('New conversation started:', data.response);
515
+ }
516
+ }
517
+
518
+ // Run the example
519
+ resetAndContinue();`;
520
+
521
+ // Files get example
522
+ const filesGetExample = `// Basic file retrieval
523
+ async function getFiles(filePaths) {
524
+ try {
525
+ const response = await fetch('${url}/api_files_get', {
526
+ method: 'POST',
527
+ headers: {
528
+ 'Content-Type': 'application/json',
529
+ 'X-API-KEY': '${token}'
530
+ },
531
+ body: JSON.stringify({
532
+ paths: filePaths
533
+ })
534
+ });
535
+
536
+ const data = await response.json();
537
+
538
+ if (response.ok) {
539
+ console.log('✅ Files retrieved successfully!');
540
+ console.log('Retrieved files:', Object.keys(data));
541
+
542
+ // Convert base64 back to text for display
543
+ for (const [filename, base64Content] of Object.entries(data)) {
544
+ try {
545
+ const textContent = atob(base64Content);
546
+ console.log(\`\${filename}: \${textContent.substring(0, 100)}...\`);
547
+ } catch (e) {
548
+ console.log(\`\${filename}: Binary file (\${base64Content.length} chars)\`);
549
+ }
550
+ }
551
+
552
+ return data;
553
+ } else {
554
+ console.error('❌ Error:', data.error);
555
+ return null;
556
+ }
557
+ } catch (error) {
558
+ console.error('❌ Request failed:', error);
559
+ return null;
560
+ }
561
+ }
562
+
563
+ // Example 1: Get specific files
564
+ const filePaths = [
565
+ "/a0/tmp/uploads/document.txt",
566
+ "/a0/tmp/uploads/data.json"
567
+ ];
568
+ getFiles(filePaths);
569
+
570
+ // Example 2: Complete attachment workflow
571
+ async function attachmentWorkflow() {
572
+ // Step 1: Send message with attachments
573
+ const messageResponse = await fetch('${url}/api_message', {
574
+ method: 'POST',
575
+ headers: {
576
+ 'Content-Type': 'application/json',
577
+ 'X-API-KEY': '${token}'
578
+ },
579
+ body: JSON.stringify({
580
+ message: "Please analyze this file",
581
+ attachments: [{
582
+ filename: "test.txt",
583
+ base64: btoa("Hello, this is test content!")
584
+ }],
585
+ lifetime_hours: 1
586
+ })
587
+ });
588
+
589
+ if (messageResponse.ok) {
590
+ console.log('Message sent with attachment');
591
+
592
+ // Step 2: Retrieve the uploaded file
593
+ const retrievedFiles = await getFiles(["/a0/tmp/uploads/test.txt"]);
594
+
595
+ if (retrievedFiles && retrievedFiles["test.txt"]) {
596
+ const originalContent = atob(retrievedFiles["test.txt"]);
597
+ console.log('Retrieved content:', originalContent);
598
+ }
599
+ }
600
+ }
601
+
602
+ // Run the complete workflow
603
+ attachmentWorkflow();`;
604
+
605
+ // Initialize ACE editors
606
+ const editors = [
607
+ { id: "api-basic-example", content: basicExample },
608
+ { id: "api-continuation-example", content: continuationExample },
609
+ { id: "api-attachment-example", content: attachmentExample },
610
+ { id: "api-log-get-example", content: logGetExample },
611
+ { id: "api-log-post-example", content: logPostExample },
612
+ { id: "api-terminate-example", content: terminateExample },
613
+ { id: "api-reset-example", content: resetExample },
614
+ { id: "api-files-get-example", content: filesGetExample }
615
+ ];
616
+
617
+ editors.forEach(({ id, content }) => {
618
+ const editor = ace.edit(id);
619
+ const dark = localStorage.getItem("darkMode");
620
+ if (dark != "false") {
621
+ editor.setTheme("ace/theme/github_dark");
622
+ } else {
623
+ editor.setTheme("ace/theme/tomorrow");
624
+ }
625
+ editor.session.setMode("ace/mode/javascript");
626
+ editor.setValue(content);
627
+ editor.clearSelection();
628
+ editor.setReadOnly(true);
629
+ });
630
+ }, 0);
631
+ </script>
632
+ </div>
633
+
634
+ <style>
635
+ #api-basic-example,
636
+ #api-continuation-example,
637
+ #api-attachment-example,
638
+ #api-log-get-example,
639
+ #api-log-post-example,
640
+ #api-terminate-example,
641
+ #api-reset-example,
642
+ #api-files-get-example {
643
+ width: 100%;
644
+ height: 20em;
645
+ margin: 8px 0 16px 0;
646
+ border-radius: 4px;
647
+ }
648
+
649
+ /* Section styling */
650
+ .api-section {
651
+ border: 1px solid var(--color-border);
652
+ border-radius: 8px;
653
+ margin: 20px 0;
654
+ padding: 16px;
655
+ }
656
+
657
+ .api-section h2 {
658
+ margin: 0 0 16px 0;
659
+ color: var(--color-text-primary);
660
+ border-bottom: 1px solid var(--color-border);
661
+ padding-bottom: 8px;
662
+ }
663
+
664
+ .api-section h3 {
665
+ margin: 20px 0 12px 0;
666
+ color: var(--color-text-primary);
667
+ font-size: 1.2em;
668
+ }
669
+
670
+ .api-section h4 {
671
+ margin: 16px 0 8px 0;
672
+ color: var(--color-text-primary);
673
+ }
674
+ </style>
675
+
676
+ </body>
677
+
678
+ </html>
webui/components/settings/mcp/server/example.html CHANGED
@@ -10,6 +10,15 @@
10
  <p>Agent Zero MCP Server is an SSE MCP running on the same URL and port as the Web UI + /mcp/sse path.</p>
11
  <p>The same applies if you run A0 on a public URL using a tunnel.</p>
12
 
 
 
 
 
 
 
 
 
 
13
  <h3>Example MCP Server Configuration JSON</h3>
14
  <div id="mcp-server-example"></div>
15
 
@@ -52,4 +61,4 @@
52
 
53
  </body>
54
 
55
- </html>
 
10
  <p>Agent Zero MCP Server is an SSE MCP running on the same URL and port as the Web UI + /mcp/sse path.</p>
11
  <p>The same applies if you run A0 on a public URL using a tunnel.</p>
12
 
13
+ <!-- API Token Information -->
14
+ <div style="background-color: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: 6px; padding: 12px; margin: 16px 0;">
15
+ <h4 style="margin: 0 0 8px 0; color: var(--color-text-primary);">API Token Information</h4>
16
+ <p style="margin: 0; color: var(--color-text-secondary); font-size: 14px;">
17
+ The token used in the URL is automatically generated from your username and password.
18
+ This same token is also used for external API endpoints. The token changes when you update your credentials.
19
+ </p>
20
+ </div>
21
+
22
  <h3>Example MCP Server Configuration JSON</h3>
23
  <div id="mcp-server-example"></div>
24
 
 
61
 
62
  </body>
63
 
64
+ </html>
webui/js/settings.js CHANGED
@@ -291,6 +291,8 @@ const settingsModalProxy = {
291
  openModal("settings/backup/backup.html");
292
  } else if (field.id === "backup_restore") {
293
  openModal("settings/backup/restore.html");
 
 
294
  }
295
  }
296
  };
 
291
  openModal("settings/backup/backup.html");
292
  } else if (field.id === "backup_restore") {
293
  openModal("settings/backup/restore.html");
294
+ } else if (field.id === "external_api_examples") {
295
+ openModal("settings/external/api-examples.html");
296
  }
297
  }
298
  };
webui/public/external_api.svg ADDED