webscout 8.2.7__py3-none-any.whl → 8.2.9__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.
- webscout/AIauto.py +33 -15
- webscout/AIbase.py +96 -37
- webscout/AIutel.py +703 -250
- webscout/Bard.py +441 -323
- webscout/Extra/Act.md +309 -0
- webscout/Extra/GitToolkit/__init__.py +10 -0
- webscout/Extra/GitToolkit/gitapi/README.md +110 -0
- webscout/Extra/GitToolkit/gitapi/__init__.py +12 -0
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -0
- webscout/Extra/GitToolkit/gitapi/user.py +96 -0
- webscout/Extra/GitToolkit/gitapi/utils.py +62 -0
- webscout/Extra/YTToolkit/README.md +375 -0
- webscout/Extra/YTToolkit/YTdownloader.py +957 -0
- webscout/Extra/YTToolkit/__init__.py +3 -0
- webscout/Extra/YTToolkit/transcriber.py +476 -0
- webscout/Extra/YTToolkit/ytapi/README.md +44 -0
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -0
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -0
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -0
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -0
- webscout/Extra/YTToolkit/ytapi/https.py +88 -0
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -0
- webscout/Extra/YTToolkit/ytapi/playlist.py +59 -0
- webscout/Extra/YTToolkit/ytapi/pool.py +8 -0
- webscout/Extra/YTToolkit/ytapi/query.py +40 -0
- webscout/Extra/YTToolkit/ytapi/stream.py +63 -0
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -0
- webscout/Extra/YTToolkit/ytapi/video.py +232 -0
- webscout/Extra/__init__.py +7 -0
- webscout/Extra/autocoder/__init__.py +9 -0
- webscout/Extra/autocoder/autocoder.py +1105 -0
- webscout/Extra/autocoder/autocoder_utiles.py +332 -0
- webscout/Extra/gguf.md +430 -0
- webscout/Extra/gguf.py +684 -0
- webscout/Extra/tempmail/README.md +488 -0
- webscout/Extra/tempmail/__init__.py +28 -0
- webscout/Extra/tempmail/async_utils.py +141 -0
- webscout/Extra/tempmail/base.py +161 -0
- webscout/Extra/tempmail/cli.py +187 -0
- webscout/Extra/tempmail/emailnator.py +84 -0
- webscout/Extra/tempmail/mail_tm.py +361 -0
- webscout/Extra/tempmail/temp_mail_io.py +292 -0
- webscout/Extra/weather.md +281 -0
- webscout/Extra/weather.py +194 -0
- webscout/Extra/weather_ascii.py +76 -0
- webscout/Litlogger/README.md +10 -0
- webscout/Litlogger/__init__.py +15 -0
- webscout/Litlogger/formats.py +4 -0
- webscout/Litlogger/handlers.py +103 -0
- webscout/Litlogger/levels.py +13 -0
- webscout/Litlogger/logger.py +92 -0
- webscout/Provider/AI21.py +177 -0
- webscout/Provider/AISEARCH/DeepFind.py +254 -0
- webscout/Provider/AISEARCH/Perplexity.py +333 -0
- webscout/Provider/AISEARCH/README.md +279 -0
- webscout/Provider/AISEARCH/__init__.py +9 -0
- webscout/Provider/AISEARCH/felo_search.py +202 -0
- webscout/Provider/AISEARCH/genspark_search.py +324 -0
- webscout/Provider/AISEARCH/hika_search.py +186 -0
- webscout/Provider/AISEARCH/iask_search.py +410 -0
- webscout/Provider/AISEARCH/monica_search.py +220 -0
- webscout/Provider/AISEARCH/scira_search.py +298 -0
- webscout/Provider/AISEARCH/webpilotai_search.py +255 -0
- webscout/Provider/Aitopia.py +316 -0
- webscout/Provider/AllenAI.py +440 -0
- webscout/Provider/Andi.py +228 -0
- webscout/Provider/Blackboxai.py +791 -0
- webscout/Provider/ChatGPTClone.py +237 -0
- webscout/Provider/ChatGPTGratis.py +194 -0
- webscout/Provider/ChatSandbox.py +342 -0
- webscout/Provider/Cloudflare.py +324 -0
- webscout/Provider/Cohere.py +208 -0
- webscout/Provider/Deepinfra.py +340 -0
- webscout/Provider/ExaAI.py +261 -0
- webscout/Provider/ExaChat.py +358 -0
- webscout/Provider/Flowith.py +217 -0
- webscout/Provider/FreeGemini.py +250 -0
- webscout/Provider/Gemini.py +169 -0
- webscout/Provider/GithubChat.py +369 -0
- webscout/Provider/GizAI.py +295 -0
- webscout/Provider/Glider.py +225 -0
- webscout/Provider/Groq.py +801 -0
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +206 -0
- webscout/Provider/HeckAI.py +375 -0
- webscout/Provider/HuggingFaceChat.py +469 -0
- webscout/Provider/Hunyuan.py +283 -0
- webscout/Provider/Jadve.py +291 -0
- webscout/Provider/Koboldai.py +384 -0
- webscout/Provider/LambdaChat.py +411 -0
- webscout/Provider/Llama3.py +259 -0
- webscout/Provider/MCPCore.py +315 -0
- webscout/Provider/Marcus.py +198 -0
- webscout/Provider/Nemotron.py +218 -0
- webscout/Provider/Netwrck.py +270 -0
- webscout/Provider/OLLAMA.py +396 -0
- webscout/Provider/OPENAI/BLACKBOXAI.py +766 -0
- webscout/Provider/OPENAI/Cloudflare.py +378 -0
- webscout/Provider/OPENAI/FreeGemini.py +283 -0
- webscout/Provider/OPENAI/NEMOTRON.py +232 -0
- webscout/Provider/OPENAI/Qwen3.py +283 -0
- webscout/Provider/OPENAI/README.md +952 -0
- webscout/Provider/OPENAI/TwoAI.py +357 -0
- webscout/Provider/OPENAI/__init__.py +40 -0
- webscout/Provider/OPENAI/ai4chat.py +293 -0
- webscout/Provider/OPENAI/api.py +969 -0
- webscout/Provider/OPENAI/base.py +249 -0
- webscout/Provider/OPENAI/c4ai.py +373 -0
- webscout/Provider/OPENAI/chatgpt.py +556 -0
- webscout/Provider/OPENAI/chatgptclone.py +494 -0
- webscout/Provider/OPENAI/chatsandbox.py +173 -0
- webscout/Provider/OPENAI/copilot.py +242 -0
- webscout/Provider/OPENAI/deepinfra.py +322 -0
- webscout/Provider/OPENAI/e2b.py +1414 -0
- webscout/Provider/OPENAI/exaai.py +417 -0
- webscout/Provider/OPENAI/exachat.py +444 -0
- webscout/Provider/OPENAI/flowith.py +162 -0
- webscout/Provider/OPENAI/freeaichat.py +359 -0
- webscout/Provider/OPENAI/glider.py +326 -0
- webscout/Provider/OPENAI/groq.py +364 -0
- webscout/Provider/OPENAI/heckai.py +308 -0
- webscout/Provider/OPENAI/llmchatco.py +335 -0
- webscout/Provider/OPENAI/mcpcore.py +389 -0
- webscout/Provider/OPENAI/multichat.py +376 -0
- webscout/Provider/OPENAI/netwrck.py +357 -0
- webscout/Provider/OPENAI/oivscode.py +287 -0
- webscout/Provider/OPENAI/opkfc.py +496 -0
- webscout/Provider/OPENAI/pydantic_imports.py +172 -0
- webscout/Provider/OPENAI/scirachat.py +477 -0
- webscout/Provider/OPENAI/sonus.py +304 -0
- webscout/Provider/OPENAI/standardinput.py +433 -0
- webscout/Provider/OPENAI/textpollinations.py +339 -0
- webscout/Provider/OPENAI/toolbaz.py +413 -0
- webscout/Provider/OPENAI/typefully.py +355 -0
- webscout/Provider/OPENAI/typegpt.py +364 -0
- webscout/Provider/OPENAI/uncovrAI.py +463 -0
- webscout/Provider/OPENAI/utils.py +318 -0
- webscout/Provider/OPENAI/venice.py +431 -0
- webscout/Provider/OPENAI/wisecat.py +387 -0
- webscout/Provider/OPENAI/writecream.py +163 -0
- webscout/Provider/OPENAI/x0gpt.py +365 -0
- webscout/Provider/OPENAI/yep.py +382 -0
- webscout/Provider/OpenGPT.py +209 -0
- webscout/Provider/Openai.py +496 -0
- webscout/Provider/PI.py +429 -0
- webscout/Provider/Perplexitylabs.py +415 -0
- webscout/Provider/QwenLM.py +254 -0
- webscout/Provider/Reka.py +214 -0
- webscout/Provider/StandardInput.py +290 -0
- webscout/Provider/TTI/README.md +82 -0
- webscout/Provider/TTI/__init__.py +7 -0
- webscout/Provider/TTI/aiarta.py +365 -0
- webscout/Provider/TTI/artbit.py +0 -0
- webscout/Provider/TTI/base.py +64 -0
- webscout/Provider/TTI/fastflux.py +200 -0
- webscout/Provider/TTI/magicstudio.py +201 -0
- webscout/Provider/TTI/piclumen.py +203 -0
- webscout/Provider/TTI/pixelmuse.py +225 -0
- webscout/Provider/TTI/pollinations.py +221 -0
- webscout/Provider/TTI/utils.py +11 -0
- webscout/Provider/TTS/README.md +192 -0
- webscout/Provider/TTS/__init__.py +10 -0
- webscout/Provider/TTS/base.py +159 -0
- webscout/Provider/TTS/deepgram.py +156 -0
- webscout/Provider/TTS/elevenlabs.py +111 -0
- webscout/Provider/TTS/gesserit.py +128 -0
- webscout/Provider/TTS/murfai.py +113 -0
- webscout/Provider/TTS/openai_fm.py +129 -0
- webscout/Provider/TTS/parler.py +111 -0
- webscout/Provider/TTS/speechma.py +580 -0
- webscout/Provider/TTS/sthir.py +94 -0
- webscout/Provider/TTS/streamElements.py +333 -0
- webscout/Provider/TTS/utils.py +280 -0
- webscout/Provider/TeachAnything.py +229 -0
- webscout/Provider/TextPollinationsAI.py +308 -0
- webscout/Provider/TwoAI.py +475 -0
- webscout/Provider/TypliAI.py +305 -0
- webscout/Provider/UNFINISHED/ChatHub.py +209 -0
- webscout/Provider/UNFINISHED/Youchat.py +330 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +263 -0
- webscout/Provider/UNFINISHED/puterjs.py +635 -0
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -0
- webscout/Provider/Venice.py +258 -0
- webscout/Provider/VercelAI.py +253 -0
- webscout/Provider/WiseCat.py +233 -0
- webscout/Provider/WrDoChat.py +370 -0
- webscout/Provider/Writecream.py +246 -0
- webscout/Provider/WritingMate.py +269 -0
- webscout/Provider/__init__.py +174 -0
- webscout/Provider/ai4chat.py +174 -0
- webscout/Provider/akashgpt.py +335 -0
- webscout/Provider/asksteve.py +220 -0
- webscout/Provider/cerebras.py +290 -0
- webscout/Provider/chatglm.py +215 -0
- webscout/Provider/cleeai.py +213 -0
- webscout/Provider/copilot.py +425 -0
- webscout/Provider/elmo.py +283 -0
- webscout/Provider/freeaichat.py +285 -0
- webscout/Provider/geminiapi.py +208 -0
- webscout/Provider/granite.py +235 -0
- webscout/Provider/hermes.py +266 -0
- webscout/Provider/julius.py +223 -0
- webscout/Provider/koala.py +170 -0
- webscout/Provider/learnfastai.py +325 -0
- webscout/Provider/llama3mitril.py +215 -0
- webscout/Provider/llmchat.py +258 -0
- webscout/Provider/llmchatco.py +306 -0
- webscout/Provider/lmarena.py +198 -0
- webscout/Provider/meta.py +801 -0
- webscout/Provider/multichat.py +364 -0
- webscout/Provider/oivscode.py +309 -0
- webscout/Provider/samurai.py +224 -0
- webscout/Provider/scira_chat.py +299 -0
- webscout/Provider/scnet.py +243 -0
- webscout/Provider/searchchat.py +292 -0
- webscout/Provider/sonus.py +258 -0
- webscout/Provider/talkai.py +194 -0
- webscout/Provider/toolbaz.py +353 -0
- webscout/Provider/turboseek.py +266 -0
- webscout/Provider/typefully.py +202 -0
- webscout/Provider/typegpt.py +289 -0
- webscout/Provider/uncovr.py +368 -0
- webscout/Provider/x0gpt.py +299 -0
- webscout/Provider/yep.py +389 -0
- webscout/__init__.py +4 -2
- webscout/cli.py +3 -28
- webscout/client.py +70 -0
- webscout/conversation.py +35 -35
- webscout/litagent/Readme.md +276 -0
- webscout/litagent/__init__.py +29 -0
- webscout/litagent/agent.py +455 -0
- webscout/litagent/constants.py +60 -0
- webscout/litprinter/__init__.py +59 -0
- webscout/optimizers.py +419 -419
- webscout/scout/README.md +404 -0
- webscout/scout/__init__.py +8 -0
- webscout/scout/core/__init__.py +7 -0
- webscout/scout/core/crawler.py +210 -0
- webscout/scout/core/scout.py +607 -0
- webscout/scout/core/search_result.py +96 -0
- webscout/scout/core/text_analyzer.py +63 -0
- webscout/scout/core/text_utils.py +277 -0
- webscout/scout/core/web_analyzer.py +52 -0
- webscout/scout/element.py +478 -0
- webscout/scout/parsers/__init__.py +69 -0
- webscout/scout/parsers/html5lib_parser.py +172 -0
- webscout/scout/parsers/html_parser.py +236 -0
- webscout/scout/parsers/lxml_parser.py +178 -0
- webscout/scout/utils.py +37 -0
- webscout/swiftcli/Readme.md +323 -0
- webscout/swiftcli/__init__.py +95 -0
- webscout/swiftcli/core/__init__.py +7 -0
- webscout/swiftcli/core/cli.py +297 -0
- webscout/swiftcli/core/context.py +104 -0
- webscout/swiftcli/core/group.py +241 -0
- webscout/swiftcli/decorators/__init__.py +28 -0
- webscout/swiftcli/decorators/command.py +221 -0
- webscout/swiftcli/decorators/options.py +220 -0
- webscout/swiftcli/decorators/output.py +252 -0
- webscout/swiftcli/exceptions.py +21 -0
- webscout/swiftcli/plugins/__init__.py +9 -0
- webscout/swiftcli/plugins/base.py +135 -0
- webscout/swiftcli/plugins/manager.py +269 -0
- webscout/swiftcli/utils/__init__.py +59 -0
- webscout/swiftcli/utils/formatting.py +252 -0
- webscout/swiftcli/utils/parsing.py +267 -0
- webscout/version.py +1 -1
- webscout/webscout_search.py +2 -182
- webscout/webscout_search_async.py +1 -179
- webscout/zeroart/README.md +89 -0
- webscout/zeroart/__init__.py +135 -0
- webscout/zeroart/base.py +66 -0
- webscout/zeroart/effects.py +101 -0
- webscout/zeroart/fonts.py +1239 -0
- {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/METADATA +262 -83
- webscout-8.2.9.dist-info/RECORD +289 -0
- {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/WHEEL +1 -1
- {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/entry_points.txt +1 -0
- webscout-8.2.7.dist-info/RECORD +0 -26
- {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Base plugin system for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from ..exceptions import PluginError
|
|
6
|
+
|
|
7
|
+
class Plugin:
|
|
8
|
+
"""
|
|
9
|
+
Base class for SwiftCLI plugins.
|
|
10
|
+
|
|
11
|
+
Plugins can extend the CLI functionality by hooking into various stages
|
|
12
|
+
of command execution. Subclass this class and override the methods
|
|
13
|
+
you want to hook into.
|
|
14
|
+
|
|
15
|
+
Attributes:
|
|
16
|
+
app: The CLI application instance
|
|
17
|
+
enabled: Whether the plugin is enabled
|
|
18
|
+
config: Plugin configuration dictionary
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
class LoggingPlugin(Plugin):
|
|
22
|
+
def before_command(self, command, args):
|
|
23
|
+
print(f"[LOG] Running command: {command}")
|
|
24
|
+
|
|
25
|
+
def after_command(self, command, args, result):
|
|
26
|
+
print(f"[LOG] Command completed: {result}")
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self):
|
|
30
|
+
self.app = None # Set by plugin manager
|
|
31
|
+
self.enabled: bool = True
|
|
32
|
+
self.config: Dict[str, Any] = {}
|
|
33
|
+
|
|
34
|
+
def init_app(self, app: Any) -> None:
|
|
35
|
+
"""
|
|
36
|
+
Initialize plugin with CLI app instance.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
app: The CLI application instance
|
|
40
|
+
"""
|
|
41
|
+
self.app = app
|
|
42
|
+
|
|
43
|
+
def before_command(self, command: str, args: List[str]) -> Optional[bool]:
|
|
44
|
+
"""
|
|
45
|
+
Called before command execution.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
command: Command name
|
|
49
|
+
args: Command arguments
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Optional[bool]: Return False to prevent command execution
|
|
53
|
+
"""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
def after_command(self, command: str, args: List[str], result: Any) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Called after command execution.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
command: Command name
|
|
62
|
+
args: Command arguments
|
|
63
|
+
result: Command result
|
|
64
|
+
"""
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
def on_error(self, command: str, error: Exception) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Called when command raises an error.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
command: Command name
|
|
73
|
+
error: The exception that was raised
|
|
74
|
+
"""
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
def on_help(self, command: str) -> Optional[str]:
|
|
78
|
+
"""
|
|
79
|
+
Called when help is requested for a command.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
command: Command name
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Optional[str]: Additional help text to display
|
|
86
|
+
"""
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
def on_completion(self, command: str, incomplete: str) -> List[str]:
|
|
90
|
+
"""
|
|
91
|
+
Called when shell completion is requested.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
command: Command name
|
|
95
|
+
incomplete: Incomplete text to complete
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
List[str]: Possible completions
|
|
99
|
+
"""
|
|
100
|
+
return []
|
|
101
|
+
|
|
102
|
+
def configure(self, config: Dict[str, Any]) -> None:
|
|
103
|
+
"""
|
|
104
|
+
Configure the plugin.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
config: Configuration dictionary
|
|
108
|
+
"""
|
|
109
|
+
self.config.update(config)
|
|
110
|
+
|
|
111
|
+
def validate_config(self) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Validate plugin configuration.
|
|
114
|
+
|
|
115
|
+
Raises:
|
|
116
|
+
PluginError: If configuration is invalid
|
|
117
|
+
"""
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
def enable(self) -> None:
|
|
121
|
+
"""Enable the plugin."""
|
|
122
|
+
self.enabled = True
|
|
123
|
+
|
|
124
|
+
def disable(self) -> None:
|
|
125
|
+
"""Disable the plugin."""
|
|
126
|
+
self.enabled = False
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def name(self) -> str:
|
|
130
|
+
"""Get plugin name."""
|
|
131
|
+
return self.__class__.__name__
|
|
132
|
+
|
|
133
|
+
def __repr__(self) -> str:
|
|
134
|
+
status = "enabled" if self.enabled else "disabled"
|
|
135
|
+
return f"<{self.name} [{status}]>"
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"""Plugin manager for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import tempfile
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Dict, List, Optional, Type
|
|
9
|
+
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
|
|
12
|
+
from ..exceptions import PluginError
|
|
13
|
+
from .base import Plugin
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
class PluginManager:
|
|
18
|
+
"""
|
|
19
|
+
Manages SwiftCLI plugins.
|
|
20
|
+
|
|
21
|
+
The plugin manager handles plugin registration, loading, and execution.
|
|
22
|
+
It provides hooks for plugins to extend CLI functionality at various points
|
|
23
|
+
during command execution.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
plugins: List of registered plugins
|
|
27
|
+
plugin_dir: Directory where plugins are stored
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
# Register a plugin
|
|
31
|
+
plugin_manager = PluginManager()
|
|
32
|
+
plugin_manager.register(LoggingPlugin())
|
|
33
|
+
|
|
34
|
+
# Load plugins from directory
|
|
35
|
+
plugin_manager.load_plugins()
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, plugin_dir: Optional[str] = None):
|
|
39
|
+
"""
|
|
40
|
+
Initialize plugin manager.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
plugin_dir: Optional custom plugin directory path
|
|
44
|
+
"""
|
|
45
|
+
self.plugins: List[Plugin] = []
|
|
46
|
+
# Use temporary directory instead of ~/.swiftcli/plugins
|
|
47
|
+
if plugin_dir:
|
|
48
|
+
self.plugin_dir = plugin_dir
|
|
49
|
+
os.makedirs(self.plugin_dir, exist_ok=True)
|
|
50
|
+
else:
|
|
51
|
+
# Create a temporary directory that will be cleaned up when the process exits
|
|
52
|
+
self.temp_dir = tempfile.TemporaryDirectory(prefix="swiftcli_")
|
|
53
|
+
self.plugin_dir = self.temp_dir.name
|
|
54
|
+
|
|
55
|
+
# Add plugin directory to Python path
|
|
56
|
+
if self.plugin_dir not in sys.path:
|
|
57
|
+
sys.path.append(self.plugin_dir)
|
|
58
|
+
|
|
59
|
+
def register(self, plugin: Plugin) -> None:
|
|
60
|
+
"""
|
|
61
|
+
Register a new plugin.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
plugin: Plugin instance to register
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
PluginError: If plugin is invalid or already registered
|
|
68
|
+
"""
|
|
69
|
+
if not isinstance(plugin, Plugin):
|
|
70
|
+
raise PluginError(f"Invalid plugin type: {type(plugin)}")
|
|
71
|
+
|
|
72
|
+
if self._get_plugin(plugin.name):
|
|
73
|
+
raise PluginError(f"Plugin already registered: {plugin.name}")
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
plugin.validate_config()
|
|
77
|
+
except Exception as e:
|
|
78
|
+
raise PluginError(f"Plugin configuration invalid: {str(e)}")
|
|
79
|
+
|
|
80
|
+
self.plugins.append(plugin)
|
|
81
|
+
|
|
82
|
+
def unregister(self, plugin_name: str) -> None:
|
|
83
|
+
"""
|
|
84
|
+
Unregister a plugin.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
plugin_name: Name of plugin to unregister
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
PluginError: If plugin not found
|
|
91
|
+
"""
|
|
92
|
+
plugin = self._get_plugin(plugin_name)
|
|
93
|
+
if not plugin:
|
|
94
|
+
raise PluginError(f"Plugin not found: {plugin_name}")
|
|
95
|
+
|
|
96
|
+
self.plugins.remove(plugin)
|
|
97
|
+
|
|
98
|
+
def load_plugins(self) -> None:
|
|
99
|
+
"""
|
|
100
|
+
Load all plugins from plugin directory.
|
|
101
|
+
|
|
102
|
+
This method searches for Python files in the plugin directory and
|
|
103
|
+
attempts to load any Plugin subclasses defined in them.
|
|
104
|
+
"""
|
|
105
|
+
for file in Path(self.plugin_dir).glob("*.py"):
|
|
106
|
+
if file.name.startswith("_"):
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
module = importlib.import_module(file.stem)
|
|
111
|
+
|
|
112
|
+
# Find Plugin subclasses in module
|
|
113
|
+
for attr_name in dir(module):
|
|
114
|
+
attr = getattr(module, attr_name)
|
|
115
|
+
if (isinstance(attr, type) and
|
|
116
|
+
issubclass(attr, Plugin) and
|
|
117
|
+
attr is not Plugin):
|
|
118
|
+
plugin = attr()
|
|
119
|
+
self.register(plugin)
|
|
120
|
+
|
|
121
|
+
except Exception as e:
|
|
122
|
+
console.print(f"[red]Error loading plugin {file.name}: {e}[/red]")
|
|
123
|
+
|
|
124
|
+
def init_plugins(self, app: Any) -> None:
|
|
125
|
+
"""
|
|
126
|
+
Initialize all plugins with the CLI application instance.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
app: The CLI application instance
|
|
130
|
+
"""
|
|
131
|
+
for plugin in self.plugins:
|
|
132
|
+
plugin.init_app(app)
|
|
133
|
+
|
|
134
|
+
def configure_plugins(self, config: Dict[str, Dict[str, Any]]) -> None:
|
|
135
|
+
"""
|
|
136
|
+
Configure plugins with provided configuration.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
config: Dictionary of plugin configurations
|
|
140
|
+
"""
|
|
141
|
+
for plugin in self.plugins:
|
|
142
|
+
if plugin.name in config:
|
|
143
|
+
plugin.configure(config[plugin.name])
|
|
144
|
+
|
|
145
|
+
def before_command(self, command: str, args: List[str]) -> bool:
|
|
146
|
+
"""
|
|
147
|
+
Run before_command hooks for all plugins.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
command: Command name
|
|
151
|
+
args: Command arguments
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
bool: False if any plugin prevents command execution
|
|
155
|
+
"""
|
|
156
|
+
for plugin in self.plugins:
|
|
157
|
+
if not plugin.enabled:
|
|
158
|
+
continue
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
result = plugin.before_command(command, args)
|
|
162
|
+
if result is False:
|
|
163
|
+
return False
|
|
164
|
+
except Exception as e:
|
|
165
|
+
console.print(f"[red]Error in plugin {plugin.name}: {e}[/red]")
|
|
166
|
+
if plugin.app and getattr(plugin.app, 'debug', False):
|
|
167
|
+
import traceback
|
|
168
|
+
traceback.print_exc()
|
|
169
|
+
|
|
170
|
+
return True
|
|
171
|
+
|
|
172
|
+
def after_command(self, command: str, args: List[str], result: Any) -> None:
|
|
173
|
+
"""
|
|
174
|
+
Run after_command hooks for all plugins.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
command: Command name
|
|
178
|
+
args: Command arguments
|
|
179
|
+
result: Command result
|
|
180
|
+
"""
|
|
181
|
+
for plugin in self.plugins:
|
|
182
|
+
if not plugin.enabled:
|
|
183
|
+
continue
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
plugin.after_command(command, args, result)
|
|
187
|
+
except Exception as e:
|
|
188
|
+
console.print(f"[red]Error in plugin {plugin.name}: {e}[/red]")
|
|
189
|
+
if plugin.app and getattr(plugin.app, 'debug', False):
|
|
190
|
+
import traceback
|
|
191
|
+
traceback.print_exc()
|
|
192
|
+
|
|
193
|
+
def on_error(self, command: str, error: Exception) -> None:
|
|
194
|
+
"""
|
|
195
|
+
Run error hooks for all plugins.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
command: Command name
|
|
199
|
+
error: The exception that was raised
|
|
200
|
+
"""
|
|
201
|
+
for plugin in self.plugins:
|
|
202
|
+
if not plugin.enabled:
|
|
203
|
+
continue
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
plugin.on_error(command, error)
|
|
207
|
+
except Exception as e:
|
|
208
|
+
console.print(f"[red]Error in plugin {plugin.name}: {e}[/red]")
|
|
209
|
+
if plugin.app and getattr(plugin.app, 'debug', False):
|
|
210
|
+
import traceback
|
|
211
|
+
traceback.print_exc()
|
|
212
|
+
|
|
213
|
+
def get_help_text(self, command: str) -> List[str]:
|
|
214
|
+
"""
|
|
215
|
+
Get additional help text from plugins.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
command: Command name
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
List[str]: List of help text strings from plugins
|
|
222
|
+
"""
|
|
223
|
+
help_texts = []
|
|
224
|
+
for plugin in self.plugins:
|
|
225
|
+
if not plugin.enabled:
|
|
226
|
+
continue
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
help_text = plugin.on_help(command)
|
|
230
|
+
if help_text:
|
|
231
|
+
help_texts.append(help_text)
|
|
232
|
+
except Exception as e:
|
|
233
|
+
console.print(f"[red]Error in plugin {plugin.name}: {e}[/red]")
|
|
234
|
+
|
|
235
|
+
return help_texts
|
|
236
|
+
|
|
237
|
+
def get_completions(self, command: str, incomplete: str) -> List[str]:
|
|
238
|
+
"""
|
|
239
|
+
Get command completions from plugins.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
command: Command name
|
|
243
|
+
incomplete: Incomplete text to complete
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
List[str]: Combined list of completions from all plugins
|
|
247
|
+
"""
|
|
248
|
+
completions = []
|
|
249
|
+
for plugin in self.plugins:
|
|
250
|
+
if not plugin.enabled:
|
|
251
|
+
continue
|
|
252
|
+
|
|
253
|
+
try:
|
|
254
|
+
plugin_completions = plugin.on_completion(command, incomplete)
|
|
255
|
+
completions.extend(plugin_completions)
|
|
256
|
+
except Exception as e:
|
|
257
|
+
console.print(f"[red]Error in plugin {plugin.name}: {e}[/red]")
|
|
258
|
+
|
|
259
|
+
return list(set(completions)) # Remove duplicates
|
|
260
|
+
|
|
261
|
+
def _get_plugin(self, name: str) -> Optional[Plugin]:
|
|
262
|
+
"""Get plugin by name."""
|
|
263
|
+
for plugin in self.plugins:
|
|
264
|
+
if plugin.name == name:
|
|
265
|
+
return plugin
|
|
266
|
+
return None
|
|
267
|
+
|
|
268
|
+
def __repr__(self) -> str:
|
|
269
|
+
return f"<PluginManager plugins={len(self.plugins)}>"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Utility functions for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
from .formatting import (
|
|
4
|
+
style_text,
|
|
5
|
+
format_error,
|
|
6
|
+
format_warning,
|
|
7
|
+
format_success,
|
|
8
|
+
format_info,
|
|
9
|
+
create_table,
|
|
10
|
+
truncate_text,
|
|
11
|
+
wrap_text,
|
|
12
|
+
format_dict,
|
|
13
|
+
format_list,
|
|
14
|
+
strip_ansi,
|
|
15
|
+
get_terminal_size,
|
|
16
|
+
clear_screen,
|
|
17
|
+
create_padding
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from .parsing import (
|
|
21
|
+
parse_args,
|
|
22
|
+
validate_required,
|
|
23
|
+
convert_type,
|
|
24
|
+
validate_choice,
|
|
25
|
+
load_config_file,
|
|
26
|
+
parse_key_value,
|
|
27
|
+
parse_list,
|
|
28
|
+
parse_dict,
|
|
29
|
+
get_env_var
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
# Formatting utilities
|
|
34
|
+
'style_text',
|
|
35
|
+
'format_error',
|
|
36
|
+
'format_warning',
|
|
37
|
+
'format_success',
|
|
38
|
+
'format_info',
|
|
39
|
+
'create_table',
|
|
40
|
+
'truncate_text',
|
|
41
|
+
'wrap_text',
|
|
42
|
+
'format_dict',
|
|
43
|
+
'format_list',
|
|
44
|
+
'strip_ansi',
|
|
45
|
+
'get_terminal_size',
|
|
46
|
+
'clear_screen',
|
|
47
|
+
'create_padding',
|
|
48
|
+
|
|
49
|
+
# Parsing utilities
|
|
50
|
+
'parse_args',
|
|
51
|
+
'validate_required',
|
|
52
|
+
'convert_type',
|
|
53
|
+
'validate_choice',
|
|
54
|
+
'load_config_file',
|
|
55
|
+
'parse_key_value',
|
|
56
|
+
'parse_list',
|
|
57
|
+
'parse_dict',
|
|
58
|
+
'get_env_var'
|
|
59
|
+
]
|