Silicon Valley - Admin commited on
Commit
dd07930
1 Parent(s): f0762d4

Add initial application files including server implementation, API specification, configuration, and ignore files

Browse files
Files changed (6) hide show
  1. .cursorignore +13 -0
  2. hypercorn.toml +9 -0
  3. openapi.yaml +189 -0
  4. poetry.toml +2 -0
  5. requirements.txt +1 -0
  6. server.py +103 -0
.cursorignore ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .DS_Store
2
+ .AppleDouble
3
+ .LSOverride
4
+ Icon
5
+ ._*
6
+ .DocumentRevisions-V100
7
+ .fseventsd
8
+ .Spotlight-V100
9
+ .TemporaryItems
10
+ .Trashes
11
+ .VolumeIcon.icns
12
+ .com.apple.timemachine.donotpresent
13
+
hypercorn.toml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # Hypercorn configuration file
2
+
3
+ # Server binding options
4
+ bind = "0.0.0.0:8000"
5
+ workers = 1
6
+
7
+ # Logging configuration
8
+ accesslog = "/var/log/serverwitch/access.log"
9
+ logconfig = "logging.toml"
openapi.yaml ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ openapi: 3.1.0
2
+ info:
3
+ title: Kaio Buu API
4
+ description: Execute commands, read files and write files on the user's server
5
+ version: 0.2.0
6
+ servers:
7
+ - url: https://run.pyboxs.com/kaiobuu
8
+ paths:
9
+ /command:
10
+ post:
11
+ operationId: post_send_command
12
+ summary: Execute a command on the server.
13
+ x-openai-isConsequential: false
14
+ requestBody:
15
+ description: Request body containing the command and session ID.
16
+ required: true
17
+ content:
18
+ application/json:
19
+ schema:
20
+ $ref: '#/components/schemas/CommandRequest'
21
+ responses:
22
+ '200':
23
+ description: Command executed successfully.
24
+ content:
25
+ application/json:
26
+ schema:
27
+ $ref: '#/components/schemas/CommandResponse'
28
+ '500':
29
+ description: Server error.
30
+ content:
31
+ application/json:
32
+ schema:
33
+ $ref: '#/components/schemas/ErrorResponse'
34
+ /read:
35
+ post:
36
+ operationId: post_read
37
+ summary: Read a file on the server.
38
+ x-openai-isConsequential: false
39
+ requestBody:
40
+ description: Request body containing the file path and session ID.
41
+ required: true
42
+ content:
43
+ application/json:
44
+ schema:
45
+ $ref: '#/components/schemas/ReadRequest'
46
+ responses:
47
+ '200':
48
+ description: File read successfully.
49
+ content:
50
+ application/json:
51
+ schema:
52
+ $ref: '#/components/schemas/ReadResponse'
53
+ '500':
54
+ description: Server error.
55
+ content:
56
+ application/json:
57
+ schema:
58
+ $ref: '#/components/schemas/ErrorResponse'
59
+ /write:
60
+ post:
61
+ operationId: post_write
62
+ summary: Write a file on the server.
63
+ x-openai-isConsequential: false
64
+ requestBody:
65
+ description: Request body containing the file path, new content and session ID.
66
+ required: true
67
+ content:
68
+ application/json:
69
+ schema:
70
+ $ref: '#/components/schemas/WriteRequest'
71
+ responses:
72
+ '200':
73
+ description: File written successfully.
74
+ content:
75
+ application/json:
76
+ schema:
77
+ $ref: '#/components/schemas/WriteResponse'
78
+ '500':
79
+ description: Server error.
80
+ content:
81
+ application/json:
82
+ schema:
83
+ $ref: '#/components/schemas/ErrorResponse'
84
+ /status:
85
+ get:
86
+ operationId: get_status
87
+ summary: Get the status of the server.
88
+ x-openai-isConsequential: false
89
+ responses:
90
+ '200':
91
+ description: Status of the server.
92
+ content:
93
+ application/json:
94
+ schema:
95
+ $ref: '#/components/schemas/StatusResponse'
96
+ components:
97
+ schemas:
98
+ CommandRequest:
99
+ type: object
100
+ required:
101
+ - session_id
102
+ - command
103
+ properties:
104
+ command:
105
+ type: string
106
+ description: Command to execute on the server.
107
+ session_id:
108
+ type: string
109
+ description: Session ID.
110
+ ReadRequest:
111
+ type: object
112
+ required:
113
+ - session_id
114
+ - path
115
+ properties:
116
+ path:
117
+ type: string
118
+ description: Absolute path of the file to be read.
119
+ session_id:
120
+ type: string
121
+ description: Session ID.
122
+ WriteRequest:
123
+ type: object
124
+ required:
125
+ - session_id
126
+ - path
127
+ - content
128
+ properties:
129
+ path:
130
+ type: string
131
+ description: Absolute path of the file to be written.
132
+ content:
133
+ type: string
134
+ description: New content of the file.
135
+ session_id:
136
+ type: string
137
+ description: Session ID.
138
+ CommandResponse:
139
+ type: object
140
+ required:
141
+ - return_code
142
+ - stdout
143
+ - stderr
144
+ properties:
145
+ return_code:
146
+ type: integer
147
+ description: Return code of the executed command.
148
+ stdout:
149
+ type: string
150
+ description: Standard output of the executed command.
151
+ stderr:
152
+ type: string
153
+ description: Standard error of the executed command.
154
+ ReadResponse:
155
+ type: object
156
+ required:
157
+ - content
158
+ properties:
159
+ content:
160
+ type: string
161
+ description: Content of the file.
162
+ WriteResponse:
163
+ type: object
164
+ required:
165
+ - size
166
+ properties:
167
+ size:
168
+ type: int
169
+ description: Number of characters written to the file.
170
+ ErrorResponse:
171
+ type: object
172
+ required:
173
+ - error
174
+ properties:
175
+ error:
176
+ type: string
177
+ description: Error message detailing what went wrong.
178
+ StatusResponse:
179
+ type: object
180
+ required:
181
+ - status
182
+ - version
183
+ properties:
184
+ status:
185
+ type: string
186
+ description: Current status of the server.
187
+ version:
188
+ type: string
189
+ description: Version of the server software.
poetry.toml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ [virtualenvs]
2
+ in-project = true
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
server.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # server.py
2
+ import asyncio
3
+ import uuid
4
+ from typing import AsyncGenerator, Dict, Tuple, Any, Optional
5
+ from dataclasses import dataclass
6
+ from quart import Quart, websocket, request, FileResponse
7
+ from quart_schema import QuartSchema, validate_request, validate_response
8
+ import importlib.metadata
9
+ import secrets
10
+ import logging
11
+
12
+ from broker import SessionBroker, SessionDoesNotExist, ClientRequest, ClientResponse, ClientError
13
+
14
+ # Configuraci贸n
15
+ TIMEOUT: int = 60
16
+ LOG_LEVEL: int = logging.INFO
17
+ TRUSTED_HOSTS: list[str] = ["127.0.0.1"]
18
+
19
+ # Crear aplicaci贸n
20
+ app = Quart(__name__)
21
+ QuartSchema(app)
22
+ app.logger.setLevel(LOG_LEVEL)
23
+
24
+ broker = SessionBroker()
25
+
26
+ # Definici贸n de modelos de datos
27
+ @dataclass
28
+ class Status:
29
+ status: str
30
+ version: str
31
+
32
+ @dataclass
33
+ class Session:
34
+ session_id: str
35
+
36
+ @dataclass
37
+ class Command:
38
+ session_id: str
39
+ command: str
40
+
41
+ @dataclass
42
+ class CommandResponse:
43
+ return_code: int
44
+ stdout: str
45
+ stderr: str
46
+
47
+ @dataclass
48
+ class ErrorResponse:
49
+ error: str
50
+
51
+ # Rutas API
52
+ @app.get("/status")
53
+ @validate_response(Status)
54
+ async def status() -> Status:
55
+ return Status(status="OK", version=importlib.metadata.version('your-package-name'))
56
+
57
+ @app.websocket('/session')
58
+ async def session_handler():
59
+ session_id = secrets.token_hex()
60
+ app.logger.info(f"New session: {session_id}")
61
+ await websocket.send_as(Session(session_id=session_id), Session)
62
+
63
+ task = asyncio.ensure_future(_receive(session_id))
64
+ try:
65
+ async for request in broker.subscribe(session_id):
66
+ app.logger.info(f"Sending request {request.request_id} to client.")
67
+ await websocket.send_as(request, ClientRequest)
68
+ finally:
69
+ task.cancel()
70
+
71
+ async def _receive(session_id: str) -> None:
72
+ while True:
73
+ response = await websocket.receive_as(ClientResponse)
74
+ app.logger.info(f"Received response for session {session_id}: {response}")
75
+ await broker.receive_response(session_id, response)
76
+
77
+ @app.post('/command')
78
+ @validate_request(Command)
79
+ @validate_response(CommandResponse, 200)
80
+ @validate_response(ErrorResponse, 500)
81
+ async def command(data: Command) -> Tuple[CommandResponse | ErrorResponse, int]:
82
+ try:
83
+ response_data = await broker.send_request(
84
+ session_id=data.session_id,
85
+ data={'action': 'command', 'command': data.command},
86
+ timeout=TIMEOUT
87
+ )
88
+ response = CommandResponse(**response_data)
89
+ return response, 200
90
+ except SessionDoesNotExist:
91
+ app.logger.warning(f"Invalid session ID: {data.session_id}")
92
+ return ErrorResponse(error='Session does not exist.'), 500
93
+ except ClientError as e:
94
+ return ErrorResponse(error=e.message), 500
95
+ except asyncio.TimeoutError:
96
+ return ErrorResponse(error='Timeout waiting for client response.'), 500
97
+
98
+ # Ejecutar aplicaci贸n
99
+ def run():
100
+ app.run(port=7860)
101
+
102
+ if __name__ == "__main__":
103
+ run()