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/cli.py
CHANGED
|
@@ -1,308 +1,308 @@
|
|
|
1
|
-
"""Main CLI application class."""
|
|
2
|
-
|
|
3
|
-
import sys
|
|
4
|
-
from typing import Any, Dict, List, Optional, Union
|
|
5
|
-
|
|
6
|
-
from rich.console import Console
|
|
7
|
-
|
|
8
|
-
from ..exceptions import UsageError
|
|
9
|
-
from ..plugins.manager import PluginManager
|
|
10
|
-
from ..utils.formatting import format_error, format_success
|
|
11
|
-
from .context import Context
|
|
12
|
-
|
|
13
|
-
console = Console()
|
|
14
|
-
|
|
15
|
-
class CLI:
|
|
16
|
-
"""
|
|
17
|
-
Main CLI application class.
|
|
18
|
-
|
|
19
|
-
The CLI class is the core of SwiftCLI. It handles command registration,
|
|
20
|
-
argument parsing, and command execution. It also manages plugins and
|
|
21
|
-
provides the main entry point for CLI applications.
|
|
22
|
-
|
|
23
|
-
Attributes:
|
|
24
|
-
name: Application name
|
|
25
|
-
help: Application description
|
|
26
|
-
version: Application version
|
|
27
|
-
debug: Debug mode flag
|
|
28
|
-
commands: Registered commands
|
|
29
|
-
groups: Command groups
|
|
30
|
-
plugin_manager: Plugin manager instance
|
|
31
|
-
|
|
32
|
-
Example:
|
|
33
|
-
>>> app = CLI(name="myapp", version="1.0.0")
|
|
34
|
-
>>> @app.command()
|
|
35
|
-
... def greet(name: str):
|
|
36
|
-
... '''Greet someone'''
|
|
37
|
-
... print(f"Hello {name}!")
|
|
38
|
-
>>> app.run()
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
def __init__(
|
|
42
|
-
self,
|
|
43
|
-
name: str,
|
|
44
|
-
help: Optional[str] = None,
|
|
45
|
-
version: Optional[str] = None,
|
|
46
|
-
debug: bool = False
|
|
47
|
-
):
|
|
48
|
-
"""
|
|
49
|
-
Initialize CLI application.
|
|
50
|
-
|
|
51
|
-
Args:
|
|
52
|
-
name: Application name
|
|
53
|
-
help: Application description
|
|
54
|
-
version: Application version
|
|
55
|
-
debug: Enable debug mode
|
|
56
|
-
"""
|
|
57
|
-
self.name = name
|
|
58
|
-
self.help = help
|
|
59
|
-
self.version = version
|
|
60
|
-
self.debug = debug
|
|
61
|
-
|
|
62
|
-
self.commands: Dict[str, Dict[str, Any]] = {}
|
|
63
|
-
self.groups: Dict[str, 'Group'] = {} # type: ignore
|
|
64
|
-
self.plugin_manager = PluginManager()
|
|
65
|
-
|
|
66
|
-
# Initialize plugin manager with this CLI instance
|
|
67
|
-
self.plugin_manager.init_plugins(self)
|
|
68
|
-
|
|
69
|
-
def command(
|
|
70
|
-
self,
|
|
71
|
-
name: Optional[str] = None,
|
|
72
|
-
help: Optional[str] = None,
|
|
73
|
-
aliases: Optional[List[str]] = None,
|
|
74
|
-
hidden: bool = False
|
|
75
|
-
):
|
|
76
|
-
"""
|
|
77
|
-
Decorator to register a command.
|
|
78
|
-
|
|
79
|
-
Args:
|
|
80
|
-
name: Command name (defaults to function name)
|
|
81
|
-
help: Command help text
|
|
82
|
-
aliases: Alternative command names
|
|
83
|
-
hidden: Hide from help output
|
|
84
|
-
|
|
85
|
-
Example:
|
|
86
|
-
@app.command()
|
|
87
|
-
def hello(name: str):
|
|
88
|
-
'''Say hello'''
|
|
89
|
-
print(f"Hello {name}!")
|
|
90
|
-
"""
|
|
91
|
-
def decorator(f):
|
|
92
|
-
cmd_name = name or f.__name__
|
|
93
|
-
self.commands[cmd_name] = {
|
|
94
|
-
'func': f,
|
|
95
|
-
'help': help or f.__doc__,
|
|
96
|
-
'aliases': aliases or [],
|
|
97
|
-
'hidden': hidden
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
# Register aliases
|
|
101
|
-
for alias in (aliases or []):
|
|
102
|
-
self.commands[alias] = self.commands[cmd_name]
|
|
103
|
-
|
|
104
|
-
return f
|
|
105
|
-
return decorator
|
|
106
|
-
|
|
107
|
-
def group(
|
|
108
|
-
self,
|
|
109
|
-
name: Optional[str] = None,
|
|
110
|
-
help: Optional[str] = None,
|
|
111
|
-
**kwargs
|
|
112
|
-
):
|
|
113
|
-
"""
|
|
114
|
-
Create a command group.
|
|
115
|
-
|
|
116
|
-
Args:
|
|
117
|
-
name: Group name
|
|
118
|
-
help: Group help text
|
|
119
|
-
**kwargs: Additional group options
|
|
120
|
-
|
|
121
|
-
Example:
|
|
122
|
-
@app.group()
|
|
123
|
-
def db():
|
|
124
|
-
'''Database commands'''
|
|
125
|
-
pass
|
|
126
|
-
|
|
127
|
-
@db.command()
|
|
128
|
-
def migrate():
|
|
129
|
-
'''Run migrations'''
|
|
130
|
-
pass
|
|
131
|
-
"""
|
|
132
|
-
from .group import Group # Import here to avoid circular dependency
|
|
133
|
-
|
|
134
|
-
def decorator(f):
|
|
135
|
-
group_name = name or f.__name__
|
|
136
|
-
group = Group(
|
|
137
|
-
name=group_name,
|
|
138
|
-
help=help or f.__doc__,
|
|
139
|
-
parent=self,
|
|
140
|
-
**kwargs
|
|
141
|
-
)
|
|
142
|
-
self.groups[group_name] = group
|
|
143
|
-
return group
|
|
144
|
-
return decorator
|
|
145
|
-
|
|
146
|
-
def run(self, args: Optional[List[str]] = None) -> int:
|
|
147
|
-
"""
|
|
148
|
-
Run the CLI application.
|
|
149
|
-
|
|
150
|
-
Args:
|
|
151
|
-
args: Command line arguments (defaults to sys.argv[1:])
|
|
152
|
-
|
|
153
|
-
Returns:
|
|
154
|
-
Exit code (0 for success, non-zero for error)
|
|
155
|
-
"""
|
|
156
|
-
try:
|
|
157
|
-
args = args or sys.argv[1:]
|
|
158
|
-
|
|
159
|
-
# Show help if no arguments
|
|
160
|
-
if not args or args[0] in ['-h', '--help']:
|
|
161
|
-
self._print_help()
|
|
162
|
-
return 0
|
|
163
|
-
|
|
164
|
-
# Show version if requested
|
|
165
|
-
if args[0] in ['-v', '--version'] and self.version:
|
|
166
|
-
console.print(self.version)
|
|
167
|
-
return 0
|
|
168
|
-
|
|
169
|
-
command_name = args[0]
|
|
170
|
-
command_args = args[1:]
|
|
171
|
-
|
|
172
|
-
# Check if it's a group command
|
|
173
|
-
if command_name in self.groups:
|
|
174
|
-
return self.groups[command_name].run(command_args)
|
|
175
|
-
|
|
176
|
-
# Check if it's a regular command
|
|
177
|
-
if command_name not in self.commands:
|
|
178
|
-
format_error(f"Unknown command: {command_name}")
|
|
179
|
-
self._print_help()
|
|
180
|
-
return 1
|
|
181
|
-
|
|
182
|
-
# Create command context
|
|
183
|
-
ctx = Context(self, command=command_name, debug=self.debug)
|
|
184
|
-
|
|
185
|
-
# Run command through plugin system
|
|
186
|
-
if not self.plugin_manager.before_command(command_name, command_args):
|
|
187
|
-
return 1
|
|
188
|
-
|
|
189
|
-
try:
|
|
190
|
-
command = self.commands[command_name]
|
|
191
|
-
result = command['func'](**self._parse_args(command, command_args))
|
|
192
|
-
self.plugin_manager.after_command(command_name, command_args, result)
|
|
193
|
-
return 0
|
|
194
|
-
except Exception as e:
|
|
195
|
-
self.plugin_manager.on_error(command_name, e)
|
|
196
|
-
if self.debug:
|
|
197
|
-
raise
|
|
198
|
-
format_error(str(e))
|
|
199
|
-
return 1
|
|
200
|
-
|
|
201
|
-
except KeyboardInterrupt:
|
|
202
|
-
console.print("\nOperation cancelled by user")
|
|
203
|
-
return 130
|
|
204
|
-
except Exception as e:
|
|
205
|
-
if self.debug:
|
|
206
|
-
raise
|
|
207
|
-
format_error(str(e))
|
|
208
|
-
return 1
|
|
209
|
-
|
|
210
|
-
def _parse_args(self, command: Dict[str, Any], args: List[str]) -> Dict[str, Any]:
|
|
211
|
-
"""Parse command arguments."""
|
|
212
|
-
from ..utils.parsing import (
|
|
213
|
-
parse_args, validate_required, convert_type,
|
|
214
|
-
validate_choice, get_env_var
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
params = {}
|
|
218
|
-
func = command['func']
|
|
219
|
-
|
|
220
|
-
# Parse command-line arguments
|
|
221
|
-
parsed_args = parse_args(args)
|
|
222
|
-
|
|
223
|
-
# Handle options
|
|
224
|
-
if hasattr(func, '_options'):
|
|
225
|
-
for opt in func._options:
|
|
226
|
-
# Use the longest parameter name (usually the --long-form) for the parameter name
|
|
227
|
-
param_names = [p.lstrip('-').replace('-', '_') for p in opt['param_decls']]
|
|
228
|
-
name = max(param_names, key=len) # Use the longest name
|
|
229
|
-
|
|
230
|
-
# Check all possible parameter names in parsed args
|
|
231
|
-
value = None
|
|
232
|
-
found = False
|
|
233
|
-
for param_name in param_names:
|
|
234
|
-
if param_name in parsed_args:
|
|
235
|
-
value = parsed_args[param_name]
|
|
236
|
-
found = True
|
|
237
|
-
break
|
|
238
|
-
|
|
239
|
-
if found:
|
|
240
|
-
if 'type' in opt:
|
|
241
|
-
value = convert_type(value, opt['type'], name)
|
|
242
|
-
if 'choices' in opt and opt['choices']:
|
|
243
|
-
validate_choice(
|
|
244
|
-
value,
|
|
245
|
-
opt['choices'],
|
|
246
|
-
name,
|
|
247
|
-
opt.get('case_sensitive', True)
|
|
248
|
-
)
|
|
249
|
-
params[name] = value
|
|
250
|
-
elif opt.get('required', False):
|
|
251
|
-
raise UsageError(f"Missing required option: {name}")
|
|
252
|
-
elif 'default' in opt:
|
|
253
|
-
params[name] = opt['default']
|
|
254
|
-
|
|
255
|
-
# Handle arguments
|
|
256
|
-
if hasattr(func, '_arguments'):
|
|
257
|
-
for i, arg in enumerate(func._arguments):
|
|
258
|
-
name = arg['name']
|
|
259
|
-
if f'arg{i}' in parsed_args:
|
|
260
|
-
value = parsed_args[f'arg{i}']
|
|
261
|
-
if 'type' in arg:
|
|
262
|
-
value = convert_type(value, arg['type'], name)
|
|
263
|
-
params[name] = value
|
|
264
|
-
elif arg.get('required', True):
|
|
265
|
-
raise UsageError(f"Missing required argument: {name}")
|
|
266
|
-
elif 'default' in arg:
|
|
267
|
-
params[name] = arg['default']
|
|
268
|
-
|
|
269
|
-
# Handle environment variables
|
|
270
|
-
if hasattr(func, '_envvars'):
|
|
271
|
-
for env in func._envvars:
|
|
272
|
-
name = env['name'].lower()
|
|
273
|
-
value = get_env_var(
|
|
274
|
-
env['name'],
|
|
275
|
-
env.get('type', str),
|
|
276
|
-
env.get('required', False),
|
|
277
|
-
env.get('default')
|
|
278
|
-
)
|
|
279
|
-
if value is not None:
|
|
280
|
-
params[name] = value
|
|
281
|
-
|
|
282
|
-
return params
|
|
283
|
-
|
|
284
|
-
def _print_help(self) -> None:
|
|
285
|
-
"""Print application help message."""
|
|
286
|
-
console.print(f"\n[bold]{self.name}[/]")
|
|
287
|
-
if self.help:
|
|
288
|
-
console.print(f"\n{self.help}")
|
|
289
|
-
|
|
290
|
-
# Show commands
|
|
291
|
-
console.print("\n[bold]Commands:[/]")
|
|
292
|
-
for name, cmd in self.commands.items():
|
|
293
|
-
if not cmd.get('hidden', False):
|
|
294
|
-
console.print(f" {name:20} {cmd['help'] or ''}")
|
|
295
|
-
|
|
296
|
-
# Show command groups
|
|
297
|
-
for name, group in self.groups.items():
|
|
298
|
-
console.print(f"\n[bold]{name} commands:[/]")
|
|
299
|
-
for cmd_name, cmd in group.commands.items():
|
|
300
|
-
if not cmd.get('hidden', False):
|
|
301
|
-
console.print(f" {cmd_name:20} {cmd['help'] or ''}")
|
|
302
|
-
|
|
303
|
-
console.print("\nUse -h or --help with any command for more info")
|
|
304
|
-
if self.version:
|
|
305
|
-
console.print("Use -v or --version to show version")
|
|
306
|
-
|
|
307
|
-
def __repr__(self) -> str:
|
|
308
|
-
return f"<CLI name={self.name}>"
|
|
1
|
+
"""Main CLI application class."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Any, Dict, List, Optional, Union
|
|
5
|
+
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
from ..exceptions import UsageError
|
|
9
|
+
from ..plugins.manager import PluginManager
|
|
10
|
+
from ..utils.formatting import format_error, format_success
|
|
11
|
+
from .context import Context
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
class CLI:
|
|
16
|
+
"""
|
|
17
|
+
Main CLI application class.
|
|
18
|
+
|
|
19
|
+
The CLI class is the core of SwiftCLI. It handles command registration,
|
|
20
|
+
argument parsing, and command execution. It also manages plugins and
|
|
21
|
+
provides the main entry point for CLI applications.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
name: Application name
|
|
25
|
+
help: Application description
|
|
26
|
+
version: Application version
|
|
27
|
+
debug: Debug mode flag
|
|
28
|
+
commands: Registered commands
|
|
29
|
+
groups: Command groups
|
|
30
|
+
plugin_manager: Plugin manager instance
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
>>> app = CLI(name="myapp", version="1.0.0")
|
|
34
|
+
>>> @app.command()
|
|
35
|
+
... def greet(name: str):
|
|
36
|
+
... '''Greet someone'''
|
|
37
|
+
... print(f"Hello {name}!")
|
|
38
|
+
>>> app.run()
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
name: str,
|
|
44
|
+
help: Optional[str] = None,
|
|
45
|
+
version: Optional[str] = None,
|
|
46
|
+
debug: bool = False
|
|
47
|
+
):
|
|
48
|
+
"""
|
|
49
|
+
Initialize CLI application.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
name: Application name
|
|
53
|
+
help: Application description
|
|
54
|
+
version: Application version
|
|
55
|
+
debug: Enable debug mode
|
|
56
|
+
"""
|
|
57
|
+
self.name = name
|
|
58
|
+
self.help = help
|
|
59
|
+
self.version = version
|
|
60
|
+
self.debug = debug
|
|
61
|
+
|
|
62
|
+
self.commands: Dict[str, Dict[str, Any]] = {}
|
|
63
|
+
self.groups: Dict[str, 'Group'] = {} # type: ignore
|
|
64
|
+
self.plugin_manager = PluginManager()
|
|
65
|
+
|
|
66
|
+
# Initialize plugin manager with this CLI instance
|
|
67
|
+
self.plugin_manager.init_plugins(self)
|
|
68
|
+
|
|
69
|
+
def command(
|
|
70
|
+
self,
|
|
71
|
+
name: Optional[str] = None,
|
|
72
|
+
help: Optional[str] = None,
|
|
73
|
+
aliases: Optional[List[str]] = None,
|
|
74
|
+
hidden: bool = False
|
|
75
|
+
):
|
|
76
|
+
"""
|
|
77
|
+
Decorator to register a command.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
name: Command name (defaults to function name)
|
|
81
|
+
help: Command help text
|
|
82
|
+
aliases: Alternative command names
|
|
83
|
+
hidden: Hide from help output
|
|
84
|
+
|
|
85
|
+
Example:
|
|
86
|
+
@app.command()
|
|
87
|
+
def hello(name: str):
|
|
88
|
+
'''Say hello'''
|
|
89
|
+
print(f"Hello {name}!")
|
|
90
|
+
"""
|
|
91
|
+
def decorator(f):
|
|
92
|
+
cmd_name = name or f.__name__
|
|
93
|
+
self.commands[cmd_name] = {
|
|
94
|
+
'func': f,
|
|
95
|
+
'help': help or f.__doc__,
|
|
96
|
+
'aliases': aliases or [],
|
|
97
|
+
'hidden': hidden
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Register aliases
|
|
101
|
+
for alias in (aliases or []):
|
|
102
|
+
self.commands[alias] = self.commands[cmd_name]
|
|
103
|
+
|
|
104
|
+
return f
|
|
105
|
+
return decorator
|
|
106
|
+
|
|
107
|
+
def group(
|
|
108
|
+
self,
|
|
109
|
+
name: Optional[str] = None,
|
|
110
|
+
help: Optional[str] = None,
|
|
111
|
+
**kwargs
|
|
112
|
+
):
|
|
113
|
+
"""
|
|
114
|
+
Create a command group.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
name: Group name
|
|
118
|
+
help: Group help text
|
|
119
|
+
**kwargs: Additional group options
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
@app.group()
|
|
123
|
+
def db():
|
|
124
|
+
'''Database commands'''
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
@db.command()
|
|
128
|
+
def migrate():
|
|
129
|
+
'''Run migrations'''
|
|
130
|
+
pass
|
|
131
|
+
"""
|
|
132
|
+
from .group import Group # Import here to avoid circular dependency
|
|
133
|
+
|
|
134
|
+
def decorator(f):
|
|
135
|
+
group_name = name or f.__name__
|
|
136
|
+
group = Group(
|
|
137
|
+
name=group_name,
|
|
138
|
+
help=help or f.__doc__,
|
|
139
|
+
parent=self,
|
|
140
|
+
**kwargs
|
|
141
|
+
)
|
|
142
|
+
self.groups[group_name] = group
|
|
143
|
+
return group
|
|
144
|
+
return decorator
|
|
145
|
+
|
|
146
|
+
def run(self, args: Optional[List[str]] = None) -> int:
|
|
147
|
+
"""
|
|
148
|
+
Run the CLI application.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
args: Command line arguments (defaults to sys.argv[1:])
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Exit code (0 for success, non-zero for error)
|
|
155
|
+
"""
|
|
156
|
+
try:
|
|
157
|
+
args = args or sys.argv[1:]
|
|
158
|
+
|
|
159
|
+
# Show help if no arguments
|
|
160
|
+
if not args or args[0] in ['-h', '--help']:
|
|
161
|
+
self._print_help()
|
|
162
|
+
return 0
|
|
163
|
+
|
|
164
|
+
# Show version if requested
|
|
165
|
+
if args[0] in ['-v', '--version'] and self.version:
|
|
166
|
+
console.print(self.version)
|
|
167
|
+
return 0
|
|
168
|
+
|
|
169
|
+
command_name = args[0]
|
|
170
|
+
command_args = args[1:]
|
|
171
|
+
|
|
172
|
+
# Check if it's a group command
|
|
173
|
+
if command_name in self.groups:
|
|
174
|
+
return self.groups[command_name].run(command_args)
|
|
175
|
+
|
|
176
|
+
# Check if it's a regular command
|
|
177
|
+
if command_name not in self.commands:
|
|
178
|
+
format_error(f"Unknown command: {command_name}")
|
|
179
|
+
self._print_help()
|
|
180
|
+
return 1
|
|
181
|
+
|
|
182
|
+
# Create command context
|
|
183
|
+
ctx = Context(self, command=command_name, debug=self.debug)
|
|
184
|
+
|
|
185
|
+
# Run command through plugin system
|
|
186
|
+
if not self.plugin_manager.before_command(command_name, command_args):
|
|
187
|
+
return 1
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
command = self.commands[command_name]
|
|
191
|
+
result = command['func'](**self._parse_args(command, command_args))
|
|
192
|
+
self.plugin_manager.after_command(command_name, command_args, result)
|
|
193
|
+
return 0
|
|
194
|
+
except Exception as e:
|
|
195
|
+
self.plugin_manager.on_error(command_name, e)
|
|
196
|
+
if self.debug:
|
|
197
|
+
raise
|
|
198
|
+
format_error(str(e))
|
|
199
|
+
return 1
|
|
200
|
+
|
|
201
|
+
except KeyboardInterrupt:
|
|
202
|
+
console.print("\nOperation cancelled by user")
|
|
203
|
+
return 130
|
|
204
|
+
except Exception as e:
|
|
205
|
+
if self.debug:
|
|
206
|
+
raise
|
|
207
|
+
format_error(str(e))
|
|
208
|
+
return 1
|
|
209
|
+
|
|
210
|
+
def _parse_args(self, command: Dict[str, Any], args: List[str]) -> Dict[str, Any]:
|
|
211
|
+
"""Parse command arguments."""
|
|
212
|
+
from ..utils.parsing import (
|
|
213
|
+
parse_args, validate_required, convert_type,
|
|
214
|
+
validate_choice, get_env_var
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
params = {}
|
|
218
|
+
func = command['func']
|
|
219
|
+
|
|
220
|
+
# Parse command-line arguments
|
|
221
|
+
parsed_args = parse_args(args)
|
|
222
|
+
|
|
223
|
+
# Handle options
|
|
224
|
+
if hasattr(func, '_options'):
|
|
225
|
+
for opt in func._options:
|
|
226
|
+
# Use the longest parameter name (usually the --long-form) for the parameter name
|
|
227
|
+
param_names = [p.lstrip('-').replace('-', '_') for p in opt['param_decls']]
|
|
228
|
+
name = max(param_names, key=len) # Use the longest name
|
|
229
|
+
|
|
230
|
+
# Check all possible parameter names in parsed args
|
|
231
|
+
value = None
|
|
232
|
+
found = False
|
|
233
|
+
for param_name in param_names:
|
|
234
|
+
if param_name in parsed_args:
|
|
235
|
+
value = parsed_args[param_name]
|
|
236
|
+
found = True
|
|
237
|
+
break
|
|
238
|
+
|
|
239
|
+
if found:
|
|
240
|
+
if 'type' in opt:
|
|
241
|
+
value = convert_type(value, opt['type'], name)
|
|
242
|
+
if 'choices' in opt and opt['choices']:
|
|
243
|
+
validate_choice(
|
|
244
|
+
value,
|
|
245
|
+
opt['choices'],
|
|
246
|
+
name,
|
|
247
|
+
opt.get('case_sensitive', True)
|
|
248
|
+
)
|
|
249
|
+
params[name] = value
|
|
250
|
+
elif opt.get('required', False):
|
|
251
|
+
raise UsageError(f"Missing required option: {name}")
|
|
252
|
+
elif 'default' in opt:
|
|
253
|
+
params[name] = opt['default']
|
|
254
|
+
|
|
255
|
+
# Handle arguments
|
|
256
|
+
if hasattr(func, '_arguments'):
|
|
257
|
+
for i, arg in enumerate(func._arguments):
|
|
258
|
+
name = arg['name']
|
|
259
|
+
if f'arg{i}' in parsed_args:
|
|
260
|
+
value = parsed_args[f'arg{i}']
|
|
261
|
+
if 'type' in arg:
|
|
262
|
+
value = convert_type(value, arg['type'], name)
|
|
263
|
+
params[name] = value
|
|
264
|
+
elif arg.get('required', True):
|
|
265
|
+
raise UsageError(f"Missing required argument: {name}")
|
|
266
|
+
elif 'default' in arg:
|
|
267
|
+
params[name] = arg['default']
|
|
268
|
+
|
|
269
|
+
# Handle environment variables
|
|
270
|
+
if hasattr(func, '_envvars'):
|
|
271
|
+
for env in func._envvars:
|
|
272
|
+
name = env['name'].lower()
|
|
273
|
+
value = get_env_var(
|
|
274
|
+
env['name'],
|
|
275
|
+
env.get('type', str),
|
|
276
|
+
env.get('required', False),
|
|
277
|
+
env.get('default')
|
|
278
|
+
)
|
|
279
|
+
if value is not None:
|
|
280
|
+
params[name] = value
|
|
281
|
+
|
|
282
|
+
return params
|
|
283
|
+
|
|
284
|
+
def _print_help(self) -> None:
|
|
285
|
+
"""Print application help message."""
|
|
286
|
+
console.print(f"\n[bold]{self.name}[/]")
|
|
287
|
+
if self.help:
|
|
288
|
+
console.print(f"\n{self.help}")
|
|
289
|
+
|
|
290
|
+
# Show commands
|
|
291
|
+
console.print("\n[bold]Commands:[/]")
|
|
292
|
+
for name, cmd in self.commands.items():
|
|
293
|
+
if not cmd.get('hidden', False):
|
|
294
|
+
console.print(f" {name:20} {cmd['help'] or ''}")
|
|
295
|
+
|
|
296
|
+
# Show command groups
|
|
297
|
+
for name, group in self.groups.items():
|
|
298
|
+
console.print(f"\n[bold]{name} commands:[/]")
|
|
299
|
+
for cmd_name, cmd in group.commands.items():
|
|
300
|
+
if not cmd.get('hidden', False):
|
|
301
|
+
console.print(f" {cmd_name:20} {cmd['help'] or ''}")
|
|
302
|
+
|
|
303
|
+
console.print("\nUse -h or --help with any command for more info")
|
|
304
|
+
if self.version:
|
|
305
|
+
console.print("Use -v or --version to show version")
|
|
306
|
+
|
|
307
|
+
def __repr__(self) -> str:
|
|
308
|
+
return f"<CLI name={self.name}>"
|