supervaizer 0.10.16__tar.gz → 0.10.18__tar.gz

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.
Files changed (79) hide show
  1. {supervaizer-0.10.16 → supervaizer-0.10.18}/PKG-INFO +1 -1
  2. {supervaizer-0.10.16 → supervaizer-0.10.18}/pyproject.toml +1 -1
  3. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/__version__.py +1 -1
  4. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/routes.py +74 -65
  5. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/base.html +5 -2
  6. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/index.html +7 -2
  7. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/server.html +3 -10
  8. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/templates/index.html +2 -2
  9. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/examples/controller_template.py +22 -20
  10. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/server.py +72 -13
  11. {supervaizer-0.10.16 → supervaizer-0.10.18}/.gitignore +0 -0
  12. {supervaizer-0.10.16 → supervaizer-0.10.18}/LICENSE.md +0 -0
  13. {supervaizer-0.10.16 → supervaizer-0.10.18}/README.md +0 -0
  14. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/__init__.py +0 -0
  15. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/account.py +0 -0
  16. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/account_service.py +0 -0
  17. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/static/favicon.ico +0 -0
  18. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/static/js/job-start-form.js +0 -0
  19. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/agent_detail.html +0 -0
  20. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/agents.html +0 -0
  21. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/agents_grid.html +0 -0
  22. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/case_detail.html +0 -0
  23. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/cases_list.html +0 -0
  24. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/cases_table.html +0 -0
  25. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/console.html +0 -0
  26. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/dashboard.html +0 -0
  27. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/job_detail.html +0 -0
  28. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/job_start_test.html +0 -0
  29. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/jobs_list.html +0 -0
  30. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/jobs_table.html +0 -0
  31. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/navigation.html +0 -0
  32. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/recent_activity.html +0 -0
  33. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/server_status_cards.html +0 -0
  34. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/admin/templates/supervaize_instructions.html +0 -0
  35. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/agent.py +0 -0
  36. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/case.py +0 -0
  37. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/cli.py +0 -0
  38. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/common.py +0 -0
  39. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/__init__.py +0 -0
  40. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/cli.py +0 -0
  41. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/commands/__init__.py +0 -0
  42. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/commands/clean.py +0 -0
  43. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/commands/down.py +0 -0
  44. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/commands/local.py +0 -0
  45. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/commands/plan.py +0 -0
  46. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/commands/status.py +0 -0
  47. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/commands/up.py +0 -0
  48. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/docker.py +0 -0
  49. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/driver_factory.py +0 -0
  50. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/drivers/__init__.py +0 -0
  51. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/drivers/aws_app_runner.py +0 -0
  52. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/drivers/base.py +0 -0
  53. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/drivers/cloud_run.py +0 -0
  54. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/drivers/do_app_platform.py +0 -0
  55. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/health.py +0 -0
  56. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/state.py +0 -0
  57. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/templates/Dockerfile.template +0 -0
  58. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/templates/debug_env.py +0 -0
  59. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/templates/docker-compose.yml.template +0 -0
  60. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/templates/dockerignore.template +0 -0
  61. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/templates/entrypoint.sh +0 -0
  62. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/deploy/utils.py +0 -0
  63. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/event.py +0 -0
  64. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/instructions.py +0 -0
  65. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/job.py +0 -0
  66. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/job_service.py +0 -0
  67. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/lifecycle.py +0 -0
  68. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/parameter.py +0 -0
  69. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/protocol/__init__.py +0 -0
  70. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/protocol/a2a/__init__.py +0 -0
  71. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/protocol/a2a/model.py +0 -0
  72. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/protocol/a2a/routes.py +0 -0
  73. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/py.typed +0 -0
  74. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/routes.py +0 -0
  75. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/server_utils.py +0 -0
  76. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/storage.py +0 -0
  77. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/telemetry.py +0 -0
  78. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/utils/__init__.py +0 -0
  79. {supervaizer-0.10.16 → supervaizer-0.10.18}/src/supervaizer/utils/version_check.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supervaizer
3
- Version: 0.10.16
3
+ Version: 0.10.18
4
4
  Summary: Controller system for Supervaize
5
5
  Project-URL: Homepage, https://supervaize.com
6
6
  Project-URL: Repository, https://github.com/supervaize/supervaizer
@@ -121,7 +121,7 @@ mypy_path = "src"
121
121
  disallow_any_expr = false
122
122
 
123
123
  [tool.bumpversion]
124
- current_version = "0.10.16"
124
+ current_version = "0.10.18"
125
125
  commit = true
126
126
  tag = true
127
127
  tag_name = "v{new_version}"
@@ -5,6 +5,6 @@
5
5
  # https://mozilla.org/MPL/2.0/.
6
6
 
7
7
 
8
- VERSION = "0.10.16"
8
+ VERSION = "0.10.18"
9
9
  API_VERSION = "v1"
10
10
  TELEMETRY_VERSION = "v1"
@@ -20,7 +20,7 @@ from pathlib import Path
20
20
  from typing import Any, AsyncGenerator, Dict, List, Optional
21
21
 
22
22
  import psutil
23
- from fastapi import APIRouter, Depends, HTTPException, Query, Request, Security
23
+ from fastapi import APIRouter, HTTPException, Query, Request, Security
24
24
  from fastapi.responses import HTMLResponse, JSONResponse, Response
25
25
  from fastapi.security import APIKeyHeader
26
26
  from fastapi.templating import Jinja2Templates
@@ -168,12 +168,26 @@ def format_uptime(seconds: int) -> str:
168
168
  return f"{minutes}m"
169
169
 
170
170
 
171
- def get_server_status() -> ServerStatus:
172
- """Get current server status and metrics."""
173
- # Get server info from storage - required, no fallback
174
- from supervaizer.server import get_server_info_from_storage
171
+ def _get_server_info(request: Optional[Request]) -> Optional[Any]:
172
+ """Get server info from storage, or from live server (app.state.server) when no persistence."""
173
+ from supervaizer.server import (
174
+ get_server_info_from_live,
175
+ get_server_info_from_storage,
176
+ )
175
177
 
176
178
  server_info = get_server_info_from_storage()
179
+ if server_info is not None:
180
+ return server_info
181
+ if request is not None:
182
+ live = getattr(request.app.state, "server", None)
183
+ if live is not None:
184
+ return get_server_info_from_live(live)
185
+ return None
186
+
187
+
188
+ def get_server_status(request: Optional[Request] = None) -> ServerStatus:
189
+ """Get current server status and metrics."""
190
+ server_info = _get_server_info(request)
177
191
  if not server_info:
178
192
  raise HTTPException(
179
193
  status_code=503,
@@ -217,12 +231,11 @@ def get_server_status() -> ServerStatus:
217
231
  )
218
232
 
219
233
 
220
- def get_server_configuration(storage: StorageManager) -> ServerConfiguration:
234
+ def get_server_configuration(
235
+ storage: StorageManager, request: Optional[Request] = None
236
+ ) -> ServerConfiguration:
221
237
  """Get server configuration details."""
222
- # Get server info from storage - required, no fallback
223
- from supervaizer.server import get_server_info_from_storage
224
-
225
- server_info = get_server_info_from_storage()
238
+ server_info = _get_server_info(request)
226
239
  if not server_info:
227
240
  raise HTTPException(
228
241
  status_code=503,
@@ -304,9 +317,9 @@ def create_admin_routes() -> APIRouter:
304
317
  async def admin_server_page(request: Request) -> Response:
305
318
  """Server status and configuration page."""
306
319
  try:
307
- # Get initial server data
308
- server_status = get_server_status()
309
- server_config = get_server_configuration(storage)
320
+ # Get initial server data (from storage or live when no persistence)
321
+ server_status = get_server_status(request)
322
+ server_config = get_server_configuration(storage, request)
310
323
 
311
324
  return templates.TemplateResponse(
312
325
  request,
@@ -327,9 +340,7 @@ def create_admin_routes() -> APIRouter:
327
340
  async def admin_agents_page(request: Request) -> Response:
328
341
  """Agents management page."""
329
342
  try:
330
- from supervaizer.server import get_server_info_from_storage
331
-
332
- server_info = get_server_info_from_storage()
343
+ server_info = _get_server_info(request)
333
344
  if not server_info:
334
345
  raise HTTPException(
335
346
  status_code=503, detail="Server information not available"
@@ -400,7 +411,7 @@ def create_admin_routes() -> APIRouter:
400
411
  async def get_server_status_api(request: Request) -> Response:
401
412
  """Get current server status for HTMX refresh."""
402
413
  try:
403
- server_status = get_server_status()
414
+ server_status = get_server_status(request)
404
415
 
405
416
  return templates.TemplateResponse(
406
417
  request,
@@ -415,24 +426,22 @@ def create_admin_routes() -> APIRouter:
415
426
  raise HTTPException(status_code=500, detail=str(e))
416
427
 
417
428
  @router.post("/api/server/register")
418
- async def register_server_with_supervisor(
419
- request: Request,
420
- _: bool = Depends(verify_admin_access),
421
- ) -> JSONResponse:
422
- """Trigger SERVER_REGISTER to the supervaizer supervisor."""
429
+ async def register_server_with_supervisor(request: Request) -> JSONResponse:
430
+ """Trigger SERVER_REGISTER to the supervaizer supervisor (no frontend API key; backend sends to SUPERVAIZE_API_URL)."""
423
431
  try:
424
432
  from supervaizer.common import ApiSuccess
425
433
  from supervaizer.routes import get_server
426
434
 
427
- get_current = request.app.dependency_overrides.get(get_server)
428
- if get_current is None:
429
- raise HTTPException(
430
- status_code=503,
431
- detail="Server instance not available (admin not running with live server)",
432
- )
433
- try:
434
- server = await get_current()
435
- except (NotImplementedError, TypeError):
435
+ # Prefer app.state.server (set at launch), else dependency override
436
+ server = getattr(request.app.state, "server", None)
437
+ if server is None:
438
+ get_current = request.app.dependency_overrides.get(get_server)
439
+ if get_current is not None:
440
+ try:
441
+ server = await get_current()
442
+ except (NotImplementedError, TypeError):
443
+ pass
444
+ if server is None:
436
445
  raise HTTPException(
437
446
  status_code=503,
438
447
  detail="Server instance not available (admin not running with live server)",
@@ -477,9 +486,7 @@ def create_admin_routes() -> APIRouter:
477
486
  ) -> Response:
478
487
  """Get agents with filtering for HTMX refresh."""
479
488
  try:
480
- from supervaizer.server import get_server_info_from_storage
481
-
482
- server_info = get_server_info_from_storage()
489
+ server_info = _get_server_info(request)
483
490
  if not server_info:
484
491
  raise HTTPException(
485
492
  status_code=503, detail="Server information not available"
@@ -548,9 +555,7 @@ def create_admin_routes() -> APIRouter:
548
555
  ) -> Response:
549
556
  """Get detailed agent information."""
550
557
  try:
551
- from supervaizer.server import get_server_info_from_storage
552
-
553
- server_info = get_server_info_from_storage()
558
+ server_info = _get_server_info(request)
554
559
  if not server_info:
555
560
  raise HTTPException(
556
561
  status_code=503, detail="Server information not available"
@@ -926,24 +931,28 @@ def create_admin_routes() -> APIRouter:
926
931
  # Combine and sort by created_at
927
932
  activities = []
928
933
  for job in recent_jobs:
929
- activities.append({
930
- "type": "job",
931
- "id": job.get("id"),
932
- "name": job.get("name"),
933
- "status": job.get("status"),
934
- "created_at": job.get("created_at"),
935
- "agent_name": job.get("agent_name"),
936
- })
934
+ activities.append(
935
+ {
936
+ "type": "job",
937
+ "id": job.get("id"),
938
+ "name": job.get("name"),
939
+ "status": job.get("status"),
940
+ "created_at": job.get("created_at"),
941
+ "agent_name": job.get("agent_name"),
942
+ }
943
+ )
937
944
 
938
945
  for case in recent_cases:
939
- activities.append({
940
- "type": "case",
941
- "id": case.get("id"),
942
- "name": case.get("name"),
943
- "status": case.get("status"),
944
- "created_at": case.get("created_at"),
945
- "job_id": case.get("job_id"),
946
- })
946
+ activities.append(
947
+ {
948
+ "type": "case",
949
+ "id": case.get("id"),
950
+ "name": case.get("name"),
951
+ "status": case.get("status"),
952
+ "created_at": case.get("created_at"),
953
+ "job_id": case.get("job_id"),
954
+ }
955
+ )
947
956
 
948
957
  # Sort by created_at descending
949
958
  activities.sort(key=lambda x: str(x.get("created_at", "")), reverse=True)
@@ -1200,23 +1209,23 @@ def get_dashboard_stats(storage: StorageManager) -> AdminStats:
1200
1209
 
1201
1210
  # Calculate job stats
1202
1211
  job_total = len(all_jobs)
1203
- job_running = len([
1204
- j for j in all_jobs if j.get("status") in ["in_progress", "awaiting"]
1205
- ])
1212
+ job_running = len(
1213
+ [j for j in all_jobs if j.get("status") in ["in_progress", "awaiting"]]
1214
+ )
1206
1215
  job_completed = len([j for j in all_jobs if j.get("status") == "completed"])
1207
- job_failed = len([
1208
- j for j in all_jobs if j.get("status") in ["failed", "cancelled"]
1209
- ])
1216
+ job_failed = len(
1217
+ [j for j in all_jobs if j.get("status") in ["failed", "cancelled"]]
1218
+ )
1210
1219
 
1211
1220
  # Calculate case stats
1212
1221
  case_total = len(all_cases)
1213
- case_running = len([
1214
- c for c in all_cases if c.get("status") in ["in_progress", "awaiting"]
1215
- ])
1222
+ case_running = len(
1223
+ [c for c in all_cases if c.get("status") in ["in_progress", "awaiting"]]
1224
+ )
1216
1225
  case_completed = len([c for c in all_cases if c.get("status") == "completed"])
1217
- case_failed = len([
1218
- c for c in all_cases if c.get("status") in ["failed", "cancelled"]
1219
- ])
1226
+ case_failed = len(
1227
+ [c for c in all_cases if c.get("status") in ["failed", "cancelled"]]
1228
+ )
1220
1229
 
1221
1230
  # TinyDB collections count (tables)
1222
1231
  collections_count = len(storage._db.tables())
@@ -35,11 +35,14 @@
35
35
  // Persist admin API key from URL so it survives navigation (e.g. Register with supervisor)
36
36
  (function() {
37
37
  var m = /[?&]key=([^&]+)/.exec(window.location.search);
38
- if (m) try { sessionStorage.setItem('admin_api_key', decodeURIComponent(m[1])); } catch (e) {}
38
+ if (m) try {
39
+ var k = decodeURIComponent(m[1]);
40
+ if (k !== 'None' && k !== 'undefined') sessionStorage.setItem('admin_api_key', k);
41
+ } catch (e) {}
39
42
  })();
40
43
  </script>
41
44
  </head>
42
- <body class="bg-gray-50 min-h-screen">
45
+ <body class="bg-gray-50 min-h-screen" data-admin-key="{{ (api_key or '')|e }}">
43
46
  <!-- Navigation -->
44
47
  {% include "navigation.html" %}
45
48
 
@@ -18,6 +18,11 @@
18
18
  </p>
19
19
  <p class="mt-1 text-xs text-gray-400 font-mono">Server ID: {{ server_id }}</p>
20
20
 
21
+ <div class="mt-6 p-4 rounded-lg bg-amber-50 border border-amber-200 text-amber-800">
22
+ <p class="font-semibold">⚠️ Do not use in production with network access enabled.</p>
23
+ <p class="mt-1 text-sm">This interface is for development and local use only. Exposing it over the network in production may allow unauthorized access.</p>
24
+ </div>
25
+
21
26
  <div class="mt-8">
22
27
  <h2 class="text-lg font-medium text-gray-900 mb-4">Links</h2>
23
28
  <ul class="space-y-2">
@@ -36,14 +41,14 @@
36
41
  OpenAPI
37
42
  </a>
38
43
  </li>
39
- <li><strong>BASE:</strong> {{ base }}
44
+ <li><strong>BASE:</strong> {{ base }}
40
45
  <br>
41
46
  <strong>PUBLIC_URL:</strong> {{ public_url }}
42
47
  <br>
43
48
  <strong>FULL_URL:</strong> {{ full_url }}
44
49
  <br>
45
50
  </li>
46
-
51
+
47
52
  {% if show_admin %}
48
53
  <li>
49
54
  <a href="{{ base }}/admin" class="text-blue-600 hover:text-blue-800 font-medium">
@@ -109,20 +109,17 @@
109
109
  }
110
110
  }, 30000);
111
111
 
112
- // Register with supervisor button
112
+ // Register with supervisor button — no API key; backend sends SERVER_REGISTER to SUPERVAIZE_API_URL
113
113
  const registerBtn = document.getElementById('register-supervisor-btn');
114
114
  const registerIndicator = document.getElementById('register-indicator');
115
115
  const registerResult = document.getElementById('register-result');
116
116
  if (registerBtn) {
117
117
  registerBtn.addEventListener('click', async function() {
118
- const params = new URLSearchParams(window.location.search);
119
- const key = params.get('key') || (function() { try { return sessionStorage.getItem('admin_api_key'); } catch (e) { return null; } })();
120
- const url = key ? '/admin/api/server/register?key=' + encodeURIComponent(key) : '/admin/api/server/register';
121
118
  registerResult.classList.add('hidden');
122
119
  registerIndicator.classList.remove('htmx-indicator');
123
120
  registerBtn.disabled = true;
124
121
  try {
125
- const res = await fetch(url, { method: 'POST' });
122
+ const res = await fetch('/admin/api/server/register', { method: 'POST' });
126
123
  const data = await res.json().catch(function() { return {}; });
127
124
  registerResult.classList.remove('hidden');
128
125
  if (res.ok && data.success) {
@@ -130,11 +127,7 @@
130
127
  registerResult.textContent = data.message || 'Registered successfully.';
131
128
  } else {
132
129
  registerResult.className = 'mt-2 text-sm text-red-700';
133
- if (res.status === 403) {
134
- registerResult.textContent = 'Authentication required. Open this page with ?key=...';
135
- } else {
136
- registerResult.textContent = (data.detail && typeof data.detail === 'object' && data.detail.message) ? data.detail.message : (data.message || 'Registration failed.');
137
- }
130
+ registerResult.textContent = (data.detail && typeof data.detail === 'object' && data.detail.message) ? data.detail.message : (data.message || data.detail || 'Registration failed.');
138
131
  }
139
132
  } catch (e) {
140
133
  registerResult.classList.remove('hidden');
@@ -46,8 +46,8 @@
46
46
  <a href="/console" class="text-blue-600 hover:text-blue-800 font-medium">
47
47
  Console
48
48
  </a>
49
- </li>
50
- <li> BASE: {{ base }}
49
+ </li>
50
+ <li> BASE: {{ base }}
51
51
  </li>
52
52
  </ul>
53
53
  </div>
@@ -34,26 +34,28 @@ DEV_PUBLIC_URL = "https://myagent-dev.loca.lt"
34
34
  PROD_PUBLIC_URL = "https://myagent.cloud-hosting.net:8001"
35
35
 
36
36
  # Define the parameters and secrets expected by the agent
37
- agent_parameters: ParametersSetup | None = ParametersSetup.from_list([
38
- Parameter(
39
- name="OPEN_API_KEY",
40
- description="OpenAPI Key",
41
- is_environment=True,
42
- is_secret=True,
43
- ),
44
- Parameter(
45
- name="SERPER_API",
46
- description="Server API key updated",
47
- is_environment=True,
48
- is_secret=True,
49
- ),
50
- Parameter(
51
- name="COMPETITOR_SUMMARY_URL",
52
- description="Competitor Summary URL",
53
- is_environment=True,
54
- is_secret=False,
55
- ),
56
- ])
37
+ agent_parameters: ParametersSetup | None = ParametersSetup.from_list(
38
+ [
39
+ Parameter(
40
+ name="OPEN_API_KEY",
41
+ description="OpenAPI Key",
42
+ is_environment=True,
43
+ is_secret=True,
44
+ ),
45
+ Parameter(
46
+ name="SERPER_API",
47
+ description="Server API key updated",
48
+ is_environment=True,
49
+ is_secret=True,
50
+ ),
51
+ Parameter(
52
+ name="COMPETITOR_SUMMARY_URL",
53
+ description="Competitor Summary URL",
54
+ is_environment=True,
55
+ is_secret=False,
56
+ ),
57
+ ]
58
+ )
57
59
 
58
60
  # Define the method used to start a job
59
61
  job_start_method: AgentMethod = AgentMethod(
@@ -65,6 +65,35 @@ def _get_or_create_server_id() -> str:
65
65
  return new_id
66
66
 
67
67
 
68
+ def _get_or_create_private_key() -> RSAPrivateKey:
69
+ """Use SUPERVAIZER_PRIVATE_KEY from env if set; else create key and set env."""
70
+ pem = os.getenv("SUPERVAIZER_PRIVATE_KEY")
71
+ if pem:
72
+ try:
73
+ return serialization.load_pem_private_key(
74
+ pem.encode("utf-8"),
75
+ password=None,
76
+ backend=default_backend(),
77
+ )
78
+ except Exception as e:
79
+ log.warning(
80
+ f"[Server] Invalid SUPERVAIZER_PRIVATE_KEY, generating new key: {e}"
81
+ )
82
+ private_key = rsa.generate_private_key(
83
+ public_exponent=65537,
84
+ key_size=2048,
85
+ backend=default_backend(),
86
+ )
87
+ pem_bytes = private_key.private_bytes(
88
+ encoding=serialization.Encoding.PEM,
89
+ format=serialization.PrivateFormat.PKCS8,
90
+ encryption_algorithm=serialization.NoEncryption(),
91
+ )
92
+ os.environ["SUPERVAIZER_PRIVATE_KEY"] = pem_bytes.decode("utf-8")
93
+ log.info("[Server] Generated new RSA private key and set SUPERVAIZER_PRIVATE_KEY")
94
+ return private_key
95
+
96
+
68
97
  class ServerInfo(BaseModel):
69
98
  """Complete server information for storage."""
70
99
 
@@ -88,14 +117,16 @@ def save_server_info_to_storage(server_instance: "Server") -> None:
88
117
  agents = []
89
118
  if hasattr(server_instance, "agents") and server_instance.agents:
90
119
  for agent in server_instance.agents:
91
- agents.append({
92
- "name": agent.name,
93
- "description": agent.description,
94
- "version": agent.version,
95
- "api_path": agent.path,
96
- "slug": agent.slug,
97
- "instructions_path": agent.instructions_path,
98
- })
120
+ agents.append(
121
+ {
122
+ "name": agent.name,
123
+ "description": agent.description,
124
+ "version": agent.version,
125
+ "api_path": agent.path,
126
+ "slug": agent.slug,
127
+ "instructions_path": agent.instructions_path,
128
+ }
129
+ )
99
130
 
100
131
  # Create server info
101
132
  server_info = ServerInfo(
@@ -131,6 +162,34 @@ def get_server_info_from_storage() -> Optional[ServerInfo]:
131
162
  return None
132
163
 
133
164
 
165
+ def get_server_info_from_live(server_instance: "Server") -> ServerInfo:
166
+ """Build ServerInfo from a live Server instance (for when storage has no ServerInfo, e.g. no persistence)."""
167
+ agents = []
168
+ if hasattr(server_instance, "agents") and server_instance.agents:
169
+ for agent in server_instance.agents:
170
+ agents.append(
171
+ {
172
+ "name": agent.name,
173
+ "description": agent.description,
174
+ "version": agent.version,
175
+ "api_path": agent.path,
176
+ "slug": agent.slug,
177
+ "instructions_path": agent.instructions_path,
178
+ }
179
+ )
180
+ start_time = getattr(server_instance, "_start_time", time.time())
181
+ return ServerInfo(
182
+ host=getattr(server_instance, "host", "N/A"),
183
+ port=getattr(server_instance, "port", 0),
184
+ api_version=API_VERSION,
185
+ environment=os.getenv("SUPERVAIZER_ENVIRONMENT", "development"),
186
+ agents=agents,
187
+ start_time=start_time,
188
+ created_at=datetime.now().isoformat(),
189
+ updated_at=datetime.now().isoformat(),
190
+ )
191
+
192
+
134
193
  class ServerAbstract(SvBaseModel):
135
194
  """
136
195
  API Server for the Supervaize Controller.
@@ -291,11 +350,7 @@ class Server(ServerAbstract):
291
350
  )
292
351
 
293
352
  if private_key is None:
294
- private_key = rsa.generate_private_key(
295
- public_exponent=65537,
296
- key_size=2048,
297
- backend=default_backend(),
298
- )
353
+ private_key = _get_or_create_private_key()
299
354
 
300
355
  public_key = private_key.public_key()
301
356
 
@@ -428,6 +483,10 @@ class Server(ServerAbstract):
428
483
  # Update the dependency
429
484
  self.app.dependency_overrides[get_server] = get_current_server
430
485
 
486
+ # Expose live server for admin when storage has no ServerInfo (e.g. no persistence)
487
+ self._start_time = time.time()
488
+ self.app.state.server = self
489
+
431
490
  if api_key:
432
491
  log.info("[Server launch] API Key authentication enabled")
433
492
  # Print the API key if it was generated
File without changes
File without changes
File without changes