cmdbox 0.6.0.2__py3-none-any.whl → 0.6.0.4__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 (29) hide show
  1. cmdbox/app/common.py +4 -2
  2. cmdbox/app/features/cli/agent_base.py +7 -65
  3. cmdbox/app/features/cli/cmdbox_audit_search.py +33 -17
  4. cmdbox/app/features/cli/cmdbox_cmd_list.py +1 -1
  5. cmdbox/app/features/cli/cmdbox_cmd_load.py +1 -1
  6. cmdbox/app/features/cli/cmdbox_web_apikey_add.py +1 -1
  7. cmdbox/app/features/cli/cmdbox_web_apikey_del.py +1 -1
  8. cmdbox/app/features/cli/cmdbox_web_group_add.py +1 -1
  9. cmdbox/app/features/cli/cmdbox_web_group_del.py +1 -1
  10. cmdbox/app/features/cli/cmdbox_web_group_edit.py +1 -1
  11. cmdbox/app/features/cli/cmdbox_web_group_list.py +1 -1
  12. cmdbox/app/features/cli/cmdbox_web_start.py +65 -26
  13. cmdbox/app/features/cli/cmdbox_web_user_add.py +1 -1
  14. cmdbox/app/features/cli/cmdbox_web_user_del.py +1 -1
  15. cmdbox/app/features/cli/cmdbox_web_user_edit.py +1 -1
  16. cmdbox/app/features/cli/cmdbox_web_user_list.py +1 -1
  17. cmdbox/app/features/web/cmdbox_web_agent.py +10 -4
  18. cmdbox/app/options.py +8 -0
  19. cmdbox/app/web.py +26 -47
  20. cmdbox/version.py +2 -2
  21. cmdbox/web/assets/cmdbox/agent.js +2 -3
  22. cmdbox/web/assets/cmdbox/common.js +26 -4
  23. cmdbox/web/assets/cmdbox/main.js +1 -2
  24. {cmdbox-0.6.0.2.dist-info → cmdbox-0.6.0.4.dist-info}/METADATA +2 -2
  25. {cmdbox-0.6.0.2.dist-info → cmdbox-0.6.0.4.dist-info}/RECORD +29 -29
  26. {cmdbox-0.6.0.2.dist-info → cmdbox-0.6.0.4.dist-info}/LICENSE +0 -0
  27. {cmdbox-0.6.0.2.dist-info → cmdbox-0.6.0.4.dist-info}/WHEEL +0 -0
  28. {cmdbox-0.6.0.2.dist-info → cmdbox-0.6.0.4.dist-info}/entry_points.txt +0 -0
  29. {cmdbox-0.6.0.2.dist-info → cmdbox-0.6.0.4.dist-info}/top_level.txt +0 -0
cmdbox/app/common.py CHANGED
@@ -135,8 +135,10 @@ def create_console(stderr:bool=False, file=None) -> Console:
135
135
  Returns:
136
136
  Console: コンソール
137
137
  """
138
- console = Console(height=False,
139
- soft_wrap=False, stderr=stderr, file=file, log_time=True, log_path=False, log_time_format='[%Y-%m-%d %H:%M:%S]')
138
+ #console = Console(soft_wrap=True, height=True, highlighter=loghandler.LogLevelHighlighter(), theme=loghandler.theme,
139
+ # stderr=stderr, file=file)
140
+ console = Console(height=True, highlighter=loghandler.LogLevelHighlighter(), theme=loghandler.theme,
141
+ soft_wrap=True, stderr=stderr, file=file, log_time=True, log_path=False, log_time_format='[%Y-%m-%d %H:%M:%S]')
140
142
  #console = Console(soft_wrap=True, stderr=stderr, file=file, log_time=True, log_path=False, log_time_format='[%Y-%m-%d %H:%M:%S]')
141
143
  return console
142
144
 
@@ -30,8 +30,7 @@ class AgentBase(feature.ResultEdgeFeature):
30
30
  dict(opt="agent", type=Options.T_STR, default="no", required=False, multi=False, hide=False, choice=["no", "use"],
31
31
  discription_ja="エージェントを使用するかどうかを指定します。",
32
32
  discription_en="Specifies whether the agent is used.",
33
- choice_show=dict(use=["agent_name", "agent_description", "agent_instruction", "agent_session_store", "llmprov",
34
- "mcp_listen_port", "mcp_ssl_listen_port"],)),
33
+ choice_show=dict(use=["agent_name", "agent_description", "agent_instruction", "agent_session_store", "llmprov",],)),
35
34
  dict(opt="agent_name", type=Options.T_STR, default=self.ver.__appid__, required=False, multi=False, hide=False, choice=None,
36
35
  discription_ja="エージェント名を指定します。",
37
36
  discription_en="Specifies the agent name."),
@@ -45,12 +44,6 @@ class AgentBase(feature.ResultEdgeFeature):
45
44
  discription_ja="エージェントのセッションを保存する方法を指定します。",
46
45
  discription_en="Specify how the agent's session is to be saved.",
47
46
  choice_show=dict(postgresql=["agent_pg_host", "agent_pg_port", "agent_pg_user", "agent_pg_password", "agent_pg_dbname"]),),
48
- dict(opt="mcp_listen_port", type=Options.T_INT, default="9081", required=False, multi=False, hide=False, choice=None,
49
- discription_ja="省略した時は `9081` を使用します。",
50
- discription_en="If omitted, `9081` is used."),
51
- dict(opt="mcp_ssl_listen_port", type=Options.T_INT, default="9443", required=False, multi=False, hide=False, choice=None,
52
- discription_ja="省略した時は `9443` を使用します。",
53
- discription_en="If omitted, `9443` is used."),
54
47
  dict(opt="agent_pg_host", type=Options.T_STR, default='localhost', required=False, multi=False, hide=False, choice=None, web="mask",
55
48
  discription_ja="postgresqlホストを指定する。",
56
49
  discription_en="Specify the postgresql host."),
@@ -114,7 +107,7 @@ class AgentBase(feature.ResultEdgeFeature):
114
107
  Returns:
115
108
  Any: FastMCP
116
109
  """
117
- from mcp.server.fastmcp import FastMCP
110
+ from fastmcp import FastMCP
118
111
  mcp = FastMCP(name=self.ver.__appid__)
119
112
  return mcp
120
113
 
@@ -174,33 +167,6 @@ class AgentBase(feature.ResultEdgeFeature):
174
167
  f"1. ユーザーのクエリからが実行したいコマンドを特定します。\n" + \
175
168
  f"2. コマンド実行に必要なパラメータのなかで、ユーザーのクエリから取得できないものは、コマンド定義にあるデフォルト値を指定して実行してください。\n" + \
176
169
  f"3. もしエラーが発生した場合は、ユーザーにコマンド名とパラメータとエラー内容を提示してください。\n"
177
- """
178
- f"2. コマンド実行に必要なパラメータを特定します。\n" + \
179
- f"3. ユーザーのクエリから指定しているパラメータを取得します。\n" + \
180
- f"4. ユーザーが指定しているパラメータと、コマンド実行に必要なパラメータを比較し、不足しているパラメータを取得します。\n" + \
181
- f"5. 以下に「パラメータ = デフォルト値」を示しているので、不足しているパラメータはデフォルト値を使用します。\n" + \
182
- f" 但しコマンドが実行に必要のないパラメータは指定しないようにしてください。\n" + \
183
- f" host = {args.host if hasattr(args, 'host') and args.host else self.default_host}\n" + \
184
- f" port = {args.port if hasattr(args, 'port') and args.port else self.default_port}\n" + \
185
- f" password = {args.password if hasattr(args, 'password') and args.password else self.default_pass}\n" + \
186
- f" svname = {args.svname if hasattr(args, 'svname') and args.svname else self.default_svname}\n" + \
187
- f" data = {args.data if hasattr(args, 'data') and args.data else self.default_data}\n" + \
188
- f" retry_count = {args.retry_count if hasattr(args, 'retry_count') and args.retry_count else 3}\n" + \
189
- f" retry_interval = {args.retry_interval if hasattr(args, 'retry_interval') and args.retry_interval else 3}\n" + \
190
- f" timeout = {args.timeout if hasattr(args, 'timeout') and args.timeout else 15}\n" + \
191
- f" output_json = {args.output_json if hasattr(args, 'output_json') and args.output_json else None}\n" + \
192
- f" output_json_append = {args.output_json_append if hasattr(args, 'output_json_append') and args.output_json_append else False}\n" + \
193
- f" stdout_log = {args.stdout_log if hasattr(args, 'stdout_log') and args.stdout_log else False}\n" + \
194
- f" capture_stdout = {args.capture_stdout if hasattr(args, 'capture_stdout') and args.capture_stdout else False}\n" + \
195
- f" capture_maxsize = {args.capture_maxsize if hasattr(args, 'capture_maxsize') and args.capture_maxsize else 100}\n" + \
196
- f" tag = {args.tag if hasattr(args, 'tag') and args.tag else None}\n" + \
197
- f" clmsg_id = {args.clmsg_id if hasattr(args, 'clmsg_id') and args.clmsg_id else None}\n" + \
198
- f" signin_file = {args.signin_file if hasattr(args, 'signin_file') and args.signin_file else f'.{self.ver.__appid__}/user_list.yml'}\n" + \
199
- f"6. 以上のパラメータを使用しても不足するパラメータは、Noneを使用します。\n" + \
200
- f"7. 以上のパラメータを使用してコマンドを実行して、コマンドの結果はJSONでユーザーに提示してください。\n" + \
201
- f"8. もし予期しないパラメータを受け取ったという旨のエラーが発生した場合は、そのパラメータを指定せずに再実行してください。\n" + \
202
- f"9. もしエラーが発生した場合は、ユーザーにコマンド名とパラメータとエラー内容を提示してください。\n"
203
- """
204
170
 
205
171
  description = description if is_japan else \
206
172
  f"Command offer registered in {self.ver.__appid__}."
@@ -210,32 +176,6 @@ class AgentBase(feature.ResultEdgeFeature):
210
176
  f"1. Identify the command you want to execute from the user's query.\n" + \
211
177
  f"2. Any parameters required to execute the command that cannot be obtained from the user's query should be executed with the default values provided in the command definition.\n" + \
212
178
  f"3. If an error occurs, provide the user with the command name, parameters, and error description.\n"
213
- """
214
- f"2. Identify the parameters required to execute the command.\n" + \
215
- f"3. Retrieve the specified parameters from the user's query.\n" + \
216
- f"4. It compares the parameters specified by the user with those required to execute the command and obtains the missing parameters.\n" + \
217
- f"5. The “Parameter = Default Value” is shown below, so use default values for missing parameters.\n" + \
218
- f" However, do not specify parameters that the command does not require for execution.\n" + \
219
- f" host = {args.host if hasattr(args, 'host') and args.host else self.default_host}\n" + \
220
- f" port = {args.port if hasattr(args, 'port') and args.port else self.default_port}\n" + \
221
- f" password = {args.password if hasattr(args, 'password') and args.password else self.default_pass}\n" + \
222
- f" svname = {args.svname if hasattr(args, 'svname') and args.svname else self.default_svname}\n" + \
223
- f" data = {args.data if hasattr(args, 'data') and args.data else self.default_data}\n" + \
224
- f" retry_count = {args.retry_count if hasattr(args, 'retry_count') and args.retry_count else 3}\n" + \
225
- f" retry_interval = {args.retry_interval if hasattr(args, 'retry_interval') and args.retry_interval else 3}\n" + \
226
- f" timeout = {args.timeout if hasattr(args, 'timeout') and args.timeout else 15}\n" + \
227
- f" output_json = {args.output_json if hasattr(args, 'output_json') and args.output_json else None}\n" + \
228
- f" output_json_append = {args.output_json_append if hasattr(args, 'output_json_append') and args.output_json_append else False}\n" + \
229
- f" stdout_log = {args.stdout_log if hasattr(args, 'stdout_log') and args.stdout_log else False}\n" + \
230
- f" capture_stdout = {args.capture_stdout if hasattr(args, 'capture_stdout') and args.capture_stdout else False}\n" + \
231
- f" capture_maxsize = {args.capture_maxsize if hasattr(args, 'capture_maxsize') and args.capture_maxsize else 100}\n" + \
232
- f" tag = {args.tag if hasattr(args, 'tag') and args.tag else None}\n" + \
233
- f" clmsg_id = {args.clmsg_id if hasattr(args, 'clmsg_id') and args.clmsg_id else None}\n" + \
234
- f"6. Use None for parameters that are missing even with the above parameters.\n" + \
235
- f"7. Execute the command using the above parameters and present the results of the command to the user in JSON.\n" + \
236
- f"8. If you receive an error stating that an unexpected parameter was received, rerun the program without that parameter.\n" + \
237
- f"9. If an error occurs, provide the user with the command name, parameters, and error description.\n"
238
- """
239
179
 
240
180
  description = args.agent_description if args.agent_description else description
241
181
  instruction = args.agent_instruction if args.agent_instruction else instruction
@@ -360,7 +300,7 @@ class AgentBase(feature.ResultEdgeFeature):
360
300
  common.reset_logger("google_adk.google.adk.sessions.database_session_service")
361
301
  common.reset_logger("mcp.server.streamable_http_manager")
362
302
  # モジュールインポート
363
- from mcp.server.fastmcp import FastMCP
303
+ from fastmcp import FastMCP
364
304
  from google.adk.sessions import BaseSessionService
365
305
  mcp:FastMCP = self.create_mcpserver(args)
366
306
  session_service:BaseSessionService = self.create_session_service(args)
@@ -410,8 +350,7 @@ class AgentBase(feature.ResultEdgeFeature):
410
350
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
411
351
  discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),)
412
352
  fn = f"{mode}_{cmd}"
413
- func_txt = f'@mcp.tool()\n'
414
- func_txt += f'def {fn}(' + ", ".join([f'{o["opt"]}:{_t2s(o, False)}' for o in choices]) + '):\n'
353
+ func_txt = f'def {fn}(' + ", ".join([f'{o["opt"]}:{_t2s(o, False)}' for o in choices]) + '):\n'
415
354
  func_txt += f' """\n'
416
355
  func_txt += f' {discription}\n'
417
356
  func_txt += f' Args:\n'
@@ -472,6 +411,9 @@ class AgentBase(feature.ResultEdgeFeature):
472
411
  exec(func_txt,
473
412
  dict(time=time,List=List, argparse=argparse, common=common, Options=Options, logging=logging, signin=signin,),
474
413
  dict(tools=tools, mcp=mcp))
414
+ exec(f"@mcp.tool\n{func_txt}",
415
+ dict(time=time,List=List, argparse=argparse, common=common, Options=Options, logging=logging, signin=signin,),
416
+ dict(tools=[], mcp=mcp))
475
417
  root_agent = self.create_agent(logger, args, tools)
476
418
  runner = self.create_runner(logger, args, session_service, root_agent)
477
419
  if logger.level == logging.DEBUG:
@@ -316,26 +316,26 @@ class AuditSearch(audit_base.AuditBase):
316
316
  Returns:
317
317
  int: レスポンスコード
318
318
  """
319
- def _date_format(pg_enabled, col, date_format):
319
+ def _date_format(pg_enabled, cal, col, date_format):
320
320
  if col not in ['clmsg_date', 'svmsg_date']:
321
321
  return col
322
322
  if pg_enabled:
323
323
  if date_format == '%u':
324
- return f"to_char({col}, 'D')"
324
+ return f"to_char({cal}, 'D')"
325
325
  elif date_format == '%m':
326
- return f"to_char({col}, 'MM')"
326
+ return f"to_char({cal}, 'MM')"
327
327
  elif date_format == '%Y':
328
- return f"to_char({col}, 'YYYY')"
328
+ return f"to_char({cal}, 'YYYY')"
329
329
  elif date_format == '%Y/%m':
330
- return f"to_char({col}, 'YYYY/MM')"
330
+ return f"to_char({cal}, 'YYYY/MM')"
331
331
  elif date_format == '%Y/%m/%d':
332
- return f"to_char({col}, 'YYYY/MM/DD')"
332
+ return f"to_char({cal}, 'YYYY/MM/DD')"
333
333
  elif date_format == '%Y/%m/%d %H':
334
- return f"to_char({col}, 'YYYY/MM/DD HH24')"
334
+ return f"to_char({cal}, 'YYYY/MM/DD HH24')"
335
335
  elif date_format == '%Y/%m/%d %H:%M':
336
- return f"to_char({col}, 'YYYY/MM/DD HH24:MI')"
336
+ return f"to_char({cal}, 'YYYY/MM/DD HH24:MI')"
337
337
  else:
338
- return col
338
+ return cal
339
339
  else:
340
340
  if date_format is not None and date_format != '':
341
341
  return f"strftime('{date_format}', {col})"
@@ -349,22 +349,22 @@ class AuditSearch(audit_base.AuditBase):
349
349
  cursor = conn.cursor()
350
350
  try:
351
351
  select = {k:v for k,v in select.items() if k != ''} if select else None
352
- select = select if select and len(select)>0 else {k:'-' for k in self.TBL_COLS}
352
+ select = select if select and len(select)>0 else {k:k for k in self.TBL_COLS}
353
353
  if pg_enabled:
354
354
  toz = common.get_tzoffset_str()
355
355
  sel = {}
356
356
  for k,v in select.items():
357
357
  if k in ['clmsg_date', 'svmsg_date']:
358
- sel[f"{k} AT TIME ZONE INTERVAL '{toz}' as {k}"] = v
358
+ sel[f"{k} AT TIME ZONE INTERVAL '{toz}'"] = (k if v is '-' else v)
359
359
  else:
360
- sel[k] = v
360
+ sel[k] = (k if v is '-' else v)
361
361
  select = sel
362
362
  sql = []
363
363
  for k,v in select.items():
364
364
  if v in ['count', 'sum', 'avg', 'min', 'max']:
365
- sql.append(f'{v}({_date_format(pg_enabled, k, select_date_format)}) AS {k}')
365
+ sql.append(f'{v}({_date_format(pg_enabled, k, k, select_date_format)}) AS {k}')
366
366
  else:
367
- sql.append(f'{_date_format(pg_enabled, k, select_date_format)} AS {k}')
367
+ sql.append(f'{_date_format(pg_enabled, k, v, select_date_format)} AS {v}')
368
368
  sql = f"SELECT {','.join(sql)} FROM audit"
369
369
  params = []
370
370
  where = []
@@ -397,8 +397,13 @@ class AuditSearch(audit_base.AuditBase):
397
397
  params.append(value)
398
398
  if filter_clmsg_tags:
399
399
  for tag in filter_clmsg_tags:
400
- where.append(f"clmsg_tag like {'%s' if pg_enabled else '?'}")
401
- params.append(f'%{tag}%')
400
+ if not tag: continue
401
+ if pg_enabled:
402
+ where.append(f"clmsg_tag ?| %s")
403
+ params.append(f'{tag}')
404
+ else:
405
+ where.append(f"clmsg_tag like '?'")
406
+ params.append(f'%{tag}%')
402
407
  if filter_svmsg_id and filter_svmsg_id != 'None':
403
408
  where.append(f'svmsg_id={"%s" if pg_enabled else "?"}')
404
409
  params.append(filter_svmsg_id)
@@ -410,7 +415,18 @@ class AuditSearch(audit_base.AuditBase):
410
415
  params.append(filter_svmsg_edate)
411
416
  sql += ' WHERE ' + ' AND '.join(where) if len(where)>0 else ''
412
417
  if groupby and len(groupby) > 0:
413
- sql += ' GROUP BY ' + ', '.join([f"{_date_format(pg_enabled, g, groupby_date_format)}" for g in groupby])
418
+ _gb = {}
419
+ if pg_enabled:
420
+ toz = common.get_tzoffset_str()
421
+ for g in groupby:
422
+ if g in ['clmsg_date', 'svmsg_date']:
423
+ _gb[f"{g} AT TIME ZONE INTERVAL '{toz}'"] = g
424
+ else:
425
+ _gb[g] = g
426
+ groupby = _gb
427
+ else:
428
+ groupby = {g:g for g in groupby}
429
+ sql += ' GROUP BY ' + ', '.join([f"{_date_format(pg_enabled, k, v, groupby_date_format)}" for k,v in groupby.items()])
414
430
  if sort and len(sort) > 0:
415
431
  sql += ' ORDER BY ' + ', '.join([f"{k} {v}" for k, v in sort.items()])
416
432
  else:
@@ -45,7 +45,7 @@ class CmdList(feature.OneshotResultEdgeFeature):
45
45
  dict(opt="kwd", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
46
46
  discription_ja=f"検索したいコマンド名を指定します。中間マッチで検索します。",
47
47
  discription_en=f"Specify the name of the command you want to search. Search with intermediate matches."),
48
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=False, multi=False, hide=True, choice=None,
48
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=False, multi=False, hide=True, choice=None,
49
49
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。",
50
50
  discription_en="Specify a file containing users and passwords with which they can signin."),
51
51
  dict(opt="groups", type=Options.T_STR, default=None, required=False, multi=True, hide=True, choice=None,
@@ -45,7 +45,7 @@ class CmdLoad(feature.OneshotResultEdgeFeature):
45
45
  dict(opt="title", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
46
46
  discription_ja=f"読込みたいコマンド名を指定します。",
47
47
  discription_en=f"Specify the name of the command to be read."),
48
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=False, multi=False, hide=True, choice=None,
48
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=False, multi=False, hide=True, choice=None,
49
49
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。",
50
50
  discription_en="Specify a file containing users and passwords with which they can signin."),
51
51
  dict(opt="groups", type=Options.T_STR, default=None, required=False, multi=True, hide=True, choice=None,
@@ -58,7 +58,7 @@ class WebApikeyAdd(feature.UnsupportEdgeFeature):
58
58
  dict(opt="apikey_name", type=Options.T_STR, default=None, required=True, multi=False, hide=False, choice=None,
59
59
  discription_ja="このユーザーのApiKey名を指定します。",
60
60
  discription_en="Specify the ApiKey name for this user."),
61
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=True, multi=False, hide=False, choice=None,
61
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=True, multi=False, hide=False, choice=None,
62
62
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
63
63
  discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),
64
64
  dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
@@ -58,7 +58,7 @@ class WebApikeyDel(feature.UnsupportEdgeFeature):
58
58
  dict(opt="apikey_name", type=Options.T_STR, default=None, required=True, multi=False, hide=False, choice=None,
59
59
  discription_ja="このユーザーのApiKey名を指定します。",
60
60
  discription_en="Specify the ApiKey name for this user."),
61
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=True, multi=False, hide=False, choice=None,
61
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=True, multi=False, hide=False, choice=None,
62
62
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
63
63
  discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),
64
64
  dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
@@ -61,7 +61,7 @@ class WebGroupAdd(feature.UnsupportEdgeFeature):
61
61
  dict(opt="group_parent", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
62
62
  discription_ja="親グループ名を指定します。",
63
63
  discription_en="Specifies the parent group name."),
64
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=True, multi=False, hide=False, choice=None,
64
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=True, multi=False, hide=False, choice=None,
65
65
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
66
66
  discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),
67
67
  dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
@@ -55,7 +55,7 @@ class WebGroupDel(feature.UnsupportEdgeFeature):
55
55
  dict(opt="group_id", type=Options.T_INT, default=None, required=True, multi=False, hide=False, choice=None,
56
56
  discription_ja="グループIDを指定します。",
57
57
  discription_en="Specify the group ID. Do not duplicate other groups."),
58
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=True, multi=False, hide=False, choice=None,
58
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=True, multi=False, hide=False, choice=None,
59
59
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
60
60
  discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),
61
61
  dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
@@ -61,7 +61,7 @@ class WebGroupEdit(feature.UnsupportEdgeFeature):
61
61
  dict(opt="group_parent", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
62
62
  discription_ja="親グループ名を指定します。",
63
63
  discription_en="Specifies the parent group name."),
64
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=True, multi=False, hide=False, choice=None,
64
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=True, multi=False, hide=False, choice=None,
65
65
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
66
66
  discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),
67
67
  dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
@@ -55,7 +55,7 @@ class WebGroupList(feature.OneshotResultEdgeFeature):
55
55
  dict(opt="group_name", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
56
56
  discription_ja="グループ名を指定して取得します。省略した時は全てのグループを取得します。",
57
57
  discription_en="Retrieved by specifying a group name. If omitted, all groups are retrieved."),
58
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=True, multi=False, hide=False, choice=None,
58
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=True, multi=False, hide=False, choice=None,
59
59
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
60
60
  discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),
61
61
  dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
@@ -157,32 +157,12 @@ class WebStart(feature.UnsupportEdgeFeature, agent_base.AgentBase):
157
157
  w = None
158
158
  try:
159
159
  args.gui_mode = False if not hasattr(args, 'gui_mode') or not args.gui_mode else args.gui_mode
160
- ssl_cert = None if args.ssl_cert is None else Path(args.ssl_cert)
161
- ssl_key = None if args.ssl_key is None else Path(args.ssl_key)
162
- ssl_ca_certs = None if args.ssl_ca_certs is None else Path(args.ssl_ca_certs)
163
- w = web.Web(logger, Path(args.data), appcls=self.appcls, ver=self.ver,
164
- redis_host=args.host, redis_port=args.port, redis_password=args.password, svname=args.svname,
165
- client_only=args.client_only, doc_root=args.doc_root, gui_html=args.gui_html, filer_html=args.filer_html,
166
- result_html=args.result_html, users_html=args.users_html,
167
- assets=args.assets, signin_html=args.signin_html, signin_file=args.signin_file, gui_mode=args.gui_mode)
168
- agent_runner = None
169
- mcp = None
170
- logger.info(f"Agent={args.agent}")
171
- if args.agent=='use':
172
- if args.agent_session_store == 'sqlite':
173
- args.agent_session_dburl = "sqlite://" + pathname2url(str(w.agent_path / 'session.db'))
174
- elif args.agent_session_store == 'postgresql':
175
- args.agent_session_dburl = f"postgresql+psycopg://{args.agent_pg_user}:{args.agent_pg_password}@{args.agent_pg_host}:{args.agent_pg_port}/{args.agent_pg_dbname}"
176
- else:
177
- args.agent_session_dburl = None
178
- agent_runner, mcp = self.init_agent_runner(logger, args)
179
- w.start(allow_host=args.allow_host, listen_port=args.listen_port, ssl_listen_port=args.ssl_listen_port,
180
- ssl_cert=ssl_cert, ssl_key=ssl_key, ssl_keypass=args.ssl_keypass, ssl_ca_certs=ssl_ca_certs,
181
- session_domain=args.session_domain, session_path=args.session_path,
182
- session_secure=args.session_secure, session_timeout=args.session_timeout,
183
- outputs_key=args.outputs_key, guvicorn_workers=args.guvicorn_workers, guvicorn_timeout=args.guvicorn_timeout,
184
- agent_runner=agent_runner, mcp=mcp, mcp_listen_port=args.mcp_listen_port, mcp_ssl_listen_port=args.mcp_ssl_listen_port)
185
-
160
+ w = self.createWeb(logger, args)
161
+ agent_runner, mcp = self._init_agent_runner(w, logger, args)
162
+ args.ssl_cert = None if args.ssl_cert is None else Path(args.ssl_cert)
163
+ args.ssl_key = None if args.ssl_key is None else Path(args.ssl_key)
164
+ args.ssl_ca_certs = None if args.ssl_ca_certs is None else Path(args.ssl_ca_certs)
165
+ self.start(w, agent_runner, mcp, logger, args)
186
166
  msg = dict(success="web complate.")
187
167
  common.print_format(msg, args.format, tm, args.output_json, args.output_json_append, pf=pf)
188
168
  return 0, msg, w
@@ -191,3 +171,62 @@ class WebStart(feature.UnsupportEdgeFeature, agent_base.AgentBase):
191
171
  msg = dict(warn=f"Web server start error. {e}")
192
172
  common.print_format(msg, args.format, tm, args.output_json, args.output_json_append, pf=pf)
193
173
  return 1, msg, w
174
+
175
+ def createWeb(self, logger:logging.Logger, args:argparse.Namespace) -> web.Web:
176
+ """
177
+ Webオブジェクトを作成します
178
+
179
+ Args:
180
+ logger (logging.Logger): ロガー
181
+ args (argparse.Namespace): 引数
182
+
183
+ Returns:
184
+ web.Web: Webオブジェクト
185
+ """
186
+ w = web.Web(logger, Path(args.data), appcls=self.appcls, ver=self.ver,
187
+ redis_host=args.host, redis_port=args.port, redis_password=args.password, svname=args.svname,
188
+ client_only=args.client_only, doc_root=args.doc_root, gui_html=args.gui_html, filer_html=args.filer_html,
189
+ result_html=args.result_html, users_html=args.users_html,
190
+ assets=args.assets, signin_html=args.signin_html, signin_file=args.signin_file, gui_mode=args.gui_mode)
191
+ return w
192
+
193
+ def _init_agent_runner(self, w:web.Web, logger:logging.Logger, args:argparse.Namespace) -> Tuple[Any, Any]:
194
+ """
195
+ エージェントをセットアップします
196
+
197
+ Args:
198
+ w (web.Web): Webオブジェクト
199
+ logger (logging.Logger): ロガー
200
+ args (argparse.Namespace): 引数
201
+
202
+ Returns:
203
+ Tuple[agent_base.AgentBase, Any]: エージェントオブジェクト, MCPオブジェクト
204
+ """
205
+ logger.info(f"Agent={args.agent}")
206
+ if args.agent=='use':
207
+ if args.agent_session_store == 'sqlite':
208
+ args.agent_session_dburl = "sqlite://" + pathname2url(str(w.agent_path / 'session.db'))
209
+ elif args.agent_session_store == 'postgresql':
210
+ args.agent_session_dburl = f"postgresql+psycopg://{args.agent_pg_user}:{args.agent_pg_password}@{args.agent_pg_host}:{args.agent_pg_port}/{args.agent_pg_dbname}"
211
+ else:
212
+ args.agent_session_dburl = None
213
+ return self.init_agent_runner(logger, args)
214
+ return None, None
215
+
216
+ def start(self, w:web.Web, agent_runner, mcp, logger:logging.Logger, args:argparse.Namespace) -> None:
217
+ """
218
+ Webモードを起動します
219
+
220
+ Args:
221
+ w (web.Web): Webオブジェクト
222
+ agent_runner: エージェントランナー
223
+ mcp: MCPオブジェクト
224
+ logger (logging.Logger): ロガー
225
+ args (argparse.Namespace): 引数
226
+ """
227
+ w.start(allow_host=args.allow_host, listen_port=args.listen_port, ssl_listen_port=args.ssl_listen_port,
228
+ ssl_cert=args.ssl_cert, ssl_key=args.ssl_key, ssl_keypass=args.ssl_keypass, ssl_ca_certs=args.ssl_ca_certs,
229
+ session_domain=args.session_domain, session_path=args.session_path,
230
+ session_secure=args.session_secure, session_timeout=args.session_timeout,
231
+ outputs_key=args.outputs_key, guvicorn_workers=args.guvicorn_workers, guvicorn_timeout=args.guvicorn_timeout,
232
+ agent_runner=agent_runner, mcp=mcp,)
@@ -70,7 +70,7 @@ class WebUserAdd(feature.UnsupportEdgeFeature):
70
70
  dict(opt="user_group", type=Options.T_STR, default=None, required=True, multi=True, hide=False, choice=None,
71
71
  discription_ja="ユーザーが所属するグループを指定します。",
72
72
  discription_en="Specifies the groups to which the user belongs."),
73
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=True, multi=False, hide=False, choice=None,
73
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=True, multi=False, hide=False, choice=None,
74
74
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
75
75
  discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),
76
76
  dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
@@ -55,7 +55,7 @@ class WebUserDel(feature.UnsupportEdgeFeature):
55
55
  dict(opt="user_id", type=Options.T_INT, default=None, required=True, multi=False, hide=False, choice=None,
56
56
  discription_ja="ユーザーIDを指定します。",
57
57
  discription_en="Specify the user ID."),
58
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=True, multi=False, hide=False, choice=None,
58
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=True, multi=False, hide=False, choice=None,
59
59
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
60
60
  discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),
61
61
  dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
@@ -70,7 +70,7 @@ class WebUserEdit(feature.UnsupportEdgeFeature):
70
70
  dict(opt="user_group", type=Options.T_STR, default=None, required=True, multi=True, hide=False, choice=None,
71
71
  discription_ja="ユーザーが所属するグループを指定します。",
72
72
  discription_en="Specifies the groups to which the user belongs."),
73
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=True, multi=False, hide=False, choice=None,
73
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=True, multi=False, hide=False, choice=None,
74
74
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
75
75
  discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),
76
76
  dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
@@ -55,7 +55,7 @@ class WebUserList(feature.OneshotResultEdgeFeature):
55
55
  dict(opt="user_name", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
56
56
  discription_ja="ユーザー名を指定して取得します。省略した時は全てのユーザーを取得します。",
57
57
  discription_en="Retrieved by specifying a user name. If omitted, all users are retrieved."),
58
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=True, multi=False, hide=False, choice=None,
58
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=True, multi=False, hide=False, choice=None,
59
59
  discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
60
60
  discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),
61
61
  dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
@@ -60,7 +60,7 @@ class Agent(feature.WebFeature):
60
60
  session_id = form.get('session_id', None)
61
61
  sessions = await web.list_agent_sessions(web.agent_runner.session_service, user_id, session_id=session_id)
62
62
  data = [dict(id=s.id, app_name=s.app_name, user_id=s.user_id, last_update_time=s.last_update_time,
63
- events=[dict(author=ev.author,text=ev.content.parts[0].text) for ev in s.events if ev.content and ev.content.parts]) for s in sessions]
63
+ events=[dict(author=ev.author,text=ev.content.parts[0].text) for ev in s.events if ev.content and ev.content.parts]) for s in sessions if s]
64
64
  data.reverse() # 最新のセッションを先頭にする
65
65
  return dict(success=data)
66
66
 
@@ -204,14 +204,20 @@ class Agent(feature.WebFeature):
204
204
  outputs = dict()
205
205
  if event.turn_complete:
206
206
  outputs['turn_complete'] = True
207
- yield json.dumps(outputs, default=common.default_json_enc)
207
+ yield common.to_str(outputs)
208
208
  if event.interrupted:
209
209
  outputs['interrupted'] = True
210
- yield json.dumps(outputs, default=common.default_json_enc)
210
+ yield common.to_str(outputs)
211
211
  #if event.is_final_response():
212
212
  msg = None
213
213
  if event.content and event.content.parts:
214
214
  msg = "\n".join([p.text for p in event.content.parts if p and p.text])
215
+ calls = event.get_function_calls()
216
+ if calls:
217
+ msg += '\n```json{"function_calls":'+common.to_str([dict(fn=c.name,args=c.args) for c in calls])+'}```'
218
+ responses = event.get_function_responses()
219
+ if responses:
220
+ msg += '\n```json{"function_responses":'+common.to_str([dict(fn=r.name, res=r.response) for r in responses])+'}```'
215
221
  elif event.actions and event.actions.escalate:
216
222
  msg = f"Agent escalated: {event.error_message or 'No specific message.'}"
217
223
  if msg:
@@ -219,7 +225,7 @@ class Agent(feature.WebFeature):
219
225
 
220
226
  outputs['message'] = msg
221
227
  web.options.audit_exec(sock, web, body=dict(agent_session=agent_session.id, result=msg))
222
- yield json.dumps(outputs, default=common.default_json_enc)
228
+ yield common.to_str(outputs)
223
229
  if event.is_final_response():
224
230
  break
225
231
  except WebSocketDisconnect:
cmdbox/app/options.py CHANGED
@@ -295,12 +295,18 @@ class Options:
295
295
  discription_ja="クライアントのメッセージIDを指定します。省略した場合はuuid4で生成されます。",
296
296
  discription_en="Specifies the message ID of the client. If omitted, uuid4 will be generated.",
297
297
  choice=None)
298
+ self._options["description"] = dict(
299
+ type=Options.T_TEXT, default=None, required=False, multi=False, hide=True,
300
+ discription_ja="このコマンド登録の説明文を指定します。Agentがこのコマンドの用途を理解するのに使用します。",
301
+ discription_en="Specifies a description of this command registration, used to help the Agent understand the use of this command.",
302
+ choice=None)
298
303
 
299
304
  def init_debugoption(self):
300
305
  # デバックオプションを追加
301
306
  self._options["debug"]["opt"] = "debug"
302
307
  self._options["tag"]["opt"] = "tag"
303
308
  self._options["clmsg_id"]["opt"] = "clmsg_id"
309
+ self._options["description"]["opt"] = "description"
304
310
  for key, mode in self._options["cmd"].items():
305
311
  if type(mode) is not dict:
306
312
  continue
@@ -315,6 +321,8 @@ class Options:
315
321
  c["choice"].append(self._options["tag"])
316
322
  if "clmsg_id" not in [_o['opt'] for _o in c["choice"]]:
317
323
  c["choice"].append(self._options["clmsg_id"])
324
+ if "description" not in [_o['opt'] for _o in c["choice"]]:
325
+ c["choice"].append(self._options["description"])
318
326
  if c["opt"] not in [_o['opt'] for _o in self._options["cmd"]["choice"]]:
319
327
  self._options["cmd"]["choice"] += [c]
320
328
  self._options["mode"][key] = mode
cmdbox/app/web.py CHANGED
@@ -5,7 +5,6 @@ from fastapi import FastAPI, Request, Response
5
5
  from pathlib import Path
6
6
  from starlette.applications import Starlette
7
7
  from starlette.middleware.sessions import SessionMiddleware
8
- from starlette.routing import Mount
9
8
  from typing import Any, Dict, List
10
9
  from uvicorn.config import Config
11
10
  import asyncio
@@ -20,7 +19,6 @@ import platform
20
19
  import requests
21
20
  import queue
22
21
  import signal
23
- import string
24
22
  import time
25
23
  import threading
26
24
  import traceback
@@ -30,10 +28,10 @@ import webbrowser
30
28
 
31
29
  class Web:
32
30
  def __init__(self, logger:logging.Logger, data:Path, appcls=None, ver=None,
33
- redis_host:str = "localhost", redis_port:int = 6379, redis_password:str = None, svname:str = 'server',
31
+ redis_host:str="localhost", redis_port:int=6379, redis_password:str=None, svname:str='server',
34
32
  client_only:bool=False, doc_root:Path=None, gui_html:str=None, filer_html:str=None, result_html:str=None, users_html:str=None,
35
33
  audit_html:str=None, agent_html:str=None, assets:List[str]=None, signin_html:str=None, signin_file:str=None, gui_mode:bool=False,
36
- web_features_packages:List[str]=None, web_features_prefix:List[str]=None):
34
+ web_features_packages:List[str]=None, web_features_prefix:List[str]=[]):
37
35
  """
38
36
  cmdboxクライアント側のwebapiサービス
39
37
 
@@ -679,7 +677,7 @@ class Web:
679
677
  ssl_cert:Path=None, ssl_key:Path=None, ssl_keypass:str=None, ssl_ca_certs:Path=None,
680
678
  session_domain:str=None, session_path:str='/', session_secure:bool=False, session_timeout:int=900, outputs_key:List[str]=[],
681
679
  guvicorn_workers:int=-1, guvicorn_timeout:int=30,
682
- agent_runner=None, mcp=None, mcp_listen_port=9081, mcp_ssl_listen_port=9443):
680
+ agent_runner=None, mcp=None,):
683
681
  """
684
682
  Webサーバを起動する
685
683
 
@@ -700,8 +698,6 @@ class Web:
700
698
  guvicorn_timeout (int, optional): Gunicornタイムアウト. Defaults to 30.
701
699
  agent_runner (Runner, optional): エージェントランナー. Defaults to None.
702
700
  mcp (MCP, optional): MCP. Defaults to None.
703
- mcp_listen_port (int, optional): MCPリスンポート. Defaults to 9081.
704
- mcp_ssl_listen_port (int, optional): MCP SSLリスンポート. Defaults to 9443.
705
701
  """
706
702
  self.allow_host = allow_host
707
703
  self.listen_port = listen_port
@@ -719,8 +715,6 @@ class Web:
719
715
  self.guvicorn_timeout = guvicorn_timeout
720
716
  self.agent_runner = agent_runner
721
717
  self.mcp = mcp
722
- self.mcp_listen_port = mcp_listen_port
723
- self.mcp_ssl_listen_port = mcp_ssl_listen_port
724
718
  if self.logger.level == logging.DEBUG:
725
719
  self.logger.debug(f"web start parameter: allow_host={self.allow_host}")
726
720
  self.logger.debug(f"web start parameter: listen_port={self.listen_port}")
@@ -738,8 +732,6 @@ class Web:
738
732
  self.logger.debug(f"web start parameter: guvicorn_timeout={self.guvicorn_timeout}")
739
733
  self.logger.debug(f"web start parameter: agent_runner={self.agent_runner}")
740
734
  self.logger.debug(f"web start parameter: mcp={self.mcp}")
741
- self.logger.debug(f"web start parameter: mcp_listen_port={self.mcp_listen_port}")
742
- self.logger.debug(f"web start parameter: mcp_ssl_listen_port={self.mcp_ssl_listen_port}")
743
735
 
744
736
  if self.agent_runner is not None:
745
737
  # google.adkが大きいので必要な時にだけ読込む
@@ -778,7 +770,10 @@ class Web:
778
770
  sessions = await session_service.list_sessions(app_name=self.ver.__appid__, user_id=user_id)
779
771
  ret = []
780
772
  for s in sessions.sessions:
781
- ret.append(await session_service.get_session(app_name=self.ver.__appid__, user_id=user_id, session_id=s.id))
773
+ session = await session_service.get_session(app_name=self.ver.__appid__, user_id=user_id, session_id=s.id)
774
+ if session is None:
775
+ continue
776
+ ret.append(session)
782
777
  return ret
783
778
  else:
784
779
  session = await session_service.get_session(app_name=self.ver.__appid__, user_id=user_id, session_id=session_id)
@@ -801,17 +796,15 @@ class Web:
801
796
  return await session_service.delete_session(app_name=self.ver.__appid__, user_id=user_id, session_id=session_id)
802
797
  self.delete_agent_session = delete_agent_session
803
798
 
804
- """
799
+ mcp_app:Starlette = None
805
800
  if self.mcp is not None:
806
- # MCPをFastAPIにマウント
807
801
  mcp_app:Starlette = self.mcp.streamable_http_app()
808
- app = FastAPI(lifespan=self.mcp.settings.lifespan)
809
- app.mount("/mcp", mcp_app)
810
- #app.include_router(mcp_app.router)
802
+ #mcp_app:Starlette = self.mcp.http_app()
803
+ if mcp_app is not None:
804
+ app = FastAPI(lifespan=mcp_app.lifespan)
811
805
  else:
812
806
  app = FastAPI()
813
- """
814
- app = FastAPI()
807
+
815
808
  @app.middleware("http")
816
809
  async def set_context_cookie(req:Request, call_next):
817
810
  res:Response = await call_next(req)
@@ -824,20 +817,13 @@ class Web:
824
817
  if self.session_secure:
825
818
  mwparam['https_only'] = True # セッションハイジャック対策
826
819
  app.add_middleware(SessionMiddleware, **mwparam)
820
+ if mcp_app is not None:
821
+ app.mount("/mcpsv", mcp_app, name="mcp")
822
+ self.logger.info(f"mcp server mount: mount_path=/mcpsv app={mcp_app} routes={mcp_app.routes}")
827
823
  self.init_webfeatures(app)
828
824
 
829
825
  self.is_running = True
830
- #uvicorn.run(app, host=self.allow_host, port=self.listen_port, workers=2)
831
- http_config = Config(app=app, host=self.allow_host, port=self.listen_port)
832
- th = ThreadedUvicorn(self.logger, config=http_config,
833
- guvicorn_config=dict(workers=self.guvicorn_workers, timeout=self.guvicorn_timeout))
834
- th.start()
835
- if self.mcp is not None and self.ssl_cert is None and self.ssl_key is None:
836
- mcp_app:Starlette = self.mcp.streamable_http_app()
837
- http_config = Config(app=mcp_app, host=self.allow_host, port=self.mcp_listen_port)
838
- mcp_th = ThreadedUvicorn(self.logger, config=http_config, force_uvicorn=True)
839
- mcp_th.start()
840
- browser_port = self.listen_port
826
+ th = None
841
827
  th_ssl = None
842
828
  if self.ssl_cert is not None and self.ssl_key is not None:
843
829
  https_config = Config(app=app, host=self.allow_host, port=self.ssl_listen_port,
@@ -847,13 +833,12 @@ class Web:
847
833
  guvicorn_config=dict(workers=self.guvicorn_workers, timeout=self.guvicorn_timeout))
848
834
  th_ssl.start()
849
835
  browser_port = self.ssl_listen_port
850
- if self.mcp is not None:
851
- mcp_app:Starlette = self.mcp.streamable_http_app()
852
- https_config = Config(app=mcp_app, host=self.allow_host, port=self.mcp_ssl_listen_port,
853
- ssl_certfile=self.ssl_cert, ssl_keyfile=self.ssl_key,
854
- ssl_keyfile_password=self.ssl_keypass, ssl_ca_certs=self.ssl_ca_certs)
855
- mcp_th_ssl = ThreadedUvicorn(self.logger, config=https_config, force_uvicorn=True)
856
- mcp_th_ssl.start()
836
+ else:
837
+ http_config = Config(app=app, host=self.allow_host, port=self.listen_port)
838
+ th = ThreadedUvicorn(self.logger, config=http_config,
839
+ guvicorn_config=dict(workers=self.guvicorn_workers, timeout=self.guvicorn_timeout))
840
+ th.start()
841
+ browser_port = self.listen_port
857
842
  try:
858
843
  if self.gui_mode:
859
844
  webbrowser.open(f'http://localhost:{browser_port}/gui')
@@ -861,21 +846,15 @@ class Web:
861
846
  f.write(str(os.getpid()))
862
847
  while self.is_running:
863
848
  gevent.sleep(1)
864
- th.stop()
865
- if self.mcp is not None:
866
- mcp_th.stop()
849
+ if th is not None:
850
+ th.stop()
867
851
  if th_ssl is not None:
868
852
  th_ssl.stop()
869
- if self.mcp is not None:
870
- mcp_th_ssl.stop()
871
853
  except KeyboardInterrupt:
872
- th.stop()
873
- if self.mcp is not None:
874
- mcp_th.stop()
854
+ if th is not None:
855
+ th.stop()
875
856
  if th_ssl is not None:
876
857
  th_ssl.stop()
877
- if self.mcp is not None:
878
- mcp_th_ssl.stop()
879
858
 
880
859
  def stop(self):
881
860
  """
cmdbox/version.py CHANGED
@@ -1,9 +1,9 @@
1
1
  import datetime
2
2
 
3
- dt_now = datetime.datetime(2025, 6, 1)
3
+ dt_now = datetime.datetime(2025, 6, 15)
4
4
  __appid__ = 'cmdbox'
5
5
  __title__ = 'cmdbox (Command Development Application)'
6
- __version__ = '0.6.0.2'
6
+ __version__ = '0.6.0.4'
7
7
  __copyright__ = f'Copyright © 2023-{dt_now.strftime("%Y")} hamacom2004jp'
8
8
  __pypiurl__ = 'https://pypi.org/project/cmdbox/'
9
9
  __srcurl__ = 'https://github.com/hamacom2004jp/cmdbox'
@@ -49,7 +49,7 @@ agent.format_agent_message = (container, messages, txt, message) => {
49
49
  const rand = cmdbox.random_string(16);
50
50
  txt.append(`<span id="${rand}"/>`);
51
51
  agent.recursive_json_parse(jobj);
52
- render_result_func(txt.find(`#${rand}`), jobj, 100);
52
+ render_result_func(txt.find(`#${rand}`), jobj, 256);
53
53
  } catch (e) {
54
54
  const msg = message.replace(/\n/g, '<br/>');
55
55
  txt.append(msg);
@@ -176,8 +176,7 @@ agent.init_form = async () => {
176
176
  if (agent.chat_reconnect_count >= max_reconnect_count) {
177
177
  clearInterval(agent.chat_reconnectInterval_handler);
178
178
  cmdbox.message({'error':'Connection to the agent has failed for several minutes. Please reload to resume reconnection.'});
179
- const rand = cmdbox.random_string(8);
180
- location.href = `../signin/agent?r=${rand}`;
179
+ location.reload(true);
181
180
  return;
182
181
  }
183
182
  agent.chat_reconnect_count++;
@@ -203,6 +203,24 @@ cmdbox.editapikey = async () => {
203
203
  daialog.draggable({cursor:'move',cancel:'.modal-body'});
204
204
  editapikey_modal.modal('show');
205
205
  };
206
+ cmdbox.agentsetting = async () => {
207
+ const user = await cmdbox.user_info();
208
+ if (!user) {
209
+ cmdbox.message('user not found');
210
+ return;
211
+ }
212
+ const agentsetting_modal = $('#agentsetting_modal').length?$('#agentsetting_modal'):$(`<div id="agentsetting_modal" class="modal" tabindex="-1" style="display: none;" aria-hidden="true"/>`);
213
+ agentsetting_modal.html('');
214
+ const daialog = $(`<div class="modal-dialog ui-draggable ui-draggable-handle"/>`).appendTo(agentsetting_modal);
215
+ const form = $(`<form id="agentsetting_form" class="modal-content novalidate"/>`).appendTo(daialog);
216
+ const header = $(`<div class="modal-header"/>`).appendTo(form);
217
+ header.append('<h5 class="modal-title">Agent Setting</h5>');
218
+ header.append('<button type="button" class="btn btn_close p-0 m-0" data-bs-dismiss="modal" aria-label="Close" style="margin-left: 0px;">'
219
+ +'<svg class="bi bi-x" width="24" height="24" fill="currentColor"><use href="#btn_x"></use></svg>'
220
+ +'</button>');
221
+ const body = $(`<div class="modal-body"/>`).appendTo(form);
222
+ const row_content = $(`<div class="row row_content"/>`).appendTo(body);
223
+ };
206
224
  /**
207
225
  * 現在のユーザーのパスワード変更
208
226
  */
@@ -324,14 +342,18 @@ $(()=>{
324
342
  const user_info_menu = $('.user_info');
325
343
  user_info_menu.removeClass('d-none').addClass('d-flex');
326
344
 
327
- if (!user_info_menu.find('.dropdown-menu .editapikey-menu-item').length) {
328
- const editapikey_item = $(`<li><a class="dropdown-item editapikey-menu-item" href="#" onclick="cmdbox.editapikey();">Edit ApiKey</a></li>`);
329
- user_info_menu.find('.dropdown-menu').append(editapikey_item);
330
- }
331
345
  if (!user_info_menu.find('.dropdown-menu .changepass-menu-item').length) {
332
346
  const changepass_item = $(`<li><a class="dropdown-item changepass-menu-item" href="#" onclick="cmdbox.passchange();">Change Password</a></li>`);
333
347
  user_info_menu.find('.dropdown-menu').append(changepass_item);
334
348
  }
349
+ if (!user_info_menu.find('.dropdown-menu .editapikey-menu-item').length) {
350
+ const editapikey_item = $(`<li><a class="dropdown-item editapikey-menu-item" href="#" onclick="cmdbox.editapikey();">Edit ApiKey</a></li>`);
351
+ user_info_menu.find('.dropdown-menu').append(editapikey_item);
352
+ }
353
+ /*if (!user_info_menu.find('.dropdown-menu .agentsetting-menu-item').length) {
354
+ const agentsetting_item = $(`<li><a class="dropdown-item agentsetting-menu-item" href="#" onclick="cmdbox.agentsetting();">Agent Setting</a></li>`);
355
+ user_info_menu.find('.dropdown-menu').append(agentsetting_item);
356
+ }*/
335
357
  if (!user_info_menu.find('.dropdown-menu .signout-menu-item').length) {
336
358
  const parts = location.pathname.split('/');
337
359
  const sitepath = parts[parts.length-1];
@@ -86,8 +86,7 @@ $(() => {
86
86
  if (cmdbox.callback_reconnect_count >= max_reconnect_count) {
87
87
  clearInterval(cmdbox.gui_callback_reconnectInterval_handler);
88
88
  cmdbox.message({'error':'Connection to the agent has failed for several minutes. Please reload to resume reconnection.'});
89
- const rand = cmdbox.random_string(8);
90
- location.href = `../signin${path}?r=${rand}`;
89
+ location.reload(true);
91
90
  return;
92
91
  }
93
92
  cmdbox.callback_reconnect_count++;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cmdbox
3
- Version: 0.6.0.2
3
+ Version: 0.6.0.4
4
4
  Summary: cmdbox: It is a command line application with a plugin mechanism.
5
5
  Home-page: https://github.com/hamacom2004jp/cmdbox
6
6
  Download-URL: https://github.com/hamacom2004jp/cmdbox
@@ -73,7 +73,7 @@ apt-get install -y pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl buil
73
73
 
74
74
  - When using `--agent use` in web mode, install the modules with dependencies.
75
75
  ```bash
76
- pip install google-adk litellm
76
+ pip install google-adk litellm fastmcp
77
77
  ```
78
78
 
79
79
  # Run
@@ -8,18 +8,18 @@ cmdbox/logconf_edge.yml,sha256=vRY-LxfWztk2QZOUN9kj_Eq6Y4qj8WzmiO9H0IRvjtQ,1081
8
8
  cmdbox/logconf_gui.yml,sha256=-95vyd0q-aB1gsabdk8rg9dJ2zRKAZc8hRxyhNOQboU,1076
9
9
  cmdbox/logconf_server.yml,sha256=n3c5-KVzjUzcUX5BQ6uE-PN9rp81yXaJql3whyCcSDQ,1091
10
10
  cmdbox/logconf_web.yml,sha256=pPbdAwckbK0cgduxcVkx2mbk-Ymz5hVzR4guIsfApMQ,1076
11
- cmdbox/version.py,sha256=C-XjKuewu-MRxDGuFXkuoMFEBvG18KXj4UwzvdfjIs0,2031
11
+ cmdbox/version.py,sha256=ebybePVS1-vifRTcWVutek50i0oYp1dzvReDgMD9JOA,2032
12
12
  cmdbox/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  cmdbox/app/app.py,sha256=YkzNBd1S1oNVwnNj1eYQUFOcrcjwdsQzv0z4mMOb_mw,9149
14
14
  cmdbox/app/client.py,sha256=n986lXeV7hhaki4iyyvsfhNptmCXDFphxlNoNe2XhKg,19542
15
- cmdbox/app/common.py,sha256=qUdf8q1vK-e167nq-O2KGJ4gU-ZZNNNzzi0Bos7fWdI,25358
15
+ cmdbox/app/common.py,sha256=puw9sEKa6EgCAEUnXmjRd6R3l01ApZEQtP23kq34SZs,25599
16
16
  cmdbox/app/edge.py,sha256=yskob1ewd5hHbPJKWvjXm6j4IpawE4gM_lOqe70gX1w,41422
17
17
  cmdbox/app/edge_tool.py,sha256=QqigDHcxRJa1BvK_DVq-ccNTVXg4BEBsHkYsaauMDu0,8050
18
18
  cmdbox/app/feature.py,sha256=O3MTyQFinKXONeCvSRAt81HqZWgXV-je_-nUZi0cdCE,9910
19
19
  cmdbox/app/filer.py,sha256=L_DSMTvnbN_ffr3JIt0obbOmVoTHEfVm2cAVz3rLH-Q,16059
20
- cmdbox/app/options.py,sha256=_VDUX0VeJ-UExJTFRlewTq6wT_3D-NVbYE0CvAmE-0g,45259
20
+ cmdbox/app/options.py,sha256=N0O1NBjkeceCgDOQrsxMEQjzPwg24DB-cUttdFOWfLk,45933
21
21
  cmdbox/app/server.py,sha256=woOmIk901ONn5a_2yz_b3I1JpLYIF8g42uQRd0_MRuQ,10417
22
- cmdbox/app/web.py,sha256=LeiaN2qbmPS_p4fz4824tuN9lI64hEaY3sCZab1o9t4,53215
22
+ cmdbox/app/web.py,sha256=H3r_Jc0YTsNCD2OtoRqAup2Gz4rLc6TF4MzfIFNbKX4,51700
23
23
  cmdbox/app/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  cmdbox/app/auth/azure_signin.py,sha256=ZVJTeZwdgM4y2loKiTMEPiXxbJwbdoTVMWciyJhgqc8,1666
25
25
  cmdbox/app/auth/azure_signin_saml.py,sha256=oM2buGTK4t6-OsUuiUXTlZk0YXZL01khuPYVB84dMDU,472
@@ -31,11 +31,11 @@ cmdbox/app/commons/convert.py,sha256=mkXPNQtX_pEH4L5DzonOY6Dh38SzJ5JQma_EY7UDBqU
31
31
  cmdbox/app/commons/loghandler.py,sha256=go5Ui1c4AKqRSnxLzwrtPakYSjRHguXk4dGat1jD738,6323
32
32
  cmdbox/app/commons/module.py,sha256=w63zqz5c6gLy-BZJ9dh4Q4C5PZyqM2Iqat5Zr9swJCI,4993
33
33
  cmdbox/app/commons/redis_client.py,sha256=cOHshqAGI3CRWLcc2IMhk6MtXsVKlQq6HwcdqYsoA-0,14821
34
- cmdbox/app/features/cli/agent_base.py,sha256=kY6lymXy9lA9-qcutRcJIBbjIHkNKKdTbvTcjKUyKs4,33625
34
+ cmdbox/app/features/cli/agent_base.py,sha256=7Hkdb1wqZZ_oCKD16fhEu9XMw0-L44x3tN4W4CY-0Gw,26657
35
35
  cmdbox/app/features/cli/audit_base.py,sha256=wWWK31EUczvP4a1MNuz2KlqwaSn9yyt908ueRvjkuow,9405
36
36
  cmdbox/app/features/cli/cmdbox_audit_createdb.py,sha256=Hmq3NVrAUBbj-UB9fum2TuWU7MIH71754S_s0X9oj40,12133
37
37
  cmdbox/app/features/cli/cmdbox_audit_delete.py,sha256=2X6A8XtViOnUH7iDj02ZrqQhYBq7WOAwi-nL10der64,18922
38
- cmdbox/app/features/cli/cmdbox_audit_search.py,sha256=Ib09SNAJ8lwkM6hiqn6knvN469PGaR9GQBJBIxRoeJM,28172
38
+ cmdbox/app/features/cli/cmdbox_audit_search.py,sha256=QgO3cbLnl8Bc3ymJAsjbB0KV5NKpBzOQ-V7pTB9-uMA,29022
39
39
  cmdbox/app/features/cli/cmdbox_audit_write.py,sha256=eKFQPsKQGSIKSER6KxdKEEXTGGyKnAmcgPkXU1ydJR0,14505
40
40
  cmdbox/app/features/cli/cmdbox_client_file_copy.py,sha256=4UsPN1ZUhC39Vc-OrwfnCmSgOWb6LS71x4HU8c6vWeI,13093
41
41
  cmdbox/app/features/cli/cmdbox_client_file_download.py,sha256=wgMiM10Z2pzkXCwIOxw-H19W6CFznHxFpFOitPp-MeQ,13239
@@ -46,8 +46,8 @@ cmdbox/app/features/cli/cmdbox_client_file_remove.py,sha256=e7e8iG-SBYbSZEwEsLaE
46
46
  cmdbox/app/features/cli/cmdbox_client_file_rmdir.py,sha256=pNrJ3YW8GSNUd7mXFt_HOHOQSehv9zYIdwwGLdUCHIA,11530
47
47
  cmdbox/app/features/cli/cmdbox_client_file_upload.py,sha256=X216q8dVAs_iuoOLUQ3wE-adD6QlHJJ9KVddCSxZhs0,13407
48
48
  cmdbox/app/features/cli/cmdbox_client_server_info.py,sha256=Nnnu4bcKyHU6drHvebIDIiu0PznqdDks0OartZgnVEM,9575
49
- cmdbox/app/features/cli/cmdbox_cmd_list.py,sha256=pyC69lYILVai2CPQA0tJ8I6VD4BUeWasowltDTfgD3Q,6570
50
- cmdbox/app/features/cli/cmdbox_cmd_load.py,sha256=FCdaoW6oIudQZL3i91IcS5X4_P2Uxy9xJHunBkUCyik,6356
49
+ cmdbox/app/features/cli/cmdbox_cmd_list.py,sha256=XGsUjwpFhSofsJQE27LWdY0r4gOyCY-MFv_LP_Asixw,6604
50
+ cmdbox/app/features/cli/cmdbox_cmd_load.py,sha256=JYCRg5JREhRgMLTlkFDrzlc7SiDA14NL2fXkMEwLtEo,6390
51
51
  cmdbox/app/features/cli/cmdbox_edge_config.py,sha256=KGqBXFQzjlYesTsP5JpeXvAsehdATHWlny5Ujnx7fUw,9460
52
52
  cmdbox/app/features/cli/cmdbox_edge_start.py,sha256=rtlYxFkW3Ud5sBKiuk8NWZ3DVXV-3T3KH4i5qwKxP_4,3824
53
53
  cmdbox/app/features/cli/cmdbox_gui_start.py,sha256=dzoso_s8-eYvL2YOK0hlJUG46LFbVNyZmSoJhpyxjg0,1390
@@ -55,21 +55,21 @@ cmdbox/app/features/cli/cmdbox_gui_stop.py,sha256=IRr73L2wIGCq9Om6aHgkgsZHhtA5CV
55
55
  cmdbox/app/features/cli/cmdbox_server_list.py,sha256=bGtV6YETNrZBVg4s-OGuZQAAppBkeJ1pqYVyJvYPGf4,5777
56
56
  cmdbox/app/features/cli/cmdbox_server_start.py,sha256=RHxK6IoSe2qeeLp-fY0OdwXKhr5zF99NritYQPGM9_4,7280
57
57
  cmdbox/app/features/cli/cmdbox_server_stop.py,sha256=PqC8TRlAbfS-ZMkiyqW-xqnqVKOdIeKNICl36A9hrw0,8644
58
- cmdbox/app/features/cli/cmdbox_web_apikey_add.py,sha256=aBj4JrkET9szf9cQQjBWoTGOCI8oQt8setJvNbdVIyA,7105
59
- cmdbox/app/features/cli/cmdbox_web_apikey_del.py,sha256=YhzmMlJhAN60WhJh1Ks_NI-pLdBydmNFFNoYg5H4Noo,7083
58
+ cmdbox/app/features/cli/cmdbox_web_apikey_add.py,sha256=ZFIiDCR7KEhHCvCkVKwFKaLDcYDnAzRrB8Ajnmc34_o,7139
59
+ cmdbox/app/features/cli/cmdbox_web_apikey_del.py,sha256=Ciw1nPw-wdzhUELO_pU4_tAOKPb3fF2rPkEqlk_PqfI,7117
60
60
  cmdbox/app/features/cli/cmdbox_web_gencert.py,sha256=mnGv18yUBkCuwhy-k-2Q6uTz0MNDBGhdWdpcHKCc_TE,9323
61
61
  cmdbox/app/features/cli/cmdbox_web_genpass.py,sha256=8eJTCRtZJLuj7d5NaZhJ326oCsPH1bGmfy4Pw363NLA,9541
62
- cmdbox/app/features/cli/cmdbox_web_group_add.py,sha256=aqeZ2BDFqRFzVG5sHKFAKuFXAat44_jwoPZkGVImmEo,7373
63
- cmdbox/app/features/cli/cmdbox_web_group_del.py,sha256=wpi2LAX7Xs12jk98GRQXETd-sX9WzsocQsCX422JJz4,6583
64
- cmdbox/app/features/cli/cmdbox_web_group_edit.py,sha256=ohlJCk1c_zbRq73udSUKsg-U5pQgdvAxe-U8GjRvy34,7312
65
- cmdbox/app/features/cli/cmdbox_web_group_list.py,sha256=LyBsGtHZjqqr7YT-7Fa2cMqu8uY_wCmfYXbm4Z9ea0s,6678
66
- cmdbox/app/features/cli/cmdbox_web_start.py,sha256=um8okOGRaXS-6e9KSACtdYRPctnE_avSCj0agt1HCRY,16293
62
+ cmdbox/app/features/cli/cmdbox_web_group_add.py,sha256=N_MUGdi2wckZe4UCU1Kgm_-nwDNHMCtrfDWWY1SVcZY,7407
63
+ cmdbox/app/features/cli/cmdbox_web_group_del.py,sha256=d7j6UaxepvjOr6SEy_biNT4nSp7gFjLEo2UqNmAYNjM,6617
64
+ cmdbox/app/features/cli/cmdbox_web_group_edit.py,sha256=8WQGtKtMdnGBhgI8_ZwUfenWLQGwj678daW5madZ_WY,7346
65
+ cmdbox/app/features/cli/cmdbox_web_group_list.py,sha256=1UlFEFf9GA248qe0o_LjnnUPE8BwGxnhRgY7aBTThfg,6712
66
+ cmdbox/app/features/cli/cmdbox_web_start.py,sha256=bW6JrnNC-sskNFfFp3zCG6K-ydqRPAzdrgRbk3Keeh0,17531
67
67
  cmdbox/app/features/cli/cmdbox_web_stop.py,sha256=yRLHhCR1sQvNep4-9-OITQkzeEQr1M-ZSLzd059D3EA,3568
68
- cmdbox/app/features/cli/cmdbox_web_user_add.py,sha256=FrbFXK0fVwPI5YR1uEsGQgNX3NLsfkPJ7gBfl6rjJkI,8565
69
- cmdbox/app/features/cli/cmdbox_web_user_del.py,sha256=bJ8Lqev4jHU68D5eYBaxkPWxmtTxWpN7sKeCJEZTIJc,6546
70
- cmdbox/app/features/cli/cmdbox_web_user_edit.py,sha256=IAcZe9J0NHXwy-7Jk656HZXlV-RLYcWtqsXYJDFMwvQ,8473
71
- cmdbox/app/features/cli/cmdbox_web_user_list.py,sha256=t2q6RthbKy0awcZR45BxRHgF-D2mOvRNC1T9thavTn4,6666
72
- cmdbox/app/features/web/cmdbox_web_agent.py,sha256=6VUqMCJxdCz4vT5gGGMHlUibjtAy8FSwQVQ0-q74XfA,14272
68
+ cmdbox/app/features/cli/cmdbox_web_user_add.py,sha256=8STmtxwiCYuClcYGzK_kt7hzhxyFI7ZI0Lg1gpTiSY4,8599
69
+ cmdbox/app/features/cli/cmdbox_web_user_del.py,sha256=2De84-SMYnOhl32tICUYe02g7m4EI991ZP9GGsfN4xU,6580
70
+ cmdbox/app/features/cli/cmdbox_web_user_edit.py,sha256=yvZ2oGYv9QVEg2tUiQkS1RpaCqG4CkpWhDrzIAsETAE,8507
71
+ cmdbox/app/features/cli/cmdbox_web_user_list.py,sha256=xzSJvhICU5cXzsQ5krJUFryhSWlsfObI3slUyRDDADU,6700
72
+ cmdbox/app/features/web/cmdbox_web_agent.py,sha256=uqdjhbnWHy5LSFc0pmDSYUun083Crb8UJRuINFPw4A8,14691
73
73
  cmdbox/app/features/web/cmdbox_web_assets.py,sha256=D1dYNrvC7xBAVAAHX6PkoB6RFehEofET4hkHeCFYq7s,1931
74
74
  cmdbox/app/features/web/cmdbox_web_audit.py,sha256=-flyijdnh3hN-BKnw0GI3gSiduLP0vQFg_AiYCLXRBY,3359
75
75
  cmdbox/app/features/web/cmdbox_web_audit_metrics.py,sha256=4zcWNcI_mCT36B0CNrtYz9rOAK6QXqyLzloFIgginxw,3189
@@ -301,16 +301,16 @@ cmdbox/web/assets/apexcharts/apexcharts.css,sha256=l-xkqykcV8a22g04B-Vpt4JFWcHlw
301
301
  cmdbox/web/assets/apexcharts/apexcharts.min.js,sha256=zceUTsCKa8Y2SqjqZjLjifXQDnqsvKRTmT8fTIUix_4,570304
302
302
  cmdbox/web/assets/bootstrap/bootstrap.bundle.min.5.3.0.js,sha256=qlPVgvl-tZTCpcxYJFdHB_m6mDe84wRr-l81VoYPTgQ,80421
303
303
  cmdbox/web/assets/bootstrap/bootstrap.min.5.3.0.css,sha256=fx038NkLY4U1TCrBDiu5FWPEa9eiZu01EiLryshJbCo,232914
304
- cmdbox/web/assets/cmdbox/agent.js,sha256=hNkBdOCHMh9UItcoxeSuO_LxtpN6bRIyNmXIqPZ2w5Q,15839
304
+ cmdbox/web/assets/cmdbox/agent.js,sha256=NJwNfnIzTP23qGsuQr7ov-u4Mo9ibU7h3FN66-yTEpA,15763
305
305
  cmdbox/web/assets/cmdbox/audit.js,sha256=5wFt4ejO4bwrOpt9yxOiwUz5XXXLOP8eo0ITTUwROZ0,19332
306
306
  cmdbox/web/assets/cmdbox/color_mode.css,sha256=U4UGBnWiBMcrSEEusgT-_o-pt4MP3myoA9Lgnn1g6qE,19803
307
- cmdbox/web/assets/cmdbox/common.js,sha256=s32LMd2eXdLE7DYlnX5HCdkCYcZm_pnpfxjICt3zA6A,65220
307
+ cmdbox/web/assets/cmdbox/common.js,sha256=q0CvDq3sFuz381CyxjogQJrdS9o1R7h1vccE1ih3jRk,66748
308
308
  cmdbox/web/assets/cmdbox/favicon.ico,sha256=2U4MhqzJklRksOQwnK-MZigZCubxCHqKG_AuNJnvYtA,34494
309
309
  cmdbox/web/assets/cmdbox/filer_modal.js,sha256=iKhNN9urjUm22na4vHYWhbj__2De9iAuDJE7TvBWtQ0,8566
310
310
  cmdbox/web/assets/cmdbox/icon.png,sha256=xdEwDdCS8CoPQk7brW-1mV8FIGYtUeSMBRlY9Oh-3nE,296172
311
311
  cmdbox/web/assets/cmdbox/list_cmd.js,sha256=4oVpXoSaUWXHKkxxCBEjoHgtrjTP1FSsL3QEHwjtaKc,21437
312
312
  cmdbox/web/assets/cmdbox/list_pipe.js,sha256=2rPkauw9VHRMXl76qRgqy81Y815Y8B1tnVKOSKKfRwc,11315
313
- cmdbox/web/assets/cmdbox/main.js,sha256=JRrFgjBEu66uMPmDHCrsvQFatl39YHEgR9BHpzHxbv4,5737
313
+ cmdbox/web/assets/cmdbox/main.js,sha256=UCI2m8GT0Ba6dQtmwBwZMUGXihYGJpFKd4xqxJaGCo8,5659
314
314
  cmdbox/web/assets/cmdbox/open_capture.js,sha256=W4IQlOYLN4Y8OaS8Xc5yp-BRlm82TVjujChr3hJKS0M,709
315
315
  cmdbox/web/assets/cmdbox/open_output_json.js,sha256=4q7mCdVmSzFudlTlW9MuIJ1-f-kDvpD6rDUU01IbKi8,727
316
316
  cmdbox/web/assets/cmdbox/result.js,sha256=m7u6-VTXT4AK_6frn4dTIjEzDg0bfajD_Jv1MWgLZRo,3154
@@ -360,9 +360,9 @@ cmdbox/web/assets/tree-menu/image/file.png,sha256=Uw4zYkHyuoZ_kSVkesHAeSeA_g9_LP
360
360
  cmdbox/web/assets/tree-menu/image/folder-close.png,sha256=TcgsKTBBF2ejgzekOEDBFBxsJf-Z5u0x9IZVi4GBR-I,284
361
361
  cmdbox/web/assets/tree-menu/image/folder-open.png,sha256=DT7y1GRK4oXJkFvqTN_oSGM5ZYARzPvjoCGL6wqkoo0,301
362
362
  cmdbox/web/assets/tree-menu/js/tree-menu.js,sha256=-GkZxI7xzHuXXHYQBHAVTcuKX4TtoiMuyIms6Xc3pxk,1029
363
- cmdbox-0.6.0.2.dist-info/LICENSE,sha256=sBzzPc5v-5LBuIFi2V4olsnoVg-3EBI0zRX5r19SOxE,1117
364
- cmdbox-0.6.0.2.dist-info/METADATA,sha256=dA13ULZvQUvcYO0qMrgWC5iRDU42WzCIz5XCjY2mIsY,30881
365
- cmdbox-0.6.0.2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
366
- cmdbox-0.6.0.2.dist-info/entry_points.txt,sha256=1LdoMUjTD_YdxlsAiAiJ1cREcXFG8-Xg2xQTNYoNpT4,47
367
- cmdbox-0.6.0.2.dist-info/top_level.txt,sha256=eMEkD5jn8_0PkCAL8h5xJu4qAzF2O8Wf3vegFkKUXR4,7
368
- cmdbox-0.6.0.2.dist-info/RECORD,,
363
+ cmdbox-0.6.0.4.dist-info/LICENSE,sha256=sBzzPc5v-5LBuIFi2V4olsnoVg-3EBI0zRX5r19SOxE,1117
364
+ cmdbox-0.6.0.4.dist-info/METADATA,sha256=ffkuAk5l_kg_k2YURFYc3bOkhDP8NEUqtr_LENF1JfU,30889
365
+ cmdbox-0.6.0.4.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
366
+ cmdbox-0.6.0.4.dist-info/entry_points.txt,sha256=1LdoMUjTD_YdxlsAiAiJ1cREcXFG8-Xg2xQTNYoNpT4,47
367
+ cmdbox-0.6.0.4.dist-info/top_level.txt,sha256=eMEkD5jn8_0PkCAL8h5xJu4qAzF2O8Wf3vegFkKUXR4,7
368
+ cmdbox-0.6.0.4.dist-info/RECORD,,