From f91767cedb474c17f60f4a54e23a464a5cd0c1a6 Mon Sep 17 00:00:00 2001 From: Aman Rusia Date: Tue, 29 Oct 2024 01:33:01 +0530 Subject: [PATCH] Added more deprecation related stuff --- gpt_action_json_schema.json | 144 +++++++++++++++++++----------------- pyproject.toml | 1 + src/wcgw/client/tools.py | 10 ++- src/wcgw/relay/serve.py | 67 +++++++++++++---- uv.lock | 11 +++ 5 files changed, 152 insertions(+), 81 deletions(-) diff --git a/gpt_action_json_schema.json b/gpt_action_json_schema.json index 6299a78..02c72b4 100644 --- a/gpt_action_json_schema.json +++ b/gpt_action_json_schema.json @@ -15,27 +15,15 @@ "x-openai-isConsequential": false, "summary": "Write File", "operationId": "write_file_v1_write_file_post", - "parameters": [ - { - "name": "user_id", - "in": "query", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "title": "User Id" - } - } - ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Writefile" + "$ref": "#/components/schemas/WritefileWithUUID" } } - } + }, + "required": true }, "responses": { "200": { @@ -67,27 +55,16 @@ "x-openai-isConsequential": false, "summary": "Bash Command", "operationId": "bash_command_v1_bash_command_post", - "parameters": [ - { - "name": "command", - "in": "query", - "required": true, - "schema": { - "type": "string", - "title": "Command" + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CommandWithUUID" + } } }, - { - "name": "user_id", - "in": "query", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "title": "User Id" - } - } - ], + "required": true + }, "responses": { "200": { "description": "Successful Response", @@ -118,42 +95,15 @@ "x-openai-isConsequential": false, "summary": "Bash Interaction", "operationId": "bash_interaction_v1_bash_interaction_post", - "parameters": [ - { - "name": "user_id", - "in": "query", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "title": "User Id" - } - }, - { - "name": "send_text", - "in": "query", - "required": false, - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Send Text" - } - } - ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Body_bash_interaction_v1_bash_interaction_post" + "$ref": "#/components/schemas/BashInteractionWithUUID" } } - } + }, + "required": true }, "responses": { "200": { @@ -183,8 +133,19 @@ }, "components": { "schemas": { - "Body_bash_interaction_v1_bash_interaction_post": { + "BashInteractionWithUUID": { "properties": { + "send_text": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Send Text" + }, "send_specials": { "anyOf": [ { @@ -222,10 +183,37 @@ } ], "title": "Send Ascii" + }, + "user_id": { + "type": "string", + "format": "uuid", + "title": "User Id" } }, "type": "object", - "title": "Body_bash_interaction_v1_bash_interaction_post" + "required": [ + "user_id" + ], + "title": "BashInteractionWithUUID" + }, + "CommandWithUUID": { + "properties": { + "command": { + "type": "string", + "title": "Command" + }, + "user_id": { + "type": "string", + "format": "uuid", + "title": "User Id" + } + }, + "type": "object", + "required": [ + "command", + "user_id" + ], + "title": "CommandWithUUID" }, "HTTPValidationError": { "properties": { @@ -290,6 +278,30 @@ "file_content" ], "title": "Writefile" + }, + "WritefileWithUUID": { + "properties": { + "file_path": { + "type": "string", + "title": "File Path" + }, + "file_content": { + "type": "string", + "title": "File Content" + }, + "user_id": { + "type": "string", + "format": "uuid", + "title": "User Id" + } + }, + "type": "object", + "required": [ + "file_path", + "file_content", + "user_id" + ], + "title": "WritefileWithUUID" } } } diff --git a/pyproject.toml b/pyproject.toml index d6edec2..ce12384 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ dependencies = [ "uvicorn>=0.31.0", "websockets>=13.1", "pydantic>=2.9.2", + "semantic-version>=2.10.0", ] [project.urls] diff --git a/src/wcgw/client/tools.py b/src/wcgw/client/tools.py index 0992a7a..e26bda3 100644 --- a/src/wcgw/client/tools.py +++ b/src/wcgw/client/tools.py @@ -18,6 +18,7 @@ ) import uuid from pydantic import BaseModel, TypeAdapter +import semantic_version import typer from websockets.sync.client import connect as syncconnect @@ -302,8 +303,9 @@ def execute_bash( + """--- ---- Failure interrupting. -If no program is running: +If any REPL session was previously running or if bashrc was sourced, or if there is issue to other REPL related reasons: Run BashCommand: "wcgw_update_prompt()" to reset the PS1 prompt. +Otherwise, you may want to try Ctrl-c again or program specific exit interactive commands. """ ) @@ -404,7 +406,6 @@ def write_file(writefile: Writefile) -> str: with open(path_, "w") as f: f.write(writefile.file_content) except OSError as e: - console.print(f"Error: {e}", style="red") return f"Error: {e}" console.print(f"File written to {path_}") return "Success" @@ -557,6 +558,11 @@ async def register_client(server_url: str, client_uuid: str = "") -> None: # Create the WebSocket connection async with websockets.connect(f"{server_url}/{client_uuid}") as websocket: + server_version = str(await websocket.recv()) + print(f"Server version: {server_version}") + client_version = importlib.metadata.version("wcgw") + await websocket.send(client_version) + print( f"Connected. Share this user id with the chatbot: {client_uuid} \nLink: https://chatgpt.com/g/g-Us0AAXkRh-wcgw-giving-shell-access" ) diff --git a/src/wcgw/relay/serve.py b/src/wcgw/relay/serve.py index a2d2c7b..ecc5e55 100644 --- a/src/wcgw/relay/serve.py +++ b/src/wcgw/relay/serve.py @@ -1,5 +1,7 @@ import asyncio import base64 +from importlib import metadata +import semantic_version # type: ignore[import-untyped] import threading import time from typing import Any, Callable, Coroutine, DefaultDict, Literal, Optional, Sequence @@ -62,10 +64,31 @@ async def register_websocket_deprecated(websocket: WebSocket, uuid: UUID) -> Non ) +CLIENT_VERSION_MINIMUM = "1.0.0" + + @app.websocket("/v1/register/{uuid}") async def register_websocket(websocket: WebSocket, uuid: UUID) -> None: await websocket.accept() + # send server version + version = metadata.version("wcgw") + await websocket.send_text(version) + + # receive client version + client_version = await websocket.receive_text() + sem_version_client = semantic_version.Version.coerce(client_version) + sem_version_server = semantic_version.Version.coerce(CLIENT_VERSION_MINIMUM) + if sem_version_client < sem_version_server: + await websocket.send_text( + f"Client version {client_version} is outdated. Please upgrade to {CLIENT_VERSION_MINIMUM} or higher." + ) + await websocket.close( + reason="Client version outdated. Please upgrade to the latest version.", + code=1002, + ) + return + # Register the callback for this client UUID async def send_data_callback(data: Mdata) -> None: await websocket.send_text(data.model_dump_json()) @@ -94,8 +117,13 @@ async def write_file_deprecated(write_file_data: Writefile, user_id: UUID) -> Re ) +class WritefileWithUUID(Writefile): + user_id: UUID + + @app.post("/v1/write_file") -async def write_file(write_file_data: Writefile, user_id: UUID) -> str: +async def write_file(write_file_data: WritefileWithUUID) -> str: + user_id = write_file_data.user_id if user_id not in clients: raise fastapi.HTTPException( status_code=404, detail="User with the provided id not found" @@ -128,8 +156,14 @@ async def execute_bash_deprecated(excute_bash_data: Any, user_id: UUID) -> Respo ) +class CommandWithUUID(BaseModel): + command: str + user_id: UUID + + @app.post("/v1/bash_command") -async def bash_command(command: str, user_id: UUID) -> str: +async def bash_command(command: CommandWithUUID) -> str: + user_id = command.user_id if user_id not in clients: raise fastapi.HTTPException( status_code=404, detail="User with the provided id not found" @@ -143,7 +177,9 @@ def put_results(result: str) -> None: gpts[user_id] = put_results - await clients[user_id](Mdata(data=BashCommand(command=command), user_id=user_id)) + await clients[user_id]( + Mdata(data=BashCommand(command=command.command), user_id=user_id) + ) start_time = time.time() while time.time() - start_time < 30: @@ -154,13 +190,13 @@ def put_results(result: str) -> None: raise fastapi.HTTPException(status_code=500, detail="Timeout error") +class BashInteractionWithUUID(BashInteraction): + user_id: UUID + + @app.post("/v1/bash_interaction") -async def bash_interaction( - user_id: UUID, - send_text: Optional[str] = None, - send_specials: Optional[list[Specials]] = None, - send_ascii: Optional[list[int]] = None, -) -> str: +async def bash_interaction(bash_interaction: BashInteractionWithUUID) -> str: + user_id = bash_interaction.user_id if user_id not in clients: raise fastapi.HTTPException( status_code=404, detail="User with the provided id not found" @@ -176,9 +212,7 @@ def put_results(result: str) -> None: await clients[user_id]( Mdata( - data=BashInteraction( - send_text=send_text, send_specials=send_specials, send_ascii=send_ascii - ), + data=bash_interaction, user_id=user_id, ) ) @@ -199,7 +233,14 @@ def run() -> None: load_dotenv() uvicorn_thread = threading.Thread( - target=uvicorn.run, args=(app,), kwargs={"host": "0.0.0.0", "port": 8000} + target=uvicorn.run, + args=(app,), + kwargs={ + "host": "0.0.0.0", + "port": 8000, + "log_level": "info", + "access_log": True, + }, ) uvicorn_thread.start() uvicorn_thread.join() diff --git a/uv.lock b/uv.lock index e064fe5..76de605 100644 --- a/uv.lock +++ b/uv.lock @@ -696,6 +696,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/67/91/5474b84e505a6ccc295b2d322d90ff6aa0746745717839ee0c5fb4fdcceb/rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1", size = 242117 }, ] +[[package]] +name = "semantic-version" +version = "2.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/31/f2289ce78b9b473d582568c234e104d2a342fd658cc288a7553d83bb8595/semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c", size = 52289 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177", size = 15552 }, +] + [[package]] name = "shell" version = "1.0.1" @@ -909,6 +918,7 @@ dependencies = [ { name = "pyte" }, { name = "python-dotenv" }, { name = "rich" }, + { name = "semantic-version" }, { name = "shell" }, { name = "tiktoken" }, { name = "toml" }, @@ -938,6 +948,7 @@ requires-dist = [ { name = "pyte", specifier = ">=0.8.2" }, { name = "python-dotenv", specifier = ">=1.0.1" }, { name = "rich", specifier = ">=13.8.1" }, + { name = "semantic-version", specifier = ">=2.10.0" }, { name = "shell", specifier = ">=1.0.1" }, { name = "tiktoken", specifier = "==0.7.0" }, { name = "toml", specifier = ">=0.10.2" },