mcp2cli 2.4.2__tar.gz → 2.5.0__tar.gz
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.
- {mcp2cli-2.4.2 → mcp2cli-2.5.0}/PKG-INFO +1 -1
- {mcp2cli-2.4.2 → mcp2cli-2.5.0}/pyproject.toml +1 -1
- {mcp2cli-2.4.2 → mcp2cli-2.5.0}/src/mcp2cli/__init__.py +145 -26
- {mcp2cli-2.4.2 → mcp2cli-2.5.0}/README.md +0 -0
- {mcp2cli-2.4.2 → mcp2cli-2.5.0}/src/mcp2cli/__main__.py +0 -0
- {mcp2cli-2.4.2 → mcp2cli-2.5.0}/src/mcp2cli/py.typed +0 -0
|
@@ -466,11 +466,18 @@ def build_oauth_provider(
|
|
|
466
466
|
client_id: str | None = None,
|
|
467
467
|
client_secret: str | None = None,
|
|
468
468
|
scope: str | None = None,
|
|
469
|
+
redirect_uri: str | None = None,
|
|
469
470
|
) -> "httpx.Auth":
|
|
470
471
|
"""Build an OAuth provider for HTTP connections.
|
|
471
472
|
|
|
472
|
-
|
|
473
|
-
|
|
473
|
+
- client_id + client_secret → client credentials flow (machine-to-machine).
|
|
474
|
+
- client_id only → authorization code + PKCE, pre-configured client
|
|
475
|
+
(no dynamic client registration).
|
|
476
|
+
- neither → authorization code + PKCE with dynamic client
|
|
477
|
+
registration.
|
|
478
|
+
|
|
479
|
+
redirect_uri controls the full callback URL (scheme, host, port, path).
|
|
480
|
+
When None, defaults to http://127.0.0.1:<random-free-port>/callback.
|
|
474
481
|
"""
|
|
475
482
|
storage = FileTokenStorage(server_url)
|
|
476
483
|
|
|
@@ -488,10 +495,39 @@ def build_oauth_provider(
|
|
|
488
495
|
)
|
|
489
496
|
|
|
490
497
|
from mcp.client.auth.oauth2 import OAuthClientProvider
|
|
491
|
-
from mcp.shared.auth import OAuthClientMetadata
|
|
498
|
+
from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata
|
|
499
|
+
|
|
500
|
+
_LOOPBACK_HOSTS = {"localhost", "127.0.0.1", "::1"}
|
|
492
501
|
|
|
493
|
-
|
|
494
|
-
|
|
502
|
+
if redirect_uri is not None:
|
|
503
|
+
parsed = urlparse(redirect_uri)
|
|
504
|
+
if parsed.scheme != "http":
|
|
505
|
+
print(
|
|
506
|
+
f"Error: --oauth-redirect-uri must use http://, got '{parsed.scheme}://'. "
|
|
507
|
+
"The local callback server is plain HTTP; use http://<host>:<port>/path.",
|
|
508
|
+
file=sys.stderr,
|
|
509
|
+
)
|
|
510
|
+
sys.exit(1)
|
|
511
|
+
if parsed.port is None:
|
|
512
|
+
print(
|
|
513
|
+
"Error: --oauth-redirect-uri must include an explicit port number "
|
|
514
|
+
"(e.g. http://localhost:3334/oauth/callback).",
|
|
515
|
+
file=sys.stderr,
|
|
516
|
+
)
|
|
517
|
+
sys.exit(1)
|
|
518
|
+
if (parsed.hostname or "") not in _LOOPBACK_HOSTS:
|
|
519
|
+
print(
|
|
520
|
+
f"Error: --oauth-redirect-uri host must be a loopback address "
|
|
521
|
+
f"(localhost, 127.0.0.1, or ::1), got '{parsed.hostname}'.",
|
|
522
|
+
file=sys.stderr,
|
|
523
|
+
)
|
|
524
|
+
sys.exit(1)
|
|
525
|
+
callback_host = parsed.hostname
|
|
526
|
+
port = parsed.port
|
|
527
|
+
else:
|
|
528
|
+
port = _find_free_port()
|
|
529
|
+
callback_host = "127.0.0.1"
|
|
530
|
+
redirect_uri = f"http://127.0.0.1:{port}/callback"
|
|
495
531
|
|
|
496
532
|
client_metadata = OAuthClientMetadata(
|
|
497
533
|
redirect_uris=[redirect_uri],
|
|
@@ -500,13 +536,36 @@ def build_oauth_provider(
|
|
|
500
536
|
scope=scope,
|
|
501
537
|
)
|
|
502
538
|
|
|
539
|
+
if client_id:
|
|
540
|
+
# Pre-seed storage with the caller-supplied client_id so the OAuth
|
|
541
|
+
# provider skips dynamic client registration entirely. The write is
|
|
542
|
+
# synchronous (plain file I/O) so no async context is needed here.
|
|
543
|
+
pre_client_info = OAuthClientInformationFull(
|
|
544
|
+
client_id=client_id,
|
|
545
|
+
client_secret=None,
|
|
546
|
+
token_endpoint_auth_method="none",
|
|
547
|
+
redirect_uris=[redirect_uri],
|
|
548
|
+
grant_types=["authorization_code", "refresh_token"],
|
|
549
|
+
response_types=["code"],
|
|
550
|
+
scope=scope,
|
|
551
|
+
)
|
|
552
|
+
storage._client_path.write_text(pre_client_info.model_dump_json())
|
|
553
|
+
|
|
503
554
|
# Reset callback handler state
|
|
504
555
|
_CallbackHandler.auth_code = None
|
|
505
556
|
_CallbackHandler.state = None
|
|
506
557
|
_CallbackHandler.error = None
|
|
507
558
|
_CallbackHandler.done = threading.Event()
|
|
508
559
|
|
|
509
|
-
|
|
560
|
+
if callback_host == "::1":
|
|
561
|
+
import socket as _socket
|
|
562
|
+
|
|
563
|
+
class _IPv6HTTPServer(HTTPServer):
|
|
564
|
+
address_family = _socket.AF_INET6
|
|
565
|
+
|
|
566
|
+
server = _IPv6HTTPServer((callback_host, port), _CallbackHandler)
|
|
567
|
+
else:
|
|
568
|
+
server = HTTPServer((callback_host, port), _CallbackHandler)
|
|
510
569
|
|
|
511
570
|
async def redirect_handler(auth_url: str) -> None:
|
|
512
571
|
print(f"Opening browser for authorization...", file=sys.stderr)
|
|
@@ -1102,8 +1161,28 @@ def extract_graphql_commands(schema: dict) -> list[CommandDef]:
|
|
|
1102
1161
|
return commands
|
|
1103
1162
|
|
|
1104
1163
|
|
|
1105
|
-
def
|
|
1164
|
+
def _truncate_description(description: str, max_len: int) -> str:
|
|
1165
|
+
"""Truncate at a word boundary and append '...'."""
|
|
1166
|
+
if len(description) <= max_len:
|
|
1167
|
+
return description
|
|
1168
|
+
return description[:max_len].rsplit(" ", 1)[0].rstrip() + "..."
|
|
1169
|
+
|
|
1170
|
+
|
|
1171
|
+
def _wrap_description(description: str, indent: int, total_width: int = 110) -> str:
|
|
1172
|
+
"""Wrap long descriptions; indent continuation lines to align with description column."""
|
|
1173
|
+
import textwrap
|
|
1174
|
+
return textwrap.fill(
|
|
1175
|
+
description,
|
|
1176
|
+
width=total_width,
|
|
1177
|
+
subsequent_indent=" " * indent,
|
|
1178
|
+
break_long_words=False,
|
|
1179
|
+
break_on_hyphens=False,
|
|
1180
|
+
)
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
def list_graphql_commands(commands: list[CommandDef], verbose: bool = False):
|
|
1106
1184
|
"""Group commands by operation type and print."""
|
|
1185
|
+
|
|
1107
1186
|
groups: dict[str, list[CommandDef]] = {}
|
|
1108
1187
|
for cmd in commands:
|
|
1109
1188
|
key = cmd.graphql_operation_type or "other"
|
|
@@ -1116,7 +1195,14 @@ def list_graphql_commands(commands: list[CommandDef]):
|
|
|
1116
1195
|
label = "queries" if group == "query" else "mutations"
|
|
1117
1196
|
print(f"\n{label}:")
|
|
1118
1197
|
for cmd in cmds:
|
|
1119
|
-
|
|
1198
|
+
if cmd.description:
|
|
1199
|
+
if verbose:
|
|
1200
|
+
wrapped = _wrap_description(cmd.description, indent=42, total_width=100)
|
|
1201
|
+
desc = f" {wrapped}"
|
|
1202
|
+
else:
|
|
1203
|
+
desc = f" {_truncate_description(cmd.description, 60)}"
|
|
1204
|
+
else:
|
|
1205
|
+
desc = ""
|
|
1120
1206
|
print(f" {cmd.name:<40}{desc}")
|
|
1121
1207
|
|
|
1122
1208
|
|
|
@@ -1232,18 +1318,19 @@ def handle_graphql(
|
|
|
1232
1318
|
oauth_provider: "httpx.Auth | None" = None,
|
|
1233
1319
|
jq_expr: str | None = None,
|
|
1234
1320
|
head: int | None = None,
|
|
1321
|
+
verbose: bool = False,
|
|
1235
1322
|
):
|
|
1236
1323
|
"""Top-level handler for --graphql mode."""
|
|
1237
1324
|
schema = load_graphql_schema(url, auth_headers, cache_key, ttl, refresh, oauth_provider=oauth_provider)
|
|
1238
1325
|
commands = extract_graphql_commands(schema)
|
|
1239
1326
|
|
|
1240
1327
|
if list_mode:
|
|
1241
|
-
list_graphql_commands(commands)
|
|
1328
|
+
list_graphql_commands(commands, verbose=verbose)
|
|
1242
1329
|
return
|
|
1243
1330
|
|
|
1244
1331
|
if not remaining:
|
|
1245
1332
|
print("Available operations:")
|
|
1246
|
-
list_graphql_commands(commands)
|
|
1333
|
+
list_graphql_commands(commands, verbose=verbose)
|
|
1247
1334
|
print("\nUse --list for the same output, or provide a subcommand.")
|
|
1248
1335
|
return
|
|
1249
1336
|
|
|
@@ -1353,6 +1440,8 @@ def _baked_to_argv(config: dict) -> list[str]:
|
|
|
1353
1440
|
argv += ["--oauth-client-secret", config["oauth_client_secret"]]
|
|
1354
1441
|
if config.get("oauth_scope"):
|
|
1355
1442
|
argv += ["--oauth-scope", config["oauth_scope"]]
|
|
1443
|
+
if config.get("oauth_redirect_uri"):
|
|
1444
|
+
argv += ["--oauth-redirect-uri", config["oauth_redirect_uri"]]
|
|
1356
1445
|
return argv
|
|
1357
1446
|
|
|
1358
1447
|
|
|
@@ -1400,6 +1489,7 @@ def _bake_create(argv: list[str]) -> None:
|
|
|
1400
1489
|
p.add_argument("--oauth-client-id", default=None)
|
|
1401
1490
|
p.add_argument("--oauth-client-secret", default=None)
|
|
1402
1491
|
p.add_argument("--oauth-scope", default=None)
|
|
1492
|
+
p.add_argument("--oauth-redirect-uri", default=None, metavar="URI")
|
|
1403
1493
|
p.add_argument("--include", default="", help="Comma-separated include globs")
|
|
1404
1494
|
p.add_argument("--exclude", default="", help="Comma-separated exclude globs")
|
|
1405
1495
|
p.add_argument("--methods", default="", help="Comma-separated HTTP methods")
|
|
@@ -1453,6 +1543,7 @@ def _bake_create(argv: list[str]) -> None:
|
|
|
1453
1543
|
"oauth_client_id": args.oauth_client_id,
|
|
1454
1544
|
"oauth_client_secret": args.oauth_client_secret,
|
|
1455
1545
|
"oauth_scope": args.oauth_scope,
|
|
1546
|
+
"oauth_redirect_uri": args.oauth_redirect_uri,
|
|
1456
1547
|
"include": [x.strip() for x in args.include.split(",") if x.strip()],
|
|
1457
1548
|
"exclude": [x.strip() for x in args.exclude.split(",") if x.strip()],
|
|
1458
1549
|
"methods": [x.strip().upper() for x in args.methods.split(",") if x.strip()],
|
|
@@ -1658,7 +1749,7 @@ def build_argparse(
|
|
|
1658
1749
|
# ---------------------------------------------------------------------------
|
|
1659
1750
|
|
|
1660
1751
|
|
|
1661
|
-
def list_openapi_commands(commands: list[CommandDef]):
|
|
1752
|
+
def list_openapi_commands(commands: list[CommandDef], verbose: bool = False):
|
|
1662
1753
|
groups: dict[str, list[CommandDef]] = {}
|
|
1663
1754
|
for cmd in commands:
|
|
1664
1755
|
prefix = cmd.name.split("-", 1)[0] if "-" in cmd.name else "other"
|
|
@@ -1670,13 +1761,24 @@ def list_openapi_commands(commands: list[CommandDef]):
|
|
|
1670
1761
|
method = (cmd.method or "").upper()
|
|
1671
1762
|
line = f" {cmd.name:<45} {method:<6}"
|
|
1672
1763
|
if cmd.description:
|
|
1673
|
-
|
|
1764
|
+
if verbose:
|
|
1765
|
+
wrapped = _wrap_description(cmd.description, indent=54, total_width=110)
|
|
1766
|
+
line += f" {wrapped}"
|
|
1767
|
+
else:
|
|
1768
|
+
line += f" {_truncate_description(cmd.description, 60)}"
|
|
1674
1769
|
print(line)
|
|
1675
1770
|
|
|
1676
1771
|
|
|
1677
|
-
def list_mcp_commands(commands: list[CommandDef]):
|
|
1772
|
+
def list_mcp_commands(commands: list[CommandDef], verbose: bool = False):
|
|
1678
1773
|
for cmd in commands:
|
|
1679
|
-
|
|
1774
|
+
if cmd.description:
|
|
1775
|
+
if verbose:
|
|
1776
|
+
wrapped = _wrap_description(cmd.description, indent=42, total_width=110)
|
|
1777
|
+
desc = f" {wrapped}"
|
|
1778
|
+
else:
|
|
1779
|
+
desc = f" {_truncate_description(cmd.description, 70)}"
|
|
1780
|
+
else:
|
|
1781
|
+
desc = ""
|
|
1680
1782
|
print(f" {cmd.name:<40}{desc}")
|
|
1681
1783
|
|
|
1682
1784
|
|
|
@@ -1856,6 +1958,7 @@ def run_mcp_http(
|
|
|
1856
1958
|
search_pattern: str | None = None,
|
|
1857
1959
|
jq_expr: str | None = None,
|
|
1858
1960
|
head: int | None = None,
|
|
1961
|
+
verbose: bool = False,
|
|
1859
1962
|
):
|
|
1860
1963
|
extra = dict(
|
|
1861
1964
|
resource_action=resource_action,
|
|
@@ -1866,6 +1969,7 @@ def run_mcp_http(
|
|
|
1866
1969
|
search_pattern=search_pattern,
|
|
1867
1970
|
jq_expr=jq_expr,
|
|
1868
1971
|
head=head,
|
|
1972
|
+
verbose=verbose,
|
|
1869
1973
|
)
|
|
1870
1974
|
|
|
1871
1975
|
async def _run():
|
|
@@ -1951,6 +2055,7 @@ def run_mcp_stdio(
|
|
|
1951
2055
|
search_pattern: str | None = None,
|
|
1952
2056
|
jq_expr: str | None = None,
|
|
1953
2057
|
head: int | None = None,
|
|
2058
|
+
verbose: bool = False,
|
|
1954
2059
|
):
|
|
1955
2060
|
extra = dict(
|
|
1956
2061
|
resource_action=resource_action,
|
|
@@ -1961,6 +2066,7 @@ def run_mcp_stdio(
|
|
|
1961
2066
|
search_pattern=search_pattern,
|
|
1962
2067
|
jq_expr=jq_expr,
|
|
1963
2068
|
head=head,
|
|
2069
|
+
verbose=verbose,
|
|
1964
2070
|
)
|
|
1965
2071
|
|
|
1966
2072
|
import anyio
|
|
@@ -2012,6 +2118,7 @@ async def _mcp_session(
|
|
|
2012
2118
|
search_pattern: str | None = None,
|
|
2013
2119
|
jq_expr: str | None = None,
|
|
2014
2120
|
head: int | None = None,
|
|
2121
|
+
verbose: bool = False,
|
|
2015
2122
|
):
|
|
2016
2123
|
# Handle resource operations
|
|
2017
2124
|
if resource_action:
|
|
@@ -2048,7 +2155,7 @@ async def _mcp_session(
|
|
|
2048
2155
|
print(f"\nTools matching '{search_pattern}':")
|
|
2049
2156
|
else:
|
|
2050
2157
|
print("\nAvailable tools:")
|
|
2051
|
-
list_mcp_commands(commands)
|
|
2158
|
+
list_mcp_commands(commands, verbose=verbose)
|
|
2052
2159
|
return
|
|
2053
2160
|
|
|
2054
2161
|
if tool_name is None:
|
|
@@ -2666,6 +2773,7 @@ def handle_mcp(
|
|
|
2666
2773
|
bake_config: BakeConfig | None = None,
|
|
2667
2774
|
jq_expr: str | None = None,
|
|
2668
2775
|
head: int | None = None,
|
|
2776
|
+
verbose: bool = False,
|
|
2669
2777
|
):
|
|
2670
2778
|
key = cache_key_override or cache_key_for(source)
|
|
2671
2779
|
|
|
@@ -2700,7 +2808,7 @@ def handle_mcp(
|
|
|
2700
2808
|
commands, bake_config.include, bake_config.exclude, bake_config.methods,
|
|
2701
2809
|
)
|
|
2702
2810
|
print("\nAvailable tools:")
|
|
2703
|
-
list_mcp_commands(commands)
|
|
2811
|
+
list_mcp_commands(commands, verbose=verbose)
|
|
2704
2812
|
return
|
|
2705
2813
|
_dispatch_mcp_call(
|
|
2706
2814
|
source, is_stdio, auth_headers, env_vars,
|
|
@@ -2708,6 +2816,7 @@ def handle_mcp(
|
|
|
2708
2816
|
toon=toon, transport=transport, oauth_provider=oauth_provider,
|
|
2709
2817
|
search_pattern=search_pattern,
|
|
2710
2818
|
jq_expr=jq_expr, head=head,
|
|
2819
|
+
verbose=verbose,
|
|
2711
2820
|
)
|
|
2712
2821
|
return
|
|
2713
2822
|
|
|
@@ -2725,7 +2834,7 @@ def handle_mcp(
|
|
|
2725
2834
|
|
|
2726
2835
|
if not remaining:
|
|
2727
2836
|
print("Available tools:")
|
|
2728
|
-
list_mcp_commands(commands)
|
|
2837
|
+
list_mcp_commands(commands, verbose=verbose)
|
|
2729
2838
|
print("\nUse --list for the same output, or provide a subcommand.")
|
|
2730
2839
|
return
|
|
2731
2840
|
|
|
@@ -2923,6 +3032,12 @@ def _build_main_parser() -> argparse.ArgumentParser:
|
|
|
2923
3032
|
metavar="PATTERN",
|
|
2924
3033
|
help="Search tools by name or description (case-insensitive substring match)",
|
|
2925
3034
|
)
|
|
3035
|
+
pre.add_argument(
|
|
3036
|
+
"--verbose",
|
|
3037
|
+
action="store_true",
|
|
3038
|
+
dest="verbose",
|
|
3039
|
+
help="Show full tool descriptions in --list output, wrapped to terminal width (default: truncated with ...)",
|
|
3040
|
+
)
|
|
2926
3041
|
pre.add_argument("--pretty", action="store_true", help="Pretty-print JSON output")
|
|
2927
3042
|
pre.add_argument("--raw", action="store_true", help="Print raw response body")
|
|
2928
3043
|
pre.add_argument(
|
|
@@ -2985,6 +3100,13 @@ def _build_main_parser() -> argparse.ArgumentParser:
|
|
|
2985
3100
|
default=None,
|
|
2986
3101
|
help="OAuth scope(s) to request",
|
|
2987
3102
|
)
|
|
3103
|
+
pre.add_argument(
|
|
3104
|
+
"--oauth-redirect-uri",
|
|
3105
|
+
default=None,
|
|
3106
|
+
metavar="URI",
|
|
3107
|
+
help="Full redirect URI for the OAuth callback (e.g. http://localhost:3334/oauth/callback). "
|
|
3108
|
+
"Overrides the default http://127.0.0.1:<random-port>/callback.",
|
|
3109
|
+
)
|
|
2988
3110
|
# Resource flags
|
|
2989
3111
|
pre.add_argument(
|
|
2990
3112
|
"--list-resources", action="store_true", help="List available resources"
|
|
@@ -3066,12 +3188,6 @@ def _setup_oauth(pre_args):
|
|
|
3066
3188
|
if not use_oauth:
|
|
3067
3189
|
return None
|
|
3068
3190
|
|
|
3069
|
-
if pre_args.oauth_client_id and not pre_args.oauth_client_secret:
|
|
3070
|
-
print(
|
|
3071
|
-
"Error: --oauth-client-secret is required with --oauth-client-id",
|
|
3072
|
-
file=sys.stderr,
|
|
3073
|
-
)
|
|
3074
|
-
sys.exit(1)
|
|
3075
3191
|
if pre_args.oauth_client_secret and not pre_args.oauth_client_id:
|
|
3076
3192
|
print(
|
|
3077
3193
|
"Error: --oauth-client-id is required with --oauth-client-secret",
|
|
@@ -3109,6 +3225,7 @@ def _setup_oauth(pre_args):
|
|
|
3109
3225
|
client_id=client_id,
|
|
3110
3226
|
client_secret=client_secret,
|
|
3111
3227
|
scope=pre_args.oauth_scope,
|
|
3228
|
+
redirect_uri=pre_args.oauth_redirect_uri,
|
|
3112
3229
|
)
|
|
3113
3230
|
|
|
3114
3231
|
|
|
@@ -3221,7 +3338,7 @@ def _handle_session_operations(
|
|
|
3221
3338
|
print(f"\nTools matching '{search_pattern}':")
|
|
3222
3339
|
else:
|
|
3223
3340
|
print("\nAvailable tools:")
|
|
3224
|
-
list_mcp_commands(commands)
|
|
3341
|
+
list_mcp_commands(commands, verbose=pre_args.verbose)
|
|
3225
3342
|
return True
|
|
3226
3343
|
|
|
3227
3344
|
# Tool call via session
|
|
@@ -3229,7 +3346,7 @@ def _handle_session_operations(
|
|
|
3229
3346
|
result = _session_request(sess_name, "list_tools")
|
|
3230
3347
|
commands = extract_mcp_commands(result)
|
|
3231
3348
|
print("Available tools:")
|
|
3232
|
-
list_mcp_commands(commands)
|
|
3349
|
+
list_mcp_commands(commands, verbose=pre_args.verbose)
|
|
3233
3350
|
print("\nUse --list for the same output, or provide a subcommand.")
|
|
3234
3351
|
return True
|
|
3235
3352
|
|
|
@@ -3326,7 +3443,7 @@ def _handle_openapi_mode(
|
|
|
3326
3443
|
print(f"\nNo tools matching '{search_pattern}'.")
|
|
3327
3444
|
return
|
|
3328
3445
|
print(f"\nTools matching '{search_pattern}':")
|
|
3329
|
-
list_openapi_commands(commands)
|
|
3446
|
+
list_openapi_commands(commands, verbose=pre_args.verbose)
|
|
3330
3447
|
return
|
|
3331
3448
|
|
|
3332
3449
|
if not remaining:
|
|
@@ -3428,6 +3545,7 @@ def _main_impl(argv: list[str], bake_config: BakeConfig | None = None):
|
|
|
3428
3545
|
oauth_provider=oauth_provider,
|
|
3429
3546
|
jq_expr=pre_args.jq,
|
|
3430
3547
|
head=pre_args.head,
|
|
3548
|
+
verbose=pre_args.verbose,
|
|
3431
3549
|
)
|
|
3432
3550
|
return
|
|
3433
3551
|
|
|
@@ -3459,6 +3577,7 @@ def _main_impl(argv: list[str], bake_config: BakeConfig | None = None):
|
|
|
3459
3577
|
bake_config=bake_config,
|
|
3460
3578
|
jq_expr=pre_args.jq,
|
|
3461
3579
|
head=pre_args.head,
|
|
3580
|
+
verbose=pre_args.verbose,
|
|
3462
3581
|
)
|
|
3463
3582
|
return
|
|
3464
3583
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|