cmdbox 0.6.4.1__py3-none-any.whl → 0.6.5__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.

Files changed (191) hide show
  1. cmdbox/app/app.py +7 -0
  2. cmdbox/app/client.py +4 -3
  3. cmdbox/app/common.py +85 -7
  4. cmdbox/app/commons/convert.py +3 -1
  5. cmdbox/app/edge.py +12 -12
  6. cmdbox/app/features/cli/_cmdbox_vision_install.py +214 -0
  7. cmdbox/app/features/cli/_cmdbox_vision_predict.py +496 -0
  8. cmdbox/app/features/cli/_cmdbox_vision_start.py +234 -0
  9. cmdbox/app/features/cli/cmdbox_cmd_list.py +3 -3
  10. cmdbox/app/features/cli/cmdbox_cmd_load.py +3 -3
  11. cmdbox/app/features/cli/cmdbox_tts_install.py +4 -2
  12. cmdbox/app/features/cli/cmdbox_tts_say.py +2 -1
  13. cmdbox/app/features/cli/cmdbox_web_apikey_add.py +3 -3
  14. cmdbox/app/features/cli/cmdbox_web_apikey_del.py +3 -3
  15. cmdbox/app/features/cli/cmdbox_web_group_add.py +3 -3
  16. cmdbox/app/features/cli/cmdbox_web_group_del.py +3 -3
  17. cmdbox/app/features/cli/cmdbox_web_group_edit.py +3 -3
  18. cmdbox/app/features/cli/cmdbox_web_group_list.py +3 -3
  19. cmdbox/app/features/cli/cmdbox_web_start.py +10 -10
  20. cmdbox/app/features/cli/cmdbox_web_user_add.py +3 -3
  21. cmdbox/app/features/cli/cmdbox_web_user_del.py +3 -3
  22. cmdbox/app/features/cli/cmdbox_web_user_edit.py +3 -3
  23. cmdbox/app/features/cli/cmdbox_web_user_list.py +3 -3
  24. cmdbox/app/features/web/cmdbox_web_exec_cmd.py +12 -14
  25. cmdbox/app/features/web/cmdbox_web_versions_used.py +1 -1
  26. cmdbox/app/filer.py +5 -2
  27. cmdbox/app/mcp.py +4 -3
  28. cmdbox/app/options.py +8 -0
  29. cmdbox/app/web.py +50 -36
  30. cmdbox/licenses/LICENSE_Hypercorn_0_17_3_MIT_License.txt +22 -0
  31. cmdbox/licenses/{LICENSE_cffi_1_17_1_MIT_License.txt → LICENSE_cffi_2_0_0_UNKNOWN.txt} +2 -5
  32. cmdbox/licenses/{LICENSE_jsonschema-specifications_2025_4_1_UNKNOWN.txt → LICENSE_h2_4_3_0_MIT_License.txt} +3 -1
  33. cmdbox/licenses/{LICENSE_backoff_2_2_1_MIT_License.txt → LICENSE_hpack_4_1_0_MIT_License.txt} +1 -1
  34. cmdbox/licenses/{LICENSE_graphviz_0_21_UNKNOWN.txt → LICENSE_hyperframe_6_1_0_MIT_License.txt} +1 -1
  35. cmdbox/licenses/{LICENSE_jsonschema_4_25_0_UNKNOWN.txt → LICENSE_priority_2_0_0_MIT_License.txt} +2 -2
  36. cmdbox/licenses/files.txt +26 -147
  37. cmdbox/version.py +2 -2
  38. cmdbox/web/assets/cmdbox/svgicon.js +9 -0
  39. cmdbox/web/voicevox_license_list.txt +41 -0
  40. {cmdbox-0.6.4.1.dist-info → cmdbox-0.6.5.dist-info}/METADATA +3 -1
  41. {cmdbox-0.6.4.1.dist-info → cmdbox-0.6.5.dist-info}/RECORD +65 -186
  42. cmdbox/app/features/cli/cmdbox_mcp_client.py +0 -174
  43. cmdbox/app/features/cli/cmdbox_mcp_proxy.py +0 -96
  44. cmdbox/licenses/LICENSE_APScheduler_3_11_0_MIT_License.txt +0 -19
  45. cmdbox/licenses/LICENSE_Authlib_1_6_1_BSD_License.txt +0 -29
  46. cmdbox/licenses/LICENSE_SQLAlchemy_2_0_42_MIT.txt +0 -19
  47. cmdbox/licenses/LICENSE_SQLAlchemy_2_0_43_MIT.txt +0 -19
  48. cmdbox/licenses/LICENSE_Werkzeug_3_1_1_BSD_License.txt +0 -28
  49. cmdbox/licenses/LICENSE_absolufy-imports_0_3_1_MIT_License.txt +0 -21
  50. cmdbox/licenses/LICENSE_aiohttp_3_12_15_Apache-2_0_AND_MIT.txt +0 -13
  51. cmdbox/licenses/LICENSE_aiosignal_1_4_0_Apache_Software_License.txt +0 -201
  52. cmdbox/licenses/LICENSE_anyio_4_9_0_MIT_License.txt +0 -20
  53. cmdbox/licenses/LICENSE_attrs_25_3_0_UNKNOWN.txt +0 -21
  54. cmdbox/licenses/LICENSE_cachetools_5_5_2_MIT_License.txt +0 -20
  55. cmdbox/licenses/LICENSE_certifi_2025_7_14_Mozilla_Public_License_2_0-MPL_2_0.txt +0 -20
  56. cmdbox/licenses/LICENSE_charset-normalizer_3_4_2_MIT_License.txt +0 -21
  57. cmdbox/licenses/LICENSE_cloudpickle_3_1_1_BSD_License.txt +0 -1
  58. cmdbox/licenses/LICENSE_cryptography_45_0_6_Apache-2_0_OR_BSD-3-Clause.txt +0 -3
  59. cmdbox/licenses/LICENSE_cyclopts_3_22_5_Apache_Software_License.txt +0 -201
  60. cmdbox/licenses/LICENSE_distro_1_9_0_Apache_Software_License.txt +0 -202
  61. cmdbox/licenses/LICENSE_dnspython_2_7_0_ISC_License-ISCL.txt +0 -35
  62. cmdbox/licenses/LICENSE_docstring_parser_0_17_0_MIT_License.txt +0 -21
  63. cmdbox/licenses/LICENSE_email_validator_2_2_0_The_Unlicense-Unlicense.txt +0 -27
  64. cmdbox/licenses/LICENSE_exceptiongroup_1_3_0_MIT_License.txt +0 -73
  65. cmdbox/licenses/LICENSE_fastapi-sso_0_18_0_MIT_License.txt +0 -21
  66. cmdbox/licenses/LICENSE_fastmcp_2_11_0_Apache_Software_License.txt +0 -201
  67. cmdbox/licenses/LICENSE_fastmcp_2_11_3_Apache_Software_License.txt +0 -201
  68. cmdbox/licenses/LICENSE_filelock_3_18_0_The_Unlicense-Unlicense.txt +0 -24
  69. cmdbox/licenses/LICENSE_frozenlist_1_7_0_Apache-2_0.txt +0 -201
  70. cmdbox/licenses/LICENSE_fsspec_2025_7_0_BSD_License.txt +0 -29
  71. cmdbox/licenses/LICENSE_google-adk_1_10_0_Apache_Software_License.txt +0 -202
  72. cmdbox/licenses/LICENSE_google-adk_1_9_0_Apache_Software_License.txt +0 -202
  73. cmdbox/licenses/LICENSE_google-api-core_2_25_1_Apache_Software_License.txt +0 -202
  74. cmdbox/licenses/LICENSE_google-api-python-client_2_177_0_Apache_Software_License.txt +0 -201
  75. cmdbox/licenses/LICENSE_google-api-python-client_2_178_0_Apache_Software_License.txt +0 -201
  76. cmdbox/licenses/LICENSE_google-auth-httplib2_0_2_0_Apache_Software_License.txt +0 -201
  77. cmdbox/licenses/LICENSE_google-auth_2_40_3_Apache_Software_License.txt +0 -201
  78. cmdbox/licenses/LICENSE_google-cloud-aiplatform_1_106_0_Apache_2_0.txt +0 -202
  79. cmdbox/licenses/LICENSE_google-cloud-aiplatform_1_108_0_Apache_2_0.txt +0 -202
  80. cmdbox/licenses/LICENSE_google-cloud-appengine-logging_1_6_2_Apache_Software_License.txt +0 -202
  81. cmdbox/licenses/LICENSE_google-cloud-audit-log_0_3_2_Apache_Software_License.txt +0 -202
  82. cmdbox/licenses/LICENSE_google-cloud-bigquery_3_35_1_Apache_Software_License.txt +0 -202
  83. cmdbox/licenses/LICENSE_google-cloud-core_2_4_3_Apache_Software_License.txt +0 -202
  84. cmdbox/licenses/LICENSE_google-cloud-logging_3_12_1_Apache_Software_License.txt +0 -202
  85. cmdbox/licenses/LICENSE_google-cloud-resource-manager_1_14_2_Apache_Software_License.txt +0 -202
  86. cmdbox/licenses/LICENSE_google-cloud-secret-manager_2_24_0_Apache_Software_License.txt +0 -202
  87. cmdbox/licenses/LICENSE_google-cloud-speech_2_33_0_Apache_Software_License.txt +0 -202
  88. cmdbox/licenses/LICENSE_google-cloud-storage_2_19_0_Apache_Software_License.txt +0 -202
  89. cmdbox/licenses/LICENSE_google-cloud-trace_1_16_2_Apache_Software_License.txt +0 -202
  90. cmdbox/licenses/LICENSE_google-crc32c_1_7_1_Apache_2_0.txt +0 -202
  91. cmdbox/licenses/LICENSE_google-genai_1_28_0_Apache_Software_License.txt +0 -202
  92. cmdbox/licenses/LICENSE_google-genai_1_29_0_Apache_Software_License.txt +0 -202
  93. cmdbox/licenses/LICENSE_google-resumable-media_2_7_2_Apache_Software_License.txt +0 -202
  94. cmdbox/licenses/LICENSE_googleapis-common-protos_1_70_0_Apache_Software_License.txt +0 -202
  95. cmdbox/licenses/LICENSE_greenlet_3_2_3_MIT_AND_Python-2_0.txt +0 -30
  96. cmdbox/licenses/LICENSE_grpc-google-iam-v1_0_14_2_Apache_Software_License.txt +0 -202
  97. cmdbox/licenses/LICENSE_grpcio-status_1_74_0_Apache_Software_License.txt +0 -610
  98. cmdbox/licenses/LICENSE_grpcio_1_74_0_Apache_Software_License.txt +0 -610
  99. cmdbox/licenses/LICENSE_httpcore_1_0_9_BSD_License.txt +0 -27
  100. cmdbox/licenses/LICENSE_httplib2_0_22_0_MIT_License.txt +0 -23
  101. cmdbox/licenses/LICENSE_httpx-sse_0_4_1_MIT.txt +0 -21
  102. cmdbox/licenses/LICENSE_httpx_0_28_1_BSD_License.txt +0 -12
  103. cmdbox/licenses/LICENSE_huggingface-hub_0_34_3_Apache_Software_License.txt +0 -201
  104. cmdbox/licenses/LICENSE_huggingface-hub_0_34_4_Apache_Software_License.txt +0 -201
  105. cmdbox/licenses/LICENSE_isodate_0_7_2_BSD_License.txt +0 -26
  106. cmdbox/licenses/LICENSE_jiter_0_10_0_MIT_License.txt +0 -1
  107. cmdbox/licenses/LICENSE_jsonschema-path_0_3_4_Apache_Software_License.txt +0 -201
  108. cmdbox/licenses/LICENSE_lazy-object-proxy_1_11_0_BSD_License.txt +0 -20
  109. cmdbox/licenses/LICENSE_litellm-enterprise_0_1_19_UNKNOWN.txt +0 -37
  110. cmdbox/licenses/LICENSE_litellm_1_74_12_MIT_License.txt +0 -26
  111. cmdbox/licenses/LICENSE_litellm_1_75_5_post1_MIT_License.txt +0 -26
  112. cmdbox/licenses/LICENSE_markdown-it-py_3_0_0_MIT_License.txt +0 -21
  113. cmdbox/licenses/LICENSE_mcp_1_12_3_MIT_License.txt +0 -21
  114. cmdbox/licenses/LICENSE_mcp_1_12_4_MIT_License.txt +0 -21
  115. cmdbox/licenses/LICENSE_multidict_6_6_3_Apache_License_2_0.txt +0 -13
  116. cmdbox/licenses/LICENSE_multidict_6_6_4_Apache_License_2_0.txt +0 -13
  117. cmdbox/licenses/LICENSE_oauthlib_3_3_1_BSD-3-Clause.txt +0 -27
  118. cmdbox/licenses/LICENSE_openai_1_98_0_Apache_Software_License.txt +0 -201
  119. cmdbox/licenses/LICENSE_openai_1_99_9_Apache_Software_License.txt +0 -201
  120. cmdbox/licenses/LICENSE_openapi-core_0_19_5_BSD_License.txt +0 -29
  121. cmdbox/licenses/LICENSE_openapi-pydantic_0_5_1_MIT_License.txt +0 -40
  122. cmdbox/licenses/LICENSE_openapi-schema-validator_0_6_3_BSD_License.txt +0 -29
  123. cmdbox/licenses/LICENSE_openapi-spec-validator_0_7_2_Apache_Software_License.txt +0 -201
  124. cmdbox/licenses/LICENSE_opentelemetry-api_1_36_0_UNKNOWN.txt +0 -201
  125. cmdbox/licenses/LICENSE_opentelemetry-exporter-gcp-trace_1_9_0_Apache_Software_License.txt +0 -201
  126. cmdbox/licenses/LICENSE_opentelemetry-resourcedetector-gcp_1_9_0a0_Apache_Software_License.txt +0 -201
  127. cmdbox/licenses/LICENSE_opentelemetry-sdk_1_36_0_UNKNOWN.txt +0 -201
  128. cmdbox/licenses/LICENSE_opentelemetry-semantic-conventions_0_57b0_UNKNOWN.txt +0 -201
  129. cmdbox/licenses/LICENSE_orjson_3_11_1_Apache_Software_License-MIT_License.txt +0 -201
  130. cmdbox/licenses/LICENSE_parse_1_20_2_MIT_License.txt +0 -19
  131. cmdbox/licenses/LICENSE_pathable_0_4_4_Apache_Software_License.txt +0 -201
  132. cmdbox/licenses/LICENSE_propcache_0_3_2_Apache_Software_License.txt +0 -202
  133. cmdbox/licenses/LICENSE_proto-plus_1_26_1_Apache_Software_License.txt +0 -202
  134. cmdbox/licenses/LICENSE_protobuf_6_31_1_3-Clause_BSD_License.txt +0 -32
  135. cmdbox/licenses/LICENSE_pyasn1_0_6_1_BSD_License.txt +0 -24
  136. cmdbox/licenses/LICENSE_pyasn1_modules_0_4_2_BSD_License.txt +0 -24
  137. cmdbox/licenses/LICENSE_pydantic-settings_2_10_1_MIT_License.txt +0 -21
  138. cmdbox/licenses/LICENSE_pyparsing_3_2_3_MIT_License.txt +0 -18
  139. cmdbox/licenses/LICENSE_pyperclip_1_9_0_BSD_License.txt +0 -27
  140. cmdbox/licenses/LICENSE_python-dateutil_2_9_0_post0_Apache_Software_License-BSD_License.txt +0 -54
  141. cmdbox/licenses/LICENSE_python-dotenv_1_1_1_BSD_License.txt +0 -27
  142. cmdbox/licenses/LICENSE_pywin32_311_Python_Software_Foundation_License.txt +0 -1
  143. cmdbox/licenses/LICENSE_redis_6_2_0_MIT_License.txt +0 -21
  144. cmdbox/licenses/LICENSE_referencing_0_36_2_UNKNOWN.txt +0 -19
  145. cmdbox/licenses/LICENSE_regex_2025_7_34_UNKNOWN.txt +0 -208
  146. cmdbox/licenses/LICENSE_rfc3339-validator_0_1_4_MIT_License.txt +0 -22
  147. cmdbox/licenses/LICENSE_rich-rst_1_3_1_MIT_License.txt +0 -7
  148. cmdbox/licenses/LICENSE_rpds-py_0_26_0_MIT.txt +0 -19
  149. cmdbox/licenses/LICENSE_rpds-py_0_27_0_UNKNOWN.txt +0 -19
  150. cmdbox/licenses/LICENSE_rsa_4_9_1_Apache_Software_License.txt +0 -13
  151. cmdbox/licenses/LICENSE_shapely_2_1_1_BSD_License.txt +0 -29
  152. cmdbox/licenses/LICENSE_sphinx-intl_2_3_1_BSD_License.txt +0 -25
  153. cmdbox/licenses/LICENSE_sse-starlette_3_0_2_UNKNOWN.txt +0 -27
  154. cmdbox/licenses/LICENSE_tenacity_8_5_0_Apache_Software_License.txt +0 -202
  155. cmdbox/licenses/LICENSE_tenacity_9_1_2_Apache_Software_License.txt +0 -202
  156. 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 +0 -21
  157. cmdbox/licenses/LICENSE_tiktoken_0_9_0_MIT_License-Copyright-c-2022_OpenAI-Shantanu_Jain-Permission_is_hereby_granted-free_of_charge-to_any_pers.txt +0 -21
  158. cmdbox/licenses/LICENSE_tokenizers_0_21_4_Apache_Software_License.txt +0 -1
  159. cmdbox/licenses/LICENSE_tqdm_4_67_1_MIT_License-Mozilla_Public_License_2_0-MPL_2_0.txt +0 -49
  160. cmdbox/licenses/LICENSE_typing_extensions_4_14_1_UNKNOWN.txt +0 -279
  161. cmdbox/licenses/LICENSE_tzlocal_5_3_1_MIT_License.txt +0 -19
  162. cmdbox/licenses/LICENSE_uritemplate_4_2_0_BSD_3-Clause_OR_Apache-2_0.txt +0 -3
  163. cmdbox/licenses/LICENSE_uvicorn_0_35_0_BSD_License.txt +0 -27
  164. cmdbox/licenses/LICENSE_voicevox_core_0_16_0_MIT.txt +0 -20
  165. cmdbox/licenses/LICENSE_watchdog_6_0_0_Apache_Software_License.txt +0 -16
  166. cmdbox/licenses/LICENSE_websockets_15_0_1_BSD_License.txt +0 -24
  167. cmdbox/licenses/LICENSE_yarl_1_20_1_Apache_Software_License.txt +0 -202
  168. /cmdbox/licenses/{LICENSE_click_8_2_1_UNKNOWN.txt → LICENSE_click_8_3_0_UNKNOWN.txt} +0 -0
  169. /cmdbox/licenses/{LICENSE_cryptography_45_0_5_Apache-2_0_OR_BSD-3-Clause.txt → LICENSE_cryptography_46_0_1_UNKNOWN.txt} +0 -0
  170. /cmdbox/licenses/{LICENSE_fastapi_0_116_1_MIT_License.txt → LICENSE_fastapi_0_116_2_MIT_License.txt} +0 -0
  171. /cmdbox/licenses/{LICENSE_gevent_25_5_1_MIT.txt → LICENSE_gevent_25_9_1_MIT.txt} +0 -0
  172. /cmdbox/licenses/{LICENSE_jaraco_functools_4_2_1_UNKNOWN.txt → LICENSE_jaraco_functools_4_3_0_UNKNOWN.txt} +0 -0
  173. /cmdbox/licenses/{LICENSE_more-itertools_10_7_0_MIT_License.txt → LICENSE_more-itertools_10_8_0_UNKNOWN.txt} +0 -0
  174. /cmdbox/licenses/{LICENSE_numpy_2_3_2_BSD_License.txt → LICENSE_numpy_2_3_3_BSD_License.txt} +0 -0
  175. /cmdbox/licenses/{LICENSE_prompt_toolkit_3_0_51_BSD_License.txt → LICENSE_prompt_toolkit_3_0_52_BSD_License.txt} +0 -0
  176. /cmdbox/licenses/{LICENSE_psycopg-binary_3_2_9_GNU_Lesser_General_Public_License_v3-LGPLv3.txt → LICENSE_psycopg-binary_3_2_10_GNU_Lesser_General_Public_License_v3-LGPLv3.txt} +0 -0
  177. /cmdbox/licenses/{LICENSE_psycopg_3_2_9_GNU_Lesser_General_Public_License_v3-LGPLv3.txt → LICENSE_psycopg_3_2_10_GNU_Lesser_General_Public_License_v3-LGPLv3.txt} +0 -0
  178. /cmdbox/licenses/{LICENSE_pycparser_2_22_BSD_License.txt → LICENSE_pycparser_2_23_BSD_License.txt} +0 -0
  179. /cmdbox/licenses/{LICENSE_pydantic_2_11_7_MIT_License.txt → LICENSE_pydantic_2_11_9_MIT_License.txt} +0 -0
  180. /cmdbox/licenses/{LICENSE_questionary_2_1_0_MIT_License.txt → LICENSE_questionary_2_1_1_MIT_License.txt} +0 -0
  181. /cmdbox/licenses/{LICENSE_requests_2_32_4_Apache_Software_License.txt → LICENSE_requests_2_32_5_Apache_Software_License.txt} +0 -0
  182. /cmdbox/licenses/{LICENSE_sphinx-sitemap_2_7_2_UNKNOWN.txt → LICENSE_sphinx-sitemap_2_8_0_UNKNOWN.txt} +0 -0
  183. /cmdbox/licenses/{LICENSE_starlette_0_47_2_BSD_License.txt → LICENSE_starlette_0_48_0_BSD_License.txt} +0 -0
  184. /cmdbox/licenses/{LICENSE_twine_6_1_0_Apache_Software_License.txt → LICENSE_twine_6_2_0_UNKNOWN.txt} +0 -0
  185. /cmdbox/licenses/{LICENSE_aiohappyeyeballs_2_6_1_Python_Software_Foundation_License.txt → LICENSE_typing_extensions_4_15_0_UNKNOWN.txt} +0 -0
  186. /cmdbox/licenses/{LICENSE_zope_event_5_1_1_Zope_Public_License.txt → LICENSE_zope_event_6_0_Zope_Public_License.txt} +0 -0
  187. /cmdbox/licenses/{LICENSE_zope_interface_7_2_Zope_Public_License.txt → LICENSE_zope_interface_8_0_Zope_Public_License.txt} +0 -0
  188. {cmdbox-0.6.4.1.dist-info → cmdbox-0.6.5.dist-info}/WHEEL +0 -0
  189. {cmdbox-0.6.4.1.dist-info → cmdbox-0.6.5.dist-info}/entry_points.txt +0 -0
  190. {cmdbox-0.6.4.1.dist-info → cmdbox-0.6.5.dist-info}/licenses/LICENSE +0 -0
  191. {cmdbox-0.6.4.1.dist-info → cmdbox-0.6.5.dist-info}/top_level.txt +0 -0
cmdbox/app/app.py CHANGED
@@ -118,6 +118,13 @@ class CmdBoxApp:
118
118
  args = argparse.Namespace(**{k:common.chopdq(v) for k,v in args_dict.items()})
119
119
  eargsparsetime = time.perf_counter()
120
120
 
121
+ if args.debug_attach:
122
+ import debugpy
123
+ debugpy.listen(("", args.debug_attach_port))
124
+ self.default_logger.info(f"Waiting for debugger attach on port {args.debug_attach_port}...")
125
+ debugpy.wait_for_client()
126
+ self.default_logger.info("Debugger attached.")
127
+
121
128
  ret = dict(success=f"Start command. {args}")
122
129
  if args.saveopt:
123
130
  if args.useopt is None:
cmdbox/app/client.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from cmdbox.app import filer
2
+ from cmdbox.app import common, filer
3
3
  from cmdbox.app.commons import convert, redis_client
4
4
  import base64
5
5
  import logging
@@ -196,12 +196,13 @@ class Client(object):
196
196
  if download_file.exists():
197
197
  self.logger.warning(f"download_file {download_file} already exists.")
198
198
  return dict(warn=f"download_file {download_file} already exists.")
199
- with open(download_file, "wb") as f:
199
+ def _wd(f):
200
200
  f.write(base64.b64decode(res_json["success"]["data"]))
201
201
  del res_json["success"]["data"]
202
202
  res_json["success"]["download_file"] = str(download_file.absolute())
203
+ common.save_file(download_file, _wd, mode='wb')
203
204
  return res_json
204
-
205
+
205
206
  def file_upload(self, svpath:str, upload_file:Path, scope:str="client", client_data:Path=None, mkdir:bool=False, orverwrite:bool=False,
206
207
  retry_count:int=3, retry_interval:int=5, timeout:int=60):
207
208
  """
cmdbox/app/common.py CHANGED
@@ -80,8 +80,9 @@ def load_yml(yml_path:Path) -> dict:
80
80
  Returns:
81
81
  dict: 読み込んだYAMLファイルの内容
82
82
  """
83
- with open(yml_path, encoding="utf-8") as f:
83
+ def _r(f):
84
84
  return yaml.safe_load(f)
85
+ return load_file(yml_path, _r)
85
86
 
86
87
  def save_yml(yml_path:Path, data:dict) -> None:
87
88
  """
@@ -91,8 +92,83 @@ def save_yml(yml_path:Path, data:dict) -> None:
91
92
  yml_path (Path): YAMLファイルのパス
92
93
  data (dict): 書き込むデータ
93
94
  """
94
- with open(yml_path, 'w') as f:
95
+ def _w(f):
95
96
  yaml.dump(data, f, default_flow_style=False, sort_keys=False)
97
+ save_file(yml_path, _w)
98
+
99
+ def load_file(file_path:Path, func, mode='r', encoding='utf-8') -> None:
100
+ """
101
+ ファイルを読み込みます。読み込み時に排他ロックします。
102
+
103
+ Args:
104
+ file_path (Path): ファイルのパス
105
+ func: 読み込んだデータを処理する関数
106
+ mode (str, optional): ファイルモード. Defaults to 'r'.
107
+ encoding (str, optional): エンコーディング. Defaults to 'utf-8'.
108
+ """
109
+ lock_file = f'{file_path}.lock'
110
+ try:
111
+ with open(lock_file, 'w') as fd:
112
+ try:
113
+ if sys.platform == 'win32':
114
+ import msvcrt
115
+ msvcrt.locking(fd.fileno(), msvcrt.LK_LOCK, 0)
116
+ else:
117
+ import fcntl
118
+ fcntl.lockf(fd, fcntl.LOCK_EX)
119
+ if 'b' in mode:
120
+ with open(file_path, mode) as f:
121
+ return func(f)
122
+ else:
123
+ with open(file_path, mode, encoding=encoding) as f:
124
+ return func(f)
125
+ finally:
126
+ if sys.platform == 'win32':
127
+ msvcrt.locking(fd.fileno(), msvcrt.LK_UNLCK, 0)
128
+ else:
129
+ fcntl.lockf(fd, fcntl.LOCK_UN)
130
+ finally:
131
+ try:
132
+ os.remove(lock_file)
133
+ except:
134
+ pass
135
+
136
+ def save_file(file_path:Path, func, mode='w', encoding='utf-8') -> None:
137
+ """
138
+ ファイルに書き込みます。書き込み時に排他ロックします。
139
+
140
+ Args:
141
+ file_path (Path): ファイルのパス
142
+ func: 書き込む関数
143
+ mode (str, optional): ファイルモード. Defaults to 'w'.
144
+ encoding (str, optional): エンコーディング. Defaults to 'utf-8'.
145
+ """
146
+ lock_file = f'{file_path}.lock'
147
+ try:
148
+ with open(lock_file, 'w') as fd:
149
+ try:
150
+ if sys.platform == 'win32':
151
+ import msvcrt
152
+ msvcrt.locking(fd.fileno(), msvcrt.LK_LOCK, 0)
153
+ else:
154
+ import fcntl
155
+ fcntl.lockf(fd, fcntl.LOCK_EX)
156
+ if 'b' in mode:
157
+ with open(file_path, mode) as f:
158
+ func(f)
159
+ else:
160
+ with open(file_path, mode, encoding=encoding) as f:
161
+ func(f)
162
+ finally:
163
+ if sys.platform == 'win32':
164
+ msvcrt.locking(fd.fileno(), msvcrt.LK_UNLCK, 0)
165
+ else:
166
+ fcntl.lockf(fd, fcntl.LOCK_UN)
167
+ finally:
168
+ try:
169
+ os.remove(lock_file)
170
+ except:
171
+ pass
96
172
 
97
173
  class CommonValue:
98
174
  _map = dict()
@@ -251,8 +327,7 @@ def load_config(mode:str, debug:bool=False, data=HOME_DIR, webcall:bool=False, v
251
327
  if not log_conf_path.exists():
252
328
  log_conf_path = Path(version.__file__).parent / f"logconf_{ver.__appid__}.yml"
253
329
  log_name = mode
254
- with open(log_conf_path) as f:
255
- log_config = yaml.safe_load(f)
330
+ log_config = load_yml(log_conf_path)
256
331
  std_key = None
257
332
  set_common_value('webcall', webcall)
258
333
  for k, h in log_config['handlers'].items():
@@ -343,8 +418,9 @@ def saveopt(opt:dict, opt_path:Path, webmode:bool=False) -> None:
343
418
  k = r['opt']
344
419
  d = r['default'] if 'default' in r else None
345
420
  opt[k] = d if k not in lopts else lopts.get(k, d)
346
- with open(opt_path, 'w') as f:
421
+ def _w(f):
347
422
  json.dump(opt, f, indent=4, default=default_json_enc)
423
+ save_file(opt_path, _w)
348
424
 
349
425
  def saveuser(user_data:dict, user_path:Path) -> None:
350
426
  """
@@ -552,9 +628,10 @@ def print_format(data:dict, format:bool, tm:float, output_json:str=None, output_
552
628
  pass
553
629
  if output_json is not None:
554
630
  try:
555
- with open(output_json, 'a' if output_json_append else 'w', encoding='utf-8') as f:
631
+ def _w(f):
556
632
  json.dump(data, f, default=default_json_enc, ensure_ascii=False)
557
633
  print('', file=f)
634
+ save_file(Path(output_json), _w, mode='a' if output_json_append else 'w')
558
635
  except Exception as e:
559
636
  pass
560
637
  return txt
@@ -584,8 +661,9 @@ def download_file(url:str, save_path:Path) -> Path:
584
661
  Path: 保存したファイルのパス
585
662
  """
586
663
  r = requests.get(url, stream=True)
587
- with open(save_path, 'wb') as f:
664
+ def _w(f):
588
665
  f.write(r.content)
666
+ save_file(Path(save_path), _w)
589
667
  return save_path
590
668
 
591
669
  def cmd(cmd:str, logger:logging.Logger, slise:int=100, newenv:Dict=None) -> Tuple[int, str, str]:
@@ -99,8 +99,10 @@ def npy2imgfile(npy, output_image_file:Path=None, image_type:str='jpeg') -> byte
99
99
  image_type = 'jpeg' if image_type == 'jpg' else image_type
100
100
  img_byte = img2byte(image, format=image_type)
101
101
  if output_image_file is not None:
102
- with open(output_image_file, 'wb') as f:
102
+ from cmdbox.app import common
103
+ def _w(f):
103
104
  f.write(img_byte)
105
+ common.save_file(output_image_file, _w, mode='wb')
104
106
  return img_byte
105
107
 
106
108
  def bytes2b64str(img:bytes) -> str:
cmdbox/app/edge.py CHANGED
@@ -515,9 +515,9 @@ class Edge(object):
515
515
  except Exception as e:
516
516
  raise HTTPException(status_code=500, detail=f'Failed to get token. {e}')
517
517
 
518
- if not hasattr(self, 'thUvicorn') or not self.thUvicorn.is_alive():
519
- self.thUvicorn = web.ThreadedUvicorn(config=Config(app=fastapi, host='localhost', port=oauth2_port))
520
- self.thUvicorn.start()
518
+ if not hasattr(self, 'thHttp') or not self.thHttp.is_alive():
519
+ self.thHttp = web.ThreadedASGI(config=Config(app=fastapi, host='localhost', port=oauth2_port))
520
+ self.thHttp.start()
521
521
  time.sleep(1)
522
522
 
523
523
  # OAuth2認証のリクエストを送信
@@ -581,9 +581,9 @@ class Edge(object):
581
581
  except Exception as e:
582
582
  raise HTTPException(status_code=500, detail=f'Failed to get token. {e}')
583
583
 
584
- if not hasattr(self, 'thUvicorn') or not self.thUvicorn.is_alive():
585
- self.thUvicorn = web.ThreadedUvicorn(config=Config(app=fastapi, host='localhost', port=oauth2_port))
586
- self.thUvicorn.start()
584
+ if not hasattr(self, 'thHttp') or not self.thHttp.is_alive():
585
+ self.thHttp = web.ThreadedASGI(config=Config(app=fastapi, host='localhost', port=oauth2_port))
586
+ self.thHttp.start()
587
587
  time.sleep(1)
588
588
 
589
589
  # OAuth2認証のリクエストを送信
@@ -652,9 +652,9 @@ class Edge(object):
652
652
  except Exception as e:
653
653
  raise HTTPException(status_code=500, detail=f'Failed to get token. {e}')
654
654
 
655
- if not hasattr(self, 'thUvicorn') or not self.thUvicorn.is_alive():
656
- self.thUvicorn = web.ThreadedUvicorn(self.logger, Config(app=fastapi, host='localhost', port=oauth2_port), {})
657
- self.thUvicorn.start()
655
+ if not hasattr(self, 'thHttp') or not self.thHttp.is_alive():
656
+ self.thHttp = web.ThreadedASGI(config=Config(app=fastapi, host='localhost', port=oauth2_port))
657
+ self.thHttp.start()
658
658
  time.sleep(1)
659
659
 
660
660
  # OAuth2認証のリクエストを送信
@@ -734,9 +734,9 @@ class Edge(object):
734
734
  except Exception as e:
735
735
  raise HTTPException(status_code=500, detail=f'Failed to get token. {e}')
736
736
 
737
- if not hasattr(self, 'thUvicorn') or not self.thUvicorn.is_alive():
738
- self.thUvicorn = web.ThreadedUvicorn(self.logger, Config(app=fastapi, host='localhost', port=saml_port), {})
739
- self.thUvicorn.start()
737
+ if not hasattr(self, 'thHttp') or not self.thHttp.is_alive():
738
+ self.thHttp = web.ThreadedASGI(config=Config(app=fastapi, host='localhost', port=saml_port))
739
+ self.thHttp.start()
740
740
  time.sleep(1)
741
741
 
742
742
  # SAML認証のリクエストを送信
@@ -0,0 +1,214 @@
1
+ from cmdbox import version
2
+ from cmdbox.app import common, client, feature
3
+ from cmdbox.app.commons import convert, redis_client
4
+ from cmdbox.app.options import Options
5
+ from cmdbox.app.features.cli import cmdbox_vision_start
6
+ from pathlib import Path
7
+ from typing import Dict, Any, Tuple, List, Union
8
+ import argparse
9
+ import glob
10
+ import logging
11
+ import pip
12
+ import requests
13
+ import shutil
14
+ import subprocess
15
+ import sys
16
+
17
+
18
+ class VisionInstall(cmdbox_vision_start.VisionStart):
19
+ def get_mode(self) -> Union[str, List[str]]:
20
+ """
21
+ この機能のモードを返します
22
+
23
+ Returns:
24
+ Union[str, List[str]]: モード
25
+ """
26
+ return 'vision'
27
+
28
+ def get_cmd(self):
29
+ """
30
+ この機能のコマンドを返します
31
+
32
+ Returns:
33
+ str: コマンド
34
+ """
35
+ return 'install'
36
+
37
+ def get_option(self):
38
+ """
39
+ この機能のオプションを返します
40
+
41
+ Returns:
42
+ Dict[str, Any]: オプション
43
+ """
44
+ return dict(
45
+ use_redis=self.USE_REDIS_MEIGHT, nouse_webmode=True, use_agent=False,
46
+ description_ja="画像/動画の推論エンジンをインストールします。",
47
+ description_en="Installs the image/video inference engine.",
48
+ choice=[
49
+ dict(opt="host", type=Options.T_STR, default=self.default_host, required=True, multi=False, hide=True, choice=None, web="mask",
50
+ description_ja="Redisサーバーのサービスホストを指定します。",
51
+ description_en="Specify the service host of the Redis server."),
52
+ dict(opt="port", type=Options.T_INT, default=self.default_port, required=True, multi=False, hide=True, choice=None, web="mask",
53
+ description_ja="Redisサーバーのサービスポートを指定します。",
54
+ description_en="Specify the service port of the Redis server."),
55
+ dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
56
+ description_ja=f"Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `{self.default_pass}` を使用します。",
57
+ description_en=f"Specify the access password of the Redis server (optional). If omitted, `{self.default_pass}` is used."),
58
+ dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
59
+ description_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
60
+ description_en="Specify the service name of the inference server. If omitted, `server` is used."),
61
+ dict(opt="retry_count", type=Options.T_INT, default=3, required=False, multi=False, hide=True, choice=None,
62
+ description_ja="Redisサーバーへの再接続回数を指定します。0以下を指定すると永遠に再接続を行います。",
63
+ description_en="Specifies the number of reconnections to the Redis server.If less than 0 is specified, reconnection is forever."),
64
+ dict(opt="retry_interval", type=Options.T_INT, default=5, required=False, multi=False, hide=True, choice=None,
65
+ description_ja="Redisサーバーに再接続までの秒数を指定します。",
66
+ description_en="Specifies the number of seconds before reconnecting to the Redis server."),
67
+ dict(opt="timeout", type=Options.T_INT, default="300", required=False, multi=False, hide=True, choice=None,
68
+ description_ja="サーバーの応答が返ってくるまでの最大待ち時間を指定。",
69
+ description_en="Specify the maximum waiting time until the server responds."),
70
+ dict(opt="client_only", type=Options.T_BOOL, default=False, required=False, multi=False, hide=True, choice=[True, False],
71
+ description_ja="サーバーへの接続を行わないようにします。",
72
+ description_en="Do not make connections to the server."),
73
+ dict(opt="vision_engine", type=Options.T_STR, default="sam2", required=True, multi=False, hide=False,
74
+ choice=["sam2"],
75
+ description_ja="使用するVisionエンジンを指定します。",
76
+ description_en="Specify the Vision engine to use."),
77
+ ]
78
+ )
79
+
80
+ def get_svcmd(self):
81
+ """
82
+ この機能のサーバー側のコマンドを返します
83
+
84
+ Returns:
85
+ str: サーバー側のコマンド
86
+ """
87
+ return 'vision_install'
88
+
89
+ def apprun(self, logger:logging.Logger, args:argparse.Namespace, tm:float, pf:List[Dict[str, float]]=[]) -> Tuple[int, Dict[str, Any], Any]:
90
+ """
91
+ この機能の実行を行います
92
+
93
+ Args:
94
+ logger (logging.Logger): ロガー
95
+ args (argparse.Namespace): 引数
96
+ tm (float): 実行開始時間
97
+ pf (List[Dict[str, float]]): 呼出元のパフォーマンス情報
98
+
99
+ Returns:
100
+ Tuple[int, Dict[str, Any], Any]: 終了コード, 結果, オブジェクト
101
+ """
102
+ if args.vision_engine is None:
103
+ msg = dict(warn=f"Please specify the --vision_engine option.")
104
+ common.print_format(msg, False, tm, args.output_json, args.output_json_append, pf=pf)
105
+ return self.RESP_WARN, msg, None
106
+ if args.vision_engine == 'sam2':
107
+ vision_engine_b64 = convert.str2b64str(args.vision_engine)
108
+ if args.client_only:
109
+ # クライアントのみの場合は、サーバーに接続せずに実行
110
+ ret = self.install(common.random_string(), args.vision_engine, logger)
111
+ else:
112
+ cl = client.Client(logger, redis_host=args.host, redis_port=args.port, redis_password=args.password, svname=args.svname)
113
+ ret = cl.redis_cli.send_cmd(self.get_svcmd(), [vision_engine_b64],
114
+ retry_count=args.retry_count, retry_interval=args.retry_interval, timeout=args.timeout, nowait=False)
115
+ common.print_format(ret, False, tm, None, False, pf=pf)
116
+ if 'success' not in ret:
117
+ return self.RESP_WARN, ret, None
118
+ return self.RESP_SUCCESS, ret, None
119
+
120
+ msg = dict(warn=f"Unsupported vision engine: {args.vision_engine}")
121
+ common.print_format(msg, False, tm, args.output_json, args.output_json_append, pf=pf)
122
+ return self.RESP_WARN, msg, None
123
+
124
+ def is_cluster_redirect(self):
125
+ """
126
+ クラスター宛のメッセージの場合、メッセージを転送するかどうかを返します
127
+
128
+ Returns:
129
+ bool: メッセージを転送する場合はTrue
130
+ """
131
+ return False
132
+
133
+ def svrun(self, data_dir:Path, logger:logging.Logger, redis_cli:redis_client.RedisClient, msg:List[str],
134
+ sessions:Dict[str, Dict[str, Any]]) -> int:
135
+ """
136
+ この機能のサーバー側の実行を行います
137
+
138
+ Args:
139
+ data_dir (Path): データディレクトリ
140
+ logger (logging.Logger): ロガー
141
+ redis_cli (redis_client.RedisClient): Redisクライアント
142
+ msg (List[str]): 受信メッセージ
143
+ sessions (Dict[str, Dict[str, Any]]): セッション情報
144
+
145
+ Returns:
146
+ int: 終了コード
147
+ """
148
+ if logger.level == logging.DEBUG:
149
+ logger.debug(f"audit write svrun msg: {msg}")
150
+ vision_engine = convert.b64str2str(msg[2])
151
+ ret = self.install(msg[1], vision_engine, logger)
152
+
153
+ if 'success' not in ret:
154
+ redis_cli.rpush(msg[1], ret)
155
+ return self.RESP_WARN
156
+ return self.RESP_SUCCESS
157
+
158
+ def install(self, reskey:str, vision_engine:str, logger:logging.Logger) -> Dict[str, Any]:
159
+ """
160
+ SAML2をインストールします
161
+
162
+ Args:
163
+ reskey (str): レスポンスキー
164
+ vision_engine (str): Visionエンジン
165
+ logger (logging.Logger): ロガー
166
+
167
+ Returns:
168
+ Dict[str, Any]: 結果
169
+ """
170
+ try:
171
+ if vision_engine == 'sam2':
172
+ #===============================================================
173
+ # SAM2のモデルファイルのダウンロード
174
+ sam2_dir = Path(version.__file__).parent / '.sam2' / 'model'
175
+ # すでにダウンローダーが存在する場合は削除
176
+ if sam2_dir.exists():
177
+ shutil.rmtree(sam2_dir)
178
+ for mk, mv in self.VISION_MODEL.items():
179
+ model_file = sam2_dir / mv['path']
180
+ model_file.parent.mkdir(parents=True, exist_ok=True)
181
+ # モデルファイルを保存
182
+ logger.info(f"Downloading.. : {mv['url']}")
183
+ responce = requests.get(f"{mv['url']}", allow_redirects=True)
184
+ if responce.status_code != 200:
185
+ _msg = f"Failed to download SAM2 model: {responce.status_code} {responce.reason}. {mv['url']}"
186
+ logger.error(_msg, exc_info=True)
187
+ return dict(warn=_msg)
188
+ def _wm(f):
189
+ f.write(responce.content)
190
+ common.save_file(model_file, _wm, mode='wb')
191
+ #===============================================================
192
+ # SAM2のpythonライブラリのインストール
193
+ whl_url = f'git+https://github.com/facebookresearch/sam2.git'
194
+ # whlファイルをpipでインストール
195
+ if logger.level == logging.DEBUG:
196
+ logger.debug(f"pip install {whl_url}")
197
+ rescode = pip.main(['install', str(whl_url)]) # pipのインストール
198
+ logger.info(f"Install wheel: {whl_url}")
199
+ if rescode != 0:
200
+ _msg = f"Failed to install SAM2 python library: Possible whl not in the environment. {whl_url}"
201
+ logger.error(_msg, exc_info=True)
202
+ return dict(warn=_msg)
203
+ #===============================================================
204
+ # 成功時の処理
205
+ rescode, _msg = (self.RESP_SUCCESS, dict(success=f'Success to install SAM2 python library. {whl_url}'))
206
+ return dict(success=_msg)
207
+ else:
208
+ _msg = f"Unsupported vision engine: {vision_engine}"
209
+ logger.error(_msg, exc_info=True)
210
+ return dict(warn=_msg)
211
+ except Exception as e:
212
+ _msg = f"Failed to install vision engine: {e}"
213
+ logger.warning(_msg, exc_info=True)
214
+ return dict(warn=_msg)