jac-scale 0.1.2__tar.gz → 0.1.3__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 (73) hide show
  1. {jac_scale-0.1.2 → jac_scale-0.1.3}/PKG-INFO +2 -2
  2. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/abstractions/config/app_config.jac +5 -2
  3. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/context.jac +2 -1
  4. jac_scale-0.1.3/jac_scale/factories/storage_factory.jac +75 -0
  5. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/impl/context.impl.jac +3 -0
  6. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/impl/serve.impl.jac +70 -9
  7. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/memory_hierarchy.jac +3 -1
  8. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/plugin.jac +38 -3
  9. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/plugin_config.jac +27 -0
  10. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/serve.jac +3 -0
  11. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/targets/kubernetes/kubernetes_config.jac +9 -15
  12. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/targets/kubernetes/kubernetes_target.jac +167 -13
  13. jac_scale-0.1.3/jac_scale/tests/fixtures/scale-feats/components/Button.cl.jac +32 -0
  14. jac_scale-0.1.3/jac_scale/tests/fixtures/scale-feats/main.jac +147 -0
  15. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/fixtures/test_api.jac +29 -0
  16. jac_scale-0.1.3/jac_scale/tests/fixtures/test_restspec.jac +37 -0
  17. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/test_deploy_k8s.py +2 -1
  18. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/test_examples.py +180 -5
  19. jac_scale-0.1.3/jac_scale/tests/test_restspec.py +192 -0
  20. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/test_serve.py +54 -0
  21. jac_scale-0.1.3/jac_scale/tests/test_storage.py +274 -0
  22. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale.egg-info/PKG-INFO +2 -2
  23. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale.egg-info/SOURCES.txt +6 -0
  24. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale.egg-info/requires.txt +1 -1
  25. {jac_scale-0.1.2 → jac_scale-0.1.3}/pyproject.toml +2 -2
  26. {jac_scale-0.1.2 → jac_scale-0.1.3}/README.md +0 -0
  27. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/__init__.py +0 -0
  28. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/abstractions/config/base_config.jac +0 -0
  29. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/abstractions/database_provider.jac +0 -0
  30. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/abstractions/deployment_target.jac +0 -0
  31. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/abstractions/image_registry.jac +0 -0
  32. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/abstractions/logger.jac +0 -0
  33. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/abstractions/models/deployment_result.jac +0 -0
  34. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/abstractions/models/resource_status.jac +0 -0
  35. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/config_loader.jac +0 -0
  36. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/factories/database_factory.jac +0 -0
  37. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/factories/deployment_factory.jac +0 -0
  38. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/factories/registry_factory.jac +0 -0
  39. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/factories/utility_factory.jac +0 -0
  40. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/google_sso_provider.jac +0 -0
  41. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/impl/config_loader.impl.jac +0 -0
  42. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/impl/memory_hierarchy.main.impl.jac +0 -0
  43. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/impl/memory_hierarchy.mongo.impl.jac +0 -0
  44. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/impl/memory_hierarchy.redis.impl.jac +0 -0
  45. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/impl/user_manager.impl.jac +0 -0
  46. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/jserver/__init__.py +0 -0
  47. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/jserver/impl/jfast_api.impl.jac +0 -0
  48. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/jserver/impl/jserver.impl.jac +0 -0
  49. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/jserver/jfast_api.jac +0 -0
  50. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/jserver/jserver.jac +0 -0
  51. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/providers/database/kubernetes_mongo.jac +0 -0
  52. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/providers/database/kubernetes_redis.jac +0 -0
  53. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/providers/registry/dockerhub.jac +0 -0
  54. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/sso_provider.jac +0 -0
  55. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/targets/kubernetes/utils/kubernetes_utils.impl.jac +0 -0
  56. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/targets/kubernetes/utils/kubernetes_utils.jac +0 -0
  57. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/__init__.py +0 -0
  58. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/conftest.py +0 -0
  59. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/fixtures/todo_app.jac +0 -0
  60. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/test_abstractions.py +0 -0
  61. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/test_factories.py +0 -0
  62. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/test_file_upload.py +0 -0
  63. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/test_hooks.py +0 -0
  64. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/test_k8s_utils.py +0 -0
  65. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/test_memory_hierarchy.py +0 -0
  66. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/tests/test_sso.py +0 -0
  67. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/user_manager.jac +0 -0
  68. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/utilities/loggers/standard_logger.jac +0 -0
  69. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale/utils.jac +0 -0
  70. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale.egg-info/dependency_links.txt +0 -0
  71. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale.egg-info/entry_points.txt +0 -0
  72. {jac_scale-0.1.2 → jac_scale-0.1.3}/jac_scale.egg-info/top_level.txt +0 -0
  73. {jac_scale-0.1.2 → jac_scale-0.1.3}/setup.cfg +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jac-scale
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Author-email: Jason Mars <jason@mars.ninja>
5
5
  Requires-Python: >=3.12
6
6
  Description-Content-Type: text/markdown
7
- Requires-Dist: jaclang>=0.9.11
7
+ Requires-Dist: jaclang>=0.9.12
8
8
  Requires-Dist: python-dotenv<2.0.0,>=1.2.1
9
9
  Requires-Dist: docker<8.0.0,>=7.1.0
10
10
  Requires-Dist: kubernetes<35.0.0,>=34.1.0
@@ -7,7 +7,8 @@ class AppConfig {
7
7
  file_name: str = 'none',
8
8
  build: bool = False,
9
9
  app_name: (str | None) = None,
10
- testing: bool = False;
10
+ testing: bool = False,
11
+ experimental: bool = False;
11
12
 
12
13
  def init(
13
14
  self: AppConfig,
@@ -15,13 +16,15 @@ class AppConfig {
15
16
  file_name: str = 'none',
16
17
  build: bool = False,
17
18
  app_name: (str | None) = None,
18
- testing: bool = False
19
+ testing: bool = False,
20
+ experimental: bool = False
19
21
  ) -> None {
20
22
  self.code_folder = code_folder;
21
23
  self.file_name = file_name;
22
24
  self.build = build;
23
25
  self.app_name = app_name;
24
26
  self.testing = testing;
27
+ self.experimental = experimental;
25
28
  }
26
29
 
27
30
  def get_code_path(self: AppConfig) -> Path {
@@ -1,8 +1,9 @@
1
+ import from contextvars { ContextVar }
1
2
  import from dataclasses { MISSING }
2
3
  import from typing { Any }
3
4
  import from uuid { UUID }
4
5
  import from jaclang.pycore.constant { Constants as Con }
5
- import from jaclang.runtimelib.context { ExecutionContext }
6
+ import from jaclang.runtimelib.context { ExecutionContext, CallState }
6
7
 
7
8
  """Jac Scale Execution Context with custom memory backend.
8
9
 
@@ -0,0 +1,75 @@
1
+ """Factory for creating storage instances."""
2
+ import from typing { Any }
3
+ import os;
4
+ import from jaclang.runtimelib.storage { Storage }
5
+ import from jaclang.project.config { get_config }
6
+
7
+ enum StorageType {
8
+ LOCAL = "local"
9
+ # Future: S3, GCS, AZURE - add when implemented
10
+ }
11
+
12
+ """Factory for creating storage instances.
13
+
14
+ Configuration priority: jac.toml > environment variable > default
15
+
16
+ In jac.toml:
17
+ [storage]
18
+ type = "local" # or "s3", "gcs", "azure"
19
+ base_path = "/data/storage"
20
+ create_dirs = true
21
+
22
+ Environment variables:
23
+ JAC_STORAGE_TYPE: Storage type (local, s3, gcs, azure)
24
+ JAC_STORAGE_PATH: Base directory for local storage
25
+ JAC_STORAGE_CREATE_DIRS: Whether to auto-create directories
26
+ """
27
+ class StorageFactory {
28
+ static def create(
29
+ storage_type: (StorageType | str), config: (dict[str, Any] | None) = None
30
+ ) -> Storage {
31
+ # Convert string to enum if needed
32
+ if isinstance(storage_type, str) {
33
+ storage_type = StorageType(storage_type);
34
+ }
35
+
36
+ if storage_type == StorageType.LOCAL {
37
+ import from jaclang.runtimelib.storage { LocalStorage }
38
+ cfg = config or {};
39
+ return LocalStorage(
40
+ base_path=cfg.get("base_path", "./storage"),
41
+ create_dirs=cfg.get("create_dirs", True)
42
+ );
43
+ }
44
+ raise ValueError(f"Unsupported storage type: {storage_type}") ;
45
+ }
46
+
47
+ static def get_default(
48
+ base_path: str = "./storage", create_dirs: bool = True
49
+ ) -> Storage {
50
+ # Get config from jac.toml if available
51
+ jac_config = get_config();
52
+
53
+ # Config priority: jac.toml > env var > parameter
54
+ if jac_config and jac_config.storage {
55
+ storage_type = StorageType(jac_config.storage.storage_type);
56
+ base_path = jac_config.storage.base_path;
57
+ create_dirs = jac_config.storage.create_dirs;
58
+ } elif os.environ.get("JAC_STORAGE_TYPE") or os.environ.get("JAC_STORAGE_PATH") {
59
+ storage_type_str = os.environ.get(
60
+ "JAC_STORAGE_TYPE", StorageType.LOCAL.value
61
+ );
62
+ storage_type = StorageType(storage_type_str);
63
+ base_path = os.environ.get("JAC_STORAGE_PATH", base_path);
64
+ create_dirs_str = os.environ.get(
65
+ "JAC_STORAGE_CREATE_DIRS", str(create_dirs)
66
+ );
67
+ create_dirs = create_dirs_str.lower() == "true";
68
+ } else {
69
+ storage_type = StorageType.LOCAL;
70
+ }
71
+
72
+ storage_config = {"base_path": base_path, "create_dirs": create_dirs};
73
+ return StorageFactory.create(storage_type, storage_config);
74
+ }
75
+ }
@@ -21,4 +21,7 @@ impl JScaleExecutionContext.init(self: JScaleExecutionContext) -> None {
21
21
  # Default user_root and entry_node to system_root
22
22
  self.user_root = self.system_root;
23
23
  self.entry_node = self.system_root;
24
+ self.call_state: ContextVar[CallState] = ContextVar(
25
+ 'call_state', default=CallState()
26
+ );
24
27
  }
@@ -486,10 +486,17 @@ impl JacAPIServer.render_base_route_callback(
486
486
 
487
487
  impl JacAPIServer.register_functions_endpoints -> None {
488
488
  for func_name in self.get_functions() {
489
+ func_obj = self.get_functions()[func_name];
490
+ restspec = func_obj.restspec if func_obj?.restspec else None;
491
+ spec_method = restspec.method if restspec else HTTPMethod.POST;
492
+ spec_path = restspec.path if restspec else None;
493
+
494
+ final_path = spec_path or f"/function/{func_name}";
495
+
489
496
  self.server.add_endpoint(
490
497
  JEndPoint(
491
- method=HTTPMethod.POST,
492
- path=f"/function/{func_name}",
498
+ method=spec_method,
499
+ path=final_path,
493
500
  callback=self.create_function_callback(func_name),
494
501
  parameters=self.create_function_parameters(func_name),
495
502
  response_model=None,
@@ -546,7 +553,7 @@ impl JacAPIServer.create_function_callback(
546
553
  ) -> Callable[..., TransportResponse] {
547
554
  import from jaclang.runtimelib.transport { TransportResponse, Meta }
548
555
  requires_auth = self.introspector.is_auth_required_for_function(func_name);
549
- def callback(**kwargs: JsonValue) -> TransportResponse {
556
+ async def callback(**kwargs: JsonValue) -> TransportResponse {
550
557
  username: (str | None) = None;
551
558
  if requires_auth {
552
559
  authorization = kwargs.pop('Authorization', None);
@@ -568,9 +575,21 @@ impl JacAPIServer.create_function_callback(
568
575
  }
569
576
  }
570
577
  print(f"Executing function '{func_name}' with params: {kwargs}");
571
- result = self.execution_manager.execute_function(
578
+ result = await self.execution_manager.execute_function(
572
579
  self.get_functions()[func_name], kwargs, (username or '__guest__')
573
580
  );
581
+ # Handle streaming responses (generators/async generators)
582
+ if (isgenerator(result) or isinstance(result, AsyncGenerator)) {
583
+ return StreamingResponse(
584
+ result,
585
+ media_type='text/event-stream',
586
+ headers={
587
+ 'Cache-Control': 'no-cache',
588
+ 'Connection': 'close',
589
+ 'X-Accel-Buffering': 'no'
590
+ }
591
+ );
592
+ }
574
593
  if 'error' in result {
575
594
  return TransportResponse.fail(
576
595
  code='EXECUTION_ERROR',
@@ -588,10 +607,15 @@ impl JacAPIServer.create_function_callback(
588
607
 
589
608
  impl JacAPIServer.register_walkers_endpoints -> None {
590
609
  for walker_name in self.get_walkers() {
610
+ walker_cls = self.get_walkers()[walker_name];
611
+ restspec = walker_cls.restspec if walker_cls?.restspec else None;
612
+ spec_method = restspec.method if restspec?.method else HTTPMethod.POST;
613
+ spec_path = restspec.path if restspec?.path else f"/walker/{walker_name}";
614
+
591
615
  self.server.add_endpoint(
592
616
  JEndPoint(
593
- method=HTTPMethod.POST,
594
- path=f"/walker/{walker_name}/{{node}}",
617
+ method=spec_method,
618
+ path=f"{spec_path}/{{node}}",
595
619
  callback=self.create_walker_callback(walker_name, has_node_param=True),
596
620
  parameters=self.create_walker_parameters(
597
621
  walker_name, invoke_on_root=False
@@ -604,8 +628,8 @@ impl JacAPIServer.register_walkers_endpoints -> None {
604
628
  );
605
629
  self.server.add_endpoint(
606
630
  JEndPoint(
607
- method=HTTPMethod.POST,
608
- path=f"/walker/{walker_name}",
631
+ method=spec_method,
632
+ path=spec_path,
609
633
  callback=self.create_walker_callback(walker_name, has_node_param=False),
610
634
  parameters=self.create_walker_parameters(
611
635
  walker_name, invoke_on_root=True
@@ -700,6 +724,17 @@ impl JacAPIServer.create_walker_callback(
700
724
  result = await self.execution_manager.spawn_walker(
701
725
  self.get_walkers()[walker_name], kwargs, (username or '__guest__')
702
726
  );
727
+ if (isgenerator(result) or isinstance(result, AsyncGenerator)) {
728
+ return StreamingResponse(
729
+ result,
730
+ media_type='text/event-stream',
731
+ headers={
732
+ 'Cache-Control': 'no-cache',
733
+ 'Connection': 'close',
734
+ 'X-Accel-Buffering': 'no'
735
+ }
736
+ );
737
+ }
703
738
  if 'error' in result {
704
739
  return TransportResponse.fail(
705
740
  code='EXECUTION_ERROR',
@@ -1260,6 +1295,20 @@ impl JacAPIServer.register_dynamic_walker_endpoint -> None {
1260
1295
  result = await self.execution_manager.spawn_walker(
1261
1296
  walkers[walker_name], kwargs, (username or '__guest__')
1262
1297
  );
1298
+
1299
+ # Handle streaming responses (generators/async generators)
1300
+ if (isgenerator(result) or isinstance(result, AsyncGenerator)) {
1301
+ return StreamingResponse(
1302
+ result,
1303
+ media_type='text/event-stream',
1304
+ headers={
1305
+ 'Cache-Control': 'no-cache',
1306
+ 'Connection': 'close',
1307
+ 'X-Accel-Buffering': 'no'
1308
+ }
1309
+ );
1310
+ }
1311
+
1263
1312
  if 'error' in result {
1264
1313
  return TransportResponse.fail(
1265
1314
  code='EXECUTION_ERROR',
@@ -1401,9 +1450,21 @@ impl JacAPIServer.register_dynamic_function_endpoint -> None {
1401
1450
  }
1402
1451
  }
1403
1452
 
1404
- result = self.execution_manager.execute_function(
1453
+ result = await self.execution_manager.execute_function(
1405
1454
  functions[function_name], kwargs, (username or '__guest__')
1406
1455
  );
1456
+ # Handle streaming responses (generators/async generators)
1457
+ if (isgenerator(result) or isinstance(result, AsyncGenerator)) {
1458
+ return StreamingResponse(
1459
+ result,
1460
+ media_type='text/event-stream',
1461
+ headers={
1462
+ 'Cache-Control': 'no-cache',
1463
+ 'Connection': 'close',
1464
+ 'X-Accel-Buffering': 'no'
1465
+ }
1466
+ );
1467
+ }
1407
1468
  if 'error' in result {
1408
1469
  return TransportResponse.fail(
1409
1470
  code='EXECUTION_ERROR',
@@ -74,7 +74,9 @@ MongoDB persistence backend - implements PersistentMemory for durable L3 storage
74
74
  Replaces SqliteMemory when MongoDB is available.
75
75
  """
76
76
  obj MongoBackend(PersistentMemory) {
77
- has client: (MongoClient | None) = None,
77
+ has client: MongoClient | None = None,
78
+ db: Any by postinit,
79
+ collection: Any by postinit,
78
80
  db_name: str = 'jac_db',
79
81
  collection_name: str = 'anchors',
80
82
  mongo_url: str = _db_config['mongodb_uri'];
@@ -4,6 +4,7 @@ import pathlib;
4
4
  import from dotenv { load_dotenv }
5
5
  import from jaclang.cli.registry { get_registry }
6
6
  import from jaclang.cli.command { Arg, ArgKind, CommandPriority, HookContext }
7
+ import from jaclang.cli.console { console }
7
8
  import from jaclang.pycore.runtime { hookimpl, plugin_manager }
8
9
  import from jaclang.runtimelib.context { ExecutionContext }
9
10
  import from jaclang.runtimelib.server { JacAPIServer as JacServer }
@@ -17,6 +18,7 @@ import from .factories.utility_factory { UtilityFactory }
17
18
  import from .abstractions.config.app_config { AppConfig }
18
19
  import from .user_manager { JacScaleUserManager }
19
20
  import from jaclang.runtimelib.server { UserManager }
21
+ import from jaclang.runtimelib.storage { Storage }
20
22
 
21
23
  """Pre-hook for jac start command to handle --scale flag."""
22
24
  def _scale_pre_hook(context: HookContext) -> None {
@@ -25,6 +27,7 @@ def _scale_pre_hook(context: HookContext) -> None {
25
27
  # Handle deployment instead of local server
26
28
  filename = context.get_arg("filename");
27
29
  build = context.get_arg("build", False);
30
+ experimental = context.get_arg("experimental", False);
28
31
  target = context.get_arg("target", "kubernetes");
29
32
  registry = context.get_arg("registry", "dockerhub");
30
33
  if not os.path.exists(filename) {
@@ -59,15 +62,27 @@ def _scale_pre_hook(context: HookContext) -> None {
59
62
  }
60
63
  # Create app config
61
64
  app_config = AppConfig(
62
- code_folder=code_folder, file_name=base_file_path, build=build
65
+ code_folder=code_folder,
66
+ file_name=base_file_path,
67
+ build=build,
68
+ experimental=experimental
63
69
  );
70
+ if experimental {
71
+ console.print(
72
+ "Installing Jaseci packages from repository (experimental mode)..."
73
+ );
74
+ } else {
75
+ console.print("Installing Jaseci packages from PyPI...");
76
+ }
64
77
  # Deploy
65
78
  result = deployment_target.deploy(app_config);
66
79
  if not result.success {
67
80
  raise RuntimeError(result.message or "Deployment failed") ;
68
81
  }
69
82
  if result.service_url {
70
- print(f"Deployment complete! Service available at: {result.service_url}");
83
+ console.print(
84
+ f"Deployment complete! Service available at: {result.service_url}"
85
+ );
71
86
  }
72
87
  # Cancel normal start execution since we handled it
73
88
  context.set_data("cancel_execution", True);
@@ -101,6 +116,13 @@ class JacCmd {
101
116
  help="Build and push Docker image (with --scale)",
102
117
  short="b"
103
118
  ),
119
+ Arg.create(
120
+ "experimental",
121
+ typ=bool,
122
+ default=False,
123
+ help="Use experimental mode (install from repo instead of PyPI)",
124
+ short="e"
125
+ ),
104
126
  Arg.create(
105
127
  "target",
106
128
  typ=str,
@@ -168,7 +190,9 @@ class JacCmd {
168
190
  app_name = os.getenv('APP_NAME') or target_config.get('app_name', 'jaseci');
169
191
  deployment_target.destroy(app_name);
170
192
 
171
- print(f"Successfully destroyed deployment '{app_name}' from {target}");
193
+ console.print(
194
+ f"Successfully destroyed deployment '{app_name}' from {target}"
195
+ );
172
196
  return 0;
173
197
  }
174
198
  }
@@ -205,6 +229,17 @@ class JacScalePlugin {
205
229
  static def get_user_manager(base_path: str) -> UserManager {
206
230
  return JacScaleUserManager(base_path=base_path);
207
231
  }
232
+
233
+ """Provide jac-scale's storage backend.
234
+
235
+ This overrides the core store() to use jac-scale's StorageFactory,
236
+ which supports cloud backends (S3, GCS, Azure) via configuration.
237
+ """
238
+ @hookimpl
239
+ static def store(base_path: str = "./storage", create_dirs: bool = True) -> Storage {
240
+ import from .factories.storage_factory { StorageFactory }
241
+ return StorageFactory.get_default(base_path, create_dirs);
242
+ }
208
243
  }
209
244
 
210
245
  # Pluggy's varnames() puts parameters with defaults into kwargnames, not argnames.
@@ -149,6 +149,33 @@ class JacScalePluginConfig {
149
149
  "type": "bool",
150
150
  "default": True,
151
151
  "description": "Enable Redis deployment in Kubernetes"
152
+ },
153
+ "plugin_versions": {
154
+ "type": "dict",
155
+ "default": {},
156
+ "description": "Package versions for PyPI installation (default mode). Use 'latest' or specific version.",
157
+ "nested": {
158
+ "jaclang": {
159
+ "type": "string",
160
+ "default": "latest",
161
+ "description": "jaclang package version"
162
+ },
163
+ "jac_scale": {
164
+ "type": "string",
165
+ "default": "latest",
166
+ "description": "jac-scale package version"
167
+ },
168
+ "jac_client": {
169
+ "type": "string",
170
+ "default": "latest",
171
+ "description": "jac-client package version"
172
+ },
173
+ "jac_byllm": {
174
+ "type": "string",
175
+ "default": "latest",
176
+ "description": "jac-byllm package version (use 'none' to skip)"
177
+ }
178
+ }
152
179
  }
153
180
  }
154
181
  },
@@ -24,6 +24,9 @@ import from enum { StrEnum }
24
24
  import from fastapi_sso.sso.google { GoogleSSO }
25
25
  import from jac_scale.utils { generate_random_password }
26
26
  import from jac_scale.config_loader { get_scale_config }
27
+ import from typing { AsyncGenerator }
28
+ import from inspect { isgenerator }
29
+ import from fastapi.responses { StreamingResponse }
27
30
 
28
31
  # Load configuration from jac.toml with env var overrides
29
32
  glob _jwt_config = get_scale_config().get_jwt_config(),
@@ -34,12 +34,12 @@ class KubernetesConfig(BaseConfig) {
34
34
  app_mount_path: str = '/app',
35
35
  code_mount_path: str = '/code',
36
36
  workspace_path: str = '/code/workspace',
37
- # Runtime environment (defaults to official Jaseci repo)
38
37
  jaseci_repo_url: str = 'https://github.com/jaseci-labs/jaseci.git',
39
38
  jaseci_branch: str = 'main',
40
39
  jaseci_commit: (str | None) = None,
41
40
  install_jaseci: bool = True,
42
- additional_packages: list[str] = [];
41
+ additional_packages: list[str] = [],
42
+ plugin_versions: dict[str, str] = {};
43
43
 
44
44
  def init(
45
45
  self: KubernetesConfig,
@@ -75,12 +75,8 @@ class KubernetesConfig(BaseConfig) {
75
75
  jaseci_branch: str = 'main',
76
76
  jaseci_commit: (str | None) = None,
77
77
  install_jaseci: bool = True,
78
- additional_packages: list[str] = []
79
- # Runtime configuration
80
- # Storage configuration
81
- # Timing configuration
82
- # Paths
83
- # Runtime environment
78
+ additional_packages: list[str] = [],
79
+ plugin_versions: dict[str, str] = {}
84
80
  ) -> None {
85
81
  self.app_name = app_name;
86
82
  self.namespace = namespace;
@@ -121,6 +117,7 @@ class KubernetesConfig(BaseConfig) {
121
117
  self.jaseci_commit = jaseci_commit;
122
118
  self.install_jaseci = install_jaseci;
123
119
  self.additional_packages = additional_packages;
120
+ self.plugin_versions = plugin_versions;
124
121
  }
125
122
 
126
123
  override def to_dict(self: KubernetesConfig) -> dict[str, Any] {
@@ -162,7 +159,8 @@ class KubernetesConfig(BaseConfig) {
162
159
  'jaseci_branch': self.jaseci_branch,
163
160
  'jaseci_commit': self.jaseci_commit,
164
161
  'install_jaseci': self.install_jaseci,
165
- 'additional_packages': self.additional_packages
162
+ 'additional_packages': self.additional_packages,
163
+ 'plugin_versions': self.plugin_versions
166
164
  }
167
165
  );
168
166
  return base;
@@ -204,12 +202,8 @@ class KubernetesConfig(BaseConfig) {
204
202
  jaseci_branch=config.get('jaseci_branch', 'main'),
205
203
  jaseci_commit=config.get('jaseci_commit'),
206
204
  install_jaseci=config.get('install_jaseci', True),
207
- additional_packages=config.get('additional_packages', [])
208
- # Runtime configuration
209
- # Storage configuration
210
- # Timing configuration
211
- # Paths
212
- # Runtime environment
205
+ additional_packages=config.get('additional_packages', []),
206
+ plugin_versions=config.get('plugin_versions', {})
213
207
  );
214
208
  }
215
209
  }