supervaizer 0.10.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. supervaizer/__init__.py +97 -0
  2. supervaizer/__version__.py +10 -0
  3. supervaizer/account.py +308 -0
  4. supervaizer/account_service.py +93 -0
  5. supervaizer/admin/routes.py +1293 -0
  6. supervaizer/admin/static/js/job-start-form.js +373 -0
  7. supervaizer/admin/templates/agent_detail.html +145 -0
  8. supervaizer/admin/templates/agents.html +249 -0
  9. supervaizer/admin/templates/agents_grid.html +82 -0
  10. supervaizer/admin/templates/base.html +233 -0
  11. supervaizer/admin/templates/case_detail.html +230 -0
  12. supervaizer/admin/templates/cases_list.html +182 -0
  13. supervaizer/admin/templates/cases_table.html +134 -0
  14. supervaizer/admin/templates/console.html +389 -0
  15. supervaizer/admin/templates/dashboard.html +153 -0
  16. supervaizer/admin/templates/job_detail.html +192 -0
  17. supervaizer/admin/templates/job_start_test.html +109 -0
  18. supervaizer/admin/templates/jobs_list.html +180 -0
  19. supervaizer/admin/templates/jobs_table.html +122 -0
  20. supervaizer/admin/templates/navigation.html +163 -0
  21. supervaizer/admin/templates/recent_activity.html +81 -0
  22. supervaizer/admin/templates/server.html +105 -0
  23. supervaizer/admin/templates/server_status_cards.html +121 -0
  24. supervaizer/admin/templates/supervaize_instructions.html +212 -0
  25. supervaizer/agent.py +956 -0
  26. supervaizer/case.py +432 -0
  27. supervaizer/cli.py +395 -0
  28. supervaizer/common.py +324 -0
  29. supervaizer/deploy/__init__.py +16 -0
  30. supervaizer/deploy/cli.py +305 -0
  31. supervaizer/deploy/commands/__init__.py +9 -0
  32. supervaizer/deploy/commands/clean.py +294 -0
  33. supervaizer/deploy/commands/down.py +119 -0
  34. supervaizer/deploy/commands/local.py +460 -0
  35. supervaizer/deploy/commands/plan.py +167 -0
  36. supervaizer/deploy/commands/status.py +169 -0
  37. supervaizer/deploy/commands/up.py +281 -0
  38. supervaizer/deploy/docker.py +377 -0
  39. supervaizer/deploy/driver_factory.py +42 -0
  40. supervaizer/deploy/drivers/__init__.py +39 -0
  41. supervaizer/deploy/drivers/aws_app_runner.py +607 -0
  42. supervaizer/deploy/drivers/base.py +196 -0
  43. supervaizer/deploy/drivers/cloud_run.py +570 -0
  44. supervaizer/deploy/drivers/do_app_platform.py +504 -0
  45. supervaizer/deploy/health.py +404 -0
  46. supervaizer/deploy/state.py +210 -0
  47. supervaizer/deploy/templates/Dockerfile.template +44 -0
  48. supervaizer/deploy/templates/debug_env.py +69 -0
  49. supervaizer/deploy/templates/docker-compose.yml.template +37 -0
  50. supervaizer/deploy/templates/dockerignore.template +66 -0
  51. supervaizer/deploy/templates/entrypoint.sh +20 -0
  52. supervaizer/deploy/utils.py +52 -0
  53. supervaizer/event.py +181 -0
  54. supervaizer/examples/controller_template.py +196 -0
  55. supervaizer/instructions.py +145 -0
  56. supervaizer/job.py +392 -0
  57. supervaizer/job_service.py +156 -0
  58. supervaizer/lifecycle.py +417 -0
  59. supervaizer/parameter.py +233 -0
  60. supervaizer/protocol/__init__.py +11 -0
  61. supervaizer/protocol/a2a/__init__.py +21 -0
  62. supervaizer/protocol/a2a/model.py +227 -0
  63. supervaizer/protocol/a2a/routes.py +99 -0
  64. supervaizer/py.typed +1 -0
  65. supervaizer/routes.py +917 -0
  66. supervaizer/server.py +553 -0
  67. supervaizer/server_utils.py +54 -0
  68. supervaizer/storage.py +462 -0
  69. supervaizer/telemetry.py +81 -0
  70. supervaizer/utils/__init__.py +16 -0
  71. supervaizer/utils/version_check.py +56 -0
  72. supervaizer-0.10.5.dist-info/METADATA +317 -0
  73. supervaizer-0.10.5.dist-info/RECORD +76 -0
  74. supervaizer-0.10.5.dist-info/WHEEL +4 -0
  75. supervaizer-0.10.5.dist-info/entry_points.txt +2 -0
  76. supervaizer-0.10.5.dist-info/licenses/LICENSE.md +346 -0
@@ -0,0 +1,169 @@
1
+ # Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
+ # If a copy of the MPL was not distributed with this file, you can obtain one at
5
+ # https://mozilla.org/MPL/2.0/.
6
+
7
+ """
8
+ Status Command
9
+
10
+ Show deployment status and health information.
11
+ """
12
+
13
+ from pathlib import Path
14
+ from typing import Optional
15
+
16
+ from rich.console import Console
17
+ from rich.table import Table
18
+
19
+ from supervaizer.common import log
20
+ from supervaizer.deploy.driver_factory import create_driver, get_supported_platforms
21
+ from supervaizer.deploy.drivers.base import DeploymentResult
22
+ from supervaizer.deploy.state import DeploymentState, StateManager
23
+
24
+ console = Console()
25
+
26
+
27
+ def deploy_status(
28
+ platform: str,
29
+ name: Optional[str] = None,
30
+ env: str = "dev",
31
+ region: Optional[str] = None,
32
+ project_id: Optional[str] = None,
33
+ verbose: bool = False,
34
+ source_dir: Optional[Path] = None,
35
+ ) -> None:
36
+ """Show deployment status and health information."""
37
+ # Validate platform
38
+ if platform not in get_supported_platforms():
39
+ console.print(f"[bold red]Error:[/] Unsupported platform: {platform}")
40
+ console.print(f"Supported platforms: {', '.join(get_supported_platforms())}")
41
+ return
42
+
43
+ # Set defaults
44
+ if not name:
45
+ name = (source_dir or Path.cwd()).name
46
+ if not region:
47
+ region = _get_default_region(platform)
48
+
49
+ console.print(f"[bold blue]Deployment Status for {name}-{env}[/bold blue]")
50
+ console.print(f"Platform: {platform}")
51
+ console.print(f"Region: {region}")
52
+ if project_id:
53
+ console.print(f"Project ID: {project_id}")
54
+
55
+ try:
56
+ # Check local state first
57
+ deployment_dir = (source_dir or Path.cwd()) / ".deployment"
58
+ if deployment_dir.exists():
59
+ state_manager = StateManager(deployment_dir)
60
+ state = state_manager.load_state()
61
+
62
+ if state:
63
+ console.print("\n[bold]Local State:[/bold]")
64
+ _display_state(state)
65
+
66
+ # Get live status from platform
67
+ driver = create_driver(platform, region, project_id)
68
+
69
+ # Check prerequisites
70
+ prerequisites = driver.check_prerequisites()
71
+ if prerequisites:
72
+ console.print("[bold red]Prerequisites not met:[/]")
73
+ for prereq in prerequisites:
74
+ console.print(f" • {prereq}")
75
+ return
76
+
77
+ # Get service status
78
+ result = driver.get_service_status(name, env)
79
+
80
+ if result.success:
81
+ console.print("\n[bold]Live Status:[/bold]")
82
+ _display_service_status(result)
83
+ else:
84
+ console.print(
85
+ f"\n[bold red]Service not found or error:[/] {result.error_message}"
86
+ )
87
+
88
+ except Exception as e:
89
+ log.error(f"Status check failed: {e}")
90
+ console.print(f"[bold red]Status check failed:[/] {e}")
91
+
92
+
93
+ def _get_default_region(platform: str) -> str:
94
+ """Get default region for platform."""
95
+ defaults = {
96
+ "cloud-run": "us-central1",
97
+ "aws-app-runner": "us-east-1",
98
+ "do-app-platform": "nyc3",
99
+ }
100
+ return defaults.get(platform, "us-central1")
101
+
102
+
103
+ def _display_state(state: DeploymentState) -> None:
104
+ """Display deployment state."""
105
+ table = Table(title="Deployment State")
106
+ table.add_column("Property", style="cyan")
107
+ table.add_column("Value", style="white")
108
+
109
+ table.add_row("Service Name", state.service_name)
110
+ table.add_row("Platform", state.platform)
111
+ table.add_row("Environment", state.environment)
112
+ table.add_row("Region", state.region)
113
+ if state.project_id:
114
+ table.add_row("Project ID", state.project_id)
115
+ table.add_row("Image Tag", state.image_tag)
116
+ if state.image_digest:
117
+ table.add_row("Image Digest", state.image_digest[:16] + "...")
118
+ if state.service_url:
119
+ table.add_row("Service URL", state.service_url)
120
+ if state.revision:
121
+ table.add_row("Revision", state.revision)
122
+ table.add_row("Status", state.status)
123
+ table.add_row("Health Status", state.health_status)
124
+ table.add_row("Port", str(state.port))
125
+ table.add_row("API Key Generated", str(state.api_key_generated))
126
+ table.add_row("RSA Key Generated", str(state.rsa_key_generated))
127
+ table.add_row("Created At", state.created_at.isoformat())
128
+ table.add_row("Updated At", state.updated_at.isoformat())
129
+
130
+ console.print(table)
131
+
132
+
133
+ def _display_service_status(result: DeploymentResult) -> None:
134
+ """Display service status."""
135
+ table = Table(title="Service Status")
136
+ table.add_column("Property", style="cyan")
137
+ table.add_column("Value", style="white")
138
+
139
+ if result.service_url:
140
+ table.add_row("Service URL", result.service_url)
141
+ table.add_row("API Documentation", f"{result.service_url}/docs")
142
+ table.add_row("ReDoc Documentation", f"{result.service_url}/redoc")
143
+
144
+ if result.service_id:
145
+ table.add_row("Service ID", result.service_id)
146
+
147
+ if result.revision:
148
+ table.add_row("Revision", result.revision)
149
+
150
+ if result.image_digest:
151
+ table.add_row("Image Digest", result.image_digest[:16] + "...")
152
+
153
+ table.add_row("Status", result.status)
154
+ table.add_row("Health Status", result.health_status)
155
+
156
+ if result.deployment_time:
157
+ table.add_row("Deployment Time", f"{result.deployment_time:.1f}s")
158
+
159
+ console.print(table)
160
+
161
+ # Health check details
162
+ if result.service_url:
163
+ console.print("\n[bold]Health Check:[/bold]")
164
+ if result.health_status == "healthy":
165
+ console.print("[green]✓[/green] Service is healthy")
166
+ elif result.health_status == "unhealthy":
167
+ console.print("[red]✗[/red] Service is unhealthy")
168
+ else:
169
+ console.print("[yellow]?[/yellow] Health status unknown")
@@ -0,0 +1,281 @@
1
+ # Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
+ # If a copy of the MPL was not distributed with this file, you can obtain one at
5
+ # https://mozilla.org/MPL/2.0/.
6
+
7
+ """
8
+ Up Command
9
+
10
+ Deploy or update the service.
11
+ """
12
+
13
+ import secrets
14
+ import string
15
+ from pathlib import Path
16
+ from typing import Optional
17
+
18
+ from rich.console import Console
19
+ from rich.progress import Progress, SpinnerColumn, TextColumn
20
+
21
+ from supervaizer.common import log
22
+ from supervaizer.deploy.docker import DockerManager, ensure_docker_running
23
+ from supervaizer.deploy.driver_factory import create_driver, get_supported_platforms
24
+ from supervaizer.deploy.drivers.base import DeploymentResult
25
+ from supervaizer.deploy.state import StateManager
26
+ from supervaizer.deploy.utils import create_deployment_directory, get_git_sha
27
+
28
+ console = Console()
29
+
30
+
31
+ def deploy_up(
32
+ platform: str,
33
+ name: Optional[str] = None,
34
+ env: str = "dev",
35
+ region: Optional[str] = None,
36
+ project_id: Optional[str] = None,
37
+ image: Optional[str] = None,
38
+ port: int = 8000,
39
+ generate_api_key: bool = False,
40
+ generate_rsa: bool = False,
41
+ yes: bool = False,
42
+ no_rollback: bool = False,
43
+ timeout: int = 300,
44
+ verbose: bool = False,
45
+ source_dir: Optional[Path] = None,
46
+ ) -> None:
47
+ """Deploy or update the service."""
48
+ # Validate platform
49
+ if platform not in get_supported_platforms():
50
+ console.print(f"[bold red]Error:[/] Unsupported platform: {platform}")
51
+ console.print(f"Supported platforms: {', '.join(get_supported_platforms())}")
52
+ return
53
+
54
+ # Set defaults
55
+ if not name:
56
+ name = (source_dir or Path.cwd()).name
57
+ if not region:
58
+ region = _get_default_region(platform)
59
+
60
+ console.print(f"[bold green]Deploying to {platform}[/bold green]")
61
+ console.print(f"Service name: {name}")
62
+ console.print(f"Environment: {env}")
63
+ console.print(f"Region: {region}")
64
+ console.print(f"Port: {port}")
65
+ if project_id:
66
+ console.print(f"Project ID: {project_id}")
67
+ if image:
68
+ console.print(f"Image: {image}")
69
+
70
+ try:
71
+ # Check Docker
72
+ if not ensure_docker_running():
73
+ console.print("[bold red]Error:[/] Docker is not running")
74
+ return
75
+
76
+ # Create deployment directory
77
+ deployment_dir = create_deployment_directory(source_dir or Path.cwd())
78
+ state_manager = StateManager(deployment_dir)
79
+
80
+ # Create driver
81
+ driver = create_driver(platform, region, project_id)
82
+
83
+ # Check prerequisites
84
+ prerequisites = driver.check_prerequisites()
85
+ if prerequisites:
86
+ console.print("[bold red]Prerequisites not met:[/]")
87
+ for prereq in prerequisites:
88
+ console.print(f" • {prereq}")
89
+ return
90
+
91
+ # Generate image tag
92
+ if not image:
93
+ image = _generate_image_tag(name, env)
94
+
95
+ # Generate secrets
96
+ secrets_dict = _generate_secrets(name, env, generate_api_key, generate_rsa)
97
+
98
+ # Build and push Docker image
99
+ with Progress(
100
+ SpinnerColumn(),
101
+ TextColumn("[progress.description]{task.description}"),
102
+ console=console,
103
+ ) as progress:
104
+ task = progress.add_task("Building Docker image...", total=None)
105
+
106
+ docker_manager = DockerManager()
107
+
108
+ # Generate Docker files
109
+ dockerfile_path = deployment_dir / "Dockerfile"
110
+ dockerignore_path = deployment_dir / ".dockerignore"
111
+ compose_path = deployment_dir / "docker-compose.yml"
112
+
113
+ docker_manager.generate_dockerfile(
114
+ output_path=dockerfile_path,
115
+ app_port=port,
116
+ )
117
+ docker_manager.generate_dockerignore(dockerignore_path)
118
+ docker_manager.generate_docker_compose(
119
+ compose_path,
120
+ port=port,
121
+ service_name=name,
122
+ environment=env,
123
+ api_key=secrets_dict.get("api_key", "test-api-key"),
124
+ rsa_key=secrets_dict.get("rsa_private_key", "test-rsa-key"),
125
+ )
126
+
127
+ # Build image
128
+ progress.update(task, description="Building Docker image...")
129
+
130
+ # Get build arguments for environment variables
131
+ from supervaizer.deploy.docker import get_docker_build_args
132
+
133
+ build_args = get_docker_build_args(port)
134
+
135
+ docker_manager.build_image(
136
+ image, source_dir or Path.cwd(), dockerfile_path, build_args=build_args
137
+ )
138
+
139
+ # Push image (this would be platform-specific)
140
+ progress.update(task, description="Pushing Docker image...")
141
+ # Note: Actual push would depend on the platform's registry
142
+
143
+ # Deploy service
144
+ with Progress(
145
+ SpinnerColumn(),
146
+ TextColumn("[progress.description]{task.description}"),
147
+ console=console,
148
+ ) as progress:
149
+ task = progress.add_task("Deploying service...", total=None)
150
+
151
+ result = driver.deploy_service(
152
+ service_name=name,
153
+ environment=env,
154
+ image_tag=image,
155
+ port=port,
156
+ env_vars=_get_default_env_vars(env),
157
+ secrets=secrets_dict,
158
+ timeout=timeout,
159
+ )
160
+
161
+ # Update state
162
+ if result.success:
163
+ state_manager.update_state(
164
+ service_name=name,
165
+ platform=platform,
166
+ environment=env,
167
+ region=region,
168
+ project_id=project_id,
169
+ image_tag=image,
170
+ image_digest=result.image_digest,
171
+ service_url=result.service_url,
172
+ revision=result.revision,
173
+ status=result.status,
174
+ health_status=result.health_status,
175
+ port=port,
176
+ api_key_generated=generate_api_key,
177
+ rsa_key_generated=generate_rsa,
178
+ )
179
+
180
+ # Display results
181
+ _display_deployment_result(result)
182
+ else:
183
+ console.print(f"[bold red]Deployment failed:[/] {result.error_message}")
184
+ if not no_rollback:
185
+ console.print(
186
+ "[yellow]Consider using --no-rollback to keep failed revision[/yellow]"
187
+ )
188
+
189
+ except Exception as e:
190
+ log.error(f"Deployment failed: {e}")
191
+ console.print(f"[bold red]Deployment failed:[/] {e}")
192
+
193
+
194
+ def _get_default_region(platform: str) -> str:
195
+ """Get default region for platform."""
196
+ defaults = {
197
+ "cloud-run": "us-central1",
198
+ "aws-app-runner": "us-east-1",
199
+ "do-app-platform": "nyc3",
200
+ }
201
+ return defaults.get(platform, "us-central1")
202
+
203
+
204
+ def _generate_image_tag(service_name: str, environment: str) -> str:
205
+ """Generate image tag for deployment."""
206
+ git_sha = get_git_sha()
207
+ return f"{service_name}-{environment}:{git_sha}"
208
+
209
+
210
+ def _generate_secrets(
211
+ service_name: str, environment: str, generate_api_key: bool, generate_rsa: bool
212
+ ) -> dict[str, str]:
213
+ """Generate secrets for deployment."""
214
+ secrets_dict = {}
215
+
216
+ if generate_api_key:
217
+ api_key = _generate_api_key()
218
+ secrets_dict[f"{service_name}-{environment}-api-key"] = api_key
219
+
220
+ if generate_rsa:
221
+ rsa_key = _generate_rsa_key()
222
+ secrets_dict[f"{service_name}-{environment}-rsa-key"] = rsa_key
223
+
224
+ return secrets_dict
225
+
226
+
227
+ def _generate_api_key() -> str:
228
+ """Generate a secure API key."""
229
+ alphabet = string.ascii_letters + string.digits
230
+ return "".join(secrets.choice(alphabet) for _ in range(32))
231
+
232
+
233
+ def _generate_rsa_key() -> str:
234
+ """Generate an RSA private key."""
235
+ from cryptography.hazmat.primitives import serialization
236
+ from cryptography.hazmat.primitives.asymmetric import rsa
237
+
238
+ private_key = rsa.generate_private_key(
239
+ public_exponent=65537,
240
+ key_size=2048,
241
+ )
242
+
243
+ pem = private_key.private_bytes(
244
+ encoding=serialization.Encoding.PEM,
245
+ format=serialization.PrivateFormat.PKCS8,
246
+ encryption_algorithm=serialization.NoEncryption(),
247
+ )
248
+
249
+ return pem.decode("utf-8")
250
+
251
+
252
+ def _get_default_env_vars(environment: str) -> dict[str, str]:
253
+ """Get default environment variables."""
254
+ return {
255
+ "SUPERVAIZER_ENVIRONMENT": environment,
256
+ "SUPERVAIZER_HOST": "0.0.0.0",
257
+ "SUPERVAIZER_PORT": "8000",
258
+ "SV_LOG_LEVEL": "INFO",
259
+ }
260
+
261
+
262
+ def _display_deployment_result(result: DeploymentResult) -> None:
263
+ """Display deployment result."""
264
+ console.print("\n[bold green]Deployment successful![/bold green]")
265
+
266
+ if result.service_url:
267
+ console.print(f"Service URL: {result.service_url}")
268
+ console.print(f"API Documentation: {result.service_url}/docs")
269
+ console.print(f"ReDoc Documentation: {result.service_url}/redoc")
270
+
271
+ if result.service_id:
272
+ console.print(f"Service ID: {result.service_id}")
273
+
274
+ if result.revision:
275
+ console.print(f"Revision: {result.revision}")
276
+
277
+ console.print(f"Status: {result.status}")
278
+ console.print(f"Health: {result.health_status}")
279
+
280
+ if result.deployment_time:
281
+ console.print(f"Deployment time: {result.deployment_time:.1f}s")