webscout 8.3.7__py3-none-any.whl → 2025.10.11__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 webscout might be problematic. Click here for more details.
- webscout/AIauto.py +250 -250
- webscout/AIbase.py +379 -379
- webscout/AIutel.py +60 -60
- webscout/Bard.py +1012 -1012
- webscout/Bing_search.py +417 -417
- webscout/DWEBS.py +529 -529
- webscout/Extra/Act.md +309 -309
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/README.md +110 -110
- webscout/Extra/GitToolkit/gitapi/__init__.py +11 -11
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -195
- webscout/Extra/GitToolkit/gitapi/user.py +96 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +61 -61
- webscout/Extra/YTToolkit/README.md +375 -375
- webscout/Extra/YTToolkit/YTdownloader.py +956 -956
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +475 -475
- webscout/Extra/YTToolkit/ytapi/README.md +44 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -6
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -118
- webscout/Extra/YTToolkit/ytapi/https.py +88 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/query.py +39 -39
- webscout/Extra/YTToolkit/ytapi/stream.py +62 -62
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +232 -232
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder.py +1105 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +332 -332
- webscout/Extra/gguf.md +429 -429
- webscout/Extra/gguf.py +1213 -1213
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +27 -27
- webscout/Extra/tempmail/async_utils.py +140 -140
- webscout/Extra/tempmail/base.py +160 -160
- webscout/Extra/tempmail/cli.py +186 -186
- webscout/Extra/tempmail/emailnator.py +84 -84
- webscout/Extra/tempmail/mail_tm.py +360 -360
- webscout/Extra/tempmail/temp_mail_io.py +291 -291
- webscout/Extra/weather.md +281 -281
- webscout/Extra/weather.py +193 -193
- webscout/Litlogger/README.md +10 -10
- webscout/Litlogger/__init__.py +15 -15
- webscout/Litlogger/formats.py +13 -13
- webscout/Litlogger/handlers.py +121 -121
- webscout/Litlogger/levels.py +13 -13
- webscout/Litlogger/logger.py +134 -134
- webscout/Provider/AISEARCH/Perplexity.py +332 -332
- webscout/Provider/AISEARCH/README.md +279 -279
- webscout/Provider/AISEARCH/__init__.py +16 -1
- webscout/Provider/AISEARCH/felo_search.py +206 -206
- webscout/Provider/AISEARCH/genspark_search.py +323 -323
- webscout/Provider/AISEARCH/hika_search.py +185 -185
- webscout/Provider/AISEARCH/iask_search.py +410 -410
- webscout/Provider/AISEARCH/monica_search.py +219 -219
- webscout/Provider/AISEARCH/scira_search.py +316 -316
- webscout/Provider/AISEARCH/stellar_search.py +177 -177
- webscout/Provider/AISEARCH/webpilotai_search.py +255 -255
- webscout/Provider/Aitopia.py +314 -314
- webscout/Provider/Apriel.py +306 -0
- webscout/Provider/ChatGPTClone.py +236 -236
- webscout/Provider/ChatSandbox.py +343 -343
- webscout/Provider/Cloudflare.py +324 -324
- webscout/Provider/Cohere.py +208 -208
- webscout/Provider/Deepinfra.py +370 -366
- webscout/Provider/ExaAI.py +260 -260
- webscout/Provider/ExaChat.py +308 -308
- webscout/Provider/Flowith.py +221 -221
- webscout/Provider/GMI.py +293 -0
- webscout/Provider/Gemini.py +164 -164
- webscout/Provider/GeminiProxy.py +167 -167
- webscout/Provider/GithubChat.py +371 -372
- webscout/Provider/Groq.py +800 -800
- webscout/Provider/HeckAI.py +383 -383
- webscout/Provider/Jadve.py +282 -282
- webscout/Provider/K2Think.py +307 -307
- webscout/Provider/Koboldai.py +205 -205
- webscout/Provider/LambdaChat.py +423 -423
- webscout/Provider/Nemotron.py +244 -244
- webscout/Provider/Netwrck.py +248 -248
- webscout/Provider/OLLAMA.py +395 -395
- webscout/Provider/OPENAI/Cloudflare.py +393 -393
- webscout/Provider/OPENAI/FalconH1.py +451 -451
- webscout/Provider/OPENAI/FreeGemini.py +296 -296
- webscout/Provider/OPENAI/K2Think.py +431 -431
- webscout/Provider/OPENAI/NEMOTRON.py +240 -240
- webscout/Provider/OPENAI/PI.py +427 -427
- webscout/Provider/OPENAI/README.md +959 -959
- webscout/Provider/OPENAI/TogetherAI.py +345 -345
- webscout/Provider/OPENAI/TwoAI.py +465 -465
- webscout/Provider/OPENAI/__init__.py +33 -18
- webscout/Provider/OPENAI/base.py +248 -248
- webscout/Provider/OPENAI/chatglm.py +528 -0
- webscout/Provider/OPENAI/chatgpt.py +592 -592
- webscout/Provider/OPENAI/chatgptclone.py +521 -521
- webscout/Provider/OPENAI/chatsandbox.py +202 -202
- webscout/Provider/OPENAI/deepinfra.py +318 -314
- webscout/Provider/OPENAI/e2b.py +1665 -1665
- webscout/Provider/OPENAI/exaai.py +420 -420
- webscout/Provider/OPENAI/exachat.py +452 -452
- webscout/Provider/OPENAI/friendli.py +232 -232
- webscout/Provider/OPENAI/{refact.py → gmi.py} +324 -274
- webscout/Provider/OPENAI/groq.py +364 -364
- webscout/Provider/OPENAI/heckai.py +314 -314
- webscout/Provider/OPENAI/llmchatco.py +337 -337
- webscout/Provider/OPENAI/netwrck.py +355 -355
- webscout/Provider/OPENAI/oivscode.py +290 -290
- webscout/Provider/OPENAI/opkfc.py +518 -518
- webscout/Provider/OPENAI/pydantic_imports.py +1 -1
- webscout/Provider/OPENAI/scirachat.py +535 -535
- webscout/Provider/OPENAI/sonus.py +308 -308
- webscout/Provider/OPENAI/standardinput.py +442 -442
- webscout/Provider/OPENAI/textpollinations.py +340 -340
- webscout/Provider/OPENAI/toolbaz.py +419 -416
- webscout/Provider/OPENAI/typefully.py +362 -362
- webscout/Provider/OPENAI/utils.py +295 -295
- webscout/Provider/OPENAI/venice.py +436 -436
- webscout/Provider/OPENAI/wisecat.py +387 -387
- webscout/Provider/OPENAI/writecream.py +166 -166
- webscout/Provider/OPENAI/x0gpt.py +378 -378
- webscout/Provider/OPENAI/yep.py +389 -389
- webscout/Provider/OpenGPT.py +230 -230
- webscout/Provider/Openai.py +243 -243
- webscout/Provider/PI.py +405 -405
- webscout/Provider/Perplexitylabs.py +430 -430
- webscout/Provider/QwenLM.py +272 -272
- webscout/Provider/STT/__init__.py +16 -1
- webscout/Provider/Sambanova.py +257 -257
- webscout/Provider/StandardInput.py +309 -309
- webscout/Provider/TTI/README.md +82 -82
- webscout/Provider/TTI/__init__.py +33 -18
- webscout/Provider/TTI/aiarta.py +413 -413
- webscout/Provider/TTI/base.py +136 -136
- webscout/Provider/TTI/bing.py +243 -243
- webscout/Provider/TTI/gpt1image.py +149 -149
- webscout/Provider/TTI/imagen.py +196 -196
- webscout/Provider/TTI/infip.py +211 -211
- webscout/Provider/TTI/magicstudio.py +232 -232
- webscout/Provider/TTI/monochat.py +219 -219
- webscout/Provider/TTI/piclumen.py +214 -214
- webscout/Provider/TTI/pixelmuse.py +232 -232
- webscout/Provider/TTI/pollinations.py +232 -232
- webscout/Provider/TTI/together.py +288 -288
- webscout/Provider/TTI/utils.py +12 -12
- webscout/Provider/TTI/venice.py +367 -367
- webscout/Provider/TTS/README.md +192 -192
- webscout/Provider/TTS/__init__.py +33 -18
- webscout/Provider/TTS/parler.py +110 -110
- webscout/Provider/TTS/streamElements.py +333 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TeachAnything.py +237 -237
- webscout/Provider/TextPollinationsAI.py +310 -310
- webscout/Provider/TogetherAI.py +356 -356
- webscout/Provider/TwoAI.py +312 -312
- webscout/Provider/TypliAI.py +311 -311
- webscout/Provider/UNFINISHED/ChatHub.py +208 -208
- webscout/Provider/UNFINISHED/ChutesAI.py +313 -313
- webscout/Provider/UNFINISHED/GizAI.py +294 -294
- webscout/Provider/UNFINISHED/Marcus.py +198 -198
- webscout/Provider/UNFINISHED/Qodo.py +477 -477
- webscout/Provider/UNFINISHED/VercelAIGateway.py +338 -338
- webscout/Provider/UNFINISHED/XenAI.py +324 -324
- webscout/Provider/UNFINISHED/Youchat.py +330 -330
- webscout/Provider/UNFINISHED/liner.py +334 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +262 -262
- webscout/Provider/UNFINISHED/puterjs.py +634 -634
- webscout/Provider/UNFINISHED/samurai.py +223 -223
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
- webscout/Provider/Venice.py +250 -250
- webscout/Provider/VercelAI.py +256 -256
- webscout/Provider/WiseCat.py +231 -231
- webscout/Provider/WrDoChat.py +366 -366
- webscout/Provider/__init__.py +33 -18
- webscout/Provider/ai4chat.py +174 -174
- webscout/Provider/akashgpt.py +331 -331
- webscout/Provider/cerebras.py +446 -446
- webscout/Provider/chatglm.py +394 -301
- webscout/Provider/cleeai.py +211 -211
- webscout/Provider/elmo.py +282 -282
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +261 -261
- webscout/Provider/hermes.py +263 -263
- webscout/Provider/julius.py +223 -223
- webscout/Provider/learnfastai.py +309 -309
- webscout/Provider/llama3mitril.py +214 -214
- webscout/Provider/llmchat.py +243 -243
- webscout/Provider/llmchatco.py +290 -290
- webscout/Provider/meta.py +801 -801
- webscout/Provider/oivscode.py +309 -309
- webscout/Provider/scira_chat.py +383 -383
- webscout/Provider/searchchat.py +292 -292
- webscout/Provider/sonus.py +258 -258
- webscout/Provider/toolbaz.py +370 -367
- webscout/Provider/turboseek.py +273 -273
- webscout/Provider/typefully.py +207 -207
- webscout/Provider/yep.py +372 -372
- webscout/__init__.py +30 -31
- webscout/__main__.py +5 -5
- webscout/auth/api_key_manager.py +189 -189
- webscout/auth/config.py +175 -175
- webscout/auth/models.py +185 -185
- webscout/auth/routes.py +664 -664
- webscout/auth/simple_logger.py +236 -236
- webscout/cli.py +523 -523
- webscout/conversation.py +438 -438
- webscout/exceptions.py +361 -361
- webscout/litagent/Readme.md +298 -298
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +581 -581
- webscout/litagent/constants.py +59 -59
- webscout/litprinter/__init__.py +58 -58
- webscout/models.py +181 -181
- webscout/optimizers.py +419 -419
- webscout/prompt_manager.py +288 -288
- webscout/sanitize.py +1078 -1078
- webscout/scout/README.md +401 -401
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +6 -6
- webscout/scout/core/crawler.py +297 -297
- webscout/scout/core/scout.py +706 -706
- webscout/scout/core/search_result.py +95 -95
- webscout/scout/core/text_analyzer.py +62 -62
- webscout/scout/core/text_utils.py +277 -277
- webscout/scout/core/web_analyzer.py +51 -51
- webscout/scout/element.py +599 -599
- webscout/scout/parsers/__init__.py +69 -69
- webscout/scout/parsers/html5lib_parser.py +172 -172
- webscout/scout/parsers/html_parser.py +236 -236
- webscout/scout/parsers/lxml_parser.py +178 -178
- webscout/scout/utils.py +37 -37
- webscout/swiftcli/Readme.md +323 -323
- webscout/swiftcli/__init__.py +95 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +308 -308
- webscout/swiftcli/core/context.py +104 -104
- webscout/swiftcli/core/group.py +241 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +221 -221
- webscout/swiftcli/decorators/options.py +220 -220
- webscout/swiftcli/decorators/output.py +302 -302
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +135 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +59 -59
- webscout/swiftcli/utils/formatting.py +252 -252
- webscout/swiftcli/utils/parsing.py +267 -267
- webscout/update_checker.py +117 -117
- webscout/version.py +1 -1
- webscout/webscout_search.py +1183 -1183
- webscout/webscout_search_async.py +649 -649
- webscout/yep_search.py +346 -346
- webscout/zeroart/README.md +89 -89
- webscout/zeroart/__init__.py +134 -134
- webscout/zeroart/base.py +66 -66
- webscout/zeroart/effects.py +100 -100
- webscout/zeroart/fonts.py +1238 -1238
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/METADATA +937 -937
- webscout-2025.10.11.dist-info/RECORD +300 -0
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/OPENAI/Qwen3.py +0 -303
- webscout/Provider/OPENAI/qodo.py +0 -630
- webscout/Provider/OPENAI/xenai.py +0 -514
- webscout/tempid.py +0 -134
- webscout-8.3.7.dist-info/RECORD +0 -301
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/WHEEL +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/top_level.txt +0 -0
webscout/swiftcli/core/group.py
CHANGED
|
@@ -1,241 +1,241 @@
|
|
|
1
|
-
"""Command group handling for SwiftCLI."""
|
|
2
|
-
|
|
3
|
-
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
from rich.console import Console
|
|
6
|
-
|
|
7
|
-
from ..exceptions import UsageError
|
|
8
|
-
from ..utils.formatting import format_error
|
|
9
|
-
from .context import Context
|
|
10
|
-
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from .cli import CLI
|
|
13
|
-
|
|
14
|
-
console = Console()
|
|
15
|
-
|
|
16
|
-
class Group:
|
|
17
|
-
"""
|
|
18
|
-
Command group that can contain subcommands.
|
|
19
|
-
|
|
20
|
-
Groups allow organizing related commands together and support command
|
|
21
|
-
chaining for building command pipelines.
|
|
22
|
-
|
|
23
|
-
Attributes:
|
|
24
|
-
name: Group name
|
|
25
|
-
help: Group description
|
|
26
|
-
commands: Registered commands
|
|
27
|
-
parent: Parent CLI instance
|
|
28
|
-
chain: Enable command chaining
|
|
29
|
-
invoke_without_command: Allow invoking group without subcommand
|
|
30
|
-
|
|
31
|
-
Example:
|
|
32
|
-
@app.group()
|
|
33
|
-
def db():
|
|
34
|
-
'''Database commands'''
|
|
35
|
-
pass
|
|
36
|
-
|
|
37
|
-
@db.command()
|
|
38
|
-
def migrate():
|
|
39
|
-
'''Run database migrations'''
|
|
40
|
-
print("Running migrations...")
|
|
41
|
-
"""
|
|
42
|
-
|
|
43
|
-
def __init__(
|
|
44
|
-
self,
|
|
45
|
-
name: str,
|
|
46
|
-
help: Optional[str] = None,
|
|
47
|
-
parent: Optional['CLI'] = None,
|
|
48
|
-
chain: bool = False,
|
|
49
|
-
invoke_without_command: bool = False
|
|
50
|
-
):
|
|
51
|
-
"""
|
|
52
|
-
Initialize command group.
|
|
53
|
-
|
|
54
|
-
Args:
|
|
55
|
-
name: Group name
|
|
56
|
-
help: Group description
|
|
57
|
-
parent: Parent CLI instance
|
|
58
|
-
chain: Enable command chaining
|
|
59
|
-
invoke_without_command: Allow invoking group without subcommand
|
|
60
|
-
"""
|
|
61
|
-
self.name = name
|
|
62
|
-
self.help = help
|
|
63
|
-
self.parent = parent
|
|
64
|
-
self.chain = chain
|
|
65
|
-
self.invoke_without_command = invoke_without_command
|
|
66
|
-
self.commands: Dict[str, Dict[str, Any]] = {}
|
|
67
|
-
|
|
68
|
-
def command(
|
|
69
|
-
self,
|
|
70
|
-
name: Optional[str] = None,
|
|
71
|
-
help: Optional[str] = None,
|
|
72
|
-
aliases: Optional[List[str]] = None,
|
|
73
|
-
hidden: bool = False
|
|
74
|
-
):
|
|
75
|
-
"""
|
|
76
|
-
Decorator to register a command in this group.
|
|
77
|
-
|
|
78
|
-
Args:
|
|
79
|
-
name: Command name (defaults to function name)
|
|
80
|
-
help: Command help text
|
|
81
|
-
aliases: Alternative command names
|
|
82
|
-
hidden: Hide from help output
|
|
83
|
-
|
|
84
|
-
Example:
|
|
85
|
-
@group.command()
|
|
86
|
-
def status():
|
|
87
|
-
'''Show status'''
|
|
88
|
-
print("Status: OK")
|
|
89
|
-
"""
|
|
90
|
-
def decorator(f):
|
|
91
|
-
cmd_name = name or f.__name__
|
|
92
|
-
self.commands[cmd_name] = {
|
|
93
|
-
'func': f,
|
|
94
|
-
'help': help or f.__doc__,
|
|
95
|
-
'aliases': aliases or [],
|
|
96
|
-
'hidden': hidden
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
# Register aliases
|
|
100
|
-
for alias in (aliases or []):
|
|
101
|
-
self.commands[alias] = self.commands[cmd_name]
|
|
102
|
-
|
|
103
|
-
return f
|
|
104
|
-
return decorator
|
|
105
|
-
|
|
106
|
-
def group(
|
|
107
|
-
self,
|
|
108
|
-
name: Optional[str] = None,
|
|
109
|
-
help: Optional[str] = None,
|
|
110
|
-
**kwargs
|
|
111
|
-
):
|
|
112
|
-
"""
|
|
113
|
-
Create a subgroup within this group.
|
|
114
|
-
|
|
115
|
-
Args:
|
|
116
|
-
name: Subgroup name
|
|
117
|
-
help: Subgroup help text
|
|
118
|
-
**kwargs: Additional group options
|
|
119
|
-
|
|
120
|
-
Example:
|
|
121
|
-
@group.group()
|
|
122
|
-
def config():
|
|
123
|
-
'''Configuration commands'''
|
|
124
|
-
pass
|
|
125
|
-
"""
|
|
126
|
-
def decorator(f):
|
|
127
|
-
subgroup = Group(
|
|
128
|
-
name=name or f.__name__,
|
|
129
|
-
help=help or f.__doc__,
|
|
130
|
-
parent=self.parent,
|
|
131
|
-
**kwargs
|
|
132
|
-
)
|
|
133
|
-
self.commands[subgroup.name] = subgroup
|
|
134
|
-
return subgroup
|
|
135
|
-
return decorator
|
|
136
|
-
|
|
137
|
-
def run(self, args: List[str]) -> int:
|
|
138
|
-
"""
|
|
139
|
-
Run a command in this group.
|
|
140
|
-
|
|
141
|
-
Args:
|
|
142
|
-
args: Command arguments
|
|
143
|
-
|
|
144
|
-
Returns:
|
|
145
|
-
Exit code (0 for success, non-zero for error)
|
|
146
|
-
"""
|
|
147
|
-
try:
|
|
148
|
-
# Show help if no arguments or help requested
|
|
149
|
-
if not args or args[0] in ['-h', '--help']:
|
|
150
|
-
self._print_help()
|
|
151
|
-
return 0
|
|
152
|
-
|
|
153
|
-
command_name = args[0]
|
|
154
|
-
command_args = args[1:]
|
|
155
|
-
|
|
156
|
-
# Check if command exists
|
|
157
|
-
if command_name not in self.commands:
|
|
158
|
-
format_error(f"Unknown command: {self.name} {command_name}")
|
|
159
|
-
self._print_help()
|
|
160
|
-
return 1
|
|
161
|
-
|
|
162
|
-
command = self.commands[command_name]
|
|
163
|
-
|
|
164
|
-
# Handle nested groups
|
|
165
|
-
if isinstance(command, Group):
|
|
166
|
-
return command.run(command_args)
|
|
167
|
-
|
|
168
|
-
# Create command context
|
|
169
|
-
ctx = Context(
|
|
170
|
-
self.parent,
|
|
171
|
-
command=f"{self.name} {command_name}",
|
|
172
|
-
debug=getattr(self.parent, 'debug', False)
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
# Run command through plugin system
|
|
176
|
-
if self.parent and not self.parent.plugin_manager.before_command(
|
|
177
|
-
f"{self.name} {command_name}",
|
|
178
|
-
command_args
|
|
179
|
-
):
|
|
180
|
-
return 1
|
|
181
|
-
|
|
182
|
-
try:
|
|
183
|
-
result = command['func'](**self._parse_args(command, command_args))
|
|
184
|
-
|
|
185
|
-
if self.parent:
|
|
186
|
-
self.parent.plugin_manager.after_command(
|
|
187
|
-
f"{self.name} {command_name}",
|
|
188
|
-
command_args,
|
|
189
|
-
result
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
# Handle command chaining
|
|
193
|
-
if self.chain and result is not None:
|
|
194
|
-
return result
|
|
195
|
-
|
|
196
|
-
return 0
|
|
197
|
-
|
|
198
|
-
except Exception as e:
|
|
199
|
-
if self.parent:
|
|
200
|
-
self.parent.plugin_manager.on_error(
|
|
201
|
-
f"{self.name} {command_name}",
|
|
202
|
-
e
|
|
203
|
-
)
|
|
204
|
-
if getattr(self.parent, 'debug', False):
|
|
205
|
-
raise
|
|
206
|
-
format_error(str(e))
|
|
207
|
-
return 1
|
|
208
|
-
|
|
209
|
-
except Exception as e:
|
|
210
|
-
if getattr(self.parent, 'debug', False):
|
|
211
|
-
raise
|
|
212
|
-
format_error(str(e))
|
|
213
|
-
return 1
|
|
214
|
-
|
|
215
|
-
def _parse_args(self, command: Dict[str, Any], args: List[str]) -> Dict[str, Any]:
|
|
216
|
-
"""Parse command arguments."""
|
|
217
|
-
# Use parent CLI's argument parser if available
|
|
218
|
-
if self.parent:
|
|
219
|
-
return self.parent._parse_args(command, args)
|
|
220
|
-
|
|
221
|
-
# Fallback to basic argument parsing
|
|
222
|
-
from ..utils.parsing import parse_args
|
|
223
|
-
return parse_args(args)
|
|
224
|
-
|
|
225
|
-
def _print_help(self) -> None:
|
|
226
|
-
"""Print group help message."""
|
|
227
|
-
console.print(f"\n[bold]{self.name}[/] - {self.help or ''}")
|
|
228
|
-
|
|
229
|
-
console.print("\n[bold]Commands:[/]")
|
|
230
|
-
for name, cmd in self.commands.items():
|
|
231
|
-
if isinstance(cmd, Group):
|
|
232
|
-
console.print(f" {name} [group]")
|
|
233
|
-
if cmd.help:
|
|
234
|
-
console.print(f" {cmd.help}")
|
|
235
|
-
elif not cmd.get('hidden', False):
|
|
236
|
-
console.print(f" {name:20} {cmd['help'] or ''}")
|
|
237
|
-
|
|
238
|
-
console.print("\nUse -h or --help with any command for more info")
|
|
239
|
-
|
|
240
|
-
def __repr__(self) -> str:
|
|
241
|
-
return f"<Group name={self.name}>"
|
|
1
|
+
"""Command group handling for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
|
|
7
|
+
from ..exceptions import UsageError
|
|
8
|
+
from ..utils.formatting import format_error
|
|
9
|
+
from .context import Context
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from .cli import CLI
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
class Group:
|
|
17
|
+
"""
|
|
18
|
+
Command group that can contain subcommands.
|
|
19
|
+
|
|
20
|
+
Groups allow organizing related commands together and support command
|
|
21
|
+
chaining for building command pipelines.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
name: Group name
|
|
25
|
+
help: Group description
|
|
26
|
+
commands: Registered commands
|
|
27
|
+
parent: Parent CLI instance
|
|
28
|
+
chain: Enable command chaining
|
|
29
|
+
invoke_without_command: Allow invoking group without subcommand
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
@app.group()
|
|
33
|
+
def db():
|
|
34
|
+
'''Database commands'''
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
@db.command()
|
|
38
|
+
def migrate():
|
|
39
|
+
'''Run database migrations'''
|
|
40
|
+
print("Running migrations...")
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
name: str,
|
|
46
|
+
help: Optional[str] = None,
|
|
47
|
+
parent: Optional['CLI'] = None,
|
|
48
|
+
chain: bool = False,
|
|
49
|
+
invoke_without_command: bool = False
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
Initialize command group.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
name: Group name
|
|
56
|
+
help: Group description
|
|
57
|
+
parent: Parent CLI instance
|
|
58
|
+
chain: Enable command chaining
|
|
59
|
+
invoke_without_command: Allow invoking group without subcommand
|
|
60
|
+
"""
|
|
61
|
+
self.name = name
|
|
62
|
+
self.help = help
|
|
63
|
+
self.parent = parent
|
|
64
|
+
self.chain = chain
|
|
65
|
+
self.invoke_without_command = invoke_without_command
|
|
66
|
+
self.commands: Dict[str, Dict[str, Any]] = {}
|
|
67
|
+
|
|
68
|
+
def command(
|
|
69
|
+
self,
|
|
70
|
+
name: Optional[str] = None,
|
|
71
|
+
help: Optional[str] = None,
|
|
72
|
+
aliases: Optional[List[str]] = None,
|
|
73
|
+
hidden: bool = False
|
|
74
|
+
):
|
|
75
|
+
"""
|
|
76
|
+
Decorator to register a command in this group.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
name: Command name (defaults to function name)
|
|
80
|
+
help: Command help text
|
|
81
|
+
aliases: Alternative command names
|
|
82
|
+
hidden: Hide from help output
|
|
83
|
+
|
|
84
|
+
Example:
|
|
85
|
+
@group.command()
|
|
86
|
+
def status():
|
|
87
|
+
'''Show status'''
|
|
88
|
+
print("Status: OK")
|
|
89
|
+
"""
|
|
90
|
+
def decorator(f):
|
|
91
|
+
cmd_name = name or f.__name__
|
|
92
|
+
self.commands[cmd_name] = {
|
|
93
|
+
'func': f,
|
|
94
|
+
'help': help or f.__doc__,
|
|
95
|
+
'aliases': aliases or [],
|
|
96
|
+
'hidden': hidden
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# Register aliases
|
|
100
|
+
for alias in (aliases or []):
|
|
101
|
+
self.commands[alias] = self.commands[cmd_name]
|
|
102
|
+
|
|
103
|
+
return f
|
|
104
|
+
return decorator
|
|
105
|
+
|
|
106
|
+
def group(
|
|
107
|
+
self,
|
|
108
|
+
name: Optional[str] = None,
|
|
109
|
+
help: Optional[str] = None,
|
|
110
|
+
**kwargs
|
|
111
|
+
):
|
|
112
|
+
"""
|
|
113
|
+
Create a subgroup within this group.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
name: Subgroup name
|
|
117
|
+
help: Subgroup help text
|
|
118
|
+
**kwargs: Additional group options
|
|
119
|
+
|
|
120
|
+
Example:
|
|
121
|
+
@group.group()
|
|
122
|
+
def config():
|
|
123
|
+
'''Configuration commands'''
|
|
124
|
+
pass
|
|
125
|
+
"""
|
|
126
|
+
def decorator(f):
|
|
127
|
+
subgroup = Group(
|
|
128
|
+
name=name or f.__name__,
|
|
129
|
+
help=help or f.__doc__,
|
|
130
|
+
parent=self.parent,
|
|
131
|
+
**kwargs
|
|
132
|
+
)
|
|
133
|
+
self.commands[subgroup.name] = subgroup
|
|
134
|
+
return subgroup
|
|
135
|
+
return decorator
|
|
136
|
+
|
|
137
|
+
def run(self, args: List[str]) -> int:
|
|
138
|
+
"""
|
|
139
|
+
Run a command in this group.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
args: Command arguments
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Exit code (0 for success, non-zero for error)
|
|
146
|
+
"""
|
|
147
|
+
try:
|
|
148
|
+
# Show help if no arguments or help requested
|
|
149
|
+
if not args or args[0] in ['-h', '--help']:
|
|
150
|
+
self._print_help()
|
|
151
|
+
return 0
|
|
152
|
+
|
|
153
|
+
command_name = args[0]
|
|
154
|
+
command_args = args[1:]
|
|
155
|
+
|
|
156
|
+
# Check if command exists
|
|
157
|
+
if command_name not in self.commands:
|
|
158
|
+
format_error(f"Unknown command: {self.name} {command_name}")
|
|
159
|
+
self._print_help()
|
|
160
|
+
return 1
|
|
161
|
+
|
|
162
|
+
command = self.commands[command_name]
|
|
163
|
+
|
|
164
|
+
# Handle nested groups
|
|
165
|
+
if isinstance(command, Group):
|
|
166
|
+
return command.run(command_args)
|
|
167
|
+
|
|
168
|
+
# Create command context
|
|
169
|
+
ctx = Context(
|
|
170
|
+
self.parent,
|
|
171
|
+
command=f"{self.name} {command_name}",
|
|
172
|
+
debug=getattr(self.parent, 'debug', False)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Run command through plugin system
|
|
176
|
+
if self.parent and not self.parent.plugin_manager.before_command(
|
|
177
|
+
f"{self.name} {command_name}",
|
|
178
|
+
command_args
|
|
179
|
+
):
|
|
180
|
+
return 1
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
result = command['func'](**self._parse_args(command, command_args))
|
|
184
|
+
|
|
185
|
+
if self.parent:
|
|
186
|
+
self.parent.plugin_manager.after_command(
|
|
187
|
+
f"{self.name} {command_name}",
|
|
188
|
+
command_args,
|
|
189
|
+
result
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Handle command chaining
|
|
193
|
+
if self.chain and result is not None:
|
|
194
|
+
return result
|
|
195
|
+
|
|
196
|
+
return 0
|
|
197
|
+
|
|
198
|
+
except Exception as e:
|
|
199
|
+
if self.parent:
|
|
200
|
+
self.parent.plugin_manager.on_error(
|
|
201
|
+
f"{self.name} {command_name}",
|
|
202
|
+
e
|
|
203
|
+
)
|
|
204
|
+
if getattr(self.parent, 'debug', False):
|
|
205
|
+
raise
|
|
206
|
+
format_error(str(e))
|
|
207
|
+
return 1
|
|
208
|
+
|
|
209
|
+
except Exception as e:
|
|
210
|
+
if getattr(self.parent, 'debug', False):
|
|
211
|
+
raise
|
|
212
|
+
format_error(str(e))
|
|
213
|
+
return 1
|
|
214
|
+
|
|
215
|
+
def _parse_args(self, command: Dict[str, Any], args: List[str]) -> Dict[str, Any]:
|
|
216
|
+
"""Parse command arguments."""
|
|
217
|
+
# Use parent CLI's argument parser if available
|
|
218
|
+
if self.parent:
|
|
219
|
+
return self.parent._parse_args(command, args)
|
|
220
|
+
|
|
221
|
+
# Fallback to basic argument parsing
|
|
222
|
+
from ..utils.parsing import parse_args
|
|
223
|
+
return parse_args(args)
|
|
224
|
+
|
|
225
|
+
def _print_help(self) -> None:
|
|
226
|
+
"""Print group help message."""
|
|
227
|
+
console.print(f"\n[bold]{self.name}[/] - {self.help or ''}")
|
|
228
|
+
|
|
229
|
+
console.print("\n[bold]Commands:[/]")
|
|
230
|
+
for name, cmd in self.commands.items():
|
|
231
|
+
if isinstance(cmd, Group):
|
|
232
|
+
console.print(f" {name} [group]")
|
|
233
|
+
if cmd.help:
|
|
234
|
+
console.print(f" {cmd.help}")
|
|
235
|
+
elif not cmd.get('hidden', False):
|
|
236
|
+
console.print(f" {name:20} {cmd['help'] or ''}")
|
|
237
|
+
|
|
238
|
+
console.print("\nUse -h or --help with any command for more info")
|
|
239
|
+
|
|
240
|
+
def __repr__(self) -> str:
|
|
241
|
+
return f"<Group name={self.name}>"
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
"""Decorators for SwiftCLI."""
|
|
2
|
-
|
|
3
|
-
from .command import command, group, argument, flag, pass_context
|
|
4
|
-
from .options import option, envvar, config_file, version_option, help_option
|
|
5
|
-
from .output import table_output, progress, panel_output, format_output, pager_output
|
|
6
|
-
|
|
7
|
-
__all__ = [
|
|
8
|
-
# Command decorators
|
|
9
|
-
'command',
|
|
10
|
-
'group',
|
|
11
|
-
'argument',
|
|
12
|
-
'flag',
|
|
13
|
-
'pass_context',
|
|
14
|
-
|
|
15
|
-
# Option decorators
|
|
16
|
-
'option',
|
|
17
|
-
'envvar',
|
|
18
|
-
'config_file',
|
|
19
|
-
'version_option',
|
|
20
|
-
'help_option',
|
|
21
|
-
|
|
22
|
-
# Output decorators
|
|
23
|
-
'table_output',
|
|
24
|
-
'progress',
|
|
25
|
-
'panel_output',
|
|
26
|
-
'format_output',
|
|
27
|
-
'pager_output'
|
|
28
|
-
]
|
|
1
|
+
"""Decorators for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
from .command import command, group, argument, flag, pass_context
|
|
4
|
+
from .options import option, envvar, config_file, version_option, help_option
|
|
5
|
+
from .output import table_output, progress, panel_output, format_output, pager_output
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
# Command decorators
|
|
9
|
+
'command',
|
|
10
|
+
'group',
|
|
11
|
+
'argument',
|
|
12
|
+
'flag',
|
|
13
|
+
'pass_context',
|
|
14
|
+
|
|
15
|
+
# Option decorators
|
|
16
|
+
'option',
|
|
17
|
+
'envvar',
|
|
18
|
+
'config_file',
|
|
19
|
+
'version_option',
|
|
20
|
+
'help_option',
|
|
21
|
+
|
|
22
|
+
# Output decorators
|
|
23
|
+
'table_output',
|
|
24
|
+
'progress',
|
|
25
|
+
'panel_output',
|
|
26
|
+
'format_output',
|
|
27
|
+
'pager_output'
|
|
28
|
+
]
|