service-forge 0.1.21__py3-none-any.whl → 0.1.24__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.

Potentially problematic release.


This version of service-forge might be problematic. Click here for more details.

@@ -7,6 +7,7 @@ from typing import AsyncGenerator
7
7
  from loguru import logger
8
8
  from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker, create_async_engine
9
9
  from service_forge.service_config import ServiceConfig
10
+ from pymongo import AsyncMongoClient
10
11
 
11
12
  class PostgresDatabase:
12
13
  def __init__(
@@ -114,6 +115,7 @@ class MongoDatabase:
114
115
  self.mongo_password = mongo_password
115
116
  self.mongo_db = mongo_db or ""
116
117
  self.client = pymongo.MongoClient(self.database_url)
118
+ self.async_client = AsyncMongoClient(self.database_url)
117
119
  self.test_connection()
118
120
 
119
121
  @property
@@ -129,6 +131,21 @@ class MongoDatabase:
129
131
  logger.error(f"MongoDB connection test failed for database '{self.name}': {e}")
130
132
  return False
131
133
 
134
+ async def test_async_connection(self) -> bool:
135
+ try:
136
+ await self.async_client.admin.command('ping')
137
+ logger.info(f"Async MongoDB connection test successful for database '{self.name}'")
138
+ return True
139
+ except Exception as e:
140
+ logger.error(f"Async MongoDB connection test failed for database '{self.name}': {e}")
141
+ return False
142
+
143
+ def get_sync_collection(self, collection_name: str):
144
+ return self.client[self.mongo_db][collection_name]
145
+
146
+ def get_async_collection(self, collection_name: str):
147
+ return self.async_client[self.mongo_db][collection_name]
148
+
132
149
  class RedisDatabase:
133
150
  def __init__(
134
151
  self,
service_forge/sft/cli.py CHANGED
@@ -9,6 +9,8 @@ from service_forge.sft.cmd.upload_service import upload_service
9
9
  from service_forge.sft.cmd.deploy_service import deploy_service
10
10
  from service_forge.sft.cmd.config_command import list_config, get_config, set_config
11
11
  from service_forge.sft.cmd.service_command import list_services, delete_service, show_service_logs
12
+ from service_forge.sft.cmd.remote_list_tars import remote_list_tars
13
+ from service_forge.sft.cmd.remote_deploy import remote_deploy_tar, remote_list_and_deploy
12
14
 
13
15
  app = typer.Typer(
14
16
  name="sft",
@@ -33,6 +35,43 @@ def list_tars_command() -> None:
33
35
  def deploy_service_command(name: str, version: str) -> None:
34
36
  deploy_service(name, version)
35
37
 
38
+ @app.command(name="remote-list")
39
+ def remote_list_tars_command(
40
+ url: str = typer.Option(
41
+ None,
42
+ "--url",
43
+ "-u",
44
+ help="Service Center URL (default: http://localhost:5000 or from service_center_address config)"
45
+ )
46
+ ) -> None:
47
+ """List tar packages and their status on remote server"""
48
+ remote_list_tars(url)
49
+
50
+ @app.command(name="remote-deploy")
51
+ def remote_deploy_command(
52
+ filename: str = typer.Argument(help="Filename of the tar package to deploy"),
53
+ url: str = typer.Option(
54
+ None,
55
+ "--url",
56
+ "-u",
57
+ help="Service Center URL (default: http://localhost:5000 or from service_center_address config)"
58
+ )
59
+ ) -> None:
60
+ """Remote deploy specified tar package"""
61
+ remote_deploy_tar(filename, url)
62
+
63
+ @app.command(name="remote-deploy-interactive")
64
+ def remote_deploy_interactive_command(
65
+ url: str = typer.Option(
66
+ None,
67
+ "--url",
68
+ "-u",
69
+ help="Service Center URL (default: http://localhost:5000 or from service_center_address config)"
70
+ )
71
+ ) -> None:
72
+ """Interactive remote deployment of tar packages (list available packages first, then select for deployment)"""
73
+ remote_list_and_deploy(url)
74
+
36
75
  config_app = typer.Typer(
37
76
  name="config",
38
77
  help="Configuration management commands",
@@ -0,0 +1,160 @@
1
+ import os
2
+ import json
3
+ import requests
4
+ from pathlib import Path
5
+ from service_forge.sft.util.logger import log_error, log_info, log_success, log_warning
6
+ from service_forge.sft.config.sft_config import sft_config
7
+
8
+ def remote_deploy_tar(filename: str, service_center_url: str = None) -> None:
9
+ """
10
+ Remote deploy specified tar package from service-center
11
+ """
12
+ # If URL is not provided, try to get it from configuration
13
+ if not service_center_url:
14
+ service_center_url = getattr(sft_config, 'service_center_address', 'http://localhost:5000')
15
+
16
+ # Ensure URL ends with /
17
+ if not service_center_url.endswith('/'):
18
+ service_center_url += '/'
19
+
20
+ api_url = f"{service_center_url}api/v1/services/deploy-from-tar"
21
+
22
+ log_info(f"Sending deployment request to {api_url}...")
23
+ log_info(f"Tar package to deploy: {filename}")
24
+
25
+ try:
26
+ # Prepare request data
27
+ data = {
28
+ "filename": filename
29
+ }
30
+
31
+ # Send POST request
32
+ response = requests.post(
33
+ api_url,
34
+ json=data,
35
+ headers={'Content-Type': 'application/json'},
36
+ timeout=300 # 5 minute timeout
37
+ )
38
+
39
+ if response.status_code != 200:
40
+ log_error(f"Deployment request failed, status code: {response.status_code}")
41
+ try:
42
+ error_data = response.json()
43
+ log_error(f"Error message: {error_data.get('message', 'Unknown error')}")
44
+ if 'data' in error_data and error_data['data']:
45
+ log_error(f"Details: {json.dumps(error_data['data'], indent=2, ensure_ascii=False)}")
46
+ except:
47
+ log_error(f"Response content: {response.text}")
48
+ return
49
+
50
+ # Parse response data
51
+ result = response.json()
52
+
53
+ if result.get('code') != 200:
54
+ log_error(f"Deployment failed: {result.get('message', 'Unknown error')}")
55
+ if 'data' in result and result['data']:
56
+ log_error(f"Details: {json.dumps(result['data'], indent=2, ensure_ascii=False)}")
57
+ return
58
+
59
+ # Deployment successful
60
+ data = result.get('data', {})
61
+ service_name = data.get('service_name', 'Unknown')
62
+ version = data.get('version', 'Unknown')
63
+ deploy_output = data.get('deploy_output', '')
64
+
65
+ log_success(f"Successfully deployed service: {service_name} version: {version}")
66
+
67
+ if deploy_output:
68
+ log_info("Deployment output:")
69
+ print(deploy_output)
70
+
71
+ except requests.exceptions.Timeout:
72
+ log_error("Deployment request timed out (exceeded 5 minutes), please check service status or try again later")
73
+ except requests.exceptions.RequestException as e:
74
+ log_error(f"Request failed: {str(e)}")
75
+ log_info(f"Please check if service-center service is running normally and if the URL is correct: {service_center_url}")
76
+ except Exception as e:
77
+ log_error(f"Exception occurred while deploying tar package: {str(e)}")
78
+
79
+ def remote_list_and_deploy(service_center_url: str = None) -> None:
80
+ """
81
+ List remote tar packages first, then let user select which package to deploy
82
+ """
83
+ # If URL is not provided, try to get it from configuration
84
+ if not service_center_url:
85
+ service_center_url = getattr(sft_config, 'service_center_address', 'http://localhost:5000')
86
+
87
+ # Ensure URL ends with /
88
+ if not service_center_url.endswith('/'):
89
+ service_center_url += '/'
90
+
91
+ api_url = f"{service_center_url}api/v1/services/tar-list"
92
+
93
+ log_info(f"Getting tar package list from {api_url}...")
94
+
95
+ try:
96
+ # 发送GET请求获取tar包列表
97
+ response = requests.get(api_url, timeout=30)
98
+
99
+ if response.status_code != 200:
100
+ log_error(f"Failed to get tar package list, status code: {response.status_code}")
101
+ return
102
+
103
+ # Parse response data
104
+ result = response.json()
105
+
106
+ if result.get('code') != 200:
107
+ log_error(f"Failed to get tar package list: {result.get('message', 'Unknown error')}")
108
+ return
109
+
110
+ tar_files = result.get('data', [])
111
+
112
+ if not tar_files:
113
+ log_info("No tar packages found")
114
+ return
115
+
116
+ # Display tar package list
117
+ log_info("Available tar package list:")
118
+ for i, tar_file in enumerate(tar_files, 1):
119
+ filename = tar_file.get('filename', '-')
120
+ service_name = tar_file.get('service_name', '-')
121
+ version = tar_file.get('version', '-')
122
+ deployed_status = "Deployed" if tar_file.get('deployed_status', False) else "Not Deployed"
123
+
124
+ print(f"{i}. {filename} (service: {service_name}, version: {version}, status: {deployed_status})")
125
+
126
+ # Let user choose
127
+ try:
128
+ choice = input("\nEnter the number of the tar package to deploy (enter 'q' to exit): ").strip()
129
+
130
+ if choice.lower() == 'q':
131
+ log_info("Deployment cancelled")
132
+ return
133
+
134
+ index = int(choice) - 1
135
+ if 0 <= index < len(tar_files):
136
+ selected_tar = tar_files[index]
137
+ filename = selected_tar.get('filename')
138
+
139
+ if selected_tar.get('deployed_status', False):
140
+ log_warning(f"Tar package {filename} is already deployed, continue deployment?")
141
+ confirm = input("Enter 'y' to continue, any other key to cancel: ").strip().lower()
142
+ if confirm != 'y':
143
+ log_info("Deployment cancelled")
144
+ return
145
+
146
+ log_info(f"Selected for deployment: {filename}")
147
+ remote_deploy_tar(filename, service_center_url)
148
+ else:
149
+ log_error("Invalid selection")
150
+
151
+ except ValueError:
152
+ log_error("Please enter a valid number")
153
+ except KeyboardInterrupt:
154
+ log_info("\nDeployment cancelled")
155
+
156
+ except requests.exceptions.RequestException as e:
157
+ log_error(f"Request failed: {str(e)}")
158
+ log_info(f"Please check if service-center service is running normally and if the URL is correct: {service_center_url}")
159
+ except Exception as e:
160
+ log_error(f"Exception occurred while getting tar package list: {str(e)}")
@@ -0,0 +1,111 @@
1
+ import os
2
+ import json
3
+ import requests
4
+ from pathlib import Path
5
+ from rich.console import Console
6
+ from rich.table import Table
7
+ from service_forge.sft.util.logger import log_error, log_info, log_success, log_warning
8
+ from service_forge.sft.config.sft_config import sft_config
9
+
10
+ def remote_list_tars(service_center_url: str = None) -> None:
11
+ """
12
+ Get remote tar package list and status from service-center
13
+ """
14
+ # If URL is not provided, try to get it from configuration
15
+ if not service_center_url:
16
+ service_center_url = getattr(sft_config, 'service_center_address', 'http://localhost:5000')
17
+
18
+ # Ensure URL ends with /
19
+ if not service_center_url.endswith('/'):
20
+ service_center_url += '/'
21
+
22
+ api_url = f"{service_center_url}api/v1/services/tar-list"
23
+
24
+ log_info(f"Getting tar package list from {api_url}...")
25
+
26
+ try:
27
+ # 发送GET请求
28
+ response = requests.get(api_url, timeout=30)
29
+
30
+ if response.status_code != 200:
31
+ log_error(f"Failed to get tar package list, status code: {response.status_code}")
32
+ try:
33
+ error_data = response.json()
34
+ log_error(f"Error message: {error_data.get('message', 'Unknown error')}")
35
+ except:
36
+ log_error(f"Response content: {response.text}")
37
+ return
38
+
39
+ # Parse response data
40
+ result = response.json()
41
+
42
+ if result.get('code') != 200:
43
+ log_error(f"Failed to get tar package list: {result.get('message', 'Unknown error')}")
44
+ return
45
+
46
+ tar_files = result.get('data', [])
47
+
48
+ if not tar_files:
49
+ log_info("No tar packages found")
50
+ return
51
+
52
+ # Use rich table to display results
53
+ console = Console()
54
+ table = Table(title="Remote Server Tar Package List", show_header=True, header_style="bold magenta")
55
+ table.add_column("Filename", style="cyan", no_wrap=True)
56
+ table.add_column("Service Name", style="green", no_wrap=True)
57
+ table.add_column("Version", style="blue", no_wrap=True)
58
+ table.add_column("Size", justify="right", style="yellow")
59
+ table.add_column("Modified Time", style="dim")
60
+ table.add_column("Deploy Status", justify="center", style="bold")
61
+
62
+ for tar_file in tar_files:
63
+ # Format file size
64
+ size = _format_size(tar_file.get('file_size', 0))
65
+
66
+ # Format modification time
67
+ modified_time = _format_time(tar_file.get('modified_time', 0))
68
+
69
+ # Deployment status
70
+ deployed_status = "✅ Deployed" if tar_file.get('deployed_status', False) else "❌ Not Deployed"
71
+ status_style = "green" if tar_file.get('deployed_status', False) else "red"
72
+
73
+ table.add_row(
74
+ tar_file.get('filename', '-'),
75
+ tar_file.get('service_name', '-'),
76
+ tar_file.get('version', '-'),
77
+ size,
78
+ modified_time,
79
+ f"[{status_style}]{deployed_status}[/{status_style}]"
80
+ )
81
+
82
+ console.print(table)
83
+ log_success(f"Found {len(tar_files)} tar packages in total")
84
+
85
+ except requests.exceptions.RequestException as e:
86
+ log_error(f"Request failed: {str(e)}")
87
+ log_info(f"Please check if service-center service is running normally and if the URL is correct: {service_center_url}")
88
+ except Exception as e:
89
+ log_error(f"Exception occurred while getting tar package list: {str(e)}")
90
+
91
+ def _format_size(size_bytes: int) -> str:
92
+ """Format file size"""
93
+ if size_bytes == 0:
94
+ return "0 B"
95
+
96
+ for unit in ['B', 'KB', 'MB', 'GB']:
97
+ if size_bytes < 1024.0:
98
+ return f"{size_bytes:.2f} {unit}"
99
+ size_bytes /= 1024.0
100
+ return f"{size_bytes:.2f} TB"
101
+
102
+ def _format_time(timestamp: float) -> str:
103
+ """Format timestamp"""
104
+ if timestamp == 0:
105
+ return "-"
106
+
107
+ try:
108
+ from datetime import datetime
109
+ return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
110
+ except:
111
+ return "-"
@@ -16,6 +16,7 @@ class Injector:
16
16
  self.ingress_yaml_path = project_dir / "ingress.yaml"
17
17
  self.dockerfile_path = project_dir / "Dockerfile"
18
18
  self.pyproject_toml_path = project_dir / "pyproject.toml"
19
+ self.start_sh_path = project_dir / "start.sh"
19
20
  self.metadata = load_metadata(self.metadata_path)
20
21
  self.name = self.metadata.name
21
22
  self.version = self.metadata.version
@@ -121,9 +122,20 @@ class Injector:
121
122
  f.write(pyproject_toml)
122
123
  print("pyproject_toml_path: ", self.pyproject_toml_path)
123
124
 
125
+ def clear_start_sh(self) -> None:
126
+ if Path(self.start_sh_path).exists():
127
+ with open(self.start_sh_path, "rb") as f:
128
+ content = f.read()
129
+ content_str = content.decode("utf-8")
130
+ lines = content_str.splitlines()
131
+ new_content = "\n".join(lines) + ("\n" if content_str.endswith(('\n', '\r')) else "")
132
+ with open(self.start_sh_path, "w", encoding="utf-8", newline="\n") as f:
133
+ f.write(new_content)
134
+
124
135
  def inject(self) -> None:
125
136
  self.inject_deployment()
126
137
  self.inject_service_config()
127
138
  self.inject_ingress()
128
139
  self.inject_dockerfile()
129
- self.inject_pyproject_toml()
140
+ self.inject_pyproject_toml()
141
+ self.clear_start_sh()
@@ -128,4 +128,4 @@ DEFAULT_PYPROJECT_TOML = """
128
128
 
129
129
  [tool.uv.sources]
130
130
  service-forge = { workspace = true }
131
- """
131
+ """
@@ -2,8 +2,8 @@ from ..utils.type_converter import TypeConverter
2
2
  from ..workflow.workflow import Workflow
3
3
  from ..api.http_api import fastapi_app
4
4
  from ..api.kafka_api import KafkaApp, kafka_app
5
- from fastapi import FastAPI
6
5
  from ..workflow.workflow_type import WorkflowType, workflow_type_register
6
+ from fastapi import FastAPI
7
7
 
8
8
  type_converter = TypeConverter()
9
9
  type_converter.register(str, Workflow, lambda s, node: node.sub_workflows.get_workflow(s))
@@ -1,6 +1,8 @@
1
1
  from typing import Any, Callable, Type, Dict, Tuple, Set, List
2
2
  from collections import deque
3
3
  import inspect
4
+ import traceback
5
+ from pydantic import BaseModel
4
6
  from typing_extensions import get_origin, get_args
5
7
 
6
8
  def is_type(value, dst_type):
@@ -57,6 +59,9 @@ class TypeConverter:
57
59
  except Exception:
58
60
  pass
59
61
 
62
+ if issubclass(dst_type, BaseModel) and isinstance(value, dict):
63
+ return dst_type(**value)
64
+
60
65
  path = self._find_path(src_type, dst_type)
61
66
  if not path:
62
67
  raise TypeError(f"No conversion path found from {src_type.__name__} to {dst_type.__name__}.")
@@ -79,6 +79,8 @@ class Workflow:
79
79
  await callback.on_workflow_start(*args, **kwargs)
80
80
  elif callback_type == CallbackEvent.ON_WORKFLOW_END:
81
81
  await callback.on_workflow_end(*args, **kwargs)
82
+ elif callback_type == CallbackEvent.ON_WORKFLOW_ERROR:
83
+ await callback.on_workflow_error(*args, **kwargs)
82
84
  elif callback_type == CallbackEvent.ON_NODE_START:
83
85
  await callback.on_node_start(*args, **kwargs)
84
86
  elif callback_type == CallbackEvent.ON_NODE_END:
@@ -122,7 +124,7 @@ class Workflow:
122
124
  raise ValueError("Multiple trigger nodes found in workflow.")
123
125
  return trigger_nodes[0]
124
126
 
125
- async def _run_node_with_callbacks(self, node: Node) -> None:
127
+ async def _run_node_with_callbacks(self, node: Node) -> bool:
126
128
  await self.call_callbacks(CallbackEvent.ON_NODE_START, node=node)
127
129
 
128
130
  try:
@@ -131,8 +133,13 @@ class Workflow:
131
133
  await self.handle_node_stream_output(node, result)
132
134
  elif asyncio.iscoroutine(result):
133
135
  await result
136
+ except Exception as e:
137
+ await self.call_callbacks(CallbackEvent.ON_WORKFLOW_ERROR, workflow=self, node=node, error=e)
138
+ logger.error(f"Error when running node {node.name}: {str(e)}, task_id: {self.task_id}")
139
+ return False
134
140
  finally:
135
141
  await self.call_callbacks(CallbackEvent.ON_NODE_END, node=node)
142
+ return True
136
143
 
137
144
  async def run_after_trigger(self) -> Any:
138
145
  logger.info(f"Running workflow: {self.name}")
@@ -143,30 +150,41 @@ class Workflow:
143
150
  for edge in self.get_trigger_node().output_edges:
144
151
  edge.end_port.trigger()
145
152
 
146
- try:
147
- for input_port in self.input_ports:
148
- if input_port.value is not None:
149
- input_port.port.node.fill_input(input_port.port, input_port.value)
150
-
151
- for node in self.nodes:
152
- for key in node.AUTO_FILL_INPUT_PORTS:
153
- if key[0] not in [edge.end_port.name for edge in node.input_edges]:
154
- node.fill_input_by_name(key[0], key[1])
155
-
156
- while self.ready_nodes:
157
- nodes = self.ready_nodes.copy()
158
- self.ready_nodes = []
159
-
160
- tasks = []
161
- for node in nodes:
162
- tasks.append(asyncio.create_task(self._run_node_with_callbacks(node)))
163
-
164
- await asyncio.gather(*tasks)
165
-
166
- except Exception as e:
167
- error_msg = f"Error in run_after_trigger: {str(e)}"
168
- logger.error(error_msg)
169
- raise e
153
+ for input_port in self.input_ports:
154
+ if input_port.value is not None:
155
+ input_port.port.node.fill_input(input_port.port, input_port.value)
156
+
157
+ for node in self.nodes:
158
+ for key in node.AUTO_FILL_INPUT_PORTS:
159
+ if key[0] not in [edge.end_port.name for edge in node.input_edges]:
160
+ node.fill_input_by_name(key[0], key[1])
161
+
162
+ while self.ready_nodes:
163
+ nodes = self.ready_nodes.copy()
164
+ self.ready_nodes = []
165
+
166
+ tasks = []
167
+ for node in nodes:
168
+ tasks.append(asyncio.create_task(self._run_node_with_callbacks(node)))
169
+
170
+ results = await asyncio.gather(*tasks, return_exceptions=True)
171
+
172
+ for i, result in enumerate(results):
173
+ if isinstance(result, Exception):
174
+ for task in tasks:
175
+ if not task.done():
176
+ task.cancel()
177
+ await asyncio.gather(*tasks, return_exceptions=True)
178
+ return
179
+ # raise result
180
+ elif result is False:
181
+ logger.error(f"Node execution failed, stopping workflow: {nodes[i].name}")
182
+ for task in tasks:
183
+ if not task.done():
184
+ task.cancel()
185
+ await asyncio.gather(*tasks, return_exceptions=True)
186
+ return
187
+ # raise RuntimeError(f"Workflow stopped due to node execution failure: {nodes[i].name}")
170
188
 
171
189
  if len(self.output_ports) > 0:
172
190
  if len(self.output_ports) == 1:
@@ -191,8 +209,11 @@ class Workflow:
191
209
  # TODO: clear new_workflow
192
210
 
193
211
  except Exception as e:
194
- error_msg = f"Error running workflow: {str(e)}, {traceback.format_exc()}"
195
- logger.error(error_msg)
212
+ await self.call_callbacks(CallbackEvent.ON_WORKFLOW_ERROR, workflow=self, node=None, error=e)
213
+ # error_msg = f"Error running workflow: {str(e)}, {traceback.format_exc()}"
214
+ # logger.error(error_msg)
215
+ # await self.call_callbacks(CallbackEvent.ON_WORKFLOW_END, workflow=self, node=None, error=e)
216
+ return
196
217
 
197
218
  async def run(self):
198
219
  tasks = []
@@ -31,7 +31,7 @@ class WorkflowCallback:
31
31
  pass
32
32
 
33
33
  @abstractmethod
34
- async def on_workflow_error(self, workflow: Workflow, error: Any) -> None:
34
+ async def on_workflow_error(self, workflow: Workflow, node: Node, error: Any) -> None:
35
35
  pass
36
36
 
37
37
  @abstractmethod
@@ -90,7 +90,7 @@ class BuiltinWorkflowCallback(WorkflowCallback):
90
90
  logger.error(f"发送 workflow_end 消息到 websocket 失败: {e}")
91
91
 
92
92
  @override
93
- async def on_workflow_error(self, workflow: Workflow, error: Any) -> None:
93
+ async def on_workflow_error(self, workflow: Workflow, node: Node | None, error: Any) -> None:
94
94
  workflow_result = WorkflowResult(result=error, is_end=False, is_error=True)
95
95
 
96
96
  if workflow.task_id in workflow.real_trigger_node.result_queues:
@@ -103,6 +103,7 @@ class BuiltinWorkflowCallback(WorkflowCallback):
103
103
  message = {
104
104
  "type": "workflow_error",
105
105
  "task_id": str(workflow.task_id),
106
+ "node": node.name if node else None,
106
107
  "error": self._serialize_result(error),
107
108
  "is_end": False,
108
109
  "is_error": True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: service-forge
3
- Version: 0.1.21
3
+ Version: 0.1.24
4
4
  Summary: Add your description here
5
5
  Author-email: euxcet <zcc.qwer@gmail.com>
6
6
  Requires-Python: >=3.11
@@ -12,7 +12,7 @@ service_forge/api/routers/service/service_router.py,sha256=hGOT-ScnXR7agHp-F9OFG
12
12
  service_forge/api/routers/websocket/websocket_manager.py,sha256=j1AFqzXQhZZyaLQwhvZefXAS-zCOPzLcRMDEuusv6V0,3605
13
13
  service_forge/api/routers/websocket/websocket_router.py,sha256=V0B7eQP8toO94-WbTrGraadXD3qeZ9lnKFcxwx6kLgM,3777
14
14
  service_forge/db/__init__.py,sha256=EWLhH8bYsMOvRF_YXF6FgL3irKA6GZeLxSGvWDRM6f8,85
15
- service_forge/db/database.py,sha256=IdF7RV-bOFmPPu7d4sQFtsF3e8mZKwMXUkMi7HPEPnc,9329
15
+ service_forge/db/database.py,sha256=WKtZ0MoOnbMw54ohfs9zKsrOZ5_qenLvXkAV_Gr2WOs,10068
16
16
  service_forge/db/migrations/feedback_migration.py,sha256=-zQ71TsOlWmQPQo1NKSIu3C1T47v3cfD6IAQ5HE_ffk,4845
17
17
  service_forge/db/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  service_forge/db/models/feedback.py,sha256=gltX3y-nNhXSR9z1cd82Vg-zwjF0JhnGbOvUapkcWKQ,1253
@@ -22,14 +22,16 @@ service_forge/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
22
22
  service_forge/model/feedback.py,sha256=Is5tkplzYkjChGb67o-Qjtbu4kSspVuaKi4Aua_QdRo,1318
23
23
  service_forge/model/websocket.py,sha256=YIUCW32sbHIEFPHjk5FiDM_rDe2aVD6OpzBQul2R5IM,267
24
24
  service_forge/proto/foo_input.py,sha256=-POJZSIFrGdBGz7FqZZ03r5uztpc5Apin9A0Yxbk6YI,90
25
- service_forge/sft/cli.py,sha256=stB_YPhZ7gAQeOxIq03-tLyl5VfU-gnRacAT05GSMis,2904
25
+ service_forge/sft/cli.py,sha256=xcM6kiGPJeHEUqfJd4uFngVFpjfVkDxW4G_PbNRD9Xs,4265
26
26
  service_forge/sft/cmd/config_command.py,sha256=I9t2HG28S6lCXpExHyZUc47b_1yB3i51tCFVk5J6TTU,2382
27
27
  service_forge/sft/cmd/deploy_service.py,sha256=5IYbCVI0Nlry1KXBhm9leJmr2bzUEXrSY-2BympLR0c,4686
28
28
  service_forge/sft/cmd/list_tars.py,sha256=Z3zvu2JLb_wNbTwi5TZXL5cZ8PxYrKks9AxkOzoUd_Q,1380
29
+ service_forge/sft/cmd/remote_deploy.py,sha256=AStAlbqGD7XeZFhL0fx2j12YWP_MVbdURbO5ZENEMgc,6510
30
+ service_forge/sft/cmd/remote_list_tars.py,sha256=mx6hkNnu0ySMyBX2Qi6blKMj5xnNnrmXq3VD_nERlmw,4176
29
31
  service_forge/sft/cmd/service_command.py,sha256=69GMMN61KtuoEFuYzFJ74ivNt8RX8q0I6rbePfJfEwQ,5538
30
32
  service_forge/sft/cmd/upload_service.py,sha256=86PvvJSXCZKH4BU6rLytuc45grX-sRnQnOHCo9zUaPY,1232
31
- service_forge/sft/config/injector.py,sha256=El8U5USveKfC0-jhhqYgaevjp4R4fwW02oMvDn7Amyk,5762
32
- service_forge/sft/config/injector_default_files.py,sha256=aTMQ2Tla3wdpvdaD_5VP2X6oeZbI0X414FM9NbirnO4,2716
33
+ service_forge/sft/config/injector.py,sha256=-eU21Ob09ond9pwHVkjklAd5_qLWWbbf0b91iCg2Kwk,6335
34
+ service_forge/sft/config/injector_default_files.py,sha256=f7mNJ5Y9yb4e9kjLn414WiQoZrOue9ok_hq_POG4I2o,2717
33
35
  service_forge/sft/config/sf_metadata.py,sha256=Y9akhSCgOd11-oqRs3LIs8FL9pvWNw6hyy57fuFcBhc,866
34
36
  service_forge/sft/config/sft_config.py,sha256=MgurtgbcSmyXbGlVX3NG84KD4Hst1gZWHdF9a8zi-6U,7707
35
37
  service_forge/sft/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -43,9 +45,9 @@ service_forge/sft/util/yaml_utils.py,sha256=9OhJNQlzj_C1NeQoUZVF8qpDovrE7RDWtNXe
43
45
  service_forge/storage/__init__.py,sha256=8Jg4R9z2JHadheV1YrHtCsFxEL5aCl9n2dMQGHcJfvM,156
44
46
  service_forge/storage/feedback_storage.py,sha256=wnuNTmEzpnS7iisiU9MrEJIgVa2G_HysqICWk_PxzfU,9124
45
47
  service_forge/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- service_forge/utils/default_type_converter.py,sha256=CuUZpMATdTwgcV1M3lbK64znwmEG85Zt3y_QGXr9tYQ,625
48
+ service_forge/utils/default_type_converter.py,sha256=KFWhlZJUrQc2e-Wm9-WfPUKp5UaI-fpVjzNLO6n37e8,625
47
49
  service_forge/utils/register.py,sha256=nxiGQBCX238FoZZhsDoDdBMv_2QzeIZpM367HPNfaqM,874
48
- service_forge/utils/type_converter.py,sha256=IRphYxyGA0ICwFvDMvqAnRnSUTpY2ZQXvTE5o99CKpo,3046
50
+ service_forge/utils/type_converter.py,sha256=eGAAnqEr-va1PBwYOzuzzzExVJNtusn-yGRv7i9WjRY,3204
49
51
  service_forge/utils/workflow_clone.py,sha256=K2Y4XXsGYQn4OTKcDYEa7UZHGXta_hztKW-pr4EYaDQ,4593
50
52
  service_forge/workflow/__init__.py,sha256=9oh4qPyA33RugrUYRAlLmFtmQPUN2wxruFQE3omTJF8,49
51
53
  service_forge/workflow/context.py,sha256=1PotSEN_l8Emd5p8_6mtXJngXGYd3NSbOs1EKHgvnlo,346
@@ -53,8 +55,8 @@ service_forge/workflow/edge.py,sha256=88Ex-9_dHAGD38OHgiqP0DrfxK0FrhvDAxThR3ilUi
53
55
  service_forge/workflow/node.py,sha256=hoO8TdfbB5inpu55YCwecnasi4RS-Bg9R8Sp-M5c2Ys,7841
54
56
  service_forge/workflow/port.py,sha256=JVj0JrnQeOWCsp7n48Cm03bfmO8r3V5oTSEsC-HTGPE,2967
55
57
  service_forge/workflow/trigger.py,sha256=2OqiHi0dFcoC8g5GDqVpVEpHKlmqtDADb74Z7PRzHlo,879
56
- service_forge/workflow/workflow.py,sha256=nSJUqU1GQV1PkW3Vbhz5dcbOkPLNWJGhfSHItFc_LYk,8474
57
- service_forge/workflow/workflow_callback.py,sha256=S__F7s-7l5LgkIXcZMcG68qCyc8NgdWQX81F0hKWL1U,5135
58
+ service_forge/workflow/workflow.py,sha256=YsA_Yeh5XjYQflztqKWoWBt9euEoHVOJMGMTquIoO04,9788
59
+ service_forge/workflow/workflow_callback.py,sha256=MJBG_DTQGCgqCjpnBhuSteZmOxitYRdtkxXlFCOh930,5219
58
60
  service_forge/workflow/workflow_config.py,sha256=Yih10b-utKIpaR-X-nfy7fPnmBNhRvlD8Bw2_mQ5lJI,1821
59
61
  service_forge/workflow/workflow_event.py,sha256=QG1VFJwUUF1bTKKPKvqBICnYxkBwpfYDEoAuxwQYhhE,371
60
62
  service_forge/workflow/workflow_factory.py,sha256=KfIxjdQhsRC0KYrEkAhqlx3oY6tABoulQGhBwBBXLq0,9933
@@ -77,7 +79,7 @@ service_forge/workflow/triggers/kafka_api_trigger.py,sha256=Zv8J75Rmg1-xqxHwpBMB
77
79
  service_forge/workflow/triggers/once_trigger.py,sha256=YmzSQBoKE-8liNFIoDCqi2UdqhHujizsXVDft81_8jA,572
78
80
  service_forge/workflow/triggers/period_trigger.py,sha256=JFX3yBjKqoRP55jiulaSG_SPO-zWLMcwEb1BwcKsWUM,767
79
81
  service_forge/workflow/triggers/websocket_api_trigger.py,sha256=JjRYb0VM_em90_w3QEErgxMDBCaBHCrSBhTaV4Fl4HY,6894
80
- service_forge-0.1.21.dist-info/METADATA,sha256=i-mb03hBz6046u5fkH2PPaiYEcbEGV9QHLHEhhsKxc4,2308
81
- service_forge-0.1.21.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
82
- service_forge-0.1.21.dist-info/entry_points.txt,sha256=WHntHW7GAyKQUEeMcMvHDZ7_xAb0-cZeAK4iJeu9lm8,51
83
- service_forge-0.1.21.dist-info/RECORD,,
82
+ service_forge-0.1.24.dist-info/METADATA,sha256=tOQocyV5Smmw3sdoR6k-0MBwB-C0eaMGlG2hRDGj4AY,2308
83
+ service_forge-0.1.24.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
84
+ service_forge-0.1.24.dist-info/entry_points.txt,sha256=WHntHW7GAyKQUEeMcMvHDZ7_xAb0-cZeAK4iJeu9lm8,51
85
+ service_forge-0.1.24.dist-info/RECORD,,