wcgw 1.3.0__tar.gz → 1.4.0__tar.gz
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.
Potentially problematic release.
This version of wcgw might be problematic. Click here for more details.
- {wcgw-1.3.0 → wcgw-1.4.0}/.vscode/settings.json +2 -1
- {wcgw-1.3.0 → wcgw-1.4.0}/PKG-INFO +6 -2
- {wcgw-1.3.0 → wcgw-1.4.0}/README.md +4 -1
- wcgw-1.4.0/add.py +6 -0
- wcgw-1.4.0/claude_desktop_config.json +15 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/pyproject.toml +3 -1
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/__init__.py +1 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/client/anthropic_client.py +0 -1
- wcgw-1.4.0/src/wcgw/client/mcp_server/Readme.md +26 -0
- wcgw-1.4.0/src/wcgw/client/mcp_server/__init__.py +11 -0
- wcgw-1.4.0/src/wcgw/client/mcp_server/server.py +222 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/client/openai_client.py +0 -1
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/client/tools.py +4 -14
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/relay/serve.py +5 -12
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/types_.py +1 -6
- wcgw-1.4.0/static/rocket-icon.png +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/tests/test_tools.py +2 -2
- {wcgw-1.3.0 → wcgw-1.4.0}/uv.lock +201 -159
- {wcgw-1.3.0 → wcgw-1.4.0}/.github/workflows/python-publish.yml +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/.github/workflows/python-tests.yml +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/.gitignore +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/.python-version +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/gpt_action_json_schema.json +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/gpt_instructions.txt +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/src/__init__.py +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/client/__init__.py +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/client/__main__.py +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/client/cli.py +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/client/common.py +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/client/diff-instructions.txt +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/client/openai_utils.py +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/src/wcgw/relay/static/privacy.txt +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/static/ss1.png +0 -0
- {wcgw-1.3.0 → wcgw-1.4.0}/tests/test_basic.py +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: wcgw
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: What could go wrong giving full shell access to chatgpt?
|
|
5
5
|
Project-URL: Homepage, https://github.com/rusiaaman/wcgw
|
|
6
6
|
Author-email: Aman Rusia <gapypi@arcfu.com>
|
|
7
7
|
Requires-Python: <3.13,>=3.10
|
|
8
8
|
Requires-Dist: anthropic>=0.39.0
|
|
9
9
|
Requires-Dist: fastapi>=0.115.0
|
|
10
|
+
Requires-Dist: mcp>=1.0.0
|
|
10
11
|
Requires-Dist: mypy>=1.11.2
|
|
11
12
|
Requires-Dist: nltk>=3.9.1
|
|
12
13
|
Requires-Dist: openai>=1.46.0
|
|
@@ -50,7 +51,7 @@ You need to keep running this client for GPT to access your shell. Run it in a v
|
|
|
50
51
|
### Option 1: using uv [Recommended]
|
|
51
52
|
```sh
|
|
52
53
|
$ curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
53
|
-
$
|
|
54
|
+
$ uvx wcgw@latest
|
|
54
55
|
```
|
|
55
56
|
|
|
56
57
|
### Option 2: using pip
|
|
@@ -109,6 +110,9 @@ If you don't have public ip and domain name, you can use `ngrok` or similar serv
|
|
|
109
110
|
The specify the server url in the `wcgw` command like so
|
|
110
111
|
`wcgw --server-url https://your-url/v1/register`
|
|
111
112
|
|
|
113
|
+
# Claude Support
|
|
114
|
+
WCGW now supports Claude Desktop through the MCP protocol, allowing you to use Claude's capabilities directly from your desktop environment. This integration enables seamless interaction between Claude and your local shell.
|
|
115
|
+
|
|
112
116
|
# [Optional] Local shell access with openai API key
|
|
113
117
|
|
|
114
118
|
Add `OPENAI_API_KEY` and `OPENAI_ORG_ID` env variables.
|
|
@@ -22,7 +22,7 @@ You need to keep running this client for GPT to access your shell. Run it in a v
|
|
|
22
22
|
### Option 1: using uv [Recommended]
|
|
23
23
|
```sh
|
|
24
24
|
$ curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
25
|
-
$
|
|
25
|
+
$ uvx wcgw@latest
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
### Option 2: using pip
|
|
@@ -81,6 +81,9 @@ If you don't have public ip and domain name, you can use `ngrok` or similar serv
|
|
|
81
81
|
The specify the server url in the `wcgw` command like so
|
|
82
82
|
`wcgw --server-url https://your-url/v1/register`
|
|
83
83
|
|
|
84
|
+
# Claude Support
|
|
85
|
+
WCGW now supports Claude Desktop through the MCP protocol, allowing you to use Claude's capabilities directly from your desktop environment. This integration enables seamless interaction between Claude and your local shell.
|
|
86
|
+
|
|
84
87
|
# [Optional] Local shell access with openai API key
|
|
85
88
|
|
|
86
89
|
Add `OPENAI_API_KEY` and `OPENAI_ORG_ID` env variables.
|
wcgw-1.4.0/add.py
ADDED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
authors = [{ name = "Aman Rusia", email = "gapypi@arcfu.com" }]
|
|
3
3
|
name = "wcgw"
|
|
4
|
-
version = "1.
|
|
4
|
+
version = "1.4.0"
|
|
5
5
|
description = "What could go wrong giving full shell access to chatgpt?"
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
requires-python = ">=3.10, <3.13"
|
|
@@ -25,6 +25,7 @@ dependencies = [
|
|
|
25
25
|
"semantic-version>=2.10.0",
|
|
26
26
|
"nltk>=3.9.1",
|
|
27
27
|
"anthropic>=0.39.0",
|
|
28
|
+
"mcp>=1.0.0",
|
|
28
29
|
]
|
|
29
30
|
|
|
30
31
|
[project.urls]
|
|
@@ -38,6 +39,7 @@ build-backend = "hatchling.build"
|
|
|
38
39
|
wcgw_local = "wcgw:app"
|
|
39
40
|
wcgw = "wcgw:listen"
|
|
40
41
|
wcgw_relay = "wcgw.relay.serve:run"
|
|
42
|
+
wcgw_mcp = "wcgw:mcp_server"
|
|
41
43
|
|
|
42
44
|
[tool.uv]
|
|
43
45
|
dev-dependencies = [
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Claude desktop support
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
Update `claude_desktop_config.json`
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"mcpServers": {
|
|
10
|
+
"wcgw": {
|
|
11
|
+
"command": "uvx",
|
|
12
|
+
"args": ["--from", "wcgw", "wcgw_mcp"]
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Then restart claude app.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
You should be able to see this icon if everything goes right.
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
Then ask claude to execute shell commands, read files, edit files, run your code, etc.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import importlib
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import traceback
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from mcp.server.models import InitializationOptions
|
|
9
|
+
import mcp.types as types
|
|
10
|
+
from mcp.types import Tool as ToolParam
|
|
11
|
+
from mcp.server import NotificationOptions, Server
|
|
12
|
+
from pydantic import AnyUrl, ValidationError
|
|
13
|
+
import mcp.server.stdio
|
|
14
|
+
from ..tools import DoneFlag, get_tool_output, which_tool_name, default_enc
|
|
15
|
+
from ...types_ import (
|
|
16
|
+
BashCommand,
|
|
17
|
+
BashInteraction,
|
|
18
|
+
CreateFileNew,
|
|
19
|
+
FileEdit,
|
|
20
|
+
ReadFile,
|
|
21
|
+
ReadImage,
|
|
22
|
+
ResetShell,
|
|
23
|
+
Initialize,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
server = Server("wcgw")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@server.list_resources()
|
|
31
|
+
async def handle_list_resources() -> list[types.Resource]:
|
|
32
|
+
return []
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@server.read_resource()
|
|
36
|
+
async def handle_read_resource(uri: AnyUrl) -> str:
|
|
37
|
+
raise ValueError("No resources available")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@server.list_prompts()
|
|
41
|
+
async def handle_list_prompts() -> list[types.Prompt]:
|
|
42
|
+
return []
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@server.get_prompt()
|
|
46
|
+
async def handle_get_prompt(
|
|
47
|
+
name: str, arguments: dict[str, str] | None
|
|
48
|
+
) -> types.GetPromptResult:
|
|
49
|
+
types.GetPromptResult(messages=[])
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@server.list_tools()
|
|
53
|
+
async def handle_list_tools() -> list[types.Tool]:
|
|
54
|
+
"""
|
|
55
|
+
List available tools.
|
|
56
|
+
Each tool specifies its arguments using JSON Schema validation.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
with open(
|
|
60
|
+
os.path.join(
|
|
61
|
+
os.path.dirname(os.path.dirname(__file__)), "diff-instructions.txt"
|
|
62
|
+
)
|
|
63
|
+
) as f:
|
|
64
|
+
diffinstructions = f.read()
|
|
65
|
+
return [
|
|
66
|
+
ToolParam(
|
|
67
|
+
inputSchema=Initialize.model_json_schema(),
|
|
68
|
+
name="Initialize",
|
|
69
|
+
description="""
|
|
70
|
+
- Always call this at the start of the conversation before anything else.
|
|
71
|
+
""",
|
|
72
|
+
),
|
|
73
|
+
ToolParam(
|
|
74
|
+
inputSchema=BashCommand.model_json_schema(),
|
|
75
|
+
name="BashCommand",
|
|
76
|
+
description="""
|
|
77
|
+
- Execute a bash command. This is stateful (beware with subsequent calls).
|
|
78
|
+
- Do not use interactive commands like nano. Prefer writing simpler commands.
|
|
79
|
+
- Status of the command and the current working directory will always be returned at the end.
|
|
80
|
+
- Optionally `exit shell has restarted` is the output, in which case environment resets, you can run fresh commands.
|
|
81
|
+
- The first line might be `(...truncated)` if the output is too long.
|
|
82
|
+
- Always run `pwd` if you get any file or directory not found error to make sure you're not lost.
|
|
83
|
+
- The control will return to you in 5 seconds regardless of the status. For heavy commands, keep checking status using BashInteraction till they are finished.
|
|
84
|
+
""",
|
|
85
|
+
),
|
|
86
|
+
ToolParam(
|
|
87
|
+
inputSchema=BashInteraction.model_json_schema(),
|
|
88
|
+
name="BashInteraction",
|
|
89
|
+
description="""
|
|
90
|
+
- Interact with running program using this tool
|
|
91
|
+
- Special keys like arrows, interrupts, enter, etc.
|
|
92
|
+
- Send text input to the running program.
|
|
93
|
+
- Send send_specials=["Enter"] to recheck status of a running program.
|
|
94
|
+
- Only one of send_text, send_specials, send_ascii should be provided.
|
|
95
|
+
""",
|
|
96
|
+
),
|
|
97
|
+
ToolParam(
|
|
98
|
+
inputSchema=ReadFile.model_json_schema(),
|
|
99
|
+
name="ReadFile",
|
|
100
|
+
description="""
|
|
101
|
+
- Read full file content
|
|
102
|
+
- Provide absolute file path only
|
|
103
|
+
""",
|
|
104
|
+
),
|
|
105
|
+
ToolParam(
|
|
106
|
+
inputSchema=CreateFileNew.model_json_schema(),
|
|
107
|
+
name="CreateFileNew",
|
|
108
|
+
description="""
|
|
109
|
+
- Write content to a new file. Provide file path and content. Use this instead of BashCommand for writing new files.
|
|
110
|
+
- This doesn't create any directories, please create directories using `mkdir -p` BashCommand.
|
|
111
|
+
- Provide absolute file path only.
|
|
112
|
+
- For editing existing files, use FileEdit instead of this tool.
|
|
113
|
+
""",
|
|
114
|
+
),
|
|
115
|
+
ToolParam(
|
|
116
|
+
inputSchema=ReadImage.model_json_schema(),
|
|
117
|
+
name="ReadImage",
|
|
118
|
+
description="Read an image from the shell.",
|
|
119
|
+
),
|
|
120
|
+
ToolParam(
|
|
121
|
+
inputSchema=ResetShell.model_json_schema(),
|
|
122
|
+
name="ResetShell",
|
|
123
|
+
description="Resets the shell. Use only if all interrupts and prompt reset attempts have failed repeatedly.",
|
|
124
|
+
),
|
|
125
|
+
ToolParam(
|
|
126
|
+
inputSchema=FileEdit.model_json_schema(),
|
|
127
|
+
name="FileEdit",
|
|
128
|
+
description="""
|
|
129
|
+
- Use absolute file path only.
|
|
130
|
+
- Use SEARCH/REPLACE blocks to edit the file.
|
|
131
|
+
"""
|
|
132
|
+
+ diffinstructions,
|
|
133
|
+
),
|
|
134
|
+
ToolParam(
|
|
135
|
+
inputSchema=ReadImage.model_json_schema(),
|
|
136
|
+
name="ReadImage",
|
|
137
|
+
description="""
|
|
138
|
+
- Read an image from the shell.
|
|
139
|
+
""",
|
|
140
|
+
),
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@server.call_tool()
|
|
145
|
+
async def handle_call_tool(
|
|
146
|
+
name: str, arguments: dict | None
|
|
147
|
+
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
|
|
148
|
+
if not arguments:
|
|
149
|
+
raise ValueError("Missing arguments")
|
|
150
|
+
|
|
151
|
+
tool_type = which_tool_name(name)
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
tool_call = tool_type(**arguments)
|
|
155
|
+
except ValidationError:
|
|
156
|
+
|
|
157
|
+
def try_json(x: str) -> Any:
|
|
158
|
+
try:
|
|
159
|
+
return json.loads(x)
|
|
160
|
+
except json.JSONDecodeError:
|
|
161
|
+
return x
|
|
162
|
+
|
|
163
|
+
tool_call = tool_type(**{k: try_json(v) for k, v in arguments.items()})
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
output_or_done, _ = get_tool_output(
|
|
167
|
+
tool_call, default_enc, 0.0, lambda x, y: ("", 0), 8000
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
except Exception as e:
|
|
171
|
+
output_or_done = f"GOT EXCEPTION while calling tool. Error: {e}"
|
|
172
|
+
tb = traceback.format_exc()
|
|
173
|
+
print(output_or_done + "\n" + tb)
|
|
174
|
+
|
|
175
|
+
assert not isinstance(output_or_done, DoneFlag)
|
|
176
|
+
if isinstance(output_or_done, str):
|
|
177
|
+
if issubclass(tool_type, Initialize):
|
|
178
|
+
output_or_done += """
|
|
179
|
+
|
|
180
|
+
You're an expert software engineer with shell and code knowledge.
|
|
181
|
+
|
|
182
|
+
Instructions:
|
|
183
|
+
|
|
184
|
+
- You should use the provided bash execution, reading and writing file tools to complete objective.
|
|
185
|
+
- First understand about the project by getting the folder structure (ignoring .git, node_modules, venv, etc.)
|
|
186
|
+
- Always read relevant files before editing.
|
|
187
|
+
- Do not provide code snippets unless asked by the user, instead directly edit the code.
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
Additional instructions:
|
|
191
|
+
Always run `pwd` if you get any file or directory not found error to make sure you're not lost, or to get absolute cwd.
|
|
192
|
+
|
|
193
|
+
Always write production ready, syntactically correct code.
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
return [types.TextContent(type="text", text=output_or_done)]
|
|
197
|
+
|
|
198
|
+
return [
|
|
199
|
+
types.ImageContent(
|
|
200
|
+
type="image",
|
|
201
|
+
data=output_or_done.data,
|
|
202
|
+
mimeType=output_or_done.media_type,
|
|
203
|
+
)
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
async def main() -> None:
|
|
208
|
+
version = importlib.metadata.version("wcgw")
|
|
209
|
+
# Run the server using stdin/stdout streams
|
|
210
|
+
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
|
|
211
|
+
await server.run(
|
|
212
|
+
read_stream,
|
|
213
|
+
write_stream,
|
|
214
|
+
InitializationOptions(
|
|
215
|
+
server_name="wcgw",
|
|
216
|
+
server_version=version,
|
|
217
|
+
capabilities=server.get_capabilities(
|
|
218
|
+
notification_options=NotificationOptions(),
|
|
219
|
+
experimental_capabilities={},
|
|
220
|
+
),
|
|
221
|
+
),
|
|
222
|
+
)
|
|
@@ -57,7 +57,6 @@ from ..types_ import (
|
|
|
57
57
|
ReadFile,
|
|
58
58
|
ReadImage,
|
|
59
59
|
ResetShell,
|
|
60
|
-
Writefile,
|
|
61
60
|
)
|
|
62
61
|
|
|
63
62
|
from .common import CostData, Models, discard_input
|
|
@@ -96,7 +95,8 @@ def ask_confirmation(prompt: Confirmation) -> str:
|
|
|
96
95
|
return "Yes" if response.lower() == "y" else "No"
|
|
97
96
|
|
|
98
97
|
|
|
99
|
-
|
|
98
|
+
PROMPT_CONST = "#@wcgw@#"
|
|
99
|
+
PROMPT = PROMPT_CONST
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
def start_shell() -> pexpect.spawn: # type: ignore
|
|
@@ -125,7 +125,7 @@ def _is_int(mystr: str) -> bool:
|
|
|
125
125
|
|
|
126
126
|
|
|
127
127
|
def _get_exit_code() -> int:
|
|
128
|
-
if PROMPT !=
|
|
128
|
+
if PROMPT != PROMPT_CONST:
|
|
129
129
|
return 0
|
|
130
130
|
# First reset the prompt in case venv was sourced or other reasons.
|
|
131
131
|
SHELL.sendline(f"export PS1={PROMPT}")
|
|
@@ -448,7 +448,7 @@ def read_image_from_shell(file_path: str) -> ImageData:
|
|
|
448
448
|
return ImageData(media_type=image_type, data=image_b64) # type: ignore
|
|
449
449
|
|
|
450
450
|
|
|
451
|
-
def write_file(writefile:
|
|
451
|
+
def write_file(writefile: CreateFileNew, error_on_exist: bool) -> str:
|
|
452
452
|
if not os.path.isabs(writefile.file_path):
|
|
453
453
|
return f"Failure: file_path should be absolute path, current working directory is {CWD}"
|
|
454
454
|
else:
|
|
@@ -661,7 +661,6 @@ TOOLS = (
|
|
|
661
661
|
| BashCommand
|
|
662
662
|
| BashInteraction
|
|
663
663
|
| ResetShell
|
|
664
|
-
| Writefile
|
|
665
664
|
| CreateFileNew
|
|
666
665
|
| FileEditFindReplace
|
|
667
666
|
| FileEdit
|
|
@@ -687,8 +686,6 @@ def which_tool_name(name: str) -> Type[TOOLS]:
|
|
|
687
686
|
return BashInteraction
|
|
688
687
|
elif name == "ResetShell":
|
|
689
688
|
return ResetShell
|
|
690
|
-
elif name == "Writefile":
|
|
691
|
-
return Writefile
|
|
692
689
|
elif name == "CreateFileNew":
|
|
693
690
|
return CreateFileNew
|
|
694
691
|
elif name == "FileEditFindReplace":
|
|
@@ -715,7 +712,6 @@ def get_tool_output(
|
|
|
715
712
|
| BashCommand
|
|
716
713
|
| BashInteraction
|
|
717
714
|
| ResetShell
|
|
718
|
-
| Writefile
|
|
719
715
|
| CreateFileNew
|
|
720
716
|
| FileEditFindReplace
|
|
721
717
|
| FileEdit
|
|
@@ -735,7 +731,6 @@ def get_tool_output(
|
|
|
735
731
|
| BashCommand
|
|
736
732
|
| BashInteraction
|
|
737
733
|
| ResetShell
|
|
738
|
-
| Writefile
|
|
739
734
|
| CreateFileNew
|
|
740
735
|
| FileEditFindReplace
|
|
741
736
|
| FileEdit
|
|
@@ -749,7 +744,6 @@ def get_tool_output(
|
|
|
749
744
|
| BashCommand
|
|
750
745
|
| BashInteraction
|
|
751
746
|
| ResetShell
|
|
752
|
-
| Writefile
|
|
753
747
|
| CreateFileNew
|
|
754
748
|
| FileEditFindReplace
|
|
755
749
|
| FileEdit
|
|
@@ -769,9 +763,6 @@ def get_tool_output(
|
|
|
769
763
|
elif isinstance(arg, (BashCommand | BashInteraction)):
|
|
770
764
|
console.print("Calling execute bash tool")
|
|
771
765
|
output = execute_bash(enc, arg, max_tokens)
|
|
772
|
-
elif isinstance(arg, Writefile):
|
|
773
|
-
console.print("Calling write file tool")
|
|
774
|
-
output = write_file(arg, False), 0
|
|
775
766
|
elif isinstance(arg, CreateFileNew):
|
|
776
767
|
console.print("Calling write file tool")
|
|
777
768
|
output = write_file(arg, True), 0
|
|
@@ -818,7 +809,6 @@ class Mdata(BaseModel):
|
|
|
818
809
|
data: (
|
|
819
810
|
BashCommand
|
|
820
811
|
| BashInteraction
|
|
821
|
-
| Writefile
|
|
822
812
|
| CreateFileNew
|
|
823
813
|
| ResetShell
|
|
824
814
|
| FileEditFindReplace
|
|
@@ -23,7 +23,6 @@ from ..types_ import (
|
|
|
23
23
|
Initialize,
|
|
24
24
|
ReadFile,
|
|
25
25
|
ResetShell,
|
|
26
|
-
Writefile,
|
|
27
26
|
Specials,
|
|
28
27
|
)
|
|
29
28
|
|
|
@@ -32,7 +31,6 @@ class Mdata(BaseModel):
|
|
|
32
31
|
data: (
|
|
33
32
|
BashCommand
|
|
34
33
|
| BashInteraction
|
|
35
|
-
| Writefile
|
|
36
34
|
| CreateFileNew
|
|
37
35
|
| ResetShell
|
|
38
36
|
| FileEditFindReplace
|
|
@@ -67,8 +65,7 @@ async def register_websocket(websocket: WebSocket, uuid: UUID) -> None:
|
|
|
67
65
|
# receive client version
|
|
68
66
|
client_version = await websocket.receive_text()
|
|
69
67
|
sem_version_client = semantic_version.Version.coerce(client_version)
|
|
70
|
-
sem_version_server = semantic_version.Version.coerce(
|
|
71
|
-
CLIENT_VERSION_MINIMUM)
|
|
68
|
+
sem_version_server = semantic_version.Version.coerce(CLIENT_VERSION_MINIMUM)
|
|
72
69
|
if sem_version_client < sem_version_server:
|
|
73
70
|
await websocket.send_text(
|
|
74
71
|
Mdata(
|
|
@@ -93,8 +90,7 @@ async def register_websocket(websocket: WebSocket, uuid: UUID) -> None:
|
|
|
93
90
|
while True:
|
|
94
91
|
received_data = await websocket.receive_text()
|
|
95
92
|
if uuid not in gpts:
|
|
96
|
-
raise fastapi.HTTPException(
|
|
97
|
-
status_code=400, detail="No call made")
|
|
93
|
+
raise fastapi.HTTPException(status_code=400, detail="No call made")
|
|
98
94
|
gpts[uuid](received_data)
|
|
99
95
|
except WebSocketDisconnect:
|
|
100
96
|
# Remove the client if the WebSocket is disconnected
|
|
@@ -281,9 +277,7 @@ async def read_file_endpoint(read_file_data: ReadFileWithUUID) -> str:
|
|
|
281
277
|
|
|
282
278
|
gpts[user_id] = put_results
|
|
283
279
|
|
|
284
|
-
await clients[user_id](
|
|
285
|
-
Mdata(data=read_file_data, user_id=user_id)
|
|
286
|
-
)
|
|
280
|
+
await clients[user_id](Mdata(data=read_file_data, user_id=user_id))
|
|
287
281
|
|
|
288
282
|
start_time = time.time()
|
|
289
283
|
while time.time() - start_time < 30:
|
|
@@ -293,6 +287,7 @@ async def read_file_endpoint(read_file_data: ReadFileWithUUID) -> str:
|
|
|
293
287
|
|
|
294
288
|
raise fastapi.HTTPException(status_code=500, detail="Timeout error")
|
|
295
289
|
|
|
290
|
+
|
|
296
291
|
class InitializeWithUUID(Initialize):
|
|
297
292
|
user_id: UUID
|
|
298
293
|
|
|
@@ -311,9 +306,7 @@ async def initialize(initialize_data: InitializeWithUUID) -> str:
|
|
|
311
306
|
|
|
312
307
|
gpts[user_id] = put_results
|
|
313
308
|
|
|
314
|
-
await clients[user_id](
|
|
315
|
-
Mdata(data=initialize_data, user_id=user_id)
|
|
316
|
-
)
|
|
309
|
+
await clients[user_id](Mdata(data=initialize_data, user_id=user_id))
|
|
317
310
|
|
|
318
311
|
start_time = time.time()
|
|
319
312
|
while time.time() - start_time < 30:
|
|
@@ -24,11 +24,6 @@ class ReadImage(BaseModel):
|
|
|
24
24
|
type: Literal["ReadImage"]
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
class Writefile(BaseModel):
|
|
28
|
-
file_path: str
|
|
29
|
-
file_content: str
|
|
30
|
-
|
|
31
|
-
|
|
32
27
|
class CreateFileNew(BaseModel):
|
|
33
28
|
file_path: str
|
|
34
29
|
file_content: str
|
|
@@ -55,4 +50,4 @@ class FileEdit(BaseModel):
|
|
|
55
50
|
|
|
56
51
|
|
|
57
52
|
class Initialize(BaseModel):
|
|
58
|
-
type: Literal["Initialize"]
|
|
53
|
+
type: Literal["Initialize"]
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import unittest
|
|
2
2
|
from unittest.mock import patch
|
|
3
3
|
from src.wcgw.client.tools import render_terminal_output, ask_confirmation, Confirmation
|
|
4
|
-
from src.wcgw.types_ import
|
|
4
|
+
from src.wcgw.types_ import CreateFileNew
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class TestTools(unittest.TestCase):
|
|
@@ -32,7 +32,7 @@ class TestTools(unittest.TestCase):
|
|
|
32
32
|
|
|
33
33
|
def test_writefile_model(self):
|
|
34
34
|
# Test the Writefile Pydantic model
|
|
35
|
-
file =
|
|
35
|
+
file = CreateFileNew(file_path="test.txt", file_content="This is a test.")
|
|
36
36
|
self.assertEqual(file.file_path, "test.txt")
|
|
37
37
|
self.assertEqual(file.file_content, "This is a test.")
|
|
38
38
|
|