jac-scale 0.1.1__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 (57) hide show
  1. jac_scale/__init__.py +0 -0
  2. jac_scale/abstractions/config/app_config.jac +30 -0
  3. jac_scale/abstractions/config/base_config.jac +26 -0
  4. jac_scale/abstractions/database_provider.jac +51 -0
  5. jac_scale/abstractions/deployment_target.jac +64 -0
  6. jac_scale/abstractions/image_registry.jac +54 -0
  7. jac_scale/abstractions/logger.jac +20 -0
  8. jac_scale/abstractions/models/deployment_result.jac +27 -0
  9. jac_scale/abstractions/models/resource_status.jac +38 -0
  10. jac_scale/config_loader.jac +31 -0
  11. jac_scale/context.jac +14 -0
  12. jac_scale/factories/database_factory.jac +43 -0
  13. jac_scale/factories/deployment_factory.jac +43 -0
  14. jac_scale/factories/registry_factory.jac +32 -0
  15. jac_scale/factories/utility_factory.jac +34 -0
  16. jac_scale/impl/config_loader.impl.jac +131 -0
  17. jac_scale/impl/context.impl.jac +24 -0
  18. jac_scale/impl/memory_hierarchy.main.impl.jac +63 -0
  19. jac_scale/impl/memory_hierarchy.mongo.impl.jac +239 -0
  20. jac_scale/impl/memory_hierarchy.redis.impl.jac +186 -0
  21. jac_scale/impl/serve.impl.jac +1785 -0
  22. jac_scale/jserver/__init__.py +0 -0
  23. jac_scale/jserver/impl/jfast_api.impl.jac +731 -0
  24. jac_scale/jserver/impl/jserver.impl.jac +79 -0
  25. jac_scale/jserver/jfast_api.jac +162 -0
  26. jac_scale/jserver/jserver.jac +101 -0
  27. jac_scale/memory_hierarchy.jac +138 -0
  28. jac_scale/plugin.jac +218 -0
  29. jac_scale/plugin_config.jac +175 -0
  30. jac_scale/providers/database/kubernetes_mongo.jac +137 -0
  31. jac_scale/providers/database/kubernetes_redis.jac +110 -0
  32. jac_scale/providers/registry/dockerhub.jac +64 -0
  33. jac_scale/serve.jac +118 -0
  34. jac_scale/targets/kubernetes/kubernetes_config.jac +215 -0
  35. jac_scale/targets/kubernetes/kubernetes_target.jac +841 -0
  36. jac_scale/targets/kubernetes/utils/kubernetes_utils.impl.jac +519 -0
  37. jac_scale/targets/kubernetes/utils/kubernetes_utils.jac +85 -0
  38. jac_scale/tests/__init__.py +0 -0
  39. jac_scale/tests/conftest.py +29 -0
  40. jac_scale/tests/fixtures/test_api.jac +159 -0
  41. jac_scale/tests/fixtures/todo_app.jac +68 -0
  42. jac_scale/tests/test_abstractions.py +88 -0
  43. jac_scale/tests/test_deploy_k8s.py +265 -0
  44. jac_scale/tests/test_examples.py +484 -0
  45. jac_scale/tests/test_factories.py +149 -0
  46. jac_scale/tests/test_file_upload.py +444 -0
  47. jac_scale/tests/test_k8s_utils.py +156 -0
  48. jac_scale/tests/test_memory_hierarchy.py +247 -0
  49. jac_scale/tests/test_serve.py +1835 -0
  50. jac_scale/tests/test_sso.py +711 -0
  51. jac_scale/utilities/loggers/standard_logger.jac +40 -0
  52. jac_scale/utils.jac +16 -0
  53. jac_scale-0.1.1.dist-info/METADATA +658 -0
  54. jac_scale-0.1.1.dist-info/RECORD +57 -0
  55. jac_scale-0.1.1.dist-info/WHEEL +5 -0
  56. jac_scale-0.1.1.dist-info/entry_points.txt +3 -0
  57. jac_scale-0.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,79 @@
1
+ """Handle execution of a DELETE endpoint."""
2
+
3
+ impl JServer._delete(self: JServer, endpoint: JEndPoint) -> 'JServer[T]' {
4
+ ;
5
+ }
6
+
7
+ """Handle execution of a PATCH endpoint."""
8
+ impl JServer._patch(self: JServer, endpoint: JEndPoint) -> 'JServer[T]' {
9
+ ;
10
+ }
11
+
12
+ """Handle execution of a PUT endpoint."""
13
+ impl JServer._put(self: JServer, endpoint: JEndPoint) -> 'JServer[T]' {
14
+ ;
15
+ }
16
+
17
+ """Handle execution of a POST endpoint."""
18
+ impl JServer._post(self: JServer, endpoint: JEndPoint) -> 'JServer[T]' {
19
+ ;
20
+ }
21
+
22
+ """Handle execution of a GET endpoint."""
23
+ impl JServer._get(self: JServer, endpoint: JEndPoint) -> 'JServer[T]' {
24
+ ;
25
+ }
26
+
27
+ """
28
+ Execute the provided endpoints by calling the appropriate HTTP method handlers.
29
+
30
+ This method iterates through the endpoint list and calls the appropriate
31
+ method (get, post, put, patch, delete) based on each endpoint's HTTP method.
32
+ """
33
+ impl JServer.execute(self: JServer) -> None {
34
+ for endpoint in self._endpoints {
35
+ if (endpoint.method == HTTPMethod.GET) {
36
+ self._get(endpoint);
37
+ } elif (endpoint.method == HTTPMethod.POST) {
38
+ self._post(endpoint);
39
+ } elif (endpoint.method == HTTPMethod.PUT) {
40
+ self._put(endpoint);
41
+ } elif (endpoint.method == HTTPMethod.PATCH) {
42
+ self._patch(endpoint);
43
+ } elif (endpoint.method == HTTPMethod.DELETE) {
44
+ self._delete(endpoint);
45
+ }
46
+ }
47
+ }
48
+
49
+ """
50
+ Add a single endpoint to the server implementation.
51
+
52
+ Args:
53
+ endpoint (JEndPoint): The endpoint to add
54
+ """
55
+ impl JServer.add_endpoint(self: JServer, endpoint: JEndPoint) -> None {
56
+ self._endpoints.append(endpoint);
57
+ }
58
+
59
+ """
60
+ Return the list of registered endpoints.
61
+ This method should return only the endpoints that have been registered
62
+ with this server implementation, not create new ones.
63
+
64
+ Returns:
65
+ List[JEndPoint]: List of registered endpoint definitions
66
+ """
67
+ impl JServer.get_endpoints(self: JServer) -> list[JEndPoint] {
68
+ return self._endpoints;
69
+ }
70
+
71
+ """
72
+ Initialize the server with a list of endpoints.
73
+ Args:
74
+ end_points (list[JEndPoint]): List of endpoint definitions to register
75
+ """
76
+ impl JServer.init(self: JServer, end_points: list[JEndPoint]) -> None {
77
+ super.init();
78
+ self._endpoints = end_points;
79
+ }
@@ -0,0 +1,162 @@
1
+ """
2
+ JFastApiServer: A FastAPI Implementation of JServer
3
+
4
+ This module provides a FastAPI-specific implementation of the JServer abstract base class.
5
+ It handles endpoint registration with FastAPI applications and provides all the FastAPI-specific
6
+ functionality like parameter injection, response model generation, and route creation.
7
+
8
+ Key Components:
9
+ - JFastApiServer: FastAPI implementation of JServer
10
+ - create_app(): Creates a basic FastAPI application for demonstration
11
+
12
+ Advanced Features:
13
+ - Parameter injection with type conversion
14
+ - Response model generation from JSON schema
15
+ - Support for async/sync callback functions
16
+ - Automatic OpenAPI documentation generation
17
+ - Integration with JAC pass execution patterns
18
+ """
19
+
20
+ import inspect;
21
+ import logging;
22
+ import uvicorn;
23
+ import from collections.abc { Callable }
24
+ import from typing { Any, Optional, TypeAlias, get_type_hints }
25
+ import from fastapi {
26
+ Body,
27
+ FastAPI,
28
+ File,
29
+ Form,
30
+ Header,
31
+ HTTPException,
32
+ Path,
33
+ Query,
34
+ Request,
35
+ UploadFile
36
+ }
37
+ import from fastapi.responses { Response, JSONResponse }
38
+ import from pydantic { BaseModel, Field, create_model }
39
+ import from .jserver { APIParameter, HTTPMethod, JEndPoint, JServer, ParameterType }
40
+ import from jaclang.runtimelib.transport { TransportResponse }
41
+
42
+ glob EndpointResponse: TypeAlias = Response | BaseModel | <>dict[(str, object)] | <>list[
43
+ object
44
+ ] | str | <>bytes | None;
45
+
46
+ """Base protocol for parameter handlers."""
47
+ obj ParameterHandler {
48
+ has type_converter: JFastApiServer;
49
+
50
+ """Generate parameter string for FastAPI function signature."""
51
+ def generate_param_string(param: APIParameter) -> str;
52
+
53
+ """Get the actual Python type from parameter type string."""
54
+ def get_type(param: APIParameter) -> type[Any] {
55
+ return self.type_converter._get_python_type(param.data_type);
56
+ }
57
+
58
+ """Get the type name for code generation."""
59
+ def get_type_name(param: APIParameter) -> str {
60
+ return self.get_type(param).__name__;
61
+ }
62
+ }
63
+
64
+ """Handler for PATH parameters."""
65
+ obj PathParameterHandler(ParameterHandler) {
66
+ def generate_param_string(param: APIParameter) -> str;
67
+ }
68
+
69
+ """Handler for QUERY parameters."""
70
+ obj QueryParameterHandler(ParameterHandler) {
71
+ def generate_param_string(param: APIParameter) -> str;
72
+ }
73
+
74
+ """Handler for HEADER parameters."""
75
+ obj HeaderParameterHandler(ParameterHandler) {
76
+ def generate_param_string(param: APIParameter) -> str;
77
+ }
78
+
79
+ """Handler for FILE parameters."""
80
+ obj FileParameterHandler(ParameterHandler) {
81
+ def generate_param_string(param: APIParameter) -> str;
82
+ }
83
+
84
+ """Handler for BODY parameters with files (multipart/form-data)."""
85
+ obj FormBodyParameterHandler(ParameterHandler) {
86
+ def generate_param_string(param: APIParameter) -> str;
87
+ }
88
+
89
+ """Handler for BODY parameters without files (JSON)."""
90
+ obj JSONBodyParameterHandler(ParameterHandler) {
91
+ has body_params: list[APIParameter];
92
+
93
+ """Generate Pydantic model for JSON body."""
94
+ def generate_body_model -> (type[BaseModel] | None);
95
+ }
96
+
97
+ """
98
+ JFastApiServer: FastAPI implementation of JServer for programmatic endpoint creation.
99
+
100
+ JFastApiServer provides FastAPI-specific implementation of the JServer interface,
101
+ handling endpoint registration with FastAPI applications.
102
+
103
+ This class implements the JServer interface by:
104
+ - Storing registered endpoints internally
105
+ - Implementing HTTP method handlers (_get, _post, _put, _patch, _delete)
106
+ - Providing FastAPI app instance for server execution
107
+
108
+ Example:
109
+ >>> # Create server with endpoints
110
+ >>> endpoints = [
111
+ ... JEndPoint(HTTPMethod.GET, "/users", get_users_callback),
112
+ ... JEndPoint(HTTPMethod.POST, "/users", create_user_callback)
113
+ ... ]
114
+ >>> server = JFastApiServer(endpoints)
115
+ >>>
116
+ >>> # Execute to create FastAPI routes
117
+ >>> server.execute()
118
+ >>> app = server.create_server()
119
+
120
+ Attributes:
121
+ app (FastAPI): The underlying FastAPI application instance
122
+ """
123
+ class JFastApiServer(JServer[FastAPI]) {
124
+ def init(
125
+ self: JFastApiServer,
126
+ endpoints: (list[JEndPoint] | None) = None,
127
+ app: (FastAPI | None) = None
128
+ ) -> None;
129
+
130
+ def _get(self: JFastApiServer, endpoint: JEndPoint) -> 'JFastApiServer';
131
+ def _post(self: JFastApiServer, endpoint: JEndPoint) -> 'JFastApiServer';
132
+ def _put(self: JFastApiServer, endpoint: JEndPoint) -> 'JFastApiServer';
133
+ def _patch(self: JFastApiServer, endpoint: JEndPoint) -> 'JFastApiServer';
134
+ def _delete(self: JFastApiServer, endpoint: JEndPoint) -> 'JFastApiServer';
135
+ def _route_priority(
136
+ self: JFastApiServer, endpoint: JEndPoint
137
+ ) -> tuple[int, int, int, str];
138
+
139
+ def execute(self: JFastApiServer) -> None;
140
+ def create_server(self: JFastApiServer) -> FastAPI;
141
+ def _create_fastapi_route(
142
+ self: JFastApiServer, method: HTTPMethod, endpoint: JEndPoint
143
+ ) -> None;
144
+
145
+ def _get_default_status_code(self: JFastApiServer, method: HTTPMethod) -> int;
146
+ def _create_endpoint_function(
147
+ self: JFastApiServer,
148
+ callback: Callable[(..., Any)],
149
+ parameters: list[APIParameter],
150
+ dependencies: list[Any]
151
+ ) -> Callable[..., Any];
152
+
153
+ def _get_python_type(self: JFastApiServer, type_string: str) -> type[Any];
154
+ def _create_response_model(
155
+ self: JFastApiServer, response_config: (dict[(str, Any)] | None) = None
156
+ ) -> (type[BaseModel] | None);
157
+
158
+ def get_app(self: JFastApiServer) -> FastAPI;
159
+ def run_server(
160
+ self: JFastApiServer, host: str = '0.0.0.0', port: int = 8000
161
+ ) -> None;
162
+ }
@@ -0,0 +1,101 @@
1
+ import from abc { ABC, abstractmethod }
2
+ import from collections.abc { Callable }
3
+ import from dataclasses { dataclass }
4
+ import from typing { Any, Generic, TypeVar }
5
+ import from pydantic { BaseModel }
6
+ import from enum { StrEnum }
7
+
8
+ glob T = TypeVar('T');
9
+
10
+ class HTTPMethod(StrEnum) {
11
+ has GET: str = 'GET',
12
+ POST: str = 'POST',
13
+ PUT: str = 'PUT',
14
+ PATCH: str = 'PATCH',
15
+ DELETE: str = 'DELETE';
16
+ }
17
+
18
+ class ParameterType(StrEnum) {
19
+ has QUERY: str = 'query',
20
+ PATH: str = 'path',
21
+ BODY: str = 'body',
22
+ HEADER: str = 'header',
23
+ FILE: str = 'file';
24
+ }
25
+
26
+ obj APIParameter {
27
+ has name: str,
28
+ <>type: ParameterType = ParameterType.QUERY,
29
+ data_type: str = 'str',
30
+ required: bool = True,
31
+ <>default: Any = None,
32
+ description: str = '';
33
+ }
34
+
35
+ """
36
+ Data class representing a single API endpoint.
37
+ This class provides a clean representation of an endpoint configuration,
38
+ including its method, path, callback function, parameters, and response model.
39
+
40
+ Attributes:
41
+ method (HTTPMethod): The HTTP method for the endpoint (GET, POST, etc.)
42
+ path (str): The URL path for the endpoint
43
+ callback (Callable): The function to be called when the endpoint is accessed
44
+ parameters (list[APIParameter] | None): List of parameters for the endpoint
45
+ response_model (type[BaseModel] | None): Pydantic model for the response
46
+ tags (list[str] | None): Tags for categorizing the endpoint
47
+ summary (str | None): Short summary of the endpoint
48
+ description (str | None): Detailed description of the endpoint
49
+ """
50
+ obj JEndPoint {
51
+ has method: HTTPMethod,
52
+ path: str,
53
+ callback: Callable[(..., Any)],
54
+ parameters: (list[APIParameter] | None) = None,
55
+ response_model: (type[BaseModel] | None) = None,
56
+ tags: (list[str] | None) = None,
57
+ summary: (str | None) = None,
58
+ description: (str | None) = None;
59
+ }
60
+
61
+ """Abstract base class for server implementations."""
62
+ class JServer(ABC, Generic[T]) {
63
+ def init(self: JServer, end_points: list[JEndPoint]) -> None;
64
+ def get_endpoints(self: JServer) -> list[JEndPoint];
65
+ def add_endpoint(self: JServer, endpoint: JEndPoint) -> None;
66
+ def execute(self: JServer) -> None;
67
+ @abstractmethod
68
+ def _get(self: JServer, endpoint: JEndPoint) -> 'JServer[T]';
69
+
70
+ @abstractmethod
71
+ def _post(self: JServer, endpoint: JEndPoint) -> 'JServer[T]';
72
+
73
+ @abstractmethod
74
+ def _put(self: JServer, endpoint: JEndPoint) -> 'JServer[T]';
75
+
76
+ @abstractmethod
77
+ def _patch(self: JServer, endpoint: JEndPoint) -> 'JServer[T]';
78
+
79
+ @abstractmethod
80
+ def _delete(self: JServer, endpoint: JEndPoint) -> 'JServer[T]';
81
+
82
+ """
83
+ Create a complete server with all endpoints registered.
84
+
85
+ This is a convenience method that gets all endpoints and executes them
86
+ to create a fully configured server. The return type depends on the
87
+ concrete implementation.
88
+
89
+ Args:
90
+ app (Optional[FastAPI]): Optional FastAPI instance to use (ignored in base implementation)
91
+
92
+ Returns:
93
+ Any: Implementation-specific configured server
94
+ """
95
+ @abstractmethod
96
+ def create_server(self: JServer) -> T { }
97
+
98
+ """Run the server on the specified host and port."""
99
+ @abstractmethod
100
+ def run_server(self: JServer, host: str = 'localhost', port: int = 8000) -> None { }
101
+ }
@@ -0,0 +1,138 @@
1
+ """
2
+ Memory Hierarchy Implementation for jac-scale.
3
+
4
+ This module provides scalable storage backends that replace the default
5
+ implementations in jaclang.runtimelib.memory:
6
+ - RedisBackend: Replaces LocalCacheMemory as L2 distributed cache
7
+ - MongoBackend: Replaces SqliteMemory as L3 persistent storage
8
+
9
+ ScaleTieredMemory extends TieredMemory by swapping in these backends.
10
+ Falls back to jaclang's SqliteMemory when MongoDB is unavailable.
11
+ """
12
+ import logging;
13
+ import from collections.abc { Callable, Generator, Iterable }
14
+ import from pickle { dumps, loads }
15
+ import from typing { Any }
16
+ import from uuid { UUID }
17
+ import redis;
18
+ import from pymongo { MongoClient }
19
+ import from pymongo.errors { ConnectionFailure }
20
+ import from jaclang.pycore.archetype { Anchor, NodeAnchor, Root }
21
+ import from jaclang.runtimelib.memory {
22
+ CacheMemory,
23
+ PersistentMemory,
24
+ TieredMemory,
25
+ SqliteMemory
26
+ }
27
+ import from jaclang.runtimelib.utils { storage_key, to_uuid }
28
+ import from jac_scale.config_loader { get_scale_config }
29
+
30
+ # Load database configuration from jac.toml with env var overrides
31
+ glob _db_config = get_scale_config().get_database_config(),
32
+ logger = logging.getLogger(__name__);
33
+
34
+ """
35
+ Redis cache backend - implements CacheMemory for distributed L2 caching.
36
+ Replaces LocalCacheMemory when Redis is available.
37
+ """
38
+ obj RedisBackend(CacheMemory) {
39
+ has redis_url: str = _db_config['redis_url'],
40
+ redis_client: (redis.Redis | None) = None;
41
+
42
+ def postinit -> None;
43
+ def is_available -> bool;
44
+ # CacheMemory interface (Memory methods + cache-specific)
45
+ def get(id: UUID) -> (Anchor | None);
46
+ def put(anchor: Anchor) -> None;
47
+ def delete(id: UUID) -> None;
48
+ def close -> None;
49
+ def has(id: UUID) -> bool;
50
+ def query(
51
+ filter: (Callable[[Anchor], bool] | None) = None
52
+ ) -> Generator[Anchor, None, None];
53
+
54
+ def get_roots -> Generator[Root, None, None];
55
+ def find(
56
+ ids: (UUID | Iterable[UUID]),
57
+ filter: (Callable[[Anchor], Anchor] | None) = None
58
+ ) -> Generator[Anchor, None, None];
59
+
60
+ def find_one(
61
+ ids: (UUID | Iterable[UUID]),
62
+ filter: (Callable[[Anchor], Anchor] | None) = None
63
+ ) -> (Anchor | None);
64
+
65
+ def commit(anchor: (Anchor | None) = None) -> None;
66
+ # CacheMemory-specific
67
+ def exists(id: UUID) -> bool;
68
+ def put_if_exists(anchor: Anchor) -> bool;
69
+ def invalidate(id: UUID) -> None;
70
+ }
71
+
72
+ """
73
+ MongoDB persistence backend - implements PersistentMemory for durable L3 storage.
74
+ Replaces SqliteMemory when MongoDB is available.
75
+ """
76
+ obj MongoBackend(PersistentMemory) {
77
+ has client: (MongoClient | None) = None,
78
+ db_name: str = 'jac_db',
79
+ collection_name: str = 'anchors',
80
+ mongo_url: str = _db_config['mongodb_uri'];
81
+
82
+ def postinit -> None;
83
+ def is_available -> bool;
84
+ # PersistentMemory interface (Memory methods + persistence-specific)
85
+ def get(id: UUID) -> (Anchor | None);
86
+ def put(anchor: Anchor) -> None;
87
+ def delete(id: UUID) -> None;
88
+ def close -> None;
89
+ def has(id: UUID) -> bool;
90
+ def query(
91
+ filter: (Callable[[Anchor], bool] | None) = None
92
+ ) -> Generator[Anchor, None, None];
93
+
94
+ def get_roots -> Generator[Root, None, None];
95
+ def find(
96
+ ids: (UUID | Iterable[UUID]),
97
+ filter: (Callable[[Anchor], Anchor] | None) = None
98
+ ) -> Generator[Anchor, None, None];
99
+
100
+ def find_one(
101
+ ids: (UUID | Iterable[UUID]),
102
+ filter: (Callable[[Anchor], Anchor] | None) = None
103
+ ) -> (Anchor | None);
104
+
105
+ def commit(anchor: (Anchor | None) = None) -> None;
106
+ # PersistentMemory-specific
107
+ def sync -> None;
108
+ def bulk_put(anchors: Iterable[Anchor]) -> None;
109
+ # Internal
110
+ def _load_anchor(raw: dict[(str, Any)]) -> (Anchor | None);
111
+ }
112
+
113
+ """
114
+ Persistence backend type for ScaleTieredMemory.
115
+ """
116
+ enum PersistenceType {
117
+ NONE,
118
+ MONGODB,
119
+ SQLITE
120
+ }
121
+
122
+ """
123
+ Scalable Tiered Memory - extends TieredMemory with distributed backends.
124
+
125
+ Swaps the default implementations:
126
+ - L2: RedisBackend instead of LocalCacheMemory (when Redis available)
127
+ - L3: MongoBackend instead of SqliteMemory (when MongoDB available)
128
+
129
+ Falls back to jaclang's SqliteMemory for L3 when MongoDB is unavailable.
130
+ Storage configuration comes from environment variables or jac.toml.
131
+ """
132
+ obj ScaleTieredMemory(TieredMemory) {
133
+ has _cache_available: bool = False,
134
+ _persistence_type: PersistenceType = PersistenceType.NONE;
135
+
136
+ def init(use_cache: bool = True) -> None;
137
+ def close -> None;
138
+ }
jac_scale/plugin.jac ADDED
@@ -0,0 +1,218 @@
1
+ """File covering plugin implementation."""
2
+ import os;
3
+ import pathlib;
4
+ import from dotenv { load_dotenv }
5
+ import from jaclang.cli.registry { get_registry }
6
+ import from jaclang.cli.command { Arg, ArgKind, CommandPriority, HookContext }
7
+ import from jaclang.pycore.runtime { hookimpl, plugin_manager }
8
+ import from jaclang.runtimelib.context { ExecutionContext }
9
+ import from jaclang.runtimelib.server { JacAPIServer as JacServer }
10
+ import from .context { JScaleExecutionContext }
11
+ import from .serve { JacAPIServer }
12
+ import from .jserver.jfast_api { JFastApiServer }
13
+ import from .config_loader { get_scale_config }
14
+ import from .factories.deployment_factory { DeploymentTargetFactory }
15
+ import from .factories.registry_factory { ImageRegistryFactory }
16
+ import from .factories.utility_factory { UtilityFactory }
17
+ import from .abstractions.config.app_config { AppConfig }
18
+
19
+ """Pre-hook for jac start command to handle --scale flag."""
20
+ def _scale_pre_hook(context: HookContext) -> None {
21
+ scale = context.get_arg("scale", False);
22
+ if scale {
23
+ # Handle deployment instead of local server
24
+ filename = context.get_arg("filename");
25
+ build = context.get_arg("build", False);
26
+ target = context.get_arg("target", "kubernetes");
27
+ registry = context.get_arg("registry", "dockerhub");
28
+ if not os.path.exists(filename) {
29
+ raise FileNotFoundError(f"File not found: '{filename}'") ;
30
+ }
31
+ code_folder = os.path.dirname(filename) or '.';
32
+ dotenv_path = os.path.join(code_folder, '.env');
33
+ load_dotenv(dotenv_path);
34
+ code_folder = os.path.relpath(code_folder);
35
+ code_folder = pathlib.Path(code_folder).as_posix();
36
+ base_file_path = os.path.basename(filename);
37
+ # Get configuration
38
+ scale_config = get_scale_config();
39
+ # Create logger
40
+ logger = UtilityFactory.create_logger('standard');
41
+ # Get target-specific config
42
+ if target == 'kubernetes' {
43
+ target_config = scale_config.get_kubernetes_config();
44
+ } else {
45
+ # For future targets, get from config
46
+ target_config = scale_config.get_kubernetes_config(); # Default for now
47
+ }
48
+ # Create deployment target
49
+ deployment_target = DeploymentTargetFactory.create(
50
+ target, target_config, logger
51
+ );
52
+ # Handle image registry if build is requested
53
+ if build {
54
+ # Use target config for registry (it contains docker credentials)
55
+ image_registry = ImageRegistryFactory.create(registry, target_config);
56
+ deployment_target.image_registry = image_registry;
57
+ }
58
+ # Create app config
59
+ app_config = AppConfig(
60
+ code_folder=code_folder, file_name=base_file_path, build=build
61
+ );
62
+ # Deploy
63
+ result = deployment_target.deploy(app_config);
64
+ if not result.success {
65
+ raise RuntimeError(result.message or "Deployment failed") ;
66
+ }
67
+ if result.service_url {
68
+ print(f"Deployment complete! Service available at: {result.service_url}");
69
+ }
70
+ # Cancel normal start execution since we handled it
71
+ context.set_data("cancel_execution", True);
72
+ context.set_data("cancel_return_code", 0);
73
+ }
74
+ }
75
+
76
+ """Jac CLI."""
77
+ class JacCmd {
78
+ """Create Jac CLI cmds."""
79
+ @hookimpl
80
+ static def create_cmd -> None {
81
+ """Jac Scale functionality.""";
82
+ registry = get_registry();
83
+
84
+ # Extend jac start with --scale and related flags
85
+ registry.extend_command(
86
+ "start",
87
+ args=[
88
+ Arg.create(
89
+ "scale",
90
+ typ=bool,
91
+ default=False,
92
+ help="Deploy to a target platform instead of running locally",
93
+ short="" # Disable auto-generated -s (conflicts with --session)
94
+ ),
95
+ Arg.create(
96
+ "build",
97
+ typ=bool,
98
+ default=False,
99
+ help="Build and push Docker image (with --scale)",
100
+ short="b"
101
+ ),
102
+ Arg.create(
103
+ "target",
104
+ typ=str,
105
+ default="kubernetes",
106
+ help="Deployment target (kubernetes, aws, gcp, etc.)",
107
+ ),
108
+ Arg.create(
109
+ "registry",
110
+ typ=str,
111
+ default="dockerhub",
112
+ help="Image registry (dockerhub, ecr, gcr, etc.)",
113
+ ),
114
+
115
+ ],
116
+ pre_hook=_scale_pre_hook,
117
+ source="jac-scale"
118
+ );
119
+
120
+ @registry.command(
121
+ name="destroy",
122
+ help="Remove deployment from target platform",
123
+ args=[
124
+ Arg.create(
125
+ "file_path", kind=ArgKind.POSITIONAL, help="Path to .jac file"
126
+ ),
127
+ Arg.create(
128
+ "target",
129
+ typ=str,
130
+ default="kubernetes",
131
+ help="Deployment target (kubernetes, aws, gcp, etc.)",
132
+ ),
133
+
134
+ ],
135
+ examples=[("jac destroy app.jac", "Remove deployment for app"), ],
136
+ group="deployment",
137
+ priority=CommandPriority.PLUGIN,
138
+ source="jac-scale"
139
+ )
140
+ def destroy(file_path: str, target: str = "kubernetes") -> int {
141
+ if not os.path.exists(file_path) {
142
+ raise FileNotFoundError(f"File not found: '{file_path}'") ;
143
+ }
144
+ code_folder = os.path.dirname(file_path) or '.';
145
+ dotenv_path = os.path.join(code_folder, '.env');
146
+ load_dotenv(dotenv_path);
147
+
148
+ # Get configuration
149
+ scale_config = get_scale_config();
150
+
151
+ # Create logger
152
+ logger = UtilityFactory.create_logger('standard');
153
+
154
+ # Get target-specific config
155
+ if target == 'kubernetes' {
156
+ target_config = scale_config.get_kubernetes_config();
157
+ } else {
158
+ # For future targets, get from config
159
+ target_config = scale_config.get_kubernetes_config(); # Default for now
160
+ }
161
+
162
+ # Create deployment target and destroy
163
+ deployment_target = DeploymentTargetFactory.create(
164
+ target, target_config, logger
165
+ );
166
+ app_name = os.getenv('APP_NAME') or target_config.get('app_name', 'jaseci');
167
+ deployment_target.destroy(app_name);
168
+
169
+ print(f"Successfully destroyed deployment '{app_name}' from {target}");
170
+ return 0;
171
+ }
172
+ }
173
+ }
174
+
175
+ """Jac Scale Plugin Implementation."""
176
+ class JacScalePlugin {
177
+ @hookimpl
178
+ static def create_j_context(user_root: (str | None)) -> ExecutionContext {
179
+ # Storage backend configured via environment (MONGODB_URI, etc.)
180
+ ctx = JScaleExecutionContext();
181
+ if user_root is not None {
182
+ ctx.set_user_root(user_root);
183
+ }
184
+ return ctx;
185
+ }
186
+
187
+ """Create the API server instance."""
188
+ @hookimpl
189
+ static def create_server(
190
+ jac_server: JacServer, host: str, port: int
191
+ ) -> JFastApiServer {
192
+ return JFastApiServer([]);
193
+ }
194
+
195
+ """Provide jac-scale's enhanced JacAPIServer class."""
196
+ @hookimpl
197
+ static def get_api_server_class -> type {
198
+ return JacAPIServer;
199
+ }
200
+ }
201
+
202
+ # Pluggy's varnames() puts parameters with defaults into kwargnames, not argnames.
203
+ # But _multicall only passes argnames to hook implementations.
204
+ # JacRuntimeInterfaceImpl strips defaults via generate_plugin_helpers, so we must too.
205
+ import inspect;
206
+ glob func = JacScalePlugin.create_j_context,
207
+ sig = inspect.signature(func),
208
+ sig_nodef = sig.replace(
209
+ parameters=[
210
+ p.replace(default=inspect.Parameter.empty)
211
+ for p in sig.parameters.values()
212
+ ]
213
+ );
214
+
215
+ with entry {
216
+ func.__signature__ = sig_nodef;
217
+ plugin_manager.register(JacScalePlugin());
218
+ }