webscout 8.2.9__py3-none-any.whl → 2026.1.19__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 +524 -251
- webscout/AIbase.py +247 -319
- webscout/AIutel.py +68 -703
- webscout/Bard.py +1072 -1026
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/__init__.py +20 -12
- webscout/Extra/GitToolkit/gitapi/gist.py +142 -0
- webscout/Extra/GitToolkit/gitapi/organization.py +91 -0
- webscout/Extra/GitToolkit/gitapi/repository.py +308 -195
- webscout/Extra/GitToolkit/gitapi/search.py +162 -0
- webscout/Extra/GitToolkit/gitapi/trending.py +236 -0
- webscout/Extra/GitToolkit/gitapi/user.py +128 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +82 -62
- webscout/Extra/YTToolkit/README.md +443 -375
- webscout/Extra/YTToolkit/YTdownloader.py +953 -957
- webscout/Extra/YTToolkit/__init__.py +3 -3
- webscout/Extra/YTToolkit/transcriber.py +595 -476
- webscout/Extra/YTToolkit/ytapi/README.md +230 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +22 -6
- webscout/Extra/YTToolkit/ytapi/captions.py +190 -0
- webscout/Extra/YTToolkit/ytapi/channel.py +302 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +178 -118
- webscout/Extra/YTToolkit/ytapi/hashtag.py +120 -0
- webscout/Extra/YTToolkit/ytapi/https.py +89 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +59 -59
- webscout/Extra/YTToolkit/ytapi/pool.py +8 -8
- webscout/Extra/YTToolkit/ytapi/query.py +143 -40
- webscout/Extra/YTToolkit/ytapi/shorts.py +122 -0
- webscout/Extra/YTToolkit/ytapi/stream.py +68 -63
- webscout/Extra/YTToolkit/ytapi/suggestions.py +97 -0
- webscout/Extra/YTToolkit/ytapi/utils.py +66 -62
- webscout/Extra/YTToolkit/ytapi/video.py +403 -232
- webscout/Extra/__init__.py +2 -3
- webscout/Extra/gguf.py +1298 -684
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +28 -28
- webscout/Extra/tempmail/async_utils.py +143 -141
- webscout/Extra/tempmail/base.py +172 -161
- webscout/Extra/tempmail/cli.py +191 -187
- webscout/Extra/tempmail/emailnator.py +88 -84
- webscout/Extra/tempmail/mail_tm.py +378 -361
- webscout/Extra/tempmail/temp_mail_io.py +304 -292
- webscout/Extra/weather.py +196 -194
- webscout/Extra/weather_ascii.py +17 -15
- webscout/Provider/AISEARCH/PERPLEXED_search.py +175 -0
- webscout/Provider/AISEARCH/Perplexity.py +292 -333
- webscout/Provider/AISEARCH/README.md +106 -279
- webscout/Provider/AISEARCH/__init__.py +16 -9
- webscout/Provider/AISEARCH/brave_search.py +298 -0
- webscout/Provider/AISEARCH/iask_search.py +357 -410
- webscout/Provider/AISEARCH/monica_search.py +200 -220
- webscout/Provider/AISEARCH/webpilotai_search.py +242 -255
- webscout/Provider/Algion.py +413 -0
- webscout/Provider/Andi.py +74 -69
- webscout/Provider/Apriel.py +313 -0
- webscout/Provider/Ayle.py +323 -0
- webscout/Provider/ChatSandbox.py +329 -342
- webscout/Provider/ClaudeOnline.py +365 -0
- webscout/Provider/Cohere.py +232 -208
- webscout/Provider/DeepAI.py +367 -0
- webscout/Provider/Deepinfra.py +467 -340
- webscout/Provider/EssentialAI.py +217 -0
- webscout/Provider/ExaAI.py +274 -261
- webscout/Provider/Gemini.py +175 -169
- webscout/Provider/GithubChat.py +385 -369
- webscout/Provider/Gradient.py +286 -0
- webscout/Provider/Groq.py +556 -801
- webscout/Provider/HadadXYZ.py +323 -0
- webscout/Provider/HeckAI.py +392 -375
- webscout/Provider/HuggingFace.py +387 -0
- webscout/Provider/IBM.py +340 -0
- webscout/Provider/Jadve.py +317 -291
- webscout/Provider/K2Think.py +306 -0
- webscout/Provider/Koboldai.py +221 -384
- webscout/Provider/Netwrck.py +273 -270
- webscout/Provider/Nvidia.py +310 -0
- webscout/Provider/OPENAI/DeepAI.py +489 -0
- webscout/Provider/OPENAI/K2Think.py +423 -0
- webscout/Provider/OPENAI/PI.py +463 -0
- webscout/Provider/OPENAI/README.md +890 -952
- webscout/Provider/OPENAI/TogetherAI.py +405 -0
- webscout/Provider/OPENAI/TwoAI.py +255 -357
- webscout/Provider/OPENAI/__init__.py +148 -40
- webscout/Provider/OPENAI/ai4chat.py +348 -293
- webscout/Provider/OPENAI/akashgpt.py +436 -0
- webscout/Provider/OPENAI/algion.py +303 -0
- webscout/Provider/OPENAI/{exachat.py → ayle.py} +365 -444
- webscout/Provider/OPENAI/base.py +253 -249
- webscout/Provider/OPENAI/cerebras.py +296 -0
- webscout/Provider/OPENAI/chatgpt.py +870 -556
- webscout/Provider/OPENAI/chatsandbox.py +233 -173
- webscout/Provider/OPENAI/deepinfra.py +403 -322
- webscout/Provider/OPENAI/e2b.py +2370 -1414
- webscout/Provider/OPENAI/elmo.py +278 -0
- webscout/Provider/OPENAI/exaai.py +452 -417
- webscout/Provider/OPENAI/freeassist.py +446 -0
- webscout/Provider/OPENAI/gradient.py +448 -0
- webscout/Provider/OPENAI/groq.py +380 -364
- webscout/Provider/OPENAI/hadadxyz.py +292 -0
- webscout/Provider/OPENAI/heckai.py +333 -308
- webscout/Provider/OPENAI/huggingface.py +321 -0
- webscout/Provider/OPENAI/ibm.py +425 -0
- webscout/Provider/OPENAI/llmchat.py +253 -0
- webscout/Provider/OPENAI/llmchatco.py +378 -335
- webscout/Provider/OPENAI/meta.py +541 -0
- webscout/Provider/OPENAI/netwrck.py +374 -357
- webscout/Provider/OPENAI/nvidia.py +317 -0
- webscout/Provider/OPENAI/oivscode.py +348 -287
- webscout/Provider/OPENAI/openrouter.py +328 -0
- webscout/Provider/OPENAI/pydantic_imports.py +1 -172
- webscout/Provider/OPENAI/sambanova.py +397 -0
- webscout/Provider/OPENAI/sonus.py +305 -304
- webscout/Provider/OPENAI/textpollinations.py +370 -339
- webscout/Provider/OPENAI/toolbaz.py +375 -413
- webscout/Provider/OPENAI/typefully.py +419 -355
- webscout/Provider/OPENAI/typliai.py +279 -0
- webscout/Provider/OPENAI/utils.py +314 -318
- webscout/Provider/OPENAI/wisecat.py +359 -387
- webscout/Provider/OPENAI/writecream.py +185 -163
- webscout/Provider/OPENAI/x0gpt.py +462 -365
- webscout/Provider/OPENAI/zenmux.py +380 -0
- webscout/Provider/OpenRouter.py +386 -0
- webscout/Provider/Openai.py +337 -496
- webscout/Provider/PI.py +443 -429
- webscout/Provider/QwenLM.py +346 -254
- webscout/Provider/STT/__init__.py +28 -0
- webscout/Provider/STT/base.py +303 -0
- webscout/Provider/STT/elevenlabs.py +264 -0
- webscout/Provider/Sambanova.py +317 -0
- webscout/Provider/TTI/README.md +69 -82
- webscout/Provider/TTI/__init__.py +37 -7
- webscout/Provider/TTI/base.py +147 -64
- webscout/Provider/TTI/claudeonline.py +393 -0
- webscout/Provider/TTI/magicstudio.py +292 -201
- webscout/Provider/TTI/miragic.py +180 -0
- webscout/Provider/TTI/pollinations.py +331 -221
- webscout/Provider/TTI/together.py +334 -0
- webscout/Provider/TTI/utils.py +14 -11
- webscout/Provider/TTS/README.md +186 -192
- webscout/Provider/TTS/__init__.py +43 -10
- webscout/Provider/TTS/base.py +523 -159
- webscout/Provider/TTS/deepgram.py +286 -156
- webscout/Provider/TTS/elevenlabs.py +189 -111
- webscout/Provider/TTS/freetts.py +218 -0
- webscout/Provider/TTS/murfai.py +288 -113
- webscout/Provider/TTS/openai_fm.py +364 -129
- webscout/Provider/TTS/parler.py +203 -111
- webscout/Provider/TTS/qwen.py +334 -0
- webscout/Provider/TTS/sherpa.py +286 -0
- webscout/Provider/TTS/speechma.py +693 -580
- webscout/Provider/TTS/streamElements.py +275 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TextPollinationsAI.py +331 -308
- webscout/Provider/TogetherAI.py +450 -0
- webscout/Provider/TwoAI.py +309 -475
- webscout/Provider/TypliAI.py +311 -305
- webscout/Provider/UNFINISHED/ChatHub.py +219 -209
- webscout/Provider/{OPENAI/glider.py → UNFINISHED/ChutesAI.py} +331 -326
- webscout/Provider/{GizAI.py → UNFINISHED/GizAI.py} +300 -295
- webscout/Provider/{Marcus.py → UNFINISHED/Marcus.py} +218 -198
- webscout/Provider/UNFINISHED/Qodo.py +481 -0
- webscout/Provider/{MCPCore.py → UNFINISHED/XenAI.py} +330 -315
- webscout/Provider/UNFINISHED/Youchat.py +347 -330
- webscout/Provider/UNFINISHED/aihumanizer.py +41 -0
- webscout/Provider/UNFINISHED/grammerchecker.py +37 -0
- webscout/Provider/UNFINISHED/liner.py +342 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +246 -263
- webscout/Provider/{samurai.py → UNFINISHED/samurai.py} +231 -224
- webscout/Provider/WiseCat.py +256 -233
- webscout/Provider/WrDoChat.py +390 -370
- webscout/Provider/__init__.py +115 -174
- webscout/Provider/ai4chat.py +181 -174
- webscout/Provider/akashgpt.py +330 -335
- webscout/Provider/cerebras.py +397 -290
- webscout/Provider/cleeai.py +236 -213
- webscout/Provider/elmo.py +291 -283
- webscout/Provider/geminiapi.py +343 -208
- webscout/Provider/julius.py +245 -223
- webscout/Provider/learnfastai.py +333 -325
- webscout/Provider/llama3mitril.py +230 -215
- webscout/Provider/llmchat.py +308 -258
- webscout/Provider/llmchatco.py +321 -306
- webscout/Provider/meta.py +996 -801
- webscout/Provider/oivscode.py +332 -309
- webscout/Provider/searchchat.py +316 -292
- webscout/Provider/sonus.py +264 -258
- webscout/Provider/toolbaz.py +359 -353
- webscout/Provider/turboseek.py +332 -266
- webscout/Provider/typefully.py +262 -202
- webscout/Provider/x0gpt.py +332 -299
- webscout/__init__.py +31 -39
- webscout/__main__.py +5 -5
- webscout/cli.py +585 -524
- webscout/client.py +1497 -70
- webscout/conversation.py +140 -436
- webscout/exceptions.py +383 -362
- webscout/litagent/__init__.py +29 -29
- webscout/litagent/agent.py +492 -455
- webscout/litagent/constants.py +60 -60
- webscout/models.py +505 -181
- webscout/optimizers.py +74 -420
- webscout/prompt_manager.py +376 -288
- webscout/sanitize.py +1514 -0
- webscout/scout/README.md +452 -404
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +7 -7
- webscout/scout/core/crawler.py +330 -210
- webscout/scout/core/scout.py +800 -607
- webscout/scout/core/search_result.py +51 -96
- webscout/scout/core/text_analyzer.py +64 -63
- webscout/scout/core/text_utils.py +412 -277
- webscout/scout/core/web_analyzer.py +54 -52
- webscout/scout/element.py +872 -478
- webscout/scout/parsers/__init__.py +70 -69
- webscout/scout/parsers/html5lib_parser.py +182 -172
- webscout/scout/parsers/html_parser.py +238 -236
- webscout/scout/parsers/lxml_parser.py +203 -178
- webscout/scout/utils.py +38 -37
- webscout/search/__init__.py +47 -0
- webscout/search/base.py +201 -0
- webscout/search/bing_main.py +45 -0
- webscout/search/brave_main.py +92 -0
- webscout/search/duckduckgo_main.py +57 -0
- webscout/search/engines/__init__.py +127 -0
- webscout/search/engines/bing/__init__.py +15 -0
- webscout/search/engines/bing/base.py +35 -0
- webscout/search/engines/bing/images.py +114 -0
- webscout/search/engines/bing/news.py +96 -0
- webscout/search/engines/bing/suggestions.py +36 -0
- webscout/search/engines/bing/text.py +109 -0
- webscout/search/engines/brave/__init__.py +19 -0
- webscout/search/engines/brave/base.py +47 -0
- webscout/search/engines/brave/images.py +213 -0
- webscout/search/engines/brave/news.py +353 -0
- webscout/search/engines/brave/suggestions.py +318 -0
- webscout/search/engines/brave/text.py +167 -0
- webscout/search/engines/brave/videos.py +364 -0
- webscout/search/engines/duckduckgo/__init__.py +25 -0
- webscout/search/engines/duckduckgo/answers.py +80 -0
- webscout/search/engines/duckduckgo/base.py +189 -0
- webscout/search/engines/duckduckgo/images.py +100 -0
- webscout/search/engines/duckduckgo/maps.py +183 -0
- webscout/search/engines/duckduckgo/news.py +70 -0
- webscout/search/engines/duckduckgo/suggestions.py +22 -0
- webscout/search/engines/duckduckgo/text.py +221 -0
- webscout/search/engines/duckduckgo/translate.py +48 -0
- webscout/search/engines/duckduckgo/videos.py +80 -0
- webscout/search/engines/duckduckgo/weather.py +84 -0
- webscout/search/engines/mojeek.py +61 -0
- webscout/search/engines/wikipedia.py +77 -0
- webscout/search/engines/yahoo/__init__.py +41 -0
- webscout/search/engines/yahoo/answers.py +19 -0
- webscout/search/engines/yahoo/base.py +34 -0
- webscout/search/engines/yahoo/images.py +323 -0
- webscout/search/engines/yahoo/maps.py +19 -0
- webscout/search/engines/yahoo/news.py +258 -0
- webscout/search/engines/yahoo/suggestions.py +140 -0
- webscout/search/engines/yahoo/text.py +273 -0
- webscout/search/engines/yahoo/translate.py +19 -0
- webscout/search/engines/yahoo/videos.py +302 -0
- webscout/search/engines/yahoo/weather.py +220 -0
- webscout/search/engines/yandex.py +67 -0
- webscout/search/engines/yep/__init__.py +13 -0
- webscout/search/engines/yep/base.py +34 -0
- webscout/search/engines/yep/images.py +101 -0
- webscout/search/engines/yep/suggestions.py +38 -0
- webscout/search/engines/yep/text.py +99 -0
- webscout/search/http_client.py +172 -0
- webscout/search/results.py +141 -0
- webscout/search/yahoo_main.py +57 -0
- webscout/search/yep_main.py +48 -0
- webscout/server/__init__.py +48 -0
- webscout/server/config.py +78 -0
- webscout/server/exceptions.py +69 -0
- webscout/server/providers.py +286 -0
- webscout/server/request_models.py +131 -0
- webscout/server/request_processing.py +404 -0
- webscout/server/routes.py +642 -0
- webscout/server/server.py +351 -0
- webscout/server/ui_templates.py +1171 -0
- webscout/swiftcli/__init__.py +79 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +574 -297
- webscout/swiftcli/core/context.py +98 -104
- webscout/swiftcli/core/group.py +268 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +243 -221
- webscout/swiftcli/decorators/options.py +247 -220
- webscout/swiftcli/decorators/output.py +392 -252
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +134 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +58 -59
- webscout/swiftcli/utils/formatting.py +251 -252
- webscout/swiftcli/utils/parsing.py +368 -267
- webscout/update_checker.py +280 -136
- webscout/utils.py +28 -14
- webscout/version.py +2 -1
- webscout/version.py.bak +3 -0
- webscout/zeroart/__init__.py +218 -135
- webscout/zeroart/base.py +70 -66
- webscout/zeroart/effects.py +155 -101
- webscout/zeroart/fonts.py +1799 -1239
- webscout-2026.1.19.dist-info/METADATA +638 -0
- webscout-2026.1.19.dist-info/RECORD +312 -0
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/WHEEL +1 -1
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/entry_points.txt +1 -1
- webscout/DWEBS.py +0 -520
- webscout/Extra/Act.md +0 -309
- webscout/Extra/GitToolkit/gitapi/README.md +0 -110
- webscout/Extra/autocoder/__init__.py +0 -9
- webscout/Extra/autocoder/autocoder.py +0 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +0 -332
- webscout/Extra/gguf.md +0 -430
- webscout/Extra/weather.md +0 -281
- webscout/Litlogger/README.md +0 -10
- webscout/Litlogger/__init__.py +0 -15
- webscout/Litlogger/formats.py +0 -4
- webscout/Litlogger/handlers.py +0 -103
- webscout/Litlogger/levels.py +0 -13
- webscout/Litlogger/logger.py +0 -92
- webscout/Provider/AI21.py +0 -177
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/AISEARCH/felo_search.py +0 -202
- webscout/Provider/AISEARCH/genspark_search.py +0 -324
- webscout/Provider/AISEARCH/hika_search.py +0 -186
- webscout/Provider/AISEARCH/scira_search.py +0 -298
- webscout/Provider/Aitopia.py +0 -316
- webscout/Provider/AllenAI.py +0 -440
- webscout/Provider/Blackboxai.py +0 -791
- webscout/Provider/ChatGPTClone.py +0 -237
- webscout/Provider/ChatGPTGratis.py +0 -194
- webscout/Provider/Cloudflare.py +0 -324
- webscout/Provider/ExaChat.py +0 -358
- webscout/Provider/Flowith.py +0 -217
- webscout/Provider/FreeGemini.py +0 -250
- webscout/Provider/Glider.py +0 -225
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/HuggingFaceChat.py +0 -469
- webscout/Provider/Hunyuan.py +0 -283
- webscout/Provider/LambdaChat.py +0 -411
- webscout/Provider/Llama3.py +0 -259
- webscout/Provider/Nemotron.py +0 -218
- webscout/Provider/OLLAMA.py +0 -396
- webscout/Provider/OPENAI/BLACKBOXAI.py +0 -766
- webscout/Provider/OPENAI/Cloudflare.py +0 -378
- webscout/Provider/OPENAI/FreeGemini.py +0 -283
- webscout/Provider/OPENAI/NEMOTRON.py +0 -232
- webscout/Provider/OPENAI/Qwen3.py +0 -283
- webscout/Provider/OPENAI/api.py +0 -969
- webscout/Provider/OPENAI/c4ai.py +0 -373
- webscout/Provider/OPENAI/chatgptclone.py +0 -494
- webscout/Provider/OPENAI/copilot.py +0 -242
- webscout/Provider/OPENAI/flowith.py +0 -162
- webscout/Provider/OPENAI/freeaichat.py +0 -359
- webscout/Provider/OPENAI/mcpcore.py +0 -389
- webscout/Provider/OPENAI/multichat.py +0 -376
- webscout/Provider/OPENAI/opkfc.py +0 -496
- webscout/Provider/OPENAI/scirachat.py +0 -477
- webscout/Provider/OPENAI/standardinput.py +0 -433
- webscout/Provider/OPENAI/typegpt.py +0 -364
- webscout/Provider/OPENAI/uncovrAI.py +0 -463
- webscout/Provider/OPENAI/venice.py +0 -431
- webscout/Provider/OPENAI/yep.py +0 -382
- webscout/Provider/OpenGPT.py +0 -209
- webscout/Provider/Perplexitylabs.py +0 -415
- webscout/Provider/Reka.py +0 -214
- webscout/Provider/StandardInput.py +0 -290
- webscout/Provider/TTI/aiarta.py +0 -365
- webscout/Provider/TTI/artbit.py +0 -0
- webscout/Provider/TTI/fastflux.py +0 -200
- webscout/Provider/TTI/piclumen.py +0 -203
- webscout/Provider/TTI/pixelmuse.py +0 -225
- webscout/Provider/TTS/gesserit.py +0 -128
- webscout/Provider/TTS/sthir.py +0 -94
- webscout/Provider/TeachAnything.py +0 -229
- webscout/Provider/UNFINISHED/puterjs.py +0 -635
- webscout/Provider/UNFINISHED/test_lmarena.py +0 -119
- webscout/Provider/Venice.py +0 -258
- webscout/Provider/VercelAI.py +0 -253
- webscout/Provider/Writecream.py +0 -246
- webscout/Provider/WritingMate.py +0 -269
- webscout/Provider/asksteve.py +0 -220
- webscout/Provider/chatglm.py +0 -215
- webscout/Provider/copilot.py +0 -425
- webscout/Provider/freeaichat.py +0 -285
- webscout/Provider/granite.py +0 -235
- webscout/Provider/hermes.py +0 -266
- webscout/Provider/koala.py +0 -170
- webscout/Provider/lmarena.py +0 -198
- webscout/Provider/multichat.py +0 -364
- webscout/Provider/scira_chat.py +0 -299
- webscout/Provider/scnet.py +0 -243
- webscout/Provider/talkai.py +0 -194
- webscout/Provider/typegpt.py +0 -289
- webscout/Provider/uncovr.py +0 -368
- webscout/Provider/yep.py +0 -389
- webscout/litagent/Readme.md +0 -276
- webscout/litprinter/__init__.py +0 -59
- webscout/swiftcli/Readme.md +0 -323
- webscout/tempid.py +0 -128
- webscout/webscout_search.py +0 -1184
- webscout/webscout_search_async.py +0 -654
- webscout/yep_search.py +0 -347
- webscout/zeroart/README.md +0 -89
- webscout-8.2.9.dist-info/METADATA +0 -1033
- webscout-8.2.9.dist-info/RECORD +0 -289
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/top_level.txt +0 -0
|
@@ -1,269 +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
|
|
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)}>"
|
|
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
|
|
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)}>"
|
|
@@ -1,59 +1,58 @@
|
|
|
1
|
-
"""Utility functions for SwiftCLI."""
|
|
2
|
-
|
|
3
|
-
from .formatting import (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
strip_ansi,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
|
|
59
|
-
]
|
|
1
|
+
"""Utility functions for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
from .formatting import (
|
|
4
|
+
clear_screen,
|
|
5
|
+
create_padding,
|
|
6
|
+
create_table,
|
|
7
|
+
format_dict,
|
|
8
|
+
format_error,
|
|
9
|
+
format_info,
|
|
10
|
+
format_list,
|
|
11
|
+
format_success,
|
|
12
|
+
format_warning,
|
|
13
|
+
get_terminal_size,
|
|
14
|
+
strip_ansi,
|
|
15
|
+
style_text,
|
|
16
|
+
truncate_text,
|
|
17
|
+
wrap_text,
|
|
18
|
+
)
|
|
19
|
+
from .parsing import (
|
|
20
|
+
convert_type,
|
|
21
|
+
get_env_var,
|
|
22
|
+
load_config_file,
|
|
23
|
+
parse_args,
|
|
24
|
+
parse_dict,
|
|
25
|
+
parse_key_value,
|
|
26
|
+
parse_list,
|
|
27
|
+
validate_choice,
|
|
28
|
+
validate_required,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
# Formatting utilities
|
|
33
|
+
'style_text',
|
|
34
|
+
'format_error',
|
|
35
|
+
'format_warning',
|
|
36
|
+
'format_success',
|
|
37
|
+
'format_info',
|
|
38
|
+
'create_table',
|
|
39
|
+
'truncate_text',
|
|
40
|
+
'wrap_text',
|
|
41
|
+
'format_dict',
|
|
42
|
+
'format_list',
|
|
43
|
+
'strip_ansi',
|
|
44
|
+
'get_terminal_size',
|
|
45
|
+
'clear_screen',
|
|
46
|
+
'create_padding',
|
|
47
|
+
|
|
48
|
+
# Parsing utilities
|
|
49
|
+
'parse_args',
|
|
50
|
+
'validate_required',
|
|
51
|
+
'convert_type',
|
|
52
|
+
'validate_choice',
|
|
53
|
+
'load_config_file',
|
|
54
|
+
'parse_key_value',
|
|
55
|
+
'parse_list',
|
|
56
|
+
'parse_dict',
|
|
57
|
+
'get_env_var'
|
|
58
|
+
]
|