qodalis-cli-server 1.0.0b1__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.
- qodalis_cli/__init__.py +112 -0
- qodalis_cli/abstractions/__init__.py +25 -0
- qodalis_cli/abstractions/cli_command_author.py +7 -0
- qodalis_cli/abstractions/cli_command_parameter_descriptor.py +6 -0
- qodalis_cli/abstractions/cli_command_processor.py +6 -0
- qodalis_cli/abstractions/cli_process_command.py +3 -0
- qodalis_cli/controllers/__init__.py +11 -0
- qodalis_cli/controllers/cli_controller.py +111 -0
- qodalis_cli/controllers/cli_controller_v2.py +91 -0
- qodalis_cli/controllers/cli_version_controller.py +45 -0
- qodalis_cli/controllers/filesystem_controller.py +329 -0
- qodalis_cli/create_cli_server.py +157 -0
- qodalis_cli/extensions/__init__.py +3 -0
- qodalis_cli/extensions/cli_builder.py +51 -0
- qodalis_cli/filesystem/__init__.py +4 -0
- qodalis_cli/filesystem/filesystem_options.py +16 -0
- qodalis_cli/filesystem/filesystem_path_validator.py +42 -0
- qodalis_cli/models/__init__.py +13 -0
- qodalis_cli/models/cli_server_command_descriptor.py +30 -0
- qodalis_cli/models/cli_server_output.py +47 -0
- qodalis_cli/models/cli_server_response.py +12 -0
- qodalis_cli/processors/__init__.py +17 -0
- qodalis_cli/processors/cli_base64_command_processor.py +61 -0
- qodalis_cli/processors/cli_echo_command_processor.py +17 -0
- qodalis_cli/processors/cli_hash_command_processor.py +49 -0
- qodalis_cli/processors/cli_http_command_processor.py +143 -0
- qodalis_cli/processors/cli_status_command_processor.py +24 -0
- qodalis_cli/processors/cli_system_command_processor.py +34 -0
- qodalis_cli/processors/cli_uuid_command_processor.py +39 -0
- qodalis_cli/server.py +44 -0
- qodalis_cli/services/__init__.py +19 -0
- qodalis_cli/services/cli_command_executor_service.py +62 -0
- qodalis_cli/services/cli_command_registry.py +68 -0
- qodalis_cli/services/cli_event_socket_manager.py +46 -0
- qodalis_cli/services/cli_log_socket_manager.py +153 -0
- qodalis_cli/services/cli_response_builder.py +73 -0
- qodalis_cli/services/cli_shell_session_manager.py +201 -0
- qodalis_cli_server-1.0.0b1.dist-info/METADATA +689 -0
- qodalis_cli_server-1.0.0b1.dist-info/RECORD +42 -0
- qodalis_cli_server-1.0.0b1.dist-info/WHEEL +4 -0
- qodalis_cli_server-1.0.0b1.dist-info/entry_points.txt +2 -0
- qodalis_cli_server-1.0.0b1.dist-info/licenses/LICENSE +21 -0
qodalis_cli/__init__.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Qodalis CLI Server - A Python CLI server framework for the Qodalis CLI ecosystem."""
|
|
2
|
+
|
|
3
|
+
from .abstractions import (
|
|
4
|
+
ICliCommandAuthor,
|
|
5
|
+
CliCommandAuthor,
|
|
6
|
+
DEFAULT_LIBRARY_AUTHOR,
|
|
7
|
+
CliProcessCommand,
|
|
8
|
+
ICliCommandParameterDescriptor,
|
|
9
|
+
CliCommandParameterDescriptor,
|
|
10
|
+
ICliCommandProcessor,
|
|
11
|
+
CliCommandProcessor,
|
|
12
|
+
ICliModule,
|
|
13
|
+
CliModule,
|
|
14
|
+
)
|
|
15
|
+
from .models import (
|
|
16
|
+
CliServerOutput,
|
|
17
|
+
CliServerResponse,
|
|
18
|
+
CliServerCommandDescriptor,
|
|
19
|
+
CliServerCommandParameterDescriptorDto,
|
|
20
|
+
)
|
|
21
|
+
from .services import (
|
|
22
|
+
ICliCommandRegistry,
|
|
23
|
+
CliCommandRegistry,
|
|
24
|
+
ICliResponseBuilder,
|
|
25
|
+
CliResponseBuilder,
|
|
26
|
+
ICliCommandExecutorService,
|
|
27
|
+
CliCommandExecutorService,
|
|
28
|
+
CliEventSocketManager,
|
|
29
|
+
CliShellSessionManager,
|
|
30
|
+
)
|
|
31
|
+
from .controllers import create_cli_router, create_cli_router_v2, create_cli_version_router, create_filesystem_router
|
|
32
|
+
from .extensions import CliBuilder
|
|
33
|
+
from .filesystem import FileSystemOptions, FileSystemPathValidator
|
|
34
|
+
|
|
35
|
+
# Re-export filesystem plugin types for convenience
|
|
36
|
+
from qodalis_cli_filesystem import (
|
|
37
|
+
FileEntry,
|
|
38
|
+
FileStat,
|
|
39
|
+
FileStorageExistsError,
|
|
40
|
+
FileStorageIsADirectoryError,
|
|
41
|
+
FileStorageNotADirectoryError,
|
|
42
|
+
FileStorageNotFoundError,
|
|
43
|
+
FileStoragePermissionError,
|
|
44
|
+
IFileStorageProvider,
|
|
45
|
+
)
|
|
46
|
+
from qodalis_cli_filesystem.providers import (
|
|
47
|
+
InMemoryFileStorageProvider,
|
|
48
|
+
OsFileStorageProvider,
|
|
49
|
+
OsProviderOptions,
|
|
50
|
+
)
|
|
51
|
+
from .processors import (
|
|
52
|
+
CliEchoCommandProcessor,
|
|
53
|
+
CliStatusCommandProcessor,
|
|
54
|
+
CliSystemCommandProcessor,
|
|
55
|
+
CliHttpCommandProcessor,
|
|
56
|
+
CliHashCommandProcessor,
|
|
57
|
+
CliBase64CommandProcessor,
|
|
58
|
+
CliUuidCommandProcessor,
|
|
59
|
+
)
|
|
60
|
+
from .create_cli_server import create_cli_server, CliServerOptions
|
|
61
|
+
|
|
62
|
+
__all__ = [
|
|
63
|
+
"ICliCommandAuthor",
|
|
64
|
+
"CliCommandAuthor",
|
|
65
|
+
"DEFAULT_LIBRARY_AUTHOR",
|
|
66
|
+
"CliProcessCommand",
|
|
67
|
+
"ICliCommandParameterDescriptor",
|
|
68
|
+
"CliCommandParameterDescriptor",
|
|
69
|
+
"ICliCommandProcessor",
|
|
70
|
+
"CliCommandProcessor",
|
|
71
|
+
"ICliModule",
|
|
72
|
+
"CliModule",
|
|
73
|
+
"CliServerOutput",
|
|
74
|
+
"CliServerResponse",
|
|
75
|
+
"CliServerCommandDescriptor",
|
|
76
|
+
"CliServerCommandParameterDescriptorDto",
|
|
77
|
+
"ICliCommandRegistry",
|
|
78
|
+
"CliCommandRegistry",
|
|
79
|
+
"ICliResponseBuilder",
|
|
80
|
+
"CliResponseBuilder",
|
|
81
|
+
"ICliCommandExecutorService",
|
|
82
|
+
"CliCommandExecutorService",
|
|
83
|
+
"CliEventSocketManager",
|
|
84
|
+
"CliShellSessionManager",
|
|
85
|
+
"create_cli_router",
|
|
86
|
+
"create_cli_router_v2",
|
|
87
|
+
"create_cli_version_router",
|
|
88
|
+
"create_filesystem_router",
|
|
89
|
+
"CliBuilder",
|
|
90
|
+
"FileSystemOptions",
|
|
91
|
+
"FileSystemPathValidator",
|
|
92
|
+
"CliEchoCommandProcessor",
|
|
93
|
+
"CliStatusCommandProcessor",
|
|
94
|
+
"CliSystemCommandProcessor",
|
|
95
|
+
"CliHttpCommandProcessor",
|
|
96
|
+
"CliHashCommandProcessor",
|
|
97
|
+
"CliBase64CommandProcessor",
|
|
98
|
+
"CliUuidCommandProcessor",
|
|
99
|
+
"create_cli_server",
|
|
100
|
+
"CliServerOptions",
|
|
101
|
+
"FileEntry",
|
|
102
|
+
"FileStat",
|
|
103
|
+
"FileStorageExistsError",
|
|
104
|
+
"FileStorageIsADirectoryError",
|
|
105
|
+
"FileStorageNotADirectoryError",
|
|
106
|
+
"FileStorageNotFoundError",
|
|
107
|
+
"FileStoragePermissionError",
|
|
108
|
+
"IFileStorageProvider",
|
|
109
|
+
"InMemoryFileStorageProvider",
|
|
110
|
+
"OsFileStorageProvider",
|
|
111
|
+
"OsProviderOptions",
|
|
112
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from qodalis_cli_server_abstractions import (
|
|
2
|
+
ICliCommandAuthor,
|
|
3
|
+
CliCommandAuthor,
|
|
4
|
+
DEFAULT_LIBRARY_AUTHOR,
|
|
5
|
+
CliProcessCommand,
|
|
6
|
+
ICliCommandParameterDescriptor,
|
|
7
|
+
CliCommandParameterDescriptor,
|
|
8
|
+
ICliCommandProcessor,
|
|
9
|
+
CliCommandProcessor,
|
|
10
|
+
ICliModule,
|
|
11
|
+
CliModule,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"ICliCommandAuthor",
|
|
16
|
+
"CliCommandAuthor",
|
|
17
|
+
"DEFAULT_LIBRARY_AUTHOR",
|
|
18
|
+
"CliProcessCommand",
|
|
19
|
+
"ICliCommandParameterDescriptor",
|
|
20
|
+
"CliCommandParameterDescriptor",
|
|
21
|
+
"ICliCommandProcessor",
|
|
22
|
+
"CliCommandProcessor",
|
|
23
|
+
"ICliModule",
|
|
24
|
+
"CliModule",
|
|
25
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .cli_controller import create_cli_router
|
|
2
|
+
from .cli_controller_v2 import create_cli_router_v2
|
|
3
|
+
from .cli_version_controller import create_cli_version_router
|
|
4
|
+
from .filesystem_controller import create_filesystem_router
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"create_cli_router",
|
|
8
|
+
"create_cli_router_v2",
|
|
9
|
+
"create_cli_version_router",
|
|
10
|
+
"create_filesystem_router",
|
|
11
|
+
]
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import platform
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from fastapi import APIRouter
|
|
7
|
+
from fastapi.responses import JSONResponse
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from ..abstractions import ICliCommandProcessor
|
|
11
|
+
from ..abstractions.cli_process_command import CliProcessCommand
|
|
12
|
+
from ..models import CliServerCommandDescriptor, CliServerCommandParameterDescriptorDto, CliServerResponse
|
|
13
|
+
from ..services.cli_command_executor_service import ICliCommandExecutorService
|
|
14
|
+
from ..services.cli_command_registry import ICliCommandRegistry
|
|
15
|
+
|
|
16
|
+
SERVER_VERSION = "1.0.0"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ExecuteRequest(BaseModel):
|
|
20
|
+
command: str = ""
|
|
21
|
+
raw_command: str = Field(alias="rawCommand", default="")
|
|
22
|
+
value: str | None = None
|
|
23
|
+
args: dict[str, Any] = Field(default_factory=dict)
|
|
24
|
+
chain_commands: list[str] = Field(alias="chainCommands", default_factory=list)
|
|
25
|
+
data: Any = None
|
|
26
|
+
|
|
27
|
+
model_config = {"populate_by_name": True}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _map_to_descriptor(processor: ICliCommandProcessor) -> dict[str, Any]:
|
|
31
|
+
params = None
|
|
32
|
+
if processor.parameters:
|
|
33
|
+
params = [
|
|
34
|
+
CliServerCommandParameterDescriptorDto(
|
|
35
|
+
name=p.name,
|
|
36
|
+
description=p.description,
|
|
37
|
+
required=p.required,
|
|
38
|
+
type=p.type,
|
|
39
|
+
aliases=p.aliases,
|
|
40
|
+
defaultValue=p.default_value,
|
|
41
|
+
).model_dump(by_alias=True, exclude_none=True)
|
|
42
|
+
for p in processor.parameters
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
subs = None
|
|
46
|
+
if processor.processors:
|
|
47
|
+
subs = [_map_to_descriptor(p) for p in processor.processors]
|
|
48
|
+
|
|
49
|
+
desc = CliServerCommandDescriptor(
|
|
50
|
+
command=processor.command,
|
|
51
|
+
description=processor.description,
|
|
52
|
+
version=processor.version,
|
|
53
|
+
parameters=params if params else None,
|
|
54
|
+
processors=subs if subs else None,
|
|
55
|
+
allowUnlistedCommands=processor.allow_unlisted_commands,
|
|
56
|
+
valueRequired=processor.value_required,
|
|
57
|
+
author={"name": processor.author.name, "email": processor.author.email},
|
|
58
|
+
)
|
|
59
|
+
return desc.model_dump(by_alias=True, exclude_none=True)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def create_cli_router(
|
|
63
|
+
registry: ICliCommandRegistry,
|
|
64
|
+
executor: ICliCommandExecutorService,
|
|
65
|
+
) -> APIRouter:
|
|
66
|
+
router = APIRouter()
|
|
67
|
+
|
|
68
|
+
@router.get("/version")
|
|
69
|
+
async def get_version() -> dict[str, str]:
|
|
70
|
+
return {"version": SERVER_VERSION}
|
|
71
|
+
|
|
72
|
+
@router.get("/capabilities")
|
|
73
|
+
async def get_capabilities() -> dict[str, Any]:
|
|
74
|
+
detected_os = (
|
|
75
|
+
"darwin" if platform.system() == "Darwin"
|
|
76
|
+
else "win32" if platform.system() == "Windows"
|
|
77
|
+
else "linux"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
shell = "powershell" if platform.system() == "Windows" else "bash"
|
|
81
|
+
shell_path = (
|
|
82
|
+
"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
|
|
83
|
+
if platform.system() == "Windows"
|
|
84
|
+
else "/bin/bash"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
"shell": True,
|
|
89
|
+
"os": detected_os,
|
|
90
|
+
"shellPath": shell_path,
|
|
91
|
+
"version": SERVER_VERSION,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@router.get("/commands")
|
|
95
|
+
async def get_commands() -> list[dict[str, Any]]:
|
|
96
|
+
return [_map_to_descriptor(p) for p in registry.processors]
|
|
97
|
+
|
|
98
|
+
@router.post("/execute")
|
|
99
|
+
async def execute_command(request: ExecuteRequest) -> JSONResponse:
|
|
100
|
+
cmd = CliProcessCommand(
|
|
101
|
+
command=request.command,
|
|
102
|
+
raw_command=request.raw_command,
|
|
103
|
+
value=request.value,
|
|
104
|
+
args=request.args,
|
|
105
|
+
chain_commands=request.chain_commands,
|
|
106
|
+
data=request.data,
|
|
107
|
+
)
|
|
108
|
+
result = await executor.execute_async(cmd)
|
|
109
|
+
return JSONResponse(result.model_dump(by_alias=True, exclude_none=True))
|
|
110
|
+
|
|
111
|
+
return router
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from fastapi import APIRouter
|
|
6
|
+
from fastapi.responses import JSONResponse
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
from ..abstractions import ICliCommandProcessor
|
|
10
|
+
from ..abstractions.cli_process_command import CliProcessCommand
|
|
11
|
+
from ..models import CliServerCommandDescriptor, CliServerCommandParameterDescriptorDto, CliServerResponse
|
|
12
|
+
from ..services.cli_command_executor_service import ICliCommandExecutorService
|
|
13
|
+
from ..services.cli_command_registry import ICliCommandRegistry
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ExecuteRequestV2(BaseModel):
|
|
17
|
+
command: str = ""
|
|
18
|
+
raw_command: str = Field(alias="rawCommand", default="")
|
|
19
|
+
value: str | None = None
|
|
20
|
+
args: dict[str, Any] = Field(default_factory=dict)
|
|
21
|
+
chain_commands: list[str] = Field(alias="chainCommands", default_factory=list)
|
|
22
|
+
data: Any = None
|
|
23
|
+
|
|
24
|
+
model_config = {"populate_by_name": True}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _map_to_descriptor_v2(processor: ICliCommandProcessor) -> dict[str, Any]:
|
|
28
|
+
params = None
|
|
29
|
+
if processor.parameters:
|
|
30
|
+
params = [
|
|
31
|
+
CliServerCommandParameterDescriptorDto(
|
|
32
|
+
name=p.name,
|
|
33
|
+
description=p.description,
|
|
34
|
+
required=p.required,
|
|
35
|
+
type=p.type,
|
|
36
|
+
aliases=p.aliases,
|
|
37
|
+
defaultValue=p.default_value,
|
|
38
|
+
).model_dump(by_alias=True, exclude_none=True)
|
|
39
|
+
for p in processor.parameters
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
subs = None
|
|
43
|
+
if processor.processors:
|
|
44
|
+
subs = [_map_to_descriptor_v2(p) for p in processor.processors]
|
|
45
|
+
|
|
46
|
+
desc = CliServerCommandDescriptor(
|
|
47
|
+
command=processor.command,
|
|
48
|
+
description=processor.description,
|
|
49
|
+
version=processor.version,
|
|
50
|
+
apiVersion=processor.api_version,
|
|
51
|
+
parameters=params if params else None,
|
|
52
|
+
processors=subs if subs else None,
|
|
53
|
+
allowUnlistedCommands=processor.allow_unlisted_commands,
|
|
54
|
+
valueRequired=processor.value_required,
|
|
55
|
+
author={"name": processor.author.name, "email": processor.author.email},
|
|
56
|
+
)
|
|
57
|
+
return desc.model_dump(by_alias=True, exclude_none=True)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def create_cli_router_v2(
|
|
61
|
+
registry: ICliCommandRegistry,
|
|
62
|
+
executor: ICliCommandExecutorService,
|
|
63
|
+
) -> APIRouter:
|
|
64
|
+
router = APIRouter()
|
|
65
|
+
|
|
66
|
+
@router.get("/version")
|
|
67
|
+
async def get_version_v2() -> dict[str, Any]:
|
|
68
|
+
return {"apiVersion": 2, "serverVersion": "2.0.0"}
|
|
69
|
+
|
|
70
|
+
@router.get("/commands")
|
|
71
|
+
async def get_commands_v2() -> list[dict[str, Any]]:
|
|
72
|
+
return [
|
|
73
|
+
_map_to_descriptor_v2(p)
|
|
74
|
+
for p in registry.processors
|
|
75
|
+
if p.api_version >= 2
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
@router.post("/execute")
|
|
79
|
+
async def execute_command_v2(request: ExecuteRequestV2) -> JSONResponse:
|
|
80
|
+
cmd = CliProcessCommand(
|
|
81
|
+
command=request.command,
|
|
82
|
+
raw_command=request.raw_command,
|
|
83
|
+
value=request.value,
|
|
84
|
+
args=request.args,
|
|
85
|
+
chain_commands=request.chain_commands,
|
|
86
|
+
data=request.data,
|
|
87
|
+
)
|
|
88
|
+
result = await executor.execute_async(cmd)
|
|
89
|
+
return JSONResponse(result.model_dump(by_alias=True, exclude_none=True))
|
|
90
|
+
|
|
91
|
+
return router
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter
|
|
8
|
+
|
|
9
|
+
SERVER_VERSION = "1.0.0"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_cli_version_router() -> APIRouter:
|
|
13
|
+
router = APIRouter()
|
|
14
|
+
|
|
15
|
+
@router.get("/versions")
|
|
16
|
+
async def get_versions() -> dict[str, Any]:
|
|
17
|
+
return {
|
|
18
|
+
"supportedVersions": [1, 2],
|
|
19
|
+
"preferredVersion": 2,
|
|
20
|
+
"serverVersion": SERVER_VERSION,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@router.get("/capabilities")
|
|
24
|
+
async def get_capabilities() -> dict[str, Any]:
|
|
25
|
+
detected_os = (
|
|
26
|
+
"darwin" if platform.system() == "Darwin"
|
|
27
|
+
else "win32" if platform.system() == "Windows"
|
|
28
|
+
else "linux"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
shell = "powershell" if platform.system() == "Windows" else "bash"
|
|
32
|
+
shell_path = (
|
|
33
|
+
"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
|
|
34
|
+
if platform.system() == "Windows"
|
|
35
|
+
else "/bin/bash"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
"shell": True,
|
|
40
|
+
"os": detected_os,
|
|
41
|
+
"shellPath": shell_path,
|
|
42
|
+
"version": SERVER_VERSION,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return router
|