devops-copilot 0.1.0__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.
- app/__init__.py +1 -0
- app/cli/__init__.py +1 -0
- app/cli/config.py +41 -0
- app/cli/main.py +251 -0
- app/core/config.py +19 -0
- app/core/database/__init__.py +24 -0
- app/core/security.py +18 -0
- app/main.py +31 -0
- app/modules/__init__.py +1 -0
- app/modules/auth/__init__.py +9 -0
- app/modules/auth/router.py +101 -0
- app/modules/auth/schema.py +34 -0
- app/modules/auth/service.py +91 -0
- app/modules/chat/__init__.py +1 -0
- app/modules/chat/agent.py +443 -0
- app/modules/chat/models.py +48 -0
- app/modules/chat/router.py +362 -0
- app/modules/chat/schema.py +39 -0
- app/modules/guardrails/__init__.py +1 -0
- app/modules/guardrails/schema.py +22 -0
- app/modules/guardrails/service.py +121 -0
- app/modules/knowledge/__init__.py +1 -0
- app/modules/knowledge/service.py +123 -0
- app/modules/servers/__init__.py +1 -0
- app/modules/servers/models.py +30 -0
- app/modules/servers/router.py +67 -0
- app/modules/servers/schema.py +45 -0
- app/modules/servers/service.py +164 -0
- devops_copilot-0.1.0.dist-info/METADATA +81 -0
- devops_copilot-0.1.0.dist-info/RECORD +33 -0
- devops_copilot-0.1.0.dist-info/WHEEL +5 -0
- devops_copilot-0.1.0.dist-info/entry_points.txt +2 -0
- devops_copilot-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from sqlmodel import SQLModel, Field
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
class Server(SQLModel, table=True):
|
|
5
|
+
"""
|
|
6
|
+
SQLModel representation of the target managed servers.
|
|
7
|
+
Stores SSH access configurations with encrypted credentials.
|
|
8
|
+
"""
|
|
9
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
10
|
+
|
|
11
|
+
# User-friendly identifier (e.g. "staging-vps")
|
|
12
|
+
name: str = Field(index=True, unique=True, nullable=False)
|
|
13
|
+
|
|
14
|
+
# Target machine host/IP address
|
|
15
|
+
ip_address: str = Field(nullable=False)
|
|
16
|
+
|
|
17
|
+
# Port target machine is listening to SSH (usually 22)
|
|
18
|
+
ssh_port: int = Field(default=22)
|
|
19
|
+
|
|
20
|
+
# Username for SSH login (e.g. "root" or "deploy")
|
|
21
|
+
ssh_username: str = Field(default="root")
|
|
22
|
+
|
|
23
|
+
# Either "password" or "key" (SSH private key authorization)
|
|
24
|
+
ssh_auth_type: str = Field(default="password")
|
|
25
|
+
|
|
26
|
+
# AES-encrypted password string or private key string
|
|
27
|
+
encrypted_credential: str = Field(nullable=False)
|
|
28
|
+
|
|
29
|
+
# Owner relation to user table
|
|
30
|
+
owner_id: int = Field(foreign_key="user.id", nullable=False)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from fastapi import APIRouter, Depends, status
|
|
2
|
+
from typing import List
|
|
3
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
4
|
+
|
|
5
|
+
from app.core.database import get_session
|
|
6
|
+
from app.modules.auth import User
|
|
7
|
+
from app.modules.auth.service import get_current_user
|
|
8
|
+
from app.modules.servers.schema import ServerCreate, ServerUpdate, ServerResponse, CommandRequest, CommandResponse
|
|
9
|
+
from app.modules.servers.service import servers_service
|
|
10
|
+
|
|
11
|
+
router = APIRouter()
|
|
12
|
+
|
|
13
|
+
@router.post("/", response_model=ServerResponse, status_code=status.HTTP_201_CREATED)
|
|
14
|
+
async def create_server(
|
|
15
|
+
server_in: ServerCreate,
|
|
16
|
+
current_user: User = Depends(get_current_user),
|
|
17
|
+
session: AsyncSession = Depends(get_session)
|
|
18
|
+
):
|
|
19
|
+
"""Register a new server connection under the logged-in administrator."""
|
|
20
|
+
return await servers_service.create_server(session, server_in, current_user.id)
|
|
21
|
+
|
|
22
|
+
@router.get("/", response_model=List[ServerResponse])
|
|
23
|
+
async def list_servers(
|
|
24
|
+
current_user: User = Depends(get_current_user),
|
|
25
|
+
session: AsyncSession = Depends(get_session)
|
|
26
|
+
):
|
|
27
|
+
"""Retrieve all managed servers registered to the current administrator."""
|
|
28
|
+
return await servers_service.get_all_servers(session, current_user.id)
|
|
29
|
+
|
|
30
|
+
@router.get("/{server_id}", response_model=ServerResponse)
|
|
31
|
+
async def get_server(
|
|
32
|
+
server_id: int,
|
|
33
|
+
current_user: User = Depends(get_current_user),
|
|
34
|
+
session: AsyncSession = Depends(get_session)
|
|
35
|
+
):
|
|
36
|
+
"""Retrieve specific server credentials configuration metadata."""
|
|
37
|
+
return await servers_service.get_server_by_id(session, server_id, current_user.id)
|
|
38
|
+
|
|
39
|
+
@router.put("/{server_id}", response_model=ServerResponse)
|
|
40
|
+
async def update_server(
|
|
41
|
+
server_id: int,
|
|
42
|
+
server_in: ServerUpdate,
|
|
43
|
+
current_user: User = Depends(get_current_user),
|
|
44
|
+
session: AsyncSession = Depends(get_session)
|
|
45
|
+
):
|
|
46
|
+
"""Update registry configuration metadata for a managed server."""
|
|
47
|
+
return await servers_service.update_server(session, server_id, server_in, current_user.id)
|
|
48
|
+
|
|
49
|
+
@router.delete("/{server_id}")
|
|
50
|
+
async def delete_server(
|
|
51
|
+
server_id: int,
|
|
52
|
+
current_user: User = Depends(get_current_user),
|
|
53
|
+
session: AsyncSession = Depends(get_session)
|
|
54
|
+
):
|
|
55
|
+
"""Delete a managed server registry."""
|
|
56
|
+
return await servers_service.delete_server(session, server_id, current_user.id)
|
|
57
|
+
|
|
58
|
+
@router.post("/{server_id}/execute", response_model=CommandResponse)
|
|
59
|
+
async def execute_command(
|
|
60
|
+
server_id: int,
|
|
61
|
+
payload: CommandRequest,
|
|
62
|
+
current_user: User = Depends(get_current_user),
|
|
63
|
+
session: AsyncSession = Depends(get_session)
|
|
64
|
+
):
|
|
65
|
+
"""Execute arbitrary terminal commands asynchronously on target server via secure SSH connection."""
|
|
66
|
+
server = await servers_service.get_server_by_id(session, server_id, current_user.id)
|
|
67
|
+
return await servers_service.execute_ssh_command(server, payload.command)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
class ServerCreate(BaseModel):
|
|
5
|
+
"""DTO for creating a new managed server connection."""
|
|
6
|
+
name: str = Field(..., min_length=3, max_length=50)
|
|
7
|
+
ip_address: str = Field(...)
|
|
8
|
+
ssh_port: int = Field(default=22, ge=1, le=65535)
|
|
9
|
+
ssh_username: str = Field(default="root")
|
|
10
|
+
ssh_auth_type: Literal["password", "key"] = Field(default="password")
|
|
11
|
+
|
|
12
|
+
# Raw password or private key string. Will be encrypted before database storage.
|
|
13
|
+
credential: str = Field(..., min_length=1)
|
|
14
|
+
|
|
15
|
+
class ServerUpdate(BaseModel):
|
|
16
|
+
"""DTO for updating an existing managed server connection."""
|
|
17
|
+
name: str | None = Field(None, min_length=3, max_length=50)
|
|
18
|
+
ip_address: str | None = None
|
|
19
|
+
ssh_port: int | None = Field(None, ge=1, le=65535)
|
|
20
|
+
ssh_username: str | None = None
|
|
21
|
+
ssh_auth_type: Literal["password", "key"] | None = None
|
|
22
|
+
credential: str | None = None
|
|
23
|
+
|
|
24
|
+
class ServerResponse(BaseModel):
|
|
25
|
+
"""Safe database projection returning public server parameters (excludes credentials)."""
|
|
26
|
+
id: int
|
|
27
|
+
name: str
|
|
28
|
+
ip_address: str
|
|
29
|
+
ssh_port: int
|
|
30
|
+
ssh_username: str
|
|
31
|
+
ssh_auth_type: str
|
|
32
|
+
owner_id: int
|
|
33
|
+
|
|
34
|
+
class Config:
|
|
35
|
+
from_attributes = True
|
|
36
|
+
|
|
37
|
+
class CommandRequest(BaseModel):
|
|
38
|
+
"""Input payload to execute custom commands on target server."""
|
|
39
|
+
command: str = Field(..., min_length=1)
|
|
40
|
+
|
|
41
|
+
class CommandResponse(BaseModel):
|
|
42
|
+
"""Response returned after running SSH commands."""
|
|
43
|
+
stdout: str
|
|
44
|
+
stderr: str
|
|
45
|
+
exit_code: int
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import asyncssh
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
from fastapi import HTTPException, status
|
|
5
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
6
|
+
from sqlmodel import select
|
|
7
|
+
|
|
8
|
+
from app.core.security import encrypt_data, decrypt_data
|
|
9
|
+
from app.modules.servers.models import Server
|
|
10
|
+
from app.modules.servers.schema import ServerCreate, ServerUpdate
|
|
11
|
+
|
|
12
|
+
class ServersService:
|
|
13
|
+
async def create_server(self, session: AsyncSession, server_in: ServerCreate, owner_id: int) -> Server:
|
|
14
|
+
"""Create a server registry after encrypting credentials."""
|
|
15
|
+
# Ensure server name is unique
|
|
16
|
+
statement = select(Server).where(Server.name == server_in.name)
|
|
17
|
+
result = await session.exec(statement)
|
|
18
|
+
if result.first():
|
|
19
|
+
raise HTTPException(
|
|
20
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
21
|
+
detail="Server name already registered"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Encrypt password or private key before saving to DB
|
|
25
|
+
encrypted_cred = encrypt_data(server_in.credential)
|
|
26
|
+
|
|
27
|
+
# Instantiate Server DB record
|
|
28
|
+
new_server = Server(
|
|
29
|
+
name=server_in.name,
|
|
30
|
+
ip_address=server_in.ip_address,
|
|
31
|
+
ssh_port=server_in.ssh_port,
|
|
32
|
+
ssh_username=server_in.ssh_username,
|
|
33
|
+
ssh_auth_type=server_in.ssh_auth_type,
|
|
34
|
+
encrypted_credential=encrypted_cred,
|
|
35
|
+
owner_id=owner_id
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
session.add(new_server)
|
|
39
|
+
await session.commit()
|
|
40
|
+
await session.refresh(new_server)
|
|
41
|
+
return new_server
|
|
42
|
+
|
|
43
|
+
async def get_all_servers(self, session: AsyncSession, owner_id: int) -> List[Server]:
|
|
44
|
+
"""Fetch all servers owned by the user."""
|
|
45
|
+
statement = select(Server).where(Server.owner_id == owner_id)
|
|
46
|
+
result = await session.exec(statement)
|
|
47
|
+
return result.all()
|
|
48
|
+
|
|
49
|
+
async def get_server_by_id(self, session: AsyncSession, server_id: int, owner_id: int) -> Optional[Server]:
|
|
50
|
+
"""Fetch a specific server by ID and verify ownership."""
|
|
51
|
+
statement = select(Server).where(Server.id == server_id, Server.owner_id == owner_id)
|
|
52
|
+
result = await session.exec(statement)
|
|
53
|
+
server = result.first()
|
|
54
|
+
if not server:
|
|
55
|
+
raise HTTPException(
|
|
56
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
57
|
+
detail="Server not found"
|
|
58
|
+
)
|
|
59
|
+
return server
|
|
60
|
+
|
|
61
|
+
async def get_server_by_name(self, session: AsyncSession, name: str, owner_id: int) -> Optional[Server]:
|
|
62
|
+
"""Fetch a specific server by name and verify ownership."""
|
|
63
|
+
statement = select(Server).where(Server.name == name, Server.owner_id == owner_id)
|
|
64
|
+
result = await session.exec(statement)
|
|
65
|
+
server = result.first()
|
|
66
|
+
if not server:
|
|
67
|
+
raise HTTPException(
|
|
68
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
69
|
+
detail="Server not found"
|
|
70
|
+
)
|
|
71
|
+
return server
|
|
72
|
+
|
|
73
|
+
async def update_server(self, session: AsyncSession, server_id: int, server_in: ServerUpdate, owner_id: int) -> Server:
|
|
74
|
+
"""Update an existing server registry configuration."""
|
|
75
|
+
server = await self.get_server_by_id(session, server_id, owner_id)
|
|
76
|
+
|
|
77
|
+
if server_in.name is not None and server_in.name != server.name:
|
|
78
|
+
# Ensure name is unique if changed
|
|
79
|
+
statement = select(Server).where(Server.name == server_in.name)
|
|
80
|
+
result = await session.exec(statement)
|
|
81
|
+
if result.first():
|
|
82
|
+
raise HTTPException(
|
|
83
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
84
|
+
detail="Server name already registered"
|
|
85
|
+
)
|
|
86
|
+
server.name = server_in.name
|
|
87
|
+
|
|
88
|
+
if server_in.ip_address is not None:
|
|
89
|
+
server.ip_address = server_in.ip_address
|
|
90
|
+
if server_in.ssh_port is not None:
|
|
91
|
+
server.ssh_port = server_in.ssh_port
|
|
92
|
+
if server_in.ssh_username is not None:
|
|
93
|
+
server.ssh_username = server_in.ssh_username
|
|
94
|
+
if server_in.ssh_auth_type is not None:
|
|
95
|
+
server.ssh_auth_type = server_in.ssh_auth_type
|
|
96
|
+
if server_in.credential is not None:
|
|
97
|
+
server.encrypted_credential = encrypt_data(server_in.credential)
|
|
98
|
+
|
|
99
|
+
session.add(server)
|
|
100
|
+
await session.commit()
|
|
101
|
+
await session.refresh(server)
|
|
102
|
+
return server
|
|
103
|
+
|
|
104
|
+
async def delete_server(self, session: AsyncSession, server_id: int, owner_id: int) -> dict:
|
|
105
|
+
"""Delete an existing server registry."""
|
|
106
|
+
server = await self.get_server_by_id(session, server_id, owner_id)
|
|
107
|
+
await session.delete(server)
|
|
108
|
+
await session.commit()
|
|
109
|
+
return {"status": "success", "message": f"Server {server_id} deleted successfully"}
|
|
110
|
+
|
|
111
|
+
async def execute_ssh_command(self, server: Server, command: str) -> dict:
|
|
112
|
+
"""
|
|
113
|
+
Decrypt server connection credentials, establish SSH tunnel,
|
|
114
|
+
execute bash command, and return outputs.
|
|
115
|
+
"""
|
|
116
|
+
# Decrypt password or SSH Private Key
|
|
117
|
+
credential = decrypt_data(server.encrypted_credential)
|
|
118
|
+
|
|
119
|
+
# Build connection arguments
|
|
120
|
+
conn_args = {
|
|
121
|
+
"host": server.ip_address,
|
|
122
|
+
"port": server.ssh_port,
|
|
123
|
+
"username": server.ssh_username,
|
|
124
|
+
"known_hosts": None, # Bypass host key verification for dynamic servers (warning: MITM vulnerable)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if server.ssh_auth_type == "password":
|
|
128
|
+
conn_args["password"] = credential
|
|
129
|
+
else:
|
|
130
|
+
try:
|
|
131
|
+
# Import private key object from decrypted PEM string
|
|
132
|
+
key = asyncssh.import_private_key(credential)
|
|
133
|
+
conn_args["client_keys"] = [key]
|
|
134
|
+
except Exception as e:
|
|
135
|
+
raise HTTPException(
|
|
136
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
137
|
+
detail=f"Invalid SSH private key format: {str(e)}"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
# Connect and execute command using asyncssh with 30s timeout
|
|
142
|
+
async def run_command_with_ssh():
|
|
143
|
+
async with asyncssh.connect(**conn_args) as conn:
|
|
144
|
+
result = await conn.run(command, check=False)
|
|
145
|
+
return {
|
|
146
|
+
"stdout": result.stdout,
|
|
147
|
+
"stderr": result.stderr,
|
|
148
|
+
"exit_code": result.exit_status
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return await asyncio.wait_for(run_command_with_ssh(), timeout=30.0)
|
|
152
|
+
except asyncio.TimeoutError:
|
|
153
|
+
raise HTTPException(
|
|
154
|
+
status_code=status.HTTP_504_GATEWAY_TIMEOUT,
|
|
155
|
+
detail=f"SSH execution timed out after 30 seconds on {server.ip_address}"
|
|
156
|
+
)
|
|
157
|
+
except (asyncssh.Error, OSError) as e:
|
|
158
|
+
raise HTTPException(
|
|
159
|
+
status_code=status.HTTP_502_BAD_GATEWAY,
|
|
160
|
+
detail=f"SSH Connection Failed to {server.ip_address}:{server.ssh_port} - {str(e)}"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Single instance representing the service provider
|
|
164
|
+
servers_service = ServersService()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: devops-copilot
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An AI-driven DevOps Copilot and CLI Client for managing bare-metal servers securely
|
|
5
|
+
Author: irzix
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: typer>=0.12.0
|
|
9
|
+
Requires-Dist: httpx>=0.27.0
|
|
10
|
+
Requires-Dist: websockets>=12.0
|
|
11
|
+
Provides-Extra: server
|
|
12
|
+
Requires-Dist: fastapi>=0.111.0; extra == "server"
|
|
13
|
+
Requires-Dist: uvicorn>=0.30.0; extra == "server"
|
|
14
|
+
Requires-Dist: sqlmodel>=0.0.22; extra == "server"
|
|
15
|
+
Requires-Dist: pyjwt>=2.8.0; extra == "server"
|
|
16
|
+
Requires-Dist: aiosqlite>=0.20.0; extra == "server"
|
|
17
|
+
Requires-Dist: cryptography>=42.0.0; extra == "server"
|
|
18
|
+
Requires-Dist: langchain>=0.2.0; extra == "server"
|
|
19
|
+
Requires-Dist: chromadb>=0.5.0; extra == "server"
|
|
20
|
+
Requires-Dist: pydantic-settings>=2.2.0; extra == "server"
|
|
21
|
+
Requires-Dist: bcrypt>=4.1.0; extra == "server"
|
|
22
|
+
Requires-Dist: python-multipart>=0.0.9; extra == "server"
|
|
23
|
+
Requires-Dist: greenlet>=3.0.0; extra == "server"
|
|
24
|
+
Requires-Dist: langchain-openai>=0.1.0; extra == "server"
|
|
25
|
+
Requires-Dist: asyncssh>=2.14.0; extra == "server"
|
|
26
|
+
|
|
27
|
+
# DevOps-Copilot 🚀
|
|
28
|
+
|
|
29
|
+
An open-source, AI-driven DevOps Copilot and CLI Client designed to manage raw root/bare-metal servers securely. It features persistent credential encryption, real-time terminal streaming, and semantic security guardrails using a local vector database.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Key Features
|
|
34
|
+
- **Lean RAG Knowledge Base:** Automatically chunks and indexes executed SSH command outputs, logs, and server configs into separate **ChromaDB** collections, enabling the agent to search past server history before executing new commands.
|
|
35
|
+
- **Semantic Guardrails:** Uses local vector search to intercept and block dangerous terminal commands.
|
|
36
|
+
- **Human-in-the-Loop (HITL):** Enforces admin approval (`[y/N]`) inside the terminal for any state-modifying actions with clean prompt synchronization.
|
|
37
|
+
- **CLI Connection Resilience:** Automatically reconnects to the WebSocket server using exponential backoff if the network drops or the server restarts.
|
|
38
|
+
- **Real-Time Streaming:** Streams LLM thoughts and active SSH `stdout`/`stderr` line-by-line using WebSockets with 30s execution timeouts.
|
|
39
|
+
- **Encrypted Credentials:** Securely encrypts passwords and SSH private keys using Fernet (AES-256).
|
|
40
|
+
- **Server & Session CRUD:** Full REST API support for updating/deleting server connections and deleting chat sessions (with cascade cleanup).
|
|
41
|
+
- **Flexible AI Models:** Powered by **OpenRouter** (supports Llama 3, Gemini, GPT, etc.).
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 📦 Quick Start (Backend Server)
|
|
46
|
+
|
|
47
|
+
### 1. Configure Settings
|
|
48
|
+
Copy the env file and populate keys:
|
|
49
|
+
```bash
|
|
50
|
+
cp .env.example .env
|
|
51
|
+
```
|
|
52
|
+
Make sure to add your `OPENROUTER_API_KEY` and a custom base64 `ENCRYPTION_KEY` in `.env`.
|
|
53
|
+
|
|
54
|
+
### 2. Run with Docker Compose
|
|
55
|
+
```bash
|
|
56
|
+
docker compose up -d --build
|
|
57
|
+
```
|
|
58
|
+
The server will boot on port `8000`. Database tables and security blacklist vectors are automatically seeded on startup.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 💻 Quick Start (CLI Client)
|
|
63
|
+
|
|
64
|
+
### 1. Install Globally
|
|
65
|
+
Install the package in editable mode from your local repository root:
|
|
66
|
+
```bash
|
|
67
|
+
uv pip install -e .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 2. Authenticate
|
|
71
|
+
Configure the server URL and log in to get your JWT access token:
|
|
72
|
+
```bash
|
|
73
|
+
devops-copilot login
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3. Interactive Chat
|
|
77
|
+
Start the real-time DevOps chat session:
|
|
78
|
+
```bash
|
|
79
|
+
devops-copilot chat
|
|
80
|
+
```
|
|
81
|
+
*Ask the agent to check stats or run actions. Approve state-modifying commands directly in the prompt.*
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
app/__init__.py,sha256=prAfDw-CuIvjA35TsMKTzgRHKOsvlHYhpthqzg7gtQc,45
|
|
2
|
+
app/main.py,sha256=oHGIJKeGghwCJU5xMkU0OetTcFrcC7CM3xjuLl_ns9g,1078
|
|
3
|
+
app/cli/__init__.py,sha256=2dEVPQKy6c3qcm1D2j981g3B9KxXamfwQpTfF_X309Q,49
|
|
4
|
+
app/cli/config.py,sha256=SIoNL7v-TicY7dFgekSgjgNNufOv8upAXbGoOKPgcOo,1483
|
|
5
|
+
app/cli/main.py,sha256=ybZ4-b5_16tzQZ0ljNsY8He4hNKSlzWh51Q10_fsmYA,11196
|
|
6
|
+
app/core/config.py,sha256=XEx9PG49BLVAC7NPhD58aTOb9pHnmiYeJ0Nuv0Cc1ag,708
|
|
7
|
+
app/core/security.py,sha256=l3jLCc7UujYQwInghM1d_VS8R8jjlPsyhtAvAdebytc,595
|
|
8
|
+
app/core/database/__init__.py,sha256=SUnd9pdtT2JFDb2AiQ13LW-z00A_B1aNWolmFAvyZs0,790
|
|
9
|
+
app/modules/__init__.py,sha256=NmdHNlZ_Beg05caUEHllKj1TIIlXdoMMVLmZ74hr4KQ,45
|
|
10
|
+
app/modules/auth/__init__.py,sha256=J4mhJsBHQRhZpEvORz2opYAACOQcyhnUNzVlu5kYhOA,336
|
|
11
|
+
app/modules/auth/router.py,sha256=mBbmYjuhGkpzOgVy9vt39rtuMwRLKLC8X3U2AWFihm4,3341
|
|
12
|
+
app/modules/auth/schema.py,sha256=iPE2hzj5yNM4v_4rC0i8oUC3W09wOs5o_cL3_FDHYbE,980
|
|
13
|
+
app/modules/auth/service.py,sha256=hPE_CRKzBXchAhWKO7kvEkrCYKk3_eMpvtDIANFs7dQ,2797
|
|
14
|
+
app/modules/chat/__init__.py,sha256=e5afKCCktlS6c0PHDejf7XTt-A5ifO3ziU34h4JoO68,46
|
|
15
|
+
app/modules/chat/agent.py,sha256=Xx9lX77dZmvYf2nkQWvLWl8tUknemCY04Zr4AIA5ons,18406
|
|
16
|
+
app/modules/chat/models.py,sha256=6g9d82vnNtYVnc1JiLgqPUGNlrEkCKTkoC-0biR4VkM,1827
|
|
17
|
+
app/modules/chat/router.py,sha256=ioky2_ffORQoOX7y5OaO6Poht--S-kEkQZcnWvmoHx8,16241
|
|
18
|
+
app/modules/chat/schema.py,sha256=vl6sSCp4F3GxLMIcHV2vtrKT9Oldw6qJ1S3_AEvx-gg,962
|
|
19
|
+
app/modules/guardrails/__init__.py,sha256=E_i6gXiXTvsyZSwvplJsAii06Tw8MHQLTPrPfcx4CL0,45
|
|
20
|
+
app/modules/guardrails/schema.py,sha256=q4xQJ9v0XXceclvcQujneawBnh14tFJuRBZPFeVZ09Q,775
|
|
21
|
+
app/modules/guardrails/service.py,sha256=s2gwHlA5weOdyVEZoCB2oxOQIv-Pvm0Np5TvCCE9b7k,4692
|
|
22
|
+
app/modules/knowledge/__init__.py,sha256=rydpZI76T8AnuAULTQOsMfcYu0BKNZ0D3RciBEmvU-c,51
|
|
23
|
+
app/modules/knowledge/service.py,sha256=Ut73BOVxVz5nE1iZ2iAUuI8slNLpDtnwOsIa0VBhxmQ,3794
|
|
24
|
+
app/modules/servers/__init__.py,sha256=z20A6YQjS8e3q7nD7eQTk3ziImlH8Rv8eJoXup0ZV-0,27
|
|
25
|
+
app/modules/servers/models.py,sha256=fxdq6y4lS_232pxkZ6QjH7A6PsgnXdav8pfOhk3PQc0,1061
|
|
26
|
+
app/modules/servers/router.py,sha256=OOqTM4d1c2xuyjgd65OeY4eaEOWmvMzswGu8uXAe3aQ,2787
|
|
27
|
+
app/modules/servers/schema.py,sha256=YC2iut3_QgyyCAsYgwPGUfIBA7FM-RpkEr_HcSagStE,1532
|
|
28
|
+
app/modules/servers/service.py,sha256=vmTiJQSSmOUrfdIi20YllKNCHsj9r2GlzFhDmFFjIYY,6992
|
|
29
|
+
devops_copilot-0.1.0.dist-info/METADATA,sha256=qsbmYxYIUcQXLA5OBhTqIUn_tYuubcxyT7wtMnB1QXE,3410
|
|
30
|
+
devops_copilot-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
31
|
+
devops_copilot-0.1.0.dist-info/entry_points.txt,sha256=pJGQfofbOaBpIy2QK7-u4fGLVT3rS6bteGs4SeSaT-U,52
|
|
32
|
+
devops_copilot-0.1.0.dist-info/top_level.txt,sha256=io9g7LCbfmTG1SFKgEOGXmCFB9uMP2H5lerm0HiHWQE,4
|
|
33
|
+
devops_copilot-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
app
|