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/app.py
CHANGED
|
@@ -6,6 +6,7 @@ import argparse
|
|
|
6
6
|
import argcomplete
|
|
7
7
|
import logging
|
|
8
8
|
import time
|
|
9
|
+
import threading
|
|
9
10
|
import sys
|
|
10
11
|
|
|
11
12
|
|
|
@@ -58,6 +59,8 @@ class CmdBoxApp:
|
|
|
58
59
|
|
|
59
60
|
# プラグイン読込み
|
|
60
61
|
sfeatureloadtime = time.perf_counter()
|
|
62
|
+
self.options._load_features_yml(self.ver, logger=self.default_logger)
|
|
63
|
+
self.options.load_features_agentrule(self.default_logger)
|
|
61
64
|
self.options.load_svcmd('cmdbox.app.features.cli', prefix="cmdbox_", excludes=[], appcls=self.appcls, ver=self.ver, logger=self.default_logger,
|
|
62
65
|
isloaded=self.options.is_features_loaded('cli'))
|
|
63
66
|
if self.cli_features_packages is not None:
|
|
@@ -92,7 +95,7 @@ class CmdBoxApp:
|
|
|
92
95
|
except argparse.ArgumentError as e:
|
|
93
96
|
msg = dict(warn=f"ArgumentError: {e}")
|
|
94
97
|
common.print_format(msg, False, 0, None, False)
|
|
95
|
-
return
|
|
98
|
+
return feature.Feature.RESP_WARN, msg, None
|
|
96
99
|
# 起動時引数で指定されたオプションをファイルから読み込んだオプションで上書きする
|
|
97
100
|
args_dict = vars(args)
|
|
98
101
|
for key, val in file_dict.items():
|
|
@@ -120,7 +123,7 @@ class CmdBoxApp:
|
|
|
120
123
|
if args.useopt is None:
|
|
121
124
|
msg = dict(warn=f"Please specify the --useopt option.")
|
|
122
125
|
common.print_format(msg, args.format, smaintime, args.output_json, args.output_json_append)
|
|
123
|
-
return
|
|
126
|
+
return feature.Feature.RESP_WARN, msg, None
|
|
124
127
|
common.saveopt(opt, args.useopt)
|
|
125
128
|
ret = dict(success=f"Save options file. {args.useopt}")
|
|
126
129
|
|
|
@@ -133,15 +136,15 @@ class CmdBoxApp:
|
|
|
133
136
|
if args.version:
|
|
134
137
|
v = self.ver.__logo__ + '\n' + self.ver.__description__
|
|
135
138
|
common.print_format(v, False, smaintime, None, False)
|
|
136
|
-
return
|
|
139
|
+
return feature.Feature.RESP_SUCCESS, v, None
|
|
137
140
|
|
|
138
141
|
if args.mode is None:
|
|
139
142
|
msg = dict(warn=f"mode is None. Please specify the --help option.")
|
|
140
143
|
common.print_format(msg, args.format, smaintime, args.output_json, args.output_json_append)
|
|
141
|
-
return
|
|
144
|
+
return feature.Feature.RESP_WARN, msg, None
|
|
142
145
|
|
|
143
146
|
sloggerinittime = time.perf_counter()
|
|
144
|
-
logger
|
|
147
|
+
logger = self.load_config(args, webcall=webcall if args.cmd != 'webcap' else True)
|
|
145
148
|
try:
|
|
146
149
|
eloggerinittime = time.perf_counter()
|
|
147
150
|
if logger.level == logging.DEBUG:
|
|
@@ -168,7 +171,19 @@ class CmdBoxApp:
|
|
|
168
171
|
else:
|
|
169
172
|
msg = dict(warn=f"Unkown mode or cmd. mode={args.mode}, cmd={args.cmd}")
|
|
170
173
|
common.print_format(msg, args.format, smaintime, args.output_json, args.output_json_append)
|
|
171
|
-
return
|
|
174
|
+
return feature.Feature.RESP_WARN, msg, None
|
|
172
175
|
finally:
|
|
173
176
|
# ログの状態をwebcallから戻す
|
|
174
|
-
|
|
177
|
+
self.load_config(args, webcall=False)
|
|
178
|
+
|
|
179
|
+
def load_config(self, args:argparse.Namespace, webcall:bool=False) -> logging.Logger:
|
|
180
|
+
"""
|
|
181
|
+
アプリケーションの設定を読み込みます。
|
|
182
|
+
"""
|
|
183
|
+
logger, _ = common.load_config(args.mode, debug=args.debug, data=args.data, webcall=webcall if args.cmd != 'webcap' else True, ver=self.ver)
|
|
184
|
+
if not hasattr(common, 'logsv') and hasattr(args, 'logsv') and args.logsv:
|
|
185
|
+
from cmdbox.app.commons import loghandler
|
|
186
|
+
common.logsv = loghandler.LogRecordTCPServer("logsv", host="localhost", port=9020, debug=args.debug)
|
|
187
|
+
threading.Thread(daemon=True, target=common.logsv.serve_until_stopped, name="logsv").start()
|
|
188
|
+
|
|
189
|
+
return logger
|
cmdbox/app/common.py
CHANGED
|
@@ -4,7 +4,6 @@ from cmdbox.app.commons import convert, module, loghandler
|
|
|
4
4
|
from cryptography.fernet import Fernet
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from rich.console import Console
|
|
7
|
-
from rich.logging import RichHandler
|
|
8
7
|
from tabulate import tabulate
|
|
9
8
|
from typing import List, Tuple, Dict, Any
|
|
10
9
|
import argparse
|
|
@@ -195,7 +194,7 @@ def console_log(console:Console, message:Any, highlight:bool=True, **kwargs) ->
|
|
|
195
194
|
**kwargs: その他のキーワード引数
|
|
196
195
|
"""
|
|
197
196
|
dtstr = datetime.datetime.now().strftime('[%Y-%m-%d %H:%M:%S]')
|
|
198
|
-
console.print(f"{dtstr} {message}", highlight=highlight, **kwargs)
|
|
197
|
+
console.print(f"{dtstr} {message}", highlight=highlight, crop=False, soft_wrap=True, **kwargs)
|
|
199
198
|
|
|
200
199
|
def default_logger(debug:bool=False, ver=version, webcall:bool=False) -> logging.Logger:
|
|
201
200
|
"""
|
|
@@ -245,13 +244,13 @@ def load_config(mode:str, debug:bool=False, data=HOME_DIR, webcall:bool=False, v
|
|
|
245
244
|
log_name = mode
|
|
246
245
|
if not log_conf_path.exists():
|
|
247
246
|
log_conf_path = Path(ver.__file__).parent / f"logconf_{ver.__appid__}.yml"
|
|
248
|
-
log_name =
|
|
247
|
+
log_name = mode
|
|
249
248
|
if not log_conf_path.exists():
|
|
250
249
|
log_conf_path = Path(version.__file__).parent / f"logconf_{mode}.yml"
|
|
251
250
|
log_name = mode
|
|
252
251
|
if not log_conf_path.exists():
|
|
253
252
|
log_conf_path = Path(version.__file__).parent / f"logconf_{ver.__appid__}.yml"
|
|
254
|
-
log_name =
|
|
253
|
+
log_name = mode
|
|
255
254
|
with open(log_conf_path) as f:
|
|
256
255
|
log_config = yaml.safe_load(f)
|
|
257
256
|
std_key = None
|
|
@@ -268,8 +267,12 @@ def load_config(mode:str, debug:bool=False, data=HOME_DIR, webcall:bool=False, v
|
|
|
268
267
|
for k, l in log_config['loggers'].items():
|
|
269
268
|
if 'handlers' in l and std_key in l['handlers']:
|
|
270
269
|
l['handlers'].remove(std_key)
|
|
271
|
-
if 'loggers' not in log_config
|
|
272
|
-
raise BaseException(f"Loggers not found
|
|
270
|
+
if 'loggers' not in log_config:
|
|
271
|
+
raise BaseException(f"Loggers not found at log_conf_path={log_conf_path}")
|
|
272
|
+
if log_name not in log_config['loggers']:
|
|
273
|
+
if ver.__appid__ not in log_config['loggers']:
|
|
274
|
+
raise BaseException(f"Loggers not found.({ver.__appid__}) at log_conf_path={log_conf_path}")
|
|
275
|
+
log_name = ver.__appid__
|
|
273
276
|
log_config['disable_existing_loggers'] = False # これを入れないとdictConfigで既存のロガーが無効になる
|
|
274
277
|
logging.config.dictConfig(log_config)
|
|
275
278
|
logger = logging.getLogger(log_name)
|
cmdbox/app/commons/loghandler.py
CHANGED
|
@@ -4,6 +4,11 @@ from rich.theme import Theme
|
|
|
4
4
|
import re
|
|
5
5
|
import logging
|
|
6
6
|
import logging.handlers
|
|
7
|
+
import pickle
|
|
8
|
+
import socketserver
|
|
9
|
+
import struct
|
|
10
|
+
import socket
|
|
11
|
+
|
|
7
12
|
|
|
8
13
|
class Colors:
|
|
9
14
|
S = "\033["
|
|
@@ -103,6 +108,9 @@ theme=Theme({
|
|
|
103
108
|
"repr.log_success": "green",})
|
|
104
109
|
|
|
105
110
|
class LogLevelHighlighter(highlighter.ReprHighlighter):
|
|
111
|
+
"""
|
|
112
|
+
ログメッセージのログレベルをハイライトします。
|
|
113
|
+
"""
|
|
106
114
|
def __init__(self):
|
|
107
115
|
#self.highlights = []
|
|
108
116
|
self.highlights.append(r"(?P<log_debug>DEBUG|EXEC)")
|
|
@@ -112,29 +120,12 @@ class LogLevelHighlighter(highlighter.ReprHighlighter):
|
|
|
112
120
|
self.highlights.append(r"(?P<log_fatal>FATAL|CRITICAL)")
|
|
113
121
|
self.highlights.append(r"(?P<log_product>CMDBOX|IINFER|USOUND|GAIAN|GAIC|WITSHAPE)")
|
|
114
122
|
self.highlights.append(r"(?P<log_success>SUCCESS|OK|PASSED|DONE|COMPLETE|START|FINISH|OPEN|CONNECTED|ALLOW)")
|
|
115
|
-
"""
|
|
116
|
-
self.highlights.append(r"(?P<tag_start><)(?P<tag_name>[-\w.:|]*)(?P<tag_contents>[\w\W]*)(?P<tag_end>>)")
|
|
117
|
-
self.highlights.append(r'(?P<attrib_name>[\w_]{1,50})=(?P<attrib_value>"?[\w_]+"?)?')
|
|
118
|
-
self.highlights.append(r"(?P<brace>[][{}()])")
|
|
119
|
-
self.highlights.append(highlighter._combine_regex(
|
|
120
|
-
r"(?P<ipv4>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})",
|
|
121
|
-
r"(?P<ipv6>([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})",
|
|
122
|
-
r"(?P<eui64>(?:[0-9A-Fa-f]{1,2}-){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){3}[0-9A-Fa-f]{4})",
|
|
123
|
-
r"(?P<eui48>(?:[0-9A-Fa-f]{1,2}-){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){2}[0-9A-Fa-f]{4})",
|
|
124
|
-
r"(?P<uuid>[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})",
|
|
125
|
-
r"(?P<call>[\w.]*?)\(",
|
|
126
|
-
r"\b(?P<bool_true>True)\b|\b(?P<bool_false>False)\b|\b(?P<none>None)\b",
|
|
127
|
-
r"(?P<ellipsis>\.\.\.)",
|
|
128
|
-
r"(?P<number_complex>(?<!\w)(?:\-?[0-9]+\.?[0-9]*(?:e[-+]?\d+?)?)(?:[-+](?:[0-9]+\.?[0-9]*(?:e[-+]?\d+)?))?j)",
|
|
129
|
-
r"(?P<number>(?<!\w)\-?[0-9]+\.?[0-9]*(e[-+]?\d+?)?\b|0x[0-9a-fA-F]*)",
|
|
130
|
-
r"(?P<path>\B(/[-\w._+]+)*\/)(?P<filename>[-\w._+]*)?",
|
|
131
|
-
r"(?<![\\\w])(?P<str>b?'''.*?(?<!\\)'''|b?'.*?(?<!\\)'|b?\"\"\".*?(?<!\\)\"\"\"|b?\".*?(?<!\\)\")",
|
|
132
|
-
r"(?P<url>(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&=%#~@]*)",
|
|
133
|
-
))
|
|
134
|
-
"""
|
|
135
123
|
self.highlights = [re.compile(h, re.IGNORECASE) for h in self.highlights]
|
|
136
124
|
|
|
137
125
|
class ColorfulStreamHandler(logging.StreamHandler):
|
|
126
|
+
"""
|
|
127
|
+
コンソールにカラフルなログメッセージを出力します。
|
|
128
|
+
"""
|
|
138
129
|
console = Console(soft_wrap=True, height=True, highlighter=LogLevelHighlighter(), theme=theme)
|
|
139
130
|
|
|
140
131
|
def emit(self, record: logging.LogRecord) -> None:
|
|
@@ -154,3 +145,93 @@ class TimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
|
|
|
154
145
|
record.levelname = level_mapping_nc[record.levelno]
|
|
155
146
|
super().emit(record)
|
|
156
147
|
|
|
148
|
+
class SocketHandler(logging.handlers.SocketHandler):
|
|
149
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
150
|
+
record.levelname = level_mapping_nc[record.levelno]
|
|
151
|
+
super().emit(record)
|
|
152
|
+
|
|
153
|
+
class LogRecordRequestHandler(socketserver.StreamRequestHandler):
|
|
154
|
+
"""
|
|
155
|
+
ログリクエストを処理するためのハンドラ
|
|
156
|
+
"""
|
|
157
|
+
def setup(self):
|
|
158
|
+
super().setup()
|
|
159
|
+
from cmdbox.app import common
|
|
160
|
+
common.set_debug(self._getLogger(), LogRecordTCPServer.debug)
|
|
161
|
+
|
|
162
|
+
def _getLogger(self):
|
|
163
|
+
if self.server.logname is not None:
|
|
164
|
+
name = self.server.logname
|
|
165
|
+
else:
|
|
166
|
+
name = self.logname
|
|
167
|
+
return logging.getLogger(name)
|
|
168
|
+
|
|
169
|
+
def handle(self):
|
|
170
|
+
"""
|
|
171
|
+
ログリクエストを処理します。
|
|
172
|
+
"""
|
|
173
|
+
while True:
|
|
174
|
+
chunk = self.connection.recv(4)
|
|
175
|
+
if len(chunk) < 4:
|
|
176
|
+
break
|
|
177
|
+
slen = struct.unpack('>L', chunk)[0]
|
|
178
|
+
chunk = self.connection.recv(slen)
|
|
179
|
+
while len(chunk) < slen:
|
|
180
|
+
chunk = chunk + self.connection.recv(slen - len(chunk))
|
|
181
|
+
obj = self.unPickle(chunk)
|
|
182
|
+
record = logging.makeLogRecord(obj)
|
|
183
|
+
self.handleLogRecord(record)
|
|
184
|
+
|
|
185
|
+
def unPickle(self, data):
|
|
186
|
+
return pickle.loads(data)
|
|
187
|
+
|
|
188
|
+
def handleLogRecord(self, record):
|
|
189
|
+
logger = self._getLogger()
|
|
190
|
+
logger.handle(record)
|
|
191
|
+
|
|
192
|
+
class LogRecordTCPServer(socketserver.ThreadingTCPServer):
|
|
193
|
+
"""
|
|
194
|
+
ログレコードを受信するためのTCPサーバー。
|
|
195
|
+
"""
|
|
196
|
+
# 停止後すぐにサーバーを再起動できるようにする
|
|
197
|
+
allow_reuse_address = True
|
|
198
|
+
|
|
199
|
+
def __init__(self, logname, host='localhost', port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
|
|
200
|
+
handler=LogRecordRequestHandler, debug=False):
|
|
201
|
+
"""
|
|
202
|
+
コンストラクタ
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
logname (str): ログ名
|
|
206
|
+
host (str): ホスト名
|
|
207
|
+
port (int): ポート番号
|
|
208
|
+
handler (socketserver.RequestHandler): リクエストハンドラ
|
|
209
|
+
debug (bool): デバッグモード
|
|
210
|
+
"""
|
|
211
|
+
socketserver.ThreadingTCPServer.__init__(self, (host, port), handler, bind_and_activate=False)
|
|
212
|
+
self.allow_reuse_address = False
|
|
213
|
+
self.allow_reuse_port = False
|
|
214
|
+
self.request_queue_size = 15
|
|
215
|
+
self.abort = 0
|
|
216
|
+
self.timeout = 1
|
|
217
|
+
self.logname = logname
|
|
218
|
+
self.handler = handler
|
|
219
|
+
LogRecordTCPServer.debug = debug
|
|
220
|
+
|
|
221
|
+
def serve_until_stopped(self):
|
|
222
|
+
import select
|
|
223
|
+
abort = 0
|
|
224
|
+
try:
|
|
225
|
+
self.server_bind()
|
|
226
|
+
self.server_activate()
|
|
227
|
+
except:
|
|
228
|
+
# すでにlogsvが起動中の場合は待機しない
|
|
229
|
+
self.server_close()
|
|
230
|
+
abort = 1
|
|
231
|
+
while not abort:
|
|
232
|
+
rd, wr, ex = select.select([self.socket.fileno()],
|
|
233
|
+
[], [],
|
|
234
|
+
self.timeout)
|
|
235
|
+
if rd:
|
|
236
|
+
self.handle_request()
|
|
237
|
+
abort = self.abort
|
|
@@ -104,7 +104,7 @@ class RedisClient(object):
|
|
|
104
104
|
i = 0
|
|
105
105
|
return True
|
|
106
106
|
except redis.exceptions.ConnectionError as e:
|
|
107
|
-
self.logger.warning(f"fail to ping server. {e}")
|
|
107
|
+
self.logger.warning(f"fail to ping server. {self.host}:{self.port}, error={e}")
|
|
108
108
|
if i >= retry_count and retry_count > 0:
|
|
109
109
|
return False
|
|
110
110
|
time.sleep(retry_interval if retry_interval > 0 else 5)
|
cmdbox/app/edge.py
CHANGED
|
@@ -9,7 +9,6 @@ from uvicorn.config import Config
|
|
|
9
9
|
import argparse
|
|
10
10
|
import json
|
|
11
11
|
import logging
|
|
12
|
-
import locale
|
|
13
12
|
import queue
|
|
14
13
|
import requests
|
|
15
14
|
import time
|
|
@@ -54,7 +53,6 @@ class Edge(object):
|
|
|
54
53
|
|
|
55
54
|
import questionary
|
|
56
55
|
ref_opts = self.options.get_cmd_choices(edge_mode, edge_cmd)
|
|
57
|
-
language, _ = locale.getlocale()
|
|
58
56
|
edge_dir = Path(self.data) / '.edge'
|
|
59
57
|
common.mkdirs(edge_dir)
|
|
60
58
|
conf_file = edge_dir / 'edge.conf'
|
|
@@ -96,7 +94,7 @@ class Edge(object):
|
|
|
96
94
|
default = str(default) if isinstance(default, int) or isinstance(default, float) else default
|
|
97
95
|
description_ja = r['description_ja'] if 'description_ja' in r else None
|
|
98
96
|
description_en = r['description_en'] if 'description_en' in r else None
|
|
99
|
-
help = description_en if
|
|
97
|
+
help = description_en if not common.is_japan() else description_ja
|
|
100
98
|
choice = r['choice'] if 'choice' in r else None
|
|
101
99
|
choice = [str(c) for c in choice] if choice is not None else None
|
|
102
100
|
required = r['required'] if 'required' in r else False
|
|
@@ -676,7 +674,7 @@ class Edge(object):
|
|
|
676
674
|
if time.time() - tm > oauth2_timeout:
|
|
677
675
|
return 1, dict(warn="Signin Timeout.")
|
|
678
676
|
time.sleep(1)
|
|
679
|
-
return
|
|
677
|
+
return feature.Feature.RESP_SUCCESS, dict(success="Signin success.")
|
|
680
678
|
|
|
681
679
|
# saml認証を使用する場合
|
|
682
680
|
elif auth_type == "saml":
|
cmdbox/app/feature.py
CHANGED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from cmdbox.app import common, feature
|
|
2
|
+
from cmdbox.app.features.cli import cmdbox_web_start
|
|
3
|
+
from cmdbox.app.options import Options
|
|
4
|
+
from typing import Dict, Any, Tuple, List, Union
|
|
5
|
+
import argparse
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AgentMcpClient(feature.UnsupportEdgeFeature):
|
|
10
|
+
def get_mode(self) -> Union[str, List[str]]:
|
|
11
|
+
"""
|
|
12
|
+
この機能のモードを返します
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
Union[str, List[str]]: モード
|
|
16
|
+
"""
|
|
17
|
+
return 'agent'
|
|
18
|
+
|
|
19
|
+
def get_cmd(self) -> str:
|
|
20
|
+
"""
|
|
21
|
+
この機能のコマンドを返します
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
str: コマンド
|
|
25
|
+
"""
|
|
26
|
+
return 'mcp_client'
|
|
27
|
+
|
|
28
|
+
def get_option(self):
|
|
29
|
+
"""
|
|
30
|
+
この機能のオプションを返します
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Dict[str, Any]: オプション
|
|
34
|
+
"""
|
|
35
|
+
return dict(
|
|
36
|
+
# webからclientを実行するとmcp処理とデッドロックが発生するため、webmodeを無効にします。
|
|
37
|
+
use_redis=self.USE_REDIS_FALSE, nouse_webmode=True, use_agent=False,
|
|
38
|
+
description_ja="リモートMCPサーバーにリクエストを行うMCPクライアントを起動します。",
|
|
39
|
+
description_en="Starts an MCP client that makes requests to a remote MCP server.",
|
|
40
|
+
choice=[
|
|
41
|
+
dict(opt="mcpserver_name", type=Options.T_STR, default='mcpserver', required=True, multi=False, hide=False, choice=None,
|
|
42
|
+
description_ja="リモートMCPサーバーの名前を指定します。省略した場合は`mcpserver`となります。",
|
|
43
|
+
description_en="Specify the name of the MCP server. If omitted, it will be `mcpserver`.",),
|
|
44
|
+
dict(opt="mcpserver_url", type=Options.T_STR, default='http://localhost:8081/mcpsv/mcp', required=True, multi=False, hide=False, choice=None,
|
|
45
|
+
description_ja="リモートMCPサーバーのURLを指定します。省略した場合は`http://localhost:8081/mcpsv/mcp`となります。",
|
|
46
|
+
description_en="Specifies the URL of the remote MCP server. If omitted, it will be `http://localhost:8081/mcpsv/mcp`.",),
|
|
47
|
+
dict(opt="mcpserver_apikey", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
|
|
48
|
+
description_ja="リモートMCPサーバーのAPI Keyを指定します。",
|
|
49
|
+
description_en="Specify the API Key of the remote MCP server.",),
|
|
50
|
+
dict(opt="mcpserver_transport", type=Options.T_STR, default='streamable-http', required=True, multi=False, hide=False, choice=['', 'streamable-http', 'sse', 'http'],
|
|
51
|
+
description_ja="リモートMCPサーバーのトランスポートを指定します。省略した場合は`streamable-http`となります。",
|
|
52
|
+
description_en="Specifies the transport of the remote MCP server. If omitted, it is `streamable-http`.",),
|
|
53
|
+
dict(opt="operation", type=Options.T_STR, default='list_tools', required=True, multi=False, hide=False,
|
|
54
|
+
choice=['list_tools', 'call_tool', 'list_resources', 'read_resource', 'list_prompts', 'get_prompt'],
|
|
55
|
+
choice_show=dict(call_tool=['tool_name', 'tool_args', 'mcp_timeout',],
|
|
56
|
+
read_resource=['resource_url',],
|
|
57
|
+
get_prompt=['prompt_name', 'prompt_args']),
|
|
58
|
+
description_ja="リモートMCPサーバーに要求する操作を指定します。省略した場合は`list_tools`となります。",
|
|
59
|
+
description_en="Specifies the operations to request from the remote MCP server. If omitted, `list_tools` is used.",),
|
|
60
|
+
dict(opt="tool_name", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
|
|
61
|
+
description_ja="リモートMCPサーバーで実行するツールの名前を指定します。",
|
|
62
|
+
description_en="Specify the name of the tool to run on the remote MCP server."),
|
|
63
|
+
dict(opt="tool_args", type=Options.T_DICT, default=None, required=False, multi=True, hide=False, choice=None,
|
|
64
|
+
description_ja="リモートMCPサーバーで実行するツールの引数を指定します。",
|
|
65
|
+
description_en="Specify arguments for the tool to run on the remote MCP server."),
|
|
66
|
+
dict(opt="mcp_timeout", type=Options.T_INT, default="60", required=False, multi=False, hide=False, choice=None,
|
|
67
|
+
description_ja="リモートMCPサーバーの応答が返ってくるまでの最大待ち時間を指定します。",
|
|
68
|
+
description_en="Specifies the maximum time to wait for a response from the remote MCP server."),
|
|
69
|
+
dict(opt="resource_url", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
|
|
70
|
+
description_ja="リモートMCPサーバーから取得するリソースのURLを指定します。",
|
|
71
|
+
description_en="Specify the URL of the resource to retrieve from the remote MCP server."),
|
|
72
|
+
dict(opt="prompt_name", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
|
|
73
|
+
description_ja="リモートMCPサーバーから取得するプロンプトの名前を指定します。",
|
|
74
|
+
description_en="Specifies the name of the prompt to be retrieved from the remote MCP server."),
|
|
75
|
+
dict(opt="prompt_args", type=Options.T_DICT, default=None, required=False, multi=True, hide=False, choice=None,
|
|
76
|
+
description_ja="リモートMCPサーバーから取得するプロンプトの引数を指定します。",
|
|
77
|
+
description_en="Specifies prompt arguments to be retrieved from the remote MCP server."),
|
|
78
|
+
dict(opt="output_json", short="o", type=Options.T_FILE, default=None, required=False, multi=False, hide=True, choice=None, fileio="out",
|
|
79
|
+
description_ja="処理結果jsonの保存先ファイルを指定。",
|
|
80
|
+
description_en="Specify the destination file for saving the processing result json."),
|
|
81
|
+
dict(opt="output_json_append", short="a", type=Options.T_BOOL, default=False, required=False, multi=False, hide=True, choice=[True, False],
|
|
82
|
+
description_ja="処理結果jsonファイルを追記保存します。",
|
|
83
|
+
description_en="Save the processing result json file by appending."),
|
|
84
|
+
dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
|
|
85
|
+
description_ja="GUIモードでのみ使用可能です。コマンド実行時の標準出力をConsole logに出力します。",
|
|
86
|
+
description_en="Available only in GUI mode. Outputs standard output during command execution to Console log."),
|
|
87
|
+
dict(opt="capture_stdout", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
|
|
88
|
+
description_ja="GUIモードでのみ使用可能です。コマンド実行時の標準出力をキャプチャーし、実行結果画面に表示します。",
|
|
89
|
+
description_en="Available only in GUI mode. Captures standard output during command execution and displays it on the execution result screen."),
|
|
90
|
+
dict(opt="capture_maxsize", type=Options.T_INT, default=self.DEFAULT_CAPTURE_MAXSIZE, required=False, multi=False, hide=True, choice=None,
|
|
91
|
+
description_ja="GUIモードでのみ使用可能です。コマンド実行時の標準出力の最大キャプチャーサイズを指定します。",
|
|
92
|
+
description_en="Available only in GUI mode. Specifies the maximum capture size of standard output when executing commands."),
|
|
93
|
+
])
|
|
94
|
+
|
|
95
|
+
async def apprun(self, logger:logging.Logger, args:argparse.Namespace, tm:float, pf:List[Dict[str, float]]=[]) -> Tuple[int, Dict[str, Any], Any]:
|
|
96
|
+
"""
|
|
97
|
+
この機能の実行を行います
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
logger (logging.Logger): ロガー
|
|
101
|
+
args (argparse.Namespace): 引数
|
|
102
|
+
tm (float): 実行開始時間
|
|
103
|
+
pf (List[Dict[str, float]]): 呼出元のパフォーマンス情報
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Tuple[int, Dict[str, Any], Any]: 終了コード, 結果, オブジェクト
|
|
107
|
+
"""
|
|
108
|
+
logger.info("apprun function has started.")
|
|
109
|
+
if not hasattr(args, 'mcpserver_name'):
|
|
110
|
+
args.mcpserver_name = 'mcpserver'
|
|
111
|
+
if not hasattr(args, 'mcpserver_url'):
|
|
112
|
+
args.mcpserver_url = 'http://localhost:8081/mcpsv/mcp'
|
|
113
|
+
if not hasattr(args, 'mcpserver_transport'):
|
|
114
|
+
args.mcpserver_transport = 'streamable-http'
|
|
115
|
+
if not hasattr(args, 'mcpserver_apikey'):
|
|
116
|
+
args.mcpserver_apikey = None
|
|
117
|
+
|
|
118
|
+
from fastmcp import Client
|
|
119
|
+
config = dict(
|
|
120
|
+
mcpServers=dict(
|
|
121
|
+
default=dict(
|
|
122
|
+
url=args.mcpserver_url,
|
|
123
|
+
transport=args.mcpserver_transport,
|
|
124
|
+
auth=args.mcpserver_apikey
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
try:
|
|
129
|
+
common.reset_logger('FastMCP.fastmcp.server.server')
|
|
130
|
+
client = Client(config)
|
|
131
|
+
if logger.level == logging.DEBUG:
|
|
132
|
+
logger.debug(f"Starting MCP client: {config}")
|
|
133
|
+
async with client:
|
|
134
|
+
if args.operation == 'list_tools':
|
|
135
|
+
result = await client.list_tools()
|
|
136
|
+
ret = dict(success=[r.__dict__ for r in result])
|
|
137
|
+
elif args.operation == 'call_tool':
|
|
138
|
+
if not args.tool_name:
|
|
139
|
+
raise ValueError("Tool name must be specified for 'call_tool' operation.")
|
|
140
|
+
if not args.tool_args:
|
|
141
|
+
args.tool_args = dict()
|
|
142
|
+
if not hasattr(args, 'mcp_timeout'):
|
|
143
|
+
args.mcp_timeout = 60
|
|
144
|
+
result = await client.call_tool(args.tool_name, arguments=args.tool_args, timeout=args.mcp_timeout)
|
|
145
|
+
ret = dict(success=result.__dict__)
|
|
146
|
+
elif args.operation == 'list_resources':
|
|
147
|
+
result = await client.list_resources()
|
|
148
|
+
ret = dict(success=[r.__dict__ for r in result])
|
|
149
|
+
elif args.operation == 'read_resource':
|
|
150
|
+
if not args.resource_url:
|
|
151
|
+
raise ValueError("Resource URL must be specified for 'read_resource' operation.")
|
|
152
|
+
result = await client.read_resource(args.resource_url)
|
|
153
|
+
ret = dict(success=result.__dict__)
|
|
154
|
+
elif args.operation == 'list_prompts':
|
|
155
|
+
result = await client.list_prompts()
|
|
156
|
+
ret = dict(success=[r.__dict__ for r in result])
|
|
157
|
+
elif args.operation == 'get_prompt':
|
|
158
|
+
if not args.prompt_name:
|
|
159
|
+
raise ValueError("Prompt name must be specified for 'get_prompt' operation.")
|
|
160
|
+
if not args.prompt_args:
|
|
161
|
+
args.prompt_args = dict()
|
|
162
|
+
result = await client.get_prompt(args.prompt_name, arguments=args.prompt_args)
|
|
163
|
+
ret = dict(success=result.__dict__)
|
|
164
|
+
else:
|
|
165
|
+
raise ValueError(f"Unknown operation: {args.operation}")
|
|
166
|
+
common.print_format(ret, args.format, tm, args.output_json, args.output_json_append, pf=pf)
|
|
167
|
+
return self.RESP_SUCCESS, ret, None
|
|
168
|
+
except Exception as e:
|
|
169
|
+
logger.setLevel(logging.ERROR)
|
|
170
|
+
for h in logger.handlers:
|
|
171
|
+
h.setLevel(logging.ERROR)
|
|
172
|
+
logger.error(f"Failed to start MCP proxy: {e}", exc_info=True)
|
|
173
|
+
msg = dict(warn=f"Failed to start MCP proxy: {e}")
|
|
174
|
+
common.print_format(msg, args.format, tm, args.output_json, args.output_json_append, pf=pf)
|
|
175
|
+
return self.RESP_ERROR, msg, None
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from cmdbox.app import common, feature
|
|
2
|
+
from cmdbox.app.features.cli import cmdbox_web_start
|
|
3
|
+
from cmdbox.app.options import Options
|
|
4
|
+
from typing import Dict, Any, Tuple, List, Union
|
|
5
|
+
import argparse
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AgentMcpProxy(feature.UnsupportEdgeFeature):
|
|
10
|
+
def get_mode(self) -> Union[str, List[str]]:
|
|
11
|
+
"""
|
|
12
|
+
この機能のモードを返します
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
Union[str, List[str]]: モード
|
|
16
|
+
"""
|
|
17
|
+
return 'agent'
|
|
18
|
+
|
|
19
|
+
def get_cmd(self) -> str:
|
|
20
|
+
"""
|
|
21
|
+
この機能のコマンドを返します
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
str: コマンド
|
|
25
|
+
"""
|
|
26
|
+
return 'mcp_proxy'
|
|
27
|
+
|
|
28
|
+
def get_option(self):
|
|
29
|
+
"""
|
|
30
|
+
この機能のオプションを返します
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Dict[str, Any]: オプション
|
|
34
|
+
"""
|
|
35
|
+
return dict(
|
|
36
|
+
use_redis=self.USE_REDIS_FALSE, nouse_webmode=True, use_agent=False,
|
|
37
|
+
description_ja="標準入力を受け付け、リモートMCPサーバーにリクエストを行うProxyサーバーを起動します。",
|
|
38
|
+
description_en="Starts a Proxy server that accepts standard input and makes requests to a remote MCP server.",
|
|
39
|
+
choice=[
|
|
40
|
+
dict(opt="mcpserver_name", type=Options.T_STR, default='mcpserver', required=True, multi=False, hide=False, choice=None,
|
|
41
|
+
description_ja="リモートMCPサーバーの名前を指定します。省略した場合は`mcpserver`となります。",
|
|
42
|
+
description_en="Specify the name of the MCP server. If omitted, it will be `mcpserver`.",),
|
|
43
|
+
dict(opt="mcpserver_url", type=Options.T_STR, default='http://localhost:8081/mcpsv/mcp', required=True, multi=False, hide=False, choice=None,
|
|
44
|
+
description_ja="リモートMCPサーバーのURLを指定します。省略した場合は`http://localhost:8081/mcpsv/mcp`となります。",
|
|
45
|
+
description_en="Specifies the URL of the remote MCP server. If omitted, it will be `http://localhost:8081/mcpsv/mcp`.",),
|
|
46
|
+
dict(opt="mcpserver_apikey", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
|
|
47
|
+
description_ja="リモートMCPサーバーのAPI Keyを指定します。",
|
|
48
|
+
description_en="Specify the API Key of the remote MCP server.",),
|
|
49
|
+
dict(opt="mcpserver_transport", type=Options.T_STR, default='streamable-http', required=True, multi=False, hide=False, choice=['', 'streamable-http', 'sse', 'http'],
|
|
50
|
+
description_ja="リモートMCPサーバーのトランスポートを指定します。省略した場合は`streamable-http`となります。",
|
|
51
|
+
description_en="Specifies the transport of the remote MCP server. If omitted, it is `streamable-http`.",),
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
def apprun(self, logger:logging.Logger, args:argparse.Namespace, tm:float, pf:List[Dict[str, float]]=[]) -> Tuple[int, Dict[str, Any], Any]:
|
|
55
|
+
"""
|
|
56
|
+
この機能の実行を行います
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
logger (logging.Logger): ロガー
|
|
60
|
+
args (argparse.Namespace): 引数
|
|
61
|
+
tm (float): 実行開始時間
|
|
62
|
+
pf (List[Dict[str, float]]): 呼出元のパフォーマンス情報
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Tuple[int, Dict[str, Any], Any]: 終了コード, 結果, オブジェクト
|
|
66
|
+
"""
|
|
67
|
+
if not hasattr(args, 'mcpserver_name'):
|
|
68
|
+
args.mcpserver_name = 'mcpserver'
|
|
69
|
+
if not hasattr(args, 'mcpserver_url'):
|
|
70
|
+
args.mcpserver_url = 'http://localhost:8081/mcpsv/mcp'
|
|
71
|
+
if not hasattr(args, 'mcpserver_transport'):
|
|
72
|
+
args.mcpserver_transport = 'streamable-http'
|
|
73
|
+
if not hasattr(args, 'mcpserver_apikey'):
|
|
74
|
+
args.mcpserver_apikey = None
|
|
75
|
+
|
|
76
|
+
from fastmcp import FastMCP
|
|
77
|
+
config = dict(
|
|
78
|
+
mcpServers=dict(
|
|
79
|
+
default=dict(
|
|
80
|
+
url=args.mcpserver_url,
|
|
81
|
+
transport=args.mcpserver_transport,
|
|
82
|
+
auth=args.mcpserver_apikey
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
try:
|
|
87
|
+
common.reset_logger('FastMCP.fastmcp.server.server')
|
|
88
|
+
proxy = FastMCP.as_proxy(config, name="Config-Based Proxy")
|
|
89
|
+
proxy.run()
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logger.setLevel(logging.ERROR)
|
|
92
|
+
for h in logger.handlers:
|
|
93
|
+
h.setLevel(logging.ERROR)
|
|
94
|
+
logger.error(f"Failed to start MCP proxy: {e}", exc_info=True)
|
|
95
|
+
return self.RESP_ERROR, dict(warn=f"Failed to start MCP proxy: {e}"), None
|
|
96
|
+
return self.RESP_SUCCESS, dict(info="MCP proxy successfully."), None
|