cmdbox 0.6.3.2__py3-none-any.whl → 0.6.4.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.
Potentially problematic release.
This version of cmdbox might be problematic. Click here for more details.
- cmdbox/app/app.py +22 -7
- cmdbox/app/common.py +9 -6
- cmdbox/app/commons/loghandler.py +101 -20
- cmdbox/app/commons/redis_client.py +1 -1
- cmdbox/app/edge.py +2 -4
- cmdbox/app/feature.py +1 -1
- cmdbox/app/features/cli/cmdbox_agent_mcp_client.py +175 -0
- cmdbox/app/features/cli/cmdbox_agent_mcp_proxy.py +96 -0
- cmdbox/app/features/cli/cmdbox_audit_createdb.py +224 -224
- cmdbox/app/features/cli/cmdbox_audit_delete.py +4 -4
- cmdbox/app/features/cli/cmdbox_audit_search.py +4 -4
- cmdbox/app/features/cli/cmdbox_audit_write.py +6 -8
- cmdbox/app/features/cli/cmdbox_client_file_copy.py +3 -3
- cmdbox/app/features/cli/cmdbox_client_file_download.py +3 -3
- cmdbox/app/features/cli/cmdbox_client_file_list.py +3 -3
- cmdbox/app/features/cli/cmdbox_client_file_mkdir.py +3 -3
- cmdbox/app/features/cli/cmdbox_client_file_move.py +3 -3
- cmdbox/app/features/cli/cmdbox_client_file_remove.py +3 -3
- cmdbox/app/features/cli/cmdbox_client_file_rmdir.py +3 -3
- cmdbox/app/features/cli/cmdbox_client_file_upload.py +3 -3
- cmdbox/app/features/cli/cmdbox_client_http.py +7 -6
- cmdbox/app/features/cli/cmdbox_client_server_info.py +4 -4
- cmdbox/app/features/cli/cmdbox_cmd_list.py +4 -4
- cmdbox/app/features/cli/cmdbox_cmd_load.py +5 -5
- cmdbox/app/features/cli/cmdbox_edge_config.py +1 -1
- cmdbox/app/features/cli/cmdbox_edge_start.py +3 -3
- cmdbox/app/features/cli/cmdbox_mcp_client.py +174 -174
- cmdbox/app/features/cli/cmdbox_mcp_proxy.py +96 -96
- cmdbox/app/features/cli/cmdbox_server_list.py +3 -3
- cmdbox/app/features/cli/cmdbox_server_start.py +103 -103
- cmdbox/app/features/cli/cmdbox_server_stop.py +6 -6
- cmdbox/app/features/cli/cmdbox_tts_install.py +317 -0
- cmdbox/app/features/cli/cmdbox_tts_say.py +179 -0
- cmdbox/app/features/cli/cmdbox_tts_start.py +329 -0
- cmdbox/app/features/cli/cmdbox_tts_stop.py +108 -0
- cmdbox/app/features/cli/cmdbox_web_apikey_add.py +91 -91
- cmdbox/app/features/cli/cmdbox_web_apikey_del.py +91 -91
- cmdbox/app/features/cli/cmdbox_web_gencert.py +7 -7
- cmdbox/app/features/cli/cmdbox_web_genpass.py +168 -168
- cmdbox/app/features/cli/cmdbox_web_group_add.py +94 -94
- cmdbox/app/features/cli/cmdbox_web_group_del.py +87 -87
- cmdbox/app/features/cli/cmdbox_web_group_edit.py +94 -94
- cmdbox/app/features/cli/cmdbox_web_group_list.py +87 -87
- cmdbox/app/features/cli/cmdbox_web_start.py +236 -235
- cmdbox/app/features/cli/cmdbox_web_stop.py +72 -72
- cmdbox/app/features/cli/cmdbox_web_user_add.py +104 -104
- cmdbox/app/features/cli/cmdbox_web_user_del.py +87 -87
- cmdbox/app/features/cli/cmdbox_web_user_edit.py +104 -104
- cmdbox/app/features/cli/cmdbox_web_user_list.py +87 -87
- cmdbox/app/features/web/cmdbox_web_agent.py +16 -4
- cmdbox/app/features/web/cmdbox_web_get_cmd_choices.py +10 -2
- cmdbox/app/features/web/cmdbox_web_save_cmd.py +1 -0
- cmdbox/app/features/web/cmdbox_web_versions_used.py +4 -0
- cmdbox/app/filer.py +9 -9
- cmdbox/app/mcp.py +19 -8
- cmdbox/app/options.py +52 -47
- cmdbox/app/server.py +224 -224
- cmdbox/app/web.py +39 -17
- cmdbox/extensions/features.yml +7 -1
- cmdbox/extensions/sample_project/sample/app/features/cli/sample_client_time.py +2 -2
- cmdbox/extensions/sample_project/sample/app/features/cli/sample_server_time.py +3 -3
- cmdbox/licenses/LICENSE_APScheduler_3_11_0_MIT_License.txt +19 -0
- cmdbox/licenses/LICENSE_SQLAlchemy_2_0_43_MIT.txt +19 -0
- cmdbox/licenses/LICENSE_Werkzeug_3_1_1_BSD_License.txt +28 -0
- cmdbox/licenses/LICENSE_absolufy-imports_0_3_1_MIT_License.txt +21 -0
- cmdbox/licenses/LICENSE_anyio_4_10_0_UNKNOWN.txt +20 -0
- cmdbox/licenses/{LICENSE_typer_0_16_0_MIT_License.txt → LICENSE_backoff_2_2_1_MIT_License.txt} +1 -1
- cmdbox/licenses/LICENSE_certifi_2025_8_3_Mozilla_Public_License_2_0-MPL_2_0.txt +20 -0
- cmdbox/licenses/LICENSE_charset-normalizer_3_4_3_MIT.txt +21 -0
- cmdbox/licenses/LICENSE_cryptography_45_0_6_Apache-2_0_OR_BSD-3-Clause.txt +3 -0
- cmdbox/licenses/LICENSE_cyclopts_3_22_5_Apache_Software_License.txt +201 -0
- cmdbox/licenses/LICENSE_fastapi-sso_0_18_0_MIT_License.txt +21 -0
- cmdbox/licenses/LICENSE_fastmcp_2_11_3_Apache_Software_License.txt +201 -0
- cmdbox/licenses/LICENSE_google-adk_1_9_0_Apache_Software_License.txt +202 -0
- cmdbox/licenses/LICENSE_google-genai_1_28_0_Apache_Software_License.txt +202 -0
- cmdbox/licenses/LICENSE_google-genai_1_29_0_Apache_Software_License.txt +202 -0
- cmdbox/licenses/LICENSE_greenlet_3_2_4_MIT_AND_Python-2_0.txt +30 -0
- cmdbox/licenses/LICENSE_isodate_0_7_2_BSD_License.txt +26 -0
- cmdbox/licenses/LICENSE_lazy-object-proxy_1_11_0_BSD_License.txt +20 -0
- cmdbox/licenses/LICENSE_litellm-enterprise_0_1_19_UNKNOWN.txt +37 -0
- cmdbox/licenses/LICENSE_litellm_1_75_5_post1_MIT_License.txt +26 -0
- cmdbox/licenses/LICENSE_markdown-it-py_4_0_0_MIT_License.txt +21 -0
- cmdbox/licenses/LICENSE_mcp_1_12_4_MIT_License.txt +21 -0
- cmdbox/licenses/LICENSE_multidict_6_6_4_Apache_License_2_0.txt +13 -0
- cmdbox/licenses/LICENSE_oauthlib_3_3_1_BSD-3-Clause.txt +27 -0
- cmdbox/licenses/LICENSE_openai_1_99_9_Apache_Software_License.txt +201 -0
- cmdbox/licenses/LICENSE_openapi-core_0_19_5_BSD_License.txt +29 -0
- cmdbox/licenses/LICENSE_openapi-schema-validator_0_6_3_BSD_License.txt +29 -0
- cmdbox/licenses/LICENSE_openapi-spec-validator_0_7_2_Apache_Software_License.txt +201 -0
- cmdbox/licenses/LICENSE_opentelemetry-api_1_36_0_UNKNOWN.txt +201 -0
- cmdbox/licenses/LICENSE_opentelemetry-sdk_1_36_0_UNKNOWN.txt +201 -0
- cmdbox/licenses/LICENSE_opentelemetry-semantic-conventions_0_57b0_UNKNOWN.txt +201 -0
- cmdbox/licenses/LICENSE_orjson_3_11_1_Apache_Software_License-MIT_License.txt +201 -0
- cmdbox/licenses/LICENSE_parse_1_20_2_MIT_License.txt +19 -0
- cmdbox/licenses/LICENSE_pathable_0_4_4_Apache_Software_License.txt +201 -0
- cmdbox/licenses/{LICENSE_pillow_11_2_1_UNKNOWN.txt → LICENSE_pillow_11_3_0_UNKNOWN.txt} +393 -3
- cmdbox/licenses/LICENSE_pyperclip_1_9_0_BSD_License.txt +27 -0
- cmdbox/licenses/LICENSE_redis_6_4_0_MIT_License.txt +21 -0
- cmdbox/licenses/LICENSE_rfc3339-validator_0_1_4_MIT_License.txt +22 -0
- cmdbox/licenses/LICENSE_rich-rst_1_3_1_MIT_License.txt +7 -0
- cmdbox/licenses/LICENSE_rpds-py_0_27_0_UNKNOWN.txt +19 -0
- cmdbox/licenses/{LICENSE_setuptools_65_5_0_MIT_License.txt → LICENSE_setuptools_80_9_0_UNKNOWN.txt} +0 -2
- cmdbox/licenses/LICENSE_sphinx-intl_2_3_2_UNKNOWN.txt +25 -0
- cmdbox/licenses/LICENSE_tenacity_9_1_2_Apache_Software_License.txt +202 -0
- cmdbox/licenses/LICENSE_tiktoken_0_11_0_MIT_License-Copyright-c-2022_OpenAI-Shantanu_Jain-Permission_is_hereby_granted-free_of_charge-to_any_per.txt +21 -0
- cmdbox/licenses/LICENSE_tokenizers_0_21_4_Apache_Software_License.txt +1 -0
- cmdbox/licenses/LICENSE_voicevox_core_0_16_0_MIT.txt +20 -0
- cmdbox/licenses/LICENSE_watchdog_6_0_0_Apache_Software_License.txt +16 -0
- cmdbox/licenses/{LICENSE_httptools_0_6_4_MIT_License.txt → LICENSE_wsproto_1_2_0_MIT_License.txt} +3 -3
- cmdbox/licenses/files.txt +69 -50
- cmdbox/logconf_cmdbox.yml +136 -0
- cmdbox/version.py +2 -2
- cmdbox/web/agent.html +16 -2
- cmdbox/web/assets/cmdbox/agent.js +226 -1
- cmdbox/web/assets/cmdbox/common.js +20 -25
- cmdbox/web/assets/cmdbox/svgicon.js +18 -0
- {cmdbox-0.6.3.2.dist-info → cmdbox-0.6.4.1.dist-info}/METADATA +29 -20
- {cmdbox-0.6.3.2.dist-info → cmdbox-0.6.4.1.dist-info}/RECORD +158 -118
- {cmdbox-0.6.3.2.dist-info → cmdbox-0.6.4.1.dist-info}/WHEEL +1 -1
- cmdbox/config.yml +0 -3
- cmdbox/licenses/LICENSE_shellingham_1_5_4_ISC_License-ISCL.txt +0 -13
- cmdbox/licenses/LICENSE_watchfiles_1_1_0_MIT_License.txt +0 -21
- cmdbox/logconf_audit.yml +0 -43
- cmdbox/logconf_client.yml +0 -43
- cmdbox/logconf_edge.yml +0 -43
- cmdbox/logconf_gui.yml +0 -43
- cmdbox/logconf_mcp.yml +0 -43
- cmdbox/logconf_server.yml +0 -43
- cmdbox/logconf_web.yml +0 -43
- /cmdbox/licenses/{LICENSE_Authlib_1_6_0_BSD_License.txt → LICENSE_Authlib_1_6_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_SQLAlchemy_2_0_41_MIT.txt → LICENSE_SQLAlchemy_2_0_42_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE_aiohttp_3_12_13_Apache-2_0.txt → LICENSE_aiohttp_3_12_15_Apache-2_0_AND_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE_aiosignal_1_3_2_Apache_Software_License.txt → LICENSE_aiosignal_1_4_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_certifi_2025_6_15_Mozilla_Public_License_2_0-MPL_2_0.txt → LICENSE_certifi_2025_7_14_Mozilla_Public_License_2_0-MPL_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE_cryptography_45_0_4_Apache-2_0_OR_BSD-3-Clause.txt → LICENSE_cryptography_45_0_5_Apache-2_0_OR_BSD-3-Clause.txt} +0 -0
- /cmdbox/licenses/{LICENSE_docstring_parser_0_16_MIT_License.txt → LICENSE_docstring_parser_0_17_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_fastapi_0_115_14_MIT_License.txt → LICENSE_fastapi_0_116_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_fastmcp_2_10_1_Apache_Software_License.txt → LICENSE_fastmcp_2_11_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_fsspec_2025_5_1_BSD_License.txt → LICENSE_fsspec_2025_7_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_google-adk_1_5_0_Apache_Software_License.txt → LICENSE_google-adk_1_10_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_google-api-python-client_2_174_0_Apache_Software_License.txt → LICENSE_google-api-python-client_2_177_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_huggingface-hub_0_33_1_Apache_Software_License.txt → LICENSE_google-api-python-client_2_178_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_google-cloud-aiplatform_1_100_0_Apache_2_0.txt → LICENSE_google-cloud-aiplatform_1_106_0_Apache_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE_google-cloud-bigquery_3_34_0_Apache_Software_License.txt → LICENSE_google-cloud-aiplatform_1_108_0_Apache_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE_google-genai_1_23_0_Apache_Software_License.txt → LICENSE_google-cloud-bigquery_3_35_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_grpcio-status_1_73_1_Apache_Software_License.txt → LICENSE_grpcio-status_1_74_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_grpcio_1_73_1_Apache_Software_License.txt → LICENSE_grpcio_1_74_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_opentelemetry-api_1_34_1_Apache_Software_License.txt → LICENSE_huggingface-hub_0_34_3_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_opentelemetry-sdk_1_34_1_Apache_Software_License.txt → LICENSE_huggingface-hub_0_34_4_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_opentelemetry-semantic-conventions_0_55b1_Apache_Software_License.txt → LICENSE_jsonschema-path_0_3_4_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_jsonschema_4_24_0_UNKNOWN.txt → LICENSE_jsonschema_4_25_0_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE_litellm_1_73_6_MIT_License.txt → LICENSE_litellm_1_74_12_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_mcp_1_10_1_MIT_License.txt → LICENSE_mcp_1_12_3_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_multidict_6_6_2_Apache_License_2_0.txt → LICENSE_multidict_6_6_3_Apache_License_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE_nh3_0_2_21_MIT.txt → LICENSE_nh3_0_3_0_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE_numpy_2_3_1_BSD_License.txt → LICENSE_numpy_2_3_2_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_openai_1_93_0_Apache_Software_License.txt → LICENSE_openai_1_98_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_tokenizers_0_21_2_Apache_Software_License.txt → LICENSE_pywin32_311_Python_Software_Foundation_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_regex_2024_11_6_Apache_Software_License.txt → LICENSE_regex_2025_7_34_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE_rich_14_0_0_MIT_License.txt → LICENSE_rich_14_1_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_rpds-py_0_25_1_MIT.txt → LICENSE_rpds-py_0_26_0_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE_sse-starlette_2_3_6_BSD_License.txt → LICENSE_sse-starlette_3_0_2_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE_starlette_0_46_2_BSD_License.txt → LICENSE_starlette_0_47_2_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE_typing_extensions_4_14_0_UNKNOWN.txt → LICENSE_typing_extensions_4_14_1_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE_zope_event_5_1_Zope_Public_License.txt → LICENSE_zope_event_5_1_1_Zope_Public_License.txt} +0 -0
- {cmdbox-0.6.3.2.dist-info → cmdbox-0.6.4.1.dist-info}/entry_points.txt +0 -0
- {cmdbox-0.6.3.2.dist-info → cmdbox-0.6.4.1.dist-info/licenses}/LICENSE +0 -0
- {cmdbox-0.6.3.2.dist-info → cmdbox-0.6.4.1.dist-info}/top_level.txt +0 -0
cmdbox/app/server.py
CHANGED
|
@@ -1,224 +1,224 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from cmdbox.app import common, filer, feature, options
|
|
3
|
-
from cmdbox.app.commons import redis_client
|
|
4
|
-
from redis import exceptions
|
|
5
|
-
from typing import List, Dict, Any
|
|
6
|
-
import logging
|
|
7
|
-
import redis
|
|
8
|
-
import time
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Server(filer.Filer):
|
|
12
|
-
|
|
13
|
-
def __init__(self, data_dir:Path, logger:logging.Logger, redis_host:str="localhost", redis_port:int=6379, redis_password:str=None, svname:str='server'):
|
|
14
|
-
"""
|
|
15
|
-
Redisサーバーに接続し、クライアントからのコマンドを受信し実行する
|
|
16
|
-
|
|
17
|
-
Args:
|
|
18
|
-
data_dir (Path): データフォルダのパス
|
|
19
|
-
logger (logging): ロガー
|
|
20
|
-
redis_host (str): Redisホスト名, by default "localhost"
|
|
21
|
-
redis_port (int): Redisポート番号, by default 6379
|
|
22
|
-
redis_password (str): Redisパスワード, by default None
|
|
23
|
-
svname (str, optional): サーバーのサービス名. by default 'server'
|
|
24
|
-
"""
|
|
25
|
-
super().__init__(data_dir, logger)
|
|
26
|
-
if svname.find('-') >= 0:
|
|
27
|
-
raise ValueError(f"Server name is invalid. '-' is not allowed. svname={svname}")
|
|
28
|
-
self.redis_host = redis_host
|
|
29
|
-
self.redis_port = redis_port
|
|
30
|
-
self.redis_password = redis_password
|
|
31
|
-
self.org_svname = svname
|
|
32
|
-
self.svname = f"{svname}-{common.random_string(size=6)}"
|
|
33
|
-
self.redis_cli = None
|
|
34
|
-
self.sessions:Dict[str, Dict[str, Any]] = {}
|
|
35
|
-
self.is_running = False
|
|
36
|
-
self.train_thread = None
|
|
37
|
-
self.cleaning_interval = 60
|
|
38
|
-
if self.logger.level == logging.DEBUG:
|
|
39
|
-
self.logger.debug(f"server init parameter: data={self.data_dir} -> {self.data_dir.absolute()}")
|
|
40
|
-
self.logger.debug(f"server init parameter: redis_host={self.redis_host}")
|
|
41
|
-
self.logger.debug(f"server init parameter: redis_port={self.redis_port}")
|
|
42
|
-
self.logger.debug(f"server init parameter: redis_password=********")
|
|
43
|
-
self.logger.debug(f"server init parameter: svname={self.svname}")
|
|
44
|
-
self.options = options.Options.getInstance()
|
|
45
|
-
|
|
46
|
-
def __enter__(self):
|
|
47
|
-
self.start_server()
|
|
48
|
-
return self
|
|
49
|
-
|
|
50
|
-
def __exit__(self, a, b, c):
|
|
51
|
-
self.terminate_server()
|
|
52
|
-
|
|
53
|
-
def start_server(self, retry_count:int=20, retry_interval:int=5):
|
|
54
|
-
"""
|
|
55
|
-
サーバー処理を開始する
|
|
56
|
-
"""
|
|
57
|
-
self.is_running = False
|
|
58
|
-
self.retry_count = retry_count
|
|
59
|
-
self.retry_interval = retry_interval
|
|
60
|
-
if self.logger.level == logging.DEBUG:
|
|
61
|
-
self.logger.debug(f"server start parameter: retry_count={self.retry_count}")
|
|
62
|
-
self.logger.debug(f"server start parameter: retry_interval={self.retry_interval}")
|
|
63
|
-
self.redis_cli = redis_client.RedisClient(self.logger, host=self.redis_host, port=self.redis_port, password=self.redis_password, svname=self.svname)
|
|
64
|
-
if self.redis_cli.check_server(find_svname=False, retry_count=self.retry_count, retry_interval=self.retry_interval, outstatus=True):
|
|
65
|
-
self.is_running = True
|
|
66
|
-
self._run_server()
|
|
67
|
-
|
|
68
|
-
def list_server(self) -> Dict[str, List[Dict[str, Any]]]:
|
|
69
|
-
"""
|
|
70
|
-
起動しているサーバーリストを取得する
|
|
71
|
-
|
|
72
|
-
Returns:
|
|
73
|
-
Dict[str, List[Dict[str, Any]]]: サーバーのリスト
|
|
74
|
-
"""
|
|
75
|
-
if self.redis_cli is None:
|
|
76
|
-
self.redis_cli = redis_client.RedisClient(self.logger, host=self.redis_host, port=self.redis_port, password=self.redis_password, svname=self.svname)
|
|
77
|
-
svlist = self.redis_cli.list_server()
|
|
78
|
-
if len(svlist) <= 0:
|
|
79
|
-
return dict(warn="No server is running.")
|
|
80
|
-
return dict(success=svlist)
|
|
81
|
-
|
|
82
|
-
def _clean_server(self):
|
|
83
|
-
"""
|
|
84
|
-
Redisサーバーに残っている停止済みのサーバーキーを削除する
|
|
85
|
-
"""
|
|
86
|
-
hblist = self.redis_cli.keys("hb-*")
|
|
87
|
-
for hb in hblist:
|
|
88
|
-
hb = hb.decode()
|
|
89
|
-
try:
|
|
90
|
-
v = self.redis_cli.hget(hb, 'ctime')
|
|
91
|
-
if v is None:
|
|
92
|
-
continue
|
|
93
|
-
except exceptions.ResponseError:
|
|
94
|
-
self.logger.warning(f"Failed to get ctime. {hb}", exc_info=True)
|
|
95
|
-
continue
|
|
96
|
-
tm = time.time() - float(v)
|
|
97
|
-
if tm > self.cleaning_interval:
|
|
98
|
-
self.redis_cli.delete(hb)
|
|
99
|
-
self.redis_cli.delete(hb.replace("hb-", "sv-"))
|
|
100
|
-
|
|
101
|
-
def _clean_reskey(self):
|
|
102
|
-
"""
|
|
103
|
-
Redisサーバーに残っている停止済みのクライアントキーを削除する
|
|
104
|
-
"""
|
|
105
|
-
rlist = self.redis_cli.keys("cl-*")
|
|
106
|
-
for reskey in rlist:
|
|
107
|
-
try:
|
|
108
|
-
tm = int(reskey.decode().split("-")[2])
|
|
109
|
-
if time.time() - tm > self.cleaning_interval:
|
|
110
|
-
self.redis_cli.delete(reskey)
|
|
111
|
-
except Exception as e:
|
|
112
|
-
self.redis_cli.delete(reskey)
|
|
113
|
-
|
|
114
|
-
def _run_server(self):
|
|
115
|
-
self.logger.info(f"start server. svname={self.svname}")
|
|
116
|
-
ltime = time.time()
|
|
117
|
-
receive_cnt = 0
|
|
118
|
-
sccess_cnt = 0
|
|
119
|
-
warn_cnt = 0
|
|
120
|
-
error_cnt = 0
|
|
121
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'receive_cnt', receive_cnt)
|
|
122
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'sccess_cnt', sccess_cnt)
|
|
123
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'warn_cnt', warn_cnt)
|
|
124
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'error_cnt', error_cnt)
|
|
125
|
-
|
|
126
|
-
def _publish(msg_str):
|
|
127
|
-
# 各サーバーにメッセージを配布する
|
|
128
|
-
hblist = self.redis_cli.keys(f"hb-{self.org_svname}-*")
|
|
129
|
-
for hb in hblist:
|
|
130
|
-
hb = hb.decode()
|
|
131
|
-
sv = hb.replace("hb-", "sv-")
|
|
132
|
-
self.redis_cli.rpush(sv, msg_str)
|
|
133
|
-
|
|
134
|
-
while self.is_running:
|
|
135
|
-
try:
|
|
136
|
-
msg = None
|
|
137
|
-
# ブロッキングリストから要素を取り出す
|
|
138
|
-
ctime = time.time()
|
|
139
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'ctime', ctime)
|
|
140
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'status', 'ready')
|
|
141
|
-
result = self.redis_cli.blpop(self.redis_cli.svname)
|
|
142
|
-
if ctime - ltime > self.cleaning_interval:
|
|
143
|
-
self._clean_server()
|
|
144
|
-
self._clean_reskey()
|
|
145
|
-
ltime = ctime
|
|
146
|
-
to_cluster = False
|
|
147
|
-
if result is None or len(result) <= 0:
|
|
148
|
-
# クラスター宛メッセージがあるか確認する
|
|
149
|
-
result = self.redis_cli.blpop(f"sv-{self.org_svname}")
|
|
150
|
-
if result is None or len(result) <= 0:
|
|
151
|
-
time.sleep(1)
|
|
152
|
-
continue
|
|
153
|
-
to_cluster = True
|
|
154
|
-
msg_str = result[1].decode()
|
|
155
|
-
msg = msg_str.split(' ')
|
|
156
|
-
if len(msg) <= 0:
|
|
157
|
-
time.sleep(1)
|
|
158
|
-
continue
|
|
159
|
-
|
|
160
|
-
st = None
|
|
161
|
-
receive_cnt += 1
|
|
162
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'receive_cnt', receive_cnt)
|
|
163
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'status', 'processing')
|
|
164
|
-
|
|
165
|
-
svcmd_feature:feature.Feature = self.options.get_svcmd_feature(msg[0])
|
|
166
|
-
if svcmd_feature is not None:
|
|
167
|
-
if to_cluster and svcmd_feature.is_cluster_redirect():
|
|
168
|
-
_publish(msg_str)
|
|
169
|
-
continue
|
|
170
|
-
if msg[0] == 'stop_server':
|
|
171
|
-
self.is_running = False
|
|
172
|
-
st = svcmd_feature.svrun(self.data_dir, self.logger, self.redis_cli, msg, self.sessions)
|
|
173
|
-
else:
|
|
174
|
-
self.logger.warning(f"Unknown command {msg}")
|
|
175
|
-
st = self.RESP_WARN
|
|
176
|
-
|
|
177
|
-
if st==self.
|
|
178
|
-
sccess_cnt += 1
|
|
179
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'sccess_cnt', sccess_cnt)
|
|
180
|
-
elif st==self.RESP_WARN:
|
|
181
|
-
warn_cnt += 1
|
|
182
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'warn_cnt', warn_cnt)
|
|
183
|
-
elif st==self.RESP_ERROR:
|
|
184
|
-
error_cnt += 1
|
|
185
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'error_cnt', error_cnt)
|
|
186
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'ctime', time.time())
|
|
187
|
-
except exceptions.TimeoutError:
|
|
188
|
-
pass
|
|
189
|
-
except exceptions.ConnectionError as e:
|
|
190
|
-
self.logger.warning(f"Connection to the server was lost. {e}", exc_info=True)
|
|
191
|
-
if not self.redis_cli.check_server(find_svname=False, retry_count=self.retry_count, retry_interval=self.retry_interval, outstatus=True):
|
|
192
|
-
self.is_running = False
|
|
193
|
-
break
|
|
194
|
-
except OSError as e:
|
|
195
|
-
self.logger.warning(f"OSError. {e}. This message is not executable in the server environment. ({msg})", exc_info=True)
|
|
196
|
-
if msg is not None and len(msg) > 1:
|
|
197
|
-
self.redis_cli.rpush(msg[1], dict(warn=f"OSError. {e}. This message is not executable in the server environment. ({msg[0]})"))
|
|
198
|
-
error_cnt += 1
|
|
199
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'error_cnt', error_cnt)
|
|
200
|
-
pass
|
|
201
|
-
except IndexError as e:
|
|
202
|
-
self.logger.warning(f"IndexError. {e}. The message received by the server is invalid. ({msg})", exc_info=True)
|
|
203
|
-
if msg is not None and len(msg) > 1:
|
|
204
|
-
self.redis_cli.rpush(msg[1], dict(warn=f"IndexError. {e}. The message received by the server is invalid. ({msg[0]})"))
|
|
205
|
-
error_cnt += 1
|
|
206
|
-
self.redis_cli.hset(self.redis_cli.hbname, 'error_cnt', error_cnt)
|
|
207
|
-
pass
|
|
208
|
-
except KeyboardInterrupt as e:
|
|
209
|
-
self.is_running = False
|
|
210
|
-
break
|
|
211
|
-
except Exception as e:
|
|
212
|
-
self.logger.warning(f"Unknown error occurred. {e}. Service will be stopped due to unknown cause.({msg})", exc_info=True)
|
|
213
|
-
self.is_running = False
|
|
214
|
-
break
|
|
215
|
-
self.redis_cli.delete(self.redis_cli.svname)
|
|
216
|
-
self.redis_cli.delete(self.redis_cli.hbname)
|
|
217
|
-
self.logger.info(f"stop server. svname={self.redis_cli.svname}")
|
|
218
|
-
|
|
219
|
-
def terminate_server(self):
|
|
220
|
-
"""
|
|
221
|
-
サーバー処理を終了する
|
|
222
|
-
"""
|
|
223
|
-
self.redis_cli.close()
|
|
224
|
-
self.logger.info(f"terminate server.")
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from cmdbox.app import common, filer, feature, options
|
|
3
|
+
from cmdbox.app.commons import redis_client
|
|
4
|
+
from redis import exceptions
|
|
5
|
+
from typing import List, Dict, Any
|
|
6
|
+
import logging
|
|
7
|
+
import redis
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Server(filer.Filer):
|
|
12
|
+
|
|
13
|
+
def __init__(self, data_dir:Path, logger:logging.Logger, redis_host:str="localhost", redis_port:int=6379, redis_password:str=None, svname:str='server'):
|
|
14
|
+
"""
|
|
15
|
+
Redisサーバーに接続し、クライアントからのコマンドを受信し実行する
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
data_dir (Path): データフォルダのパス
|
|
19
|
+
logger (logging): ロガー
|
|
20
|
+
redis_host (str): Redisホスト名, by default "localhost"
|
|
21
|
+
redis_port (int): Redisポート番号, by default 6379
|
|
22
|
+
redis_password (str): Redisパスワード, by default None
|
|
23
|
+
svname (str, optional): サーバーのサービス名. by default 'server'
|
|
24
|
+
"""
|
|
25
|
+
super().__init__(data_dir, logger)
|
|
26
|
+
if svname.find('-') >= 0:
|
|
27
|
+
raise ValueError(f"Server name is invalid. '-' is not allowed. svname={svname}")
|
|
28
|
+
self.redis_host = redis_host
|
|
29
|
+
self.redis_port = redis_port
|
|
30
|
+
self.redis_password = redis_password
|
|
31
|
+
self.org_svname = svname
|
|
32
|
+
self.svname = f"{svname}-{common.random_string(size=6)}"
|
|
33
|
+
self.redis_cli = None
|
|
34
|
+
self.sessions:Dict[str, Dict[str, Any]] = {}
|
|
35
|
+
self.is_running = False
|
|
36
|
+
self.train_thread = None
|
|
37
|
+
self.cleaning_interval = 60
|
|
38
|
+
if self.logger.level == logging.DEBUG:
|
|
39
|
+
self.logger.debug(f"server init parameter: data={self.data_dir} -> {self.data_dir.absolute()}")
|
|
40
|
+
self.logger.debug(f"server init parameter: redis_host={self.redis_host}")
|
|
41
|
+
self.logger.debug(f"server init parameter: redis_port={self.redis_port}")
|
|
42
|
+
self.logger.debug(f"server init parameter: redis_password=********")
|
|
43
|
+
self.logger.debug(f"server init parameter: svname={self.svname}")
|
|
44
|
+
self.options = options.Options.getInstance()
|
|
45
|
+
|
|
46
|
+
def __enter__(self):
|
|
47
|
+
self.start_server()
|
|
48
|
+
return self
|
|
49
|
+
|
|
50
|
+
def __exit__(self, a, b, c):
|
|
51
|
+
self.terminate_server()
|
|
52
|
+
|
|
53
|
+
def start_server(self, retry_count:int=20, retry_interval:int=5):
|
|
54
|
+
"""
|
|
55
|
+
サーバー処理を開始する
|
|
56
|
+
"""
|
|
57
|
+
self.is_running = False
|
|
58
|
+
self.retry_count = retry_count
|
|
59
|
+
self.retry_interval = retry_interval
|
|
60
|
+
if self.logger.level == logging.DEBUG:
|
|
61
|
+
self.logger.debug(f"server start parameter: retry_count={self.retry_count}")
|
|
62
|
+
self.logger.debug(f"server start parameter: retry_interval={self.retry_interval}")
|
|
63
|
+
self.redis_cli = redis_client.RedisClient(self.logger, host=self.redis_host, port=self.redis_port, password=self.redis_password, svname=self.svname)
|
|
64
|
+
if self.redis_cli.check_server(find_svname=False, retry_count=self.retry_count, retry_interval=self.retry_interval, outstatus=True):
|
|
65
|
+
self.is_running = True
|
|
66
|
+
self._run_server()
|
|
67
|
+
|
|
68
|
+
def list_server(self) -> Dict[str, List[Dict[str, Any]]]:
|
|
69
|
+
"""
|
|
70
|
+
起動しているサーバーリストを取得する
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Dict[str, List[Dict[str, Any]]]: サーバーのリスト
|
|
74
|
+
"""
|
|
75
|
+
if self.redis_cli is None:
|
|
76
|
+
self.redis_cli = redis_client.RedisClient(self.logger, host=self.redis_host, port=self.redis_port, password=self.redis_password, svname=self.svname)
|
|
77
|
+
svlist = self.redis_cli.list_server()
|
|
78
|
+
if len(svlist) <= 0:
|
|
79
|
+
return dict(warn="No server is running.")
|
|
80
|
+
return dict(success=svlist)
|
|
81
|
+
|
|
82
|
+
def _clean_server(self):
|
|
83
|
+
"""
|
|
84
|
+
Redisサーバーに残っている停止済みのサーバーキーを削除する
|
|
85
|
+
"""
|
|
86
|
+
hblist = self.redis_cli.keys("hb-*")
|
|
87
|
+
for hb in hblist:
|
|
88
|
+
hb = hb.decode()
|
|
89
|
+
try:
|
|
90
|
+
v = self.redis_cli.hget(hb, 'ctime')
|
|
91
|
+
if v is None:
|
|
92
|
+
continue
|
|
93
|
+
except exceptions.ResponseError:
|
|
94
|
+
self.logger.warning(f"Failed to get ctime. {hb}", exc_info=True)
|
|
95
|
+
continue
|
|
96
|
+
tm = time.time() - float(v)
|
|
97
|
+
if tm > self.cleaning_interval:
|
|
98
|
+
self.redis_cli.delete(hb)
|
|
99
|
+
self.redis_cli.delete(hb.replace("hb-", "sv-"))
|
|
100
|
+
|
|
101
|
+
def _clean_reskey(self):
|
|
102
|
+
"""
|
|
103
|
+
Redisサーバーに残っている停止済みのクライアントキーを削除する
|
|
104
|
+
"""
|
|
105
|
+
rlist = self.redis_cli.keys("cl-*")
|
|
106
|
+
for reskey in rlist:
|
|
107
|
+
try:
|
|
108
|
+
tm = int(reskey.decode().split("-")[2])
|
|
109
|
+
if time.time() - tm > self.cleaning_interval:
|
|
110
|
+
self.redis_cli.delete(reskey)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
self.redis_cli.delete(reskey)
|
|
113
|
+
|
|
114
|
+
def _run_server(self):
|
|
115
|
+
self.logger.info(f"start server. svname={self.svname}")
|
|
116
|
+
ltime = time.time()
|
|
117
|
+
receive_cnt = 0
|
|
118
|
+
sccess_cnt = 0
|
|
119
|
+
warn_cnt = 0
|
|
120
|
+
error_cnt = 0
|
|
121
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'receive_cnt', receive_cnt)
|
|
122
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'sccess_cnt', sccess_cnt)
|
|
123
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'warn_cnt', warn_cnt)
|
|
124
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'error_cnt', error_cnt)
|
|
125
|
+
|
|
126
|
+
def _publish(msg_str):
|
|
127
|
+
# 各サーバーにメッセージを配布する
|
|
128
|
+
hblist = self.redis_cli.keys(f"hb-{self.org_svname}-*")
|
|
129
|
+
for hb in hblist:
|
|
130
|
+
hb = hb.decode()
|
|
131
|
+
sv = hb.replace("hb-", "sv-")
|
|
132
|
+
self.redis_cli.rpush(sv, msg_str)
|
|
133
|
+
|
|
134
|
+
while self.is_running:
|
|
135
|
+
try:
|
|
136
|
+
msg = None
|
|
137
|
+
# ブロッキングリストから要素を取り出す
|
|
138
|
+
ctime = time.time()
|
|
139
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'ctime', ctime)
|
|
140
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'status', 'ready')
|
|
141
|
+
result = self.redis_cli.blpop(self.redis_cli.svname)
|
|
142
|
+
if ctime - ltime > self.cleaning_interval:
|
|
143
|
+
self._clean_server()
|
|
144
|
+
self._clean_reskey()
|
|
145
|
+
ltime = ctime
|
|
146
|
+
to_cluster = False
|
|
147
|
+
if result is None or len(result) <= 0:
|
|
148
|
+
# クラスター宛メッセージがあるか確認する
|
|
149
|
+
result = self.redis_cli.blpop(f"sv-{self.org_svname}")
|
|
150
|
+
if result is None or len(result) <= 0:
|
|
151
|
+
time.sleep(1)
|
|
152
|
+
continue
|
|
153
|
+
to_cluster = True
|
|
154
|
+
msg_str = result[1].decode()
|
|
155
|
+
msg = msg_str.split(' ')
|
|
156
|
+
if len(msg) <= 0:
|
|
157
|
+
time.sleep(1)
|
|
158
|
+
continue
|
|
159
|
+
|
|
160
|
+
st = None
|
|
161
|
+
receive_cnt += 1
|
|
162
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'receive_cnt', receive_cnt)
|
|
163
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'status', 'processing')
|
|
164
|
+
|
|
165
|
+
svcmd_feature:feature.Feature = self.options.get_svcmd_feature(msg[0])
|
|
166
|
+
if svcmd_feature is not None:
|
|
167
|
+
if to_cluster and svcmd_feature.is_cluster_redirect():
|
|
168
|
+
_publish(msg_str)
|
|
169
|
+
continue
|
|
170
|
+
if msg[0] == 'stop_server':
|
|
171
|
+
self.is_running = False
|
|
172
|
+
st = svcmd_feature.svrun(self.data_dir, self.logger, self.redis_cli, msg, self.sessions)
|
|
173
|
+
else:
|
|
174
|
+
self.logger.warning(f"Unknown command {msg}")
|
|
175
|
+
st = self.RESP_WARN
|
|
176
|
+
|
|
177
|
+
if st==self.RESP_SUCCESS:
|
|
178
|
+
sccess_cnt += 1
|
|
179
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'sccess_cnt', sccess_cnt)
|
|
180
|
+
elif st==self.RESP_WARN:
|
|
181
|
+
warn_cnt += 1
|
|
182
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'warn_cnt', warn_cnt)
|
|
183
|
+
elif st==self.RESP_ERROR:
|
|
184
|
+
error_cnt += 1
|
|
185
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'error_cnt', error_cnt)
|
|
186
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'ctime', time.time())
|
|
187
|
+
except exceptions.TimeoutError:
|
|
188
|
+
pass
|
|
189
|
+
except exceptions.ConnectionError as e:
|
|
190
|
+
self.logger.warning(f"Connection to the server was lost. {e}", exc_info=True)
|
|
191
|
+
if not self.redis_cli.check_server(find_svname=False, retry_count=self.retry_count, retry_interval=self.retry_interval, outstatus=True):
|
|
192
|
+
self.is_running = False
|
|
193
|
+
break
|
|
194
|
+
except OSError as e:
|
|
195
|
+
self.logger.warning(f"OSError. {e}. This message is not executable in the server environment. ({msg})", exc_info=True)
|
|
196
|
+
if msg is not None and len(msg) > 1:
|
|
197
|
+
self.redis_cli.rpush(msg[1], dict(warn=f"OSError. {e}. This message is not executable in the server environment. ({msg[0]})"))
|
|
198
|
+
error_cnt += 1
|
|
199
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'error_cnt', error_cnt)
|
|
200
|
+
pass
|
|
201
|
+
except IndexError as e:
|
|
202
|
+
self.logger.warning(f"IndexError. {e}. The message received by the server is invalid. ({msg})", exc_info=True)
|
|
203
|
+
if msg is not None and len(msg) > 1:
|
|
204
|
+
self.redis_cli.rpush(msg[1], dict(warn=f"IndexError. {e}. The message received by the server is invalid. ({msg[0]})"))
|
|
205
|
+
error_cnt += 1
|
|
206
|
+
self.redis_cli.hset(self.redis_cli.hbname, 'error_cnt', error_cnt)
|
|
207
|
+
pass
|
|
208
|
+
except KeyboardInterrupt as e:
|
|
209
|
+
self.is_running = False
|
|
210
|
+
break
|
|
211
|
+
except Exception as e:
|
|
212
|
+
self.logger.warning(f"Unknown error occurred. {e}. Service will be stopped due to unknown cause.({msg})", exc_info=True)
|
|
213
|
+
self.is_running = False
|
|
214
|
+
break
|
|
215
|
+
self.redis_cli.delete(self.redis_cli.svname)
|
|
216
|
+
self.redis_cli.delete(self.redis_cli.hbname)
|
|
217
|
+
self.logger.info(f"stop server. svname={self.redis_cli.svname}")
|
|
218
|
+
|
|
219
|
+
def terminate_server(self):
|
|
220
|
+
"""
|
|
221
|
+
サーバー処理を終了する
|
|
222
|
+
"""
|
|
223
|
+
self.redis_cli.close()
|
|
224
|
+
self.logger.info(f"terminate server.")
|
cmdbox/app/web.py
CHANGED
|
@@ -793,10 +793,15 @@ class Web:
|
|
|
793
793
|
"""
|
|
794
794
|
if session_id is None:
|
|
795
795
|
session_id = common.random_string(32)
|
|
796
|
-
|
|
797
|
-
|
|
796
|
+
try:
|
|
797
|
+
session = await session_service.get_session(app_name=self.ver.__appid__, user_id=user_id, session_id=session_id)
|
|
798
|
+
if session is None:
|
|
799
|
+
session = await session_service.create_session(app_name=self.ver.__appid__, user_id=user_id, session_id=session_id)
|
|
800
|
+
return session
|
|
801
|
+
except NotImplementedError:
|
|
802
|
+
# セッションが1件もない場合はNotImplementedErrorが発生することがある
|
|
798
803
|
session = await session_service.create_session(app_name=self.ver.__appid__, user_id=user_id, session_id=session_id)
|
|
799
|
-
|
|
804
|
+
return session
|
|
800
805
|
self.create_agent_session = create_agent_session
|
|
801
806
|
async def list_agent_sessions(session_service:BaseSessionService, user_id:str, session_id:str=None) -> List[Session]:
|
|
802
807
|
"""
|
|
@@ -809,20 +814,37 @@ class Web:
|
|
|
809
814
|
Returns:
|
|
810
815
|
List[Session]: セッションリスト
|
|
811
816
|
"""
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
+
try:
|
|
818
|
+
if session_id is None:
|
|
819
|
+
sessions = await session_service.list_sessions(app_name=self.ver.__appid__, user_id=user_id)
|
|
820
|
+
ret = []
|
|
821
|
+
for s in sessions.sessions:
|
|
822
|
+
try:
|
|
823
|
+
session = await session_service.get_session(app_name=self.ver.__appid__, user_id=user_id, session_id=s.id)
|
|
824
|
+
if session is None:
|
|
825
|
+
continue
|
|
826
|
+
ret.append(session)
|
|
827
|
+
except:
|
|
828
|
+
# セッションが取得できない場合は削除する
|
|
829
|
+
try:
|
|
830
|
+
await session_service.delete_session(app_name=self.ver.__appid__, user_id=user_id, session_id=s.id)
|
|
831
|
+
except:
|
|
832
|
+
pass
|
|
833
|
+
finally:
|
|
834
|
+
continue
|
|
835
|
+
return ret
|
|
836
|
+
else:
|
|
837
|
+
session = await session_service.get_session(app_name=self.ver.__appid__, user_id=user_id, session_id=session_id)
|
|
817
838
|
if session is None:
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
839
|
+
return []
|
|
840
|
+
return [session]
|
|
841
|
+
except NotImplementedError:
|
|
842
|
+
# セッションが1件もない場合はNotImplementedErrorが発生することがある
|
|
843
|
+
return []
|
|
844
|
+
except Exception as e:
|
|
845
|
+
# それ以外のエラーが発生した時はログに出力して空リストを返す
|
|
846
|
+
self.logger.warning(f"list_agent_sessions warning: {e}", exc_info=True)
|
|
847
|
+
return []
|
|
826
848
|
self.list_agent_sessions = list_agent_sessions
|
|
827
849
|
async def delete_agent_session(session_service:BaseSessionService, user_id:str, session_id:str) -> bool:
|
|
828
850
|
"""
|
|
@@ -841,7 +863,7 @@ class Web:
|
|
|
841
863
|
|
|
842
864
|
mcp_app:Starlette = None
|
|
843
865
|
if self.mcp is not None:
|
|
844
|
-
mcp_app:Starlette = self.mcp.
|
|
866
|
+
mcp_app:Starlette = self.mcp.http_app()
|
|
845
867
|
#mcp_app:Starlette = self.mcp.http_app()
|
|
846
868
|
if mcp_app is not None:
|
|
847
869
|
app = FastAPI(lifespan=mcp_app.lifespan)
|
cmdbox/extensions/features.yml
CHANGED
|
@@ -44,7 +44,13 @@ agentrule: # Specifies a list of rules that determi
|
|
|
44
44
|
cmds: [list, load] # Specify the "cmd" to which the rule applies. Multiple items can be specified in a list.
|
|
45
45
|
rule: allow # Specifies whether the specified command is allowed or not. Values are allow or deny.
|
|
46
46
|
- mode: client
|
|
47
|
-
cmds: [http]
|
|
47
|
+
cmds: [file_download, file_list, http, server_info]
|
|
48
|
+
rule: allow
|
|
49
|
+
- mode: server
|
|
50
|
+
cmds: [list]
|
|
51
|
+
rule: allow
|
|
52
|
+
- mode: tts
|
|
53
|
+
cmds: [say]
|
|
48
54
|
rule: allow
|
|
49
55
|
audit:
|
|
50
56
|
enabled: true # Specify whether to enable the audit function.
|
|
@@ -60,8 +60,8 @@ class ClientTime(feature.Feature):
|
|
|
60
60
|
ret = dict(success=dict(data=dt.strftime('%Y-%m-%d %H:%M:%S')))
|
|
61
61
|
common.print_format(ret, args.format, tm, args.output_json, args.output_json_append, pf=pf)
|
|
62
62
|
if 'success' not in ret:
|
|
63
|
-
return
|
|
64
|
-
return
|
|
63
|
+
return self.RESP_WARN, ret, None
|
|
64
|
+
return self.RESP_SUCCESS, ret, None
|
|
65
65
|
|
|
66
66
|
def edgerun(self, opt, tool, logger, timeout, prevres = None):
|
|
67
67
|
"""
|
|
@@ -92,8 +92,8 @@ class ServerTime(feature.Feature):
|
|
|
92
92
|
retry_count=args.retry_count, retry_interval=args.retry_interval, timeout=args.timeout)
|
|
93
93
|
common.print_format(ret, args.format, tm, args.output_json, args.output_json_append, pf=pf)
|
|
94
94
|
if 'success' not in ret:
|
|
95
|
-
return
|
|
96
|
-
return
|
|
95
|
+
return self.RESP_WARN, ret, None
|
|
96
|
+
return self.RESP_SUCCESS, ret, None
|
|
97
97
|
|
|
98
98
|
def is_cluster_redirect(self):
|
|
99
99
|
"""
|
|
@@ -124,7 +124,7 @@ class ServerTime(feature.Feature):
|
|
|
124
124
|
dt = datetime.datetime.now(tz)
|
|
125
125
|
ret = dict(success=dict(data=dt.strftime('%Y-%m-%d %H:%M:%S')))
|
|
126
126
|
redis_cli.rpush(msg[1], ret)
|
|
127
|
-
return self.
|
|
127
|
+
return self.RESP_SUCCESS
|
|
128
128
|
|
|
129
129
|
def edgerun(self, opt, tool, logger, timeout, prevres = None):
|
|
130
130
|
"""
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
This is the MIT license: http://www.opensource.org/licenses/mit-license.php
|
|
2
|
+
|
|
3
|
+
Copyright (c) Alex Grönholm
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
6
|
+
software and associated documentation files (the "Software"), to deal in the Software
|
|
7
|
+
without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
8
|
+
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
9
|
+
to whom the Software is furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all copies or
|
|
12
|
+
substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
15
|
+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
16
|
+
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
17
|
+
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
18
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
19
|
+
DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright 2005-2025 SQLAlchemy authors and contributors <see AUTHORS file>.
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to
|
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
8
|
+
so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Copyright 2007 Pallets
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
|
4
|
+
modification, are permitted provided that the following conditions are
|
|
5
|
+
met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
notice, this list of conditions and the following disclaimer in the
|
|
12
|
+
documentation and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
15
|
+
contributors may be used to endorse or promote products derived from
|
|
16
|
+
this software without specific prior written permission.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
19
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
20
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
21
|
+
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
22
|
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
23
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
24
|
+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
25
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
26
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
27
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
28
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|