wcgw 1.5.3__py3-none-any.whl → 2.0.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.
Potentially problematic release.
This version of wcgw might be problematic. Click here for more details.
- wcgw/client/anthropic_client.py +83 -49
- wcgw/client/cli.py +2 -0
- wcgw/client/computer_use.py +1 -1
- wcgw/client/mcp_server/Readme.md +27 -3
- wcgw/client/mcp_server/__init__.py +7 -2
- wcgw/client/mcp_server/server.py +34 -30
- wcgw/client/openai_client.py +1 -0
- wcgw/client/tools.py +13 -2
- wcgw-2.0.0.dist-info/METADATA +156 -0
- wcgw-2.0.0.dist-info/RECORD +22 -0
- wcgw-1.5.3.dist-info/METADATA +0 -178
- wcgw-1.5.3.dist-info/RECORD +0 -22
- {wcgw-1.5.3.dist-info → wcgw-2.0.0.dist-info}/WHEEL +0 -0
- {wcgw-1.5.3.dist-info → wcgw-2.0.0.dist-info}/entry_points.txt +0 -0
wcgw/client/anthropic_client.py
CHANGED
|
@@ -4,7 +4,7 @@ import mimetypes
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
import sys
|
|
6
6
|
import traceback
|
|
7
|
-
from typing import Callable, DefaultDict, Optional, cast
|
|
7
|
+
from typing import Callable, DefaultDict, Optional, cast, Literal
|
|
8
8
|
import anthropic
|
|
9
9
|
from anthropic import Anthropic
|
|
10
10
|
from anthropic.types import (
|
|
@@ -110,7 +110,10 @@ def parse_user_message_special(msg: str) -> MessageParam:
|
|
|
110
110
|
"type": "image",
|
|
111
111
|
"source": {
|
|
112
112
|
"type": "base64",
|
|
113
|
-
"media_type":
|
|
113
|
+
"media_type": cast(
|
|
114
|
+
'Literal["image/jpeg", "image/png", "image/gif", "image/webp"]',
|
|
115
|
+
image_type or "image/png",
|
|
116
|
+
),
|
|
114
117
|
"data": image_b64,
|
|
115
118
|
},
|
|
116
119
|
}
|
|
@@ -131,6 +134,7 @@ def loop(
|
|
|
131
134
|
first_message: Optional[str] = None,
|
|
132
135
|
limit: Optional[float] = None,
|
|
133
136
|
resume: Optional[str] = None,
|
|
137
|
+
computer_use: bool = False,
|
|
134
138
|
) -> tuple[str, float]:
|
|
135
139
|
load_dotenv()
|
|
136
140
|
|
|
@@ -222,10 +226,14 @@ def loop(
|
|
|
222
226
|
- Use SEARCH/REPLACE blocks to edit the file.
|
|
223
227
|
""",
|
|
224
228
|
),
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
if computer_use:
|
|
232
|
+
tools += [
|
|
233
|
+
ToolParam(
|
|
234
|
+
input_schema=GetScreenInfo.model_json_schema(),
|
|
235
|
+
name="GetScreenInfo",
|
|
236
|
+
description="""
|
|
229
237
|
- Important: call this first in the conversation before ScreenShot, Mouse, and Keyboard tools.
|
|
230
238
|
- Get display information of a linux os running on docker using image "ghcr.io/anthropics/anthropic-quickstarts:computer-use-demo-latest"
|
|
231
239
|
- If user hasn't provided docker image id, check using `docker ps` and provide the id.
|
|
@@ -233,11 +241,11 @@ def loop(
|
|
|
233
241
|
- Connects shell to the docker environment.
|
|
234
242
|
- Note: once this is called, the shell enters the docker environment. All bash commands will run over there.
|
|
235
243
|
""",
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
244
|
+
),
|
|
245
|
+
ToolParam(
|
|
246
|
+
input_schema=ScreenShot.model_json_schema(),
|
|
247
|
+
name="ScreenShot",
|
|
248
|
+
description="""
|
|
241
249
|
- Capture screenshot of the linux os on docker.
|
|
242
250
|
- All actions on UI using mouse and keyboard return within 0.5 seconds.
|
|
243
251
|
* So if you're doing something that takes longer for UI to update like heavy page loading, keep checking UI for update usign ScreenShot upto 10 turns.
|
|
@@ -246,27 +254,27 @@ def loop(
|
|
|
246
254
|
* If you don't notice even slightest of the change, it's likely you clicked on the wrong place.
|
|
247
255
|
|
|
248
256
|
""",
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
257
|
+
),
|
|
258
|
+
ToolParam(
|
|
259
|
+
input_schema=Mouse.model_json_schema(),
|
|
260
|
+
name="Mouse",
|
|
261
|
+
description="""
|
|
254
262
|
- Interact with the linux os on docker using mouse.
|
|
255
263
|
- Uses xdotool
|
|
256
264
|
- About left_click_drag: the current mouse position will be used as the starting point, click and drag to the given x, y coordinates. Useful in things like sliders, moving things around, etc.
|
|
257
265
|
""",
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
266
|
+
),
|
|
267
|
+
ToolParam(
|
|
268
|
+
input_schema=Keyboard.model_json_schema(),
|
|
269
|
+
name="Keyboard",
|
|
270
|
+
description="""
|
|
263
271
|
- Interact with the linux os on docker using keyboard.
|
|
264
272
|
- Emulate keyboard input to the screen
|
|
265
273
|
- Uses xdootool to send keyboard input, keys like Return, BackSpace, Escape, Page_Up, etc. can be used.
|
|
266
274
|
- Do not use it to interact with Bash tool.
|
|
267
275
|
""",
|
|
268
|
-
|
|
269
|
-
|
|
276
|
+
),
|
|
277
|
+
]
|
|
270
278
|
uname_sysname = os.uname().sysname
|
|
271
279
|
uname_machine = os.uname().machine
|
|
272
280
|
|
|
@@ -355,53 +363,79 @@ System information:
|
|
|
355
363
|
type_ = chunk.type
|
|
356
364
|
if type_ in {"message_start", "message_stop"}:
|
|
357
365
|
continue
|
|
358
|
-
elif type_ == "content_block_start"
|
|
366
|
+
elif type_ == "content_block_start" and hasattr(
|
|
367
|
+
chunk, "content_block"
|
|
368
|
+
):
|
|
359
369
|
content_block = chunk.content_block
|
|
360
|
-
if
|
|
370
|
+
if (
|
|
371
|
+
hasattr(content_block, "type")
|
|
372
|
+
and content_block.type == "text"
|
|
373
|
+
and hasattr(content_block, "text")
|
|
374
|
+
):
|
|
361
375
|
chunk_str = content_block.text
|
|
362
376
|
assistant_console.print(chunk_str, end="")
|
|
363
377
|
full_response += chunk_str
|
|
364
378
|
elif content_block.type == "tool_use":
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
379
|
+
if (
|
|
380
|
+
hasattr(content_block, "input")
|
|
381
|
+
and hasattr(content_block, "name")
|
|
382
|
+
and hasattr(content_block, "id")
|
|
383
|
+
):
|
|
384
|
+
assert content_block.input == {}
|
|
385
|
+
tool_calls.append(
|
|
386
|
+
{
|
|
387
|
+
"name": str(content_block.name),
|
|
388
|
+
"input": str(""),
|
|
389
|
+
"done": False,
|
|
390
|
+
"id": str(content_block.id),
|
|
391
|
+
}
|
|
392
|
+
)
|
|
374
393
|
else:
|
|
375
394
|
error_console.log(
|
|
376
395
|
f"Ignoring unknown content block type {content_block.type}"
|
|
377
396
|
)
|
|
378
|
-
elif type_ == "content_block_delta":
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
397
|
+
elif type_ == "content_block_delta" and hasattr(chunk, "delta"):
|
|
398
|
+
delta = chunk.delta
|
|
399
|
+
if hasattr(delta, "type"):
|
|
400
|
+
delta_type = str(delta.type)
|
|
401
|
+
if delta_type == "text_delta" and hasattr(delta, "text"):
|
|
402
|
+
chunk_str = delta.text
|
|
403
|
+
assistant_console.print(chunk_str, end="")
|
|
404
|
+
full_response += chunk_str
|
|
405
|
+
elif delta_type == "input_json_delta" and hasattr(
|
|
406
|
+
delta, "partial_json"
|
|
407
|
+
):
|
|
408
|
+
partial_json = delta.partial_json
|
|
409
|
+
if isinstance(tool_calls[-1]["input"], str):
|
|
410
|
+
tool_calls[-1]["input"] += partial_json
|
|
411
|
+
else:
|
|
412
|
+
error_console.log(
|
|
413
|
+
f"Ignoring unknown content block delta type {delta_type}"
|
|
414
|
+
)
|
|
385
415
|
else:
|
|
386
|
-
|
|
387
|
-
f"Ignoring unknown content block delta type {chunk.delta.type}"
|
|
388
|
-
)
|
|
416
|
+
raise ValueError("Content block delta has no type")
|
|
389
417
|
elif type_ == "content_block_stop":
|
|
390
418
|
if tool_calls and not tool_calls[-1]["done"]:
|
|
391
419
|
tc = tool_calls[-1]
|
|
420
|
+
tool_name = str(tc["name"])
|
|
421
|
+
tool_input = str(tc["input"])
|
|
422
|
+
tool_id = str(tc["id"])
|
|
423
|
+
|
|
392
424
|
tool_parsed = which_tool_name(
|
|
393
|
-
|
|
394
|
-
).model_validate_json(
|
|
425
|
+
tool_name
|
|
426
|
+
).model_validate_json(tool_input)
|
|
427
|
+
|
|
395
428
|
system_console.print(
|
|
396
429
|
f"\n---------------------------------------\n# Assistant invoked tool: {tool_parsed}"
|
|
397
430
|
)
|
|
431
|
+
|
|
398
432
|
_histories.append(
|
|
399
433
|
{
|
|
400
434
|
"role": "assistant",
|
|
401
435
|
"content": [
|
|
402
436
|
ToolUseBlockParam(
|
|
403
|
-
id=
|
|
404
|
-
name=
|
|
437
|
+
id=tool_id,
|
|
438
|
+
name=tool_name,
|
|
405
439
|
input=tool_parsed.model_dump(),
|
|
406
440
|
type="tool_use",
|
|
407
441
|
)
|
|
@@ -453,7 +487,7 @@ System information:
|
|
|
453
487
|
tool_results.append(
|
|
454
488
|
ToolResultBlockParam(
|
|
455
489
|
type="tool_result",
|
|
456
|
-
tool_use_id=tc["id"],
|
|
490
|
+
tool_use_id=str(tc["id"]),
|
|
457
491
|
content=tool_results_content,
|
|
458
492
|
)
|
|
459
493
|
)
|
wcgw/client/cli.py
CHANGED
|
@@ -16,6 +16,7 @@ def loop(
|
|
|
16
16
|
first_message: Optional[str] = None,
|
|
17
17
|
limit: Optional[float] = None,
|
|
18
18
|
resume: Optional[str] = None,
|
|
19
|
+
computer_use: bool = False,
|
|
19
20
|
version: bool = typer.Option(False, "--version", "-v"),
|
|
20
21
|
) -> tuple[str, float]:
|
|
21
22
|
if version:
|
|
@@ -27,6 +28,7 @@ def loop(
|
|
|
27
28
|
first_message=first_message,
|
|
28
29
|
limit=limit,
|
|
29
30
|
resume=resume,
|
|
31
|
+
computer_use=computer_use,
|
|
30
32
|
)
|
|
31
33
|
else:
|
|
32
34
|
return openai_loop(
|
wcgw/client/computer_use.py
CHANGED
|
@@ -161,7 +161,7 @@ class ComputerTool:
|
|
|
161
161
|
assert not result.error, result.error
|
|
162
162
|
assert result.output, "Could not get screen info"
|
|
163
163
|
width, height, display_num = map(
|
|
164
|
-
lambda x: None if not x else int(x), result.output.split(",")
|
|
164
|
+
lambda x: None if not x else int(x), result.output.strip().split(",")
|
|
165
165
|
)
|
|
166
166
|
if width is None:
|
|
167
167
|
width = 1080
|
wcgw/client/mcp_server/Readme.md
CHANGED
|
@@ -31,15 +31,39 @@ Then restart claude app.
|
|
|
31
31
|
|
|
32
32
|
### [Optional] Computer use support using desktop on docker
|
|
33
33
|
|
|
34
|
-
Computer use is
|
|
34
|
+
Computer use is disabled by default. Add `--computer-use` to enable it. This will add necessary tools to Claude including ScreenShot, Mouse and Keyboard control.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"mcpServers": {
|
|
39
|
+
"wcgw": {
|
|
40
|
+
"command": "uv",
|
|
41
|
+
"args": [
|
|
42
|
+
"tool",
|
|
43
|
+
"run",
|
|
44
|
+
"--from",
|
|
45
|
+
"wcgw@latest",
|
|
46
|
+
"--python",
|
|
47
|
+
"3.12",
|
|
48
|
+
"wcgw_mcp",
|
|
49
|
+
"--computer-use"
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Claude will be able to connect to any docker container with linux environment. Native system control isn't supported outside docker.
|
|
57
|
+
|
|
58
|
+
You'll need to run a docker image with desktop and optional VNC connection. Here's a demo image:
|
|
37
59
|
|
|
38
60
|
```sh
|
|
39
61
|
docker run -p 6080:6080 ghcr.io/anthropics/anthropic-quickstarts:computer-use-demo-latest
|
|
40
62
|
```
|
|
41
63
|
|
|
42
|
-
|
|
64
|
+
Then ask claude desktop app to control the docker os. It'll connect to the docker container and control it.
|
|
65
|
+
|
|
66
|
+
Connect to `http://localhost:6080/vnc.html` for desktop view (VNC) of the system running in the docker.
|
|
43
67
|
|
|
44
68
|
## Usage
|
|
45
69
|
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
# mypy: disable-error-code="import-untyped"
|
|
1
2
|
from wcgw.client.mcp_server import server
|
|
2
3
|
import asyncio
|
|
4
|
+
from typer import Typer
|
|
3
5
|
|
|
6
|
+
main = Typer()
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
|
|
9
|
+
@main.command()
|
|
10
|
+
def app(computer_use: bool = False) -> None:
|
|
6
11
|
"""Main entry point for the package."""
|
|
7
|
-
asyncio.run(server.main())
|
|
12
|
+
asyncio.run(server.main(computer_use))
|
|
8
13
|
|
|
9
14
|
|
|
10
15
|
# Optionally expose other important items at package level
|
wcgw/client/mcp_server/server.py
CHANGED
|
@@ -32,6 +32,8 @@ from ..computer_use import SLEEP_TIME_MAX_S
|
|
|
32
32
|
|
|
33
33
|
tools.TIMEOUT = SLEEP_TIME_MAX_S
|
|
34
34
|
|
|
35
|
+
COMPUTER_USE_ON_DOCKER_ENABLED = False
|
|
36
|
+
|
|
35
37
|
server = Server("wcgw")
|
|
36
38
|
|
|
37
39
|
|
|
@@ -71,7 +73,7 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
|
71
73
|
) as f:
|
|
72
74
|
diffinstructions = f.read()
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
tools = [
|
|
75
77
|
ToolParam(
|
|
76
78
|
inputSchema=Initialize.model_json_schema(),
|
|
77
79
|
name="Initialize",
|
|
@@ -142,17 +144,13 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
|
142
144
|
"""
|
|
143
145
|
+ diffinstructions,
|
|
144
146
|
),
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
""
|
|
151
|
-
|
|
152
|
-
ToolParam(
|
|
153
|
-
inputSchema=GetScreenInfo.model_json_schema(),
|
|
154
|
-
name="GetScreenInfo",
|
|
155
|
-
description="""
|
|
147
|
+
]
|
|
148
|
+
if COMPUTER_USE_ON_DOCKER_ENABLED:
|
|
149
|
+
tools += [
|
|
150
|
+
ToolParam(
|
|
151
|
+
inputSchema=GetScreenInfo.model_json_schema(),
|
|
152
|
+
name="GetScreenInfo",
|
|
153
|
+
description="""
|
|
156
154
|
- Important: call this first in the conversation before ScreenShot, Mouse, and Keyboard tools.
|
|
157
155
|
- Get display information of a linux os running on docker using image "ghcr.io/anthropics/anthropic-quickstarts:computer-use-demo-latest"
|
|
158
156
|
- If user hasn't provided docker image id, check using `docker ps` and provide the id.
|
|
@@ -160,11 +158,11 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
|
160
158
|
- Connects shell to the docker environment.
|
|
161
159
|
- Note: once this is called, the shell enters the docker environment. All bash commands will run over there.
|
|
162
160
|
""",
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
161
|
+
),
|
|
162
|
+
ToolParam(
|
|
163
|
+
inputSchema=ScreenShot.model_json_schema(),
|
|
164
|
+
name="ScreenShot",
|
|
165
|
+
description="""
|
|
168
166
|
- Capture screenshot of the linux os on docker.
|
|
169
167
|
- All actions on UI using mouse and keyboard return within 0.5 seconds.
|
|
170
168
|
* So if you're doing something that takes longer for UI to update like heavy page loading, keep checking UI for update usign ScreenShot upto 10 turns.
|
|
@@ -172,28 +170,30 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
|
172
170
|
* After 10 turns of no change, ask user for permission to keep checking.
|
|
173
171
|
* If you don't notice even slightest of the change, it's likely you clicked on the wrong place.
|
|
174
172
|
""",
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
173
|
+
),
|
|
174
|
+
ToolParam(
|
|
175
|
+
inputSchema=Mouse.model_json_schema(),
|
|
176
|
+
name="Mouse",
|
|
177
|
+
description="""
|
|
180
178
|
- Interact with the linux os on docker using mouse.
|
|
181
179
|
- Uses xdotool
|
|
182
180
|
- About left_click_drag: the current mouse position will be used as the starting point, click and drag to the given x, y coordinates. Useful in things like sliders, moving things around, etc.
|
|
183
181
|
""",
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
182
|
+
),
|
|
183
|
+
ToolParam(
|
|
184
|
+
inputSchema=Keyboard.model_json_schema(),
|
|
185
|
+
name="Keyboard",
|
|
186
|
+
description="""
|
|
189
187
|
- Interact with the linux os on docker using keyboard.
|
|
190
188
|
- Emulate keyboard input to the screen
|
|
191
189
|
- Uses xdootool to send keyboard input, keys like Return, BackSpace, Escape, Page_Up, etc. can be used.
|
|
192
190
|
- Do not use it to interact with Bash tool.
|
|
193
191
|
- Make sure you've selected a text area or an editable element before sending text.
|
|
194
192
|
""",
|
|
195
|
-
|
|
196
|
-
|
|
193
|
+
),
|
|
194
|
+
]
|
|
195
|
+
|
|
196
|
+
return tools
|
|
197
197
|
|
|
198
198
|
|
|
199
199
|
@server.call_tool() # type: ignore
|
|
@@ -263,7 +263,11 @@ async def handle_call_tool(
|
|
|
263
263
|
return content
|
|
264
264
|
|
|
265
265
|
|
|
266
|
-
async def main() -> None:
|
|
266
|
+
async def main(computer_use: bool) -> None:
|
|
267
|
+
global COMPUTER_USE_ON_DOCKER_ENABLED
|
|
268
|
+
if computer_use:
|
|
269
|
+
COMPUTER_USE_ON_DOCKER_ENABLED = True
|
|
270
|
+
|
|
267
271
|
version = importlib.metadata.version("wcgw")
|
|
268
272
|
# Run the server using stdin/stdout streams
|
|
269
273
|
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
|
wcgw/client/openai_client.py
CHANGED
wcgw/client/tools.py
CHANGED
|
@@ -962,7 +962,7 @@ run = Typer(pretty_exceptions_show_locals=False, no_args_is_help=True)
|
|
|
962
962
|
|
|
963
963
|
@run.command()
|
|
964
964
|
def app(
|
|
965
|
-
server_url: str = "
|
|
965
|
+
server_url: str = "",
|
|
966
966
|
client_uuid: Optional[str] = None,
|
|
967
967
|
version: bool = typer.Option(False, "--version", "-v"),
|
|
968
968
|
) -> None:
|
|
@@ -970,7 +970,18 @@ def app(
|
|
|
970
970
|
version_ = importlib.metadata.version("wcgw")
|
|
971
971
|
print(f"wcgw version: {version_}")
|
|
972
972
|
exit()
|
|
973
|
-
|
|
973
|
+
if not server_url:
|
|
974
|
+
server_url = os.environ.get("WCGW_RELAY_SERVER", "")
|
|
975
|
+
if not server_url:
|
|
976
|
+
print(
|
|
977
|
+
"Error: Please provide relay server url using --server_url or WCGW_RELAY_SERVER environment variable"
|
|
978
|
+
)
|
|
979
|
+
print(
|
|
980
|
+
"\tNOTE: you need to run a relay server first, author doesn't host a relay server anymore."
|
|
981
|
+
)
|
|
982
|
+
print("\thttps://github.com/rusiaaman/wcgw/blob/main/openai.md")
|
|
983
|
+
print("\tExample `--server-url=ws://localhost:8000/v1/register`")
|
|
984
|
+
raise typer.Exit(1)
|
|
974
985
|
register_client(server_url, client_uuid or "")
|
|
975
986
|
|
|
976
987
|
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: wcgw
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: What could go wrong giving full shell access to chatgpt?
|
|
5
|
+
Project-URL: Homepage, https://github.com/rusiaaman/wcgw
|
|
6
|
+
Author-email: Aman Rusia <gapypi@arcfu.com>
|
|
7
|
+
Requires-Python: <3.13,>=3.11
|
|
8
|
+
Requires-Dist: anthropic>=0.39.0
|
|
9
|
+
Requires-Dist: fastapi>=0.115.0
|
|
10
|
+
Requires-Dist: mcp
|
|
11
|
+
Requires-Dist: mypy>=1.11.2
|
|
12
|
+
Requires-Dist: nltk>=3.9.1
|
|
13
|
+
Requires-Dist: openai>=1.46.0
|
|
14
|
+
Requires-Dist: petname>=2.6
|
|
15
|
+
Requires-Dist: pexpect>=4.9.0
|
|
16
|
+
Requires-Dist: pydantic>=2.9.2
|
|
17
|
+
Requires-Dist: pyte>=0.8.2
|
|
18
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
19
|
+
Requires-Dist: rich>=13.8.1
|
|
20
|
+
Requires-Dist: semantic-version>=2.10.0
|
|
21
|
+
Requires-Dist: shell>=1.0.1
|
|
22
|
+
Requires-Dist: tiktoken==0.7.0
|
|
23
|
+
Requires-Dist: toml>=0.10.2
|
|
24
|
+
Requires-Dist: typer>=0.12.5
|
|
25
|
+
Requires-Dist: types-pexpect>=4.9.0.20240806
|
|
26
|
+
Requires-Dist: uvicorn>=0.31.0
|
|
27
|
+
Requires-Dist: websockets>=13.1
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# Shell and Coding agent on Claude desktop app
|
|
31
|
+
|
|
32
|
+
- An MCP server on claude desktop for autonomous shell, coding and desktop control agent.
|
|
33
|
+
|
|
34
|
+
[](https://github.com/rusiaaman/wcgw/actions/workflows/python-tests.yml)
|
|
35
|
+
[](https://github.com/rusiaaman/wcgw/actions/workflows/python-types.yml)
|
|
36
|
+
[](https://github.com/rusiaaman/wcgw/actions/workflows/python-publish.yml)
|
|
37
|
+
|
|
38
|
+
## Updates
|
|
39
|
+
|
|
40
|
+
- [01 Dec 2024] Deprecated chatgpt app support
|
|
41
|
+
|
|
42
|
+
- [26 Nov 2024] Introduced claude desktop support through mcp
|
|
43
|
+
|
|
44
|
+
## 🚀 Highlights
|
|
45
|
+
|
|
46
|
+
- ⚡ **Full Shell Access**: No restrictions, complete control.
|
|
47
|
+
- ⚡ **Desktop control on Claude**: Screen capture, mouse control, keyboard control on claude desktop (on mac with docker linux)
|
|
48
|
+
- ⚡ **Create, Execute, Iterate**: Ask claude to keep running compiler checks till all errors are fixed, or ask it to keep checking for the status of a long running command till it's done.
|
|
49
|
+
- ⚡ **Interactive Command Handling**: Supports interactive commands using arrow keys, interrupt, and ansi escape sequences.
|
|
50
|
+
- ⚡ **REPL support**: [beta] Supports python/node and other REPL execution.
|
|
51
|
+
|
|
52
|
+
## Setup
|
|
53
|
+
|
|
54
|
+
Update `claude_desktop_config.json` (~/Library/Application Support/Claude/claude_desktop_config.json)
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"wcgw": {
|
|
60
|
+
"command": "uv",
|
|
61
|
+
"args": [
|
|
62
|
+
"tool",
|
|
63
|
+
"run",
|
|
64
|
+
"--from",
|
|
65
|
+
"wcgw@latest",
|
|
66
|
+
"--python",
|
|
67
|
+
"3.12",
|
|
68
|
+
"wcgw_mcp"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Then restart claude app.
|
|
76
|
+
|
|
77
|
+
## [Optional] Computer use support using desktop on docker
|
|
78
|
+
|
|
79
|
+
Computer use is disabled by default. Add `--computer-use` to enable it. This will add necessary tools to Claude including ScreenShot, Mouse and Keyboard control.
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"mcpServers": {
|
|
84
|
+
"wcgw": {
|
|
85
|
+
"command": "uv",
|
|
86
|
+
"args": [
|
|
87
|
+
"tool",
|
|
88
|
+
"run",
|
|
89
|
+
"--from",
|
|
90
|
+
"wcgw@latest",
|
|
91
|
+
"--python",
|
|
92
|
+
"3.12",
|
|
93
|
+
"wcgw_mcp",
|
|
94
|
+
"--computer-use"
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Claude will be able to connect to any docker container with linux environment. Native system control isn't supported outside docker.
|
|
102
|
+
|
|
103
|
+
You'll need to run a docker image with desktop and optional VNC connection. Here's a demo image:
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
docker run -p 6080:6080 ghcr.io/anthropics/anthropic-quickstarts:computer-use-demo-latest
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Then ask claude desktop app to control the docker os. It'll connect to the docker container and control it.
|
|
110
|
+
|
|
111
|
+
Connect to `http://localhost:6080/vnc.html` for desktop view (VNC) of the system running in the docker.
|
|
112
|
+
|
|
113
|
+
## Usage
|
|
114
|
+
|
|
115
|
+
Wait for a few seconds. You should be able to see this icon if everything goes right.
|
|
116
|
+
|
|
117
|
+

|
|
118
|
+
over here
|
|
119
|
+
|
|
120
|
+

|
|
121
|
+
|
|
122
|
+
Then ask claude to execute shell commands, read files, edit files, run your code, etc.
|
|
123
|
+
|
|
124
|
+
If you've run the docker for LLM to access, you can ask it to control the "docker os". If you don't provide the docker container id to it, it'll try to search for available docker using `docker ps` command.
|
|
125
|
+
|
|
126
|
+
## Example
|
|
127
|
+
|
|
128
|
+
### Computer use example
|
|
129
|
+
|
|
130
|
+

|
|
131
|
+
|
|
132
|
+
### Shell example
|
|
133
|
+
|
|
134
|
+

|
|
135
|
+
|
|
136
|
+
## [Optional] Local shell access with openai API key or anthropic API key
|
|
137
|
+
|
|
138
|
+
### Openai
|
|
139
|
+
|
|
140
|
+
Add `OPENAI_API_KEY` and `OPENAI_ORG_ID` env variables.
|
|
141
|
+
|
|
142
|
+
Then run
|
|
143
|
+
|
|
144
|
+
`uvx --from wcgw@latest wcgw_local --limit 0.1` # Cost limit $0.1
|
|
145
|
+
|
|
146
|
+
You can now directly write messages or press enter key to open vim for multiline message and text pasting.
|
|
147
|
+
|
|
148
|
+
### Anthropic
|
|
149
|
+
|
|
150
|
+
Add `ANTHROPIC_API_KEY` env variable.
|
|
151
|
+
|
|
152
|
+
Then run
|
|
153
|
+
|
|
154
|
+
`uvx --from wcgw@latest wcgw_local --claude`
|
|
155
|
+
|
|
156
|
+
You can now directly write messages or press enter key to open vim for multiline message and text pasting.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
wcgw/__init__.py,sha256=9K2QW7QuSLhMTVbKbBYd9UUp-ZyrfBrxcjuD_xk458k,118
|
|
2
|
+
wcgw/types_.py,sha256=rDz4olJS2zvYC13jzeOppA2tci-tVDyWAqeA5BesAaU,1773
|
|
3
|
+
wcgw/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
wcgw/client/__main__.py,sha256=wcCrL4PjG51r5wVKqJhcoJPTLfHW0wNbD31DrUN0MWI,28
|
|
5
|
+
wcgw/client/anthropic_client.py,sha256=yhFavV51c7zl2AqWQS2i-4KtLh4BRI-odFlppg5sIDY,19965
|
|
6
|
+
wcgw/client/cli.py,sha256=-z0kpDAW3mzfQrQeZfaVJhBCAQY3HXnt9GdgQ8s-u0Y,1003
|
|
7
|
+
wcgw/client/common.py,sha256=grH-yV_4tnTQZ29xExn4YicGLxEq98z-HkEZwH0ReSg,1410
|
|
8
|
+
wcgw/client/computer_use.py,sha256=7m_EMdyvJXZz5L0sVJA9zN2pQNtTkiQE__ie3qsvfG8,14878
|
|
9
|
+
wcgw/client/diff-instructions.txt,sha256=s5AJKG23JsjwRYhFZFQVvwDpF67vElawrmdXwvukR1A,1683
|
|
10
|
+
wcgw/client/openai_client.py,sha256=F5TEv5DhU9twsywSZGtuVkPo6xVaaoaEjvIh88FnIUQ,17780
|
|
11
|
+
wcgw/client/openai_utils.py,sha256=YNwCsA-Wqq7jWrxP0rfQmBTb1dI0s7dWXzQqyTzOZT4,2629
|
|
12
|
+
wcgw/client/sys_utils.py,sha256=GajPntKhaTUMn6EOmopENWZNR2G_BJyuVbuot0x6veI,1376
|
|
13
|
+
wcgw/client/tools.py,sha256=DQ5oRhaXkNDMQccbM59tyMjL76VIyihM5rgkvN-_2B4,33237
|
|
14
|
+
wcgw/client/mcp_server/Readme.md,sha256=I8N4dHkTUVGNQ63BQkBMBhCCBTgqGOSF_pUR6iOEiUk,2495
|
|
15
|
+
wcgw/client/mcp_server/__init__.py,sha256=hyPPwO9cabAJsOMWhKyat9yl7OlSmIobaoAZKHu3DMc,381
|
|
16
|
+
wcgw/client/mcp_server/server.py,sha256=M9pJ3DktGsxf6cufXbZ0xxs0HIKNLGc75O_biV2UKYA,10571
|
|
17
|
+
wcgw/relay/serve.py,sha256=RUcUeyL4Xt0EEo12Ul6VQjb4tRle4uIdsa85v7XXxEw,8771
|
|
18
|
+
wcgw/relay/static/privacy.txt,sha256=s9qBdbx2SexCpC_z33sg16TptmAwDEehMCLz4L50JLc,529
|
|
19
|
+
wcgw-2.0.0.dist-info/METADATA,sha256=oJwWfTlEZnRw470t40fgZMdVRfS5qSstxBgDSISWEb4,5054
|
|
20
|
+
wcgw-2.0.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
21
|
+
wcgw-2.0.0.dist-info/entry_points.txt,sha256=eKo1omwbAggWlQ0l7GKoR7uV1-j16nk9tK0BhC2Oz_E,120
|
|
22
|
+
wcgw-2.0.0.dist-info/RECORD,,
|
wcgw-1.5.3.dist-info/METADATA
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: wcgw
|
|
3
|
-
Version: 1.5.3
|
|
4
|
-
Summary: What could go wrong giving full shell access to chatgpt?
|
|
5
|
-
Project-URL: Homepage, https://github.com/rusiaaman/wcgw
|
|
6
|
-
Author-email: Aman Rusia <gapypi@arcfu.com>
|
|
7
|
-
Requires-Python: <3.13,>=3.11
|
|
8
|
-
Requires-Dist: anthropic>=0.39.0
|
|
9
|
-
Requires-Dist: fastapi>=0.115.0
|
|
10
|
-
Requires-Dist: mcp
|
|
11
|
-
Requires-Dist: mypy>=1.11.2
|
|
12
|
-
Requires-Dist: nltk>=3.9.1
|
|
13
|
-
Requires-Dist: openai>=1.46.0
|
|
14
|
-
Requires-Dist: petname>=2.6
|
|
15
|
-
Requires-Dist: pexpect>=4.9.0
|
|
16
|
-
Requires-Dist: pydantic>=2.9.2
|
|
17
|
-
Requires-Dist: pyte>=0.8.2
|
|
18
|
-
Requires-Dist: python-dotenv>=1.0.1
|
|
19
|
-
Requires-Dist: rich>=13.8.1
|
|
20
|
-
Requires-Dist: semantic-version>=2.10.0
|
|
21
|
-
Requires-Dist: shell>=1.0.1
|
|
22
|
-
Requires-Dist: tiktoken==0.7.0
|
|
23
|
-
Requires-Dist: toml>=0.10.2
|
|
24
|
-
Requires-Dist: typer>=0.12.5
|
|
25
|
-
Requires-Dist: types-pexpect>=4.9.0.20240806
|
|
26
|
-
Requires-Dist: uvicorn>=0.31.0
|
|
27
|
-
Requires-Dist: websockets>=13.1
|
|
28
|
-
Description-Content-Type: text/markdown
|
|
29
|
-
|
|
30
|
-
# Shell and Coding agent on Chatgpt and Claude desktop apps
|
|
31
|
-
|
|
32
|
-
- An MCP server on claude desktop for autonomous shell, coding and desktop control agent.
|
|
33
|
-
- A custom gpt on chatgpt web/desktop apps to interact with your local shell, edit files, run code, etc.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
[](https://github.com/rusiaaman/wcgw/actions/workflows/python-tests.yml)
|
|
37
|
-
[](https://github.com/rusiaaman/wcgw/actions/workflows/python-publish.yml)
|
|
38
|
-
|
|
39
|
-
[New feature] [26-Nov-2024] Claude desktop support for shell, computer-control, coding agent.
|
|
40
|
-
[src/wcgw/client/mcp_server/Readme.md](src/wcgw/client/mcp_server/Readme.md)
|
|
41
|
-
|
|
42
|
-
### 🚀 Highlights
|
|
43
|
-
|
|
44
|
-
- ⚡ **Full Shell Access**: No restrictions, complete control.
|
|
45
|
-
- ⚡ **Desktop control on Claude**: Screen capture, mouse control, keyboard control on claude desktop (on mac with docker linux)
|
|
46
|
-
- ⚡ **Create, Execute, Iterate**: Ask the gpt to keep running compiler checks till all errors are fixed, or ask it to keep checking for the status of a long running command till it's done.
|
|
47
|
-
- ⚡ **Interactive Command Handling**: Supports interactive commands using arrow keys, interrupt, and ansi escape sequences.
|
|
48
|
-
- ⚡ **REPL support**: [beta] Supports python/node and other REPL execution.
|
|
49
|
-
|
|
50
|
-
## Claude
|
|
51
|
-
Full readme [src/wcgw/client/mcp_server/Readme.md](src/wcgw/client/mcp_server/Readme.md)
|
|
52
|
-
### Setup
|
|
53
|
-
|
|
54
|
-
Update `claude_desktop_config.json`
|
|
55
|
-
|
|
56
|
-
```json
|
|
57
|
-
{
|
|
58
|
-
"mcpServers": {
|
|
59
|
-
"wcgw": {
|
|
60
|
-
"command": "uvx",
|
|
61
|
-
"args": ["--from", "wcgw@latest", "wcgw_mcp"]
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Then restart claude app.
|
|
68
|
-
You can then ask claude to execute shell commands, read files, edit files, run your code, etc.
|
|
69
|
-
|
|
70
|
-
## ChatGPT
|
|
71
|
-
|
|
72
|
-
### 🪜 Steps:
|
|
73
|
-
|
|
74
|
-
1. Run the [cli client](https://github.com/rusiaaman/wcgw?tab=readme-ov-file#client) in any directory of choice.
|
|
75
|
-
2. Share the generated id with this GPT: `https://chatgpt.com/g/g-Us0AAXkRh-wcgw-giving-shell-access`
|
|
76
|
-
3. The custom GPT can now run any command on your cli
|
|
77
|
-
|
|
78
|
-
### Client
|
|
79
|
-
|
|
80
|
-
You need to keep running this client for GPT to access your shell. Run it in a version controlled project's root.
|
|
81
|
-
|
|
82
|
-
#### Option 1: using uv [Recommended]
|
|
83
|
-
|
|
84
|
-
```sh
|
|
85
|
-
$ curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
86
|
-
$ uvx wcgw@latest
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
#### Option 2: using pip
|
|
90
|
-
|
|
91
|
-
Supports python >=3.10 and <3.13
|
|
92
|
-
|
|
93
|
-
```sh
|
|
94
|
-
$ pip3 install wcgw
|
|
95
|
-
$ wcgw
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
This will print a UUID that you need to share with the gpt.
|
|
99
|
-
|
|
100
|
-
### Chat
|
|
101
|
-
|
|
102
|
-
Open the following link or search the "wcgw" custom gpt using "Explore GPTs" on chatgpt.com
|
|
103
|
-
|
|
104
|
-
https://chatgpt.com/g/g-Us0AAXkRh-wcgw-giving-shell-access
|
|
105
|
-
|
|
106
|
-
Finally, let the chatgpt know your user id in any format. E.g., "user_id=<your uuid>" followed by rest of your instructions.
|
|
107
|
-
|
|
108
|
-
NOTE: you can resume a broken connection
|
|
109
|
-
`wcgw --client-uuid $previous_uuid`
|
|
110
|
-
|
|
111
|
-
### How it works on chatgpt app?
|
|
112
|
-
|
|
113
|
-
Your commands are relayed through a server to the terminal client. [You could host the server on your own](https://github.com/rusiaaman/wcgw?tab=readme-ov-file#creating-your-own-custom-gpt-and-the-relay-server). For public convenience I've hosted one at https://wcgw.arcfu.com thanks to the gcloud free tier plan.
|
|
114
|
-
|
|
115
|
-
Chatgpt sends a request to the relay server using the user id that you share with it. The relay server holds a websocket with the terminal client against the user id and acts as a proxy to pass the request.
|
|
116
|
-
|
|
117
|
-
It's secure in both the directions. Either a malicious actor or a malicious Chatgpt has to correctly guess your UUID for any security breach.
|
|
118
|
-
|
|
119
|
-
# Showcase
|
|
120
|
-
|
|
121
|
-
## Claude desktop
|
|
122
|
-
|
|
123
|
-
### Resize image and move it to a new dir
|
|
124
|
-
|
|
125
|
-

|
|
126
|
-
|
|
127
|
-
## Chatgpt app
|
|
128
|
-
|
|
129
|
-
### Unit tests and github actions
|
|
130
|
-
|
|
131
|
-
[The first version of unit tests and github workflow to test on multiple python versions were written by the custom chatgpt](https://chatgpt.com/share/6717f922-8998-8005-b825-45d4b348b4dd)
|
|
132
|
-
|
|
133
|
-
### Create a todo app using react + typescript + vite
|
|
134
|
-
|
|
135
|
-

|
|
136
|
-
|
|
137
|
-
# Privacy
|
|
138
|
-
|
|
139
|
-
The relay server doesn't store any data. I can't access any information passing through it and only secure channels are used to communicate.
|
|
140
|
-
|
|
141
|
-
You may host the server on your own and create a custom gpt using the following section.
|
|
142
|
-
|
|
143
|
-
# Creating your own custom gpt and the relay server.
|
|
144
|
-
|
|
145
|
-
I've used the following instructions and action json schema to create the custom GPT. (Replace wcgw.arcfu.com with the address to your server)
|
|
146
|
-
|
|
147
|
-
https://github.com/rusiaaman/wcgw/blob/main/gpt_instructions.txt
|
|
148
|
-
https://github.com/rusiaaman/wcgw/blob/main/gpt_action_json_schema.json
|
|
149
|
-
|
|
150
|
-
Run the server
|
|
151
|
-
`gunicorn --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:443 src.wcgw.relay.serve:app --certfile fullchain.pem --keyfile privkey.pem`
|
|
152
|
-
|
|
153
|
-
If you don't have public ip and domain name, you can use `ngrok` or similar services to get a https address to the api.
|
|
154
|
-
|
|
155
|
-
The specify the server url in the `wcgw` command like so
|
|
156
|
-
`wcgw --server-url https://your-url/v1/register`
|
|
157
|
-
|
|
158
|
-
# [Optional] Local shell access with openai API key or anthropic API key
|
|
159
|
-
|
|
160
|
-
## Openai
|
|
161
|
-
|
|
162
|
-
Add `OPENAI_API_KEY` and `OPENAI_ORG_ID` env variables.
|
|
163
|
-
|
|
164
|
-
Then run
|
|
165
|
-
|
|
166
|
-
`uvx --from wcgw@latest wcgw_local --limit 0.1` # Cost limit $0.1
|
|
167
|
-
|
|
168
|
-
You can now directly write messages or press enter key to open vim for multiline message and text pasting.
|
|
169
|
-
|
|
170
|
-
## Anthropic
|
|
171
|
-
|
|
172
|
-
Add `ANTHROPIC_API_KEY` env variable.
|
|
173
|
-
|
|
174
|
-
Then run
|
|
175
|
-
|
|
176
|
-
`uvx --from wcgw@latest wcgw_local --claude`
|
|
177
|
-
|
|
178
|
-
You can now directly write messages or press enter key to open vim for multiline message and text pasting.
|
wcgw-1.5.3.dist-info/RECORD
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
wcgw/__init__.py,sha256=9K2QW7QuSLhMTVbKbBYd9UUp-ZyrfBrxcjuD_xk458k,118
|
|
2
|
-
wcgw/types_.py,sha256=rDz4olJS2zvYC13jzeOppA2tci-tVDyWAqeA5BesAaU,1773
|
|
3
|
-
wcgw/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
wcgw/client/__main__.py,sha256=wcCrL4PjG51r5wVKqJhcoJPTLfHW0wNbD31DrUN0MWI,28
|
|
5
|
-
wcgw/client/anthropic_client.py,sha256=c6nNNojsWLFWjMJcEsEp7g31Ps9SIgCqMk4dhrOn9V4,18314
|
|
6
|
-
wcgw/client/cli.py,sha256=Oja42CHkVO8puqOXflko9NeephYCMa85aBmQTEjBZtI,932
|
|
7
|
-
wcgw/client/common.py,sha256=grH-yV_4tnTQZ29xExn4YicGLxEq98z-HkEZwH0ReSg,1410
|
|
8
|
-
wcgw/client/computer_use.py,sha256=eGiINKfgY8WWT-NDUa6vUKd1MTWE7dTjSlvjZHPCWzc,14870
|
|
9
|
-
wcgw/client/diff-instructions.txt,sha256=s5AJKG23JsjwRYhFZFQVvwDpF67vElawrmdXwvukR1A,1683
|
|
10
|
-
wcgw/client/openai_client.py,sha256=L61ajFVQW2QPS3C0n1YsjgF4vQKfMIZHmp6iFBHutX8,17748
|
|
11
|
-
wcgw/client/openai_utils.py,sha256=YNwCsA-Wqq7jWrxP0rfQmBTb1dI0s7dWXzQqyTzOZT4,2629
|
|
12
|
-
wcgw/client/sys_utils.py,sha256=GajPntKhaTUMn6EOmopENWZNR2G_BJyuVbuot0x6veI,1376
|
|
13
|
-
wcgw/client/tools.py,sha256=d7Fni7JU3aOh2vXBAw5k5rsxkdQVcxoxc5vipvEsA2g,32680
|
|
14
|
-
wcgw/client/mcp_server/Readme.md,sha256=1hNZtqltsORug7OzUjjoK5O8q5s9-Y3S0_rlzT-Wfg4,2033
|
|
15
|
-
wcgw/client/mcp_server/__init__.py,sha256=cQ7PUrEmXUpio8x0SEoGWP5hCRPd7z2bAkNCbYbtTys,236
|
|
16
|
-
wcgw/client/mcp_server/server.py,sha256=i1nn16LSsbySm3LCt_T_D2G33nPtGNDJakp5FzKV6vQ,10416
|
|
17
|
-
wcgw/relay/serve.py,sha256=RUcUeyL4Xt0EEo12Ul6VQjb4tRle4uIdsa85v7XXxEw,8771
|
|
18
|
-
wcgw/relay/static/privacy.txt,sha256=s9qBdbx2SexCpC_z33sg16TptmAwDEehMCLz4L50JLc,529
|
|
19
|
-
wcgw-1.5.3.dist-info/METADATA,sha256=9raP3OP6KWq5bzIEuwJ41_04g5fS5m1HVSNm-YI8Dkw,6508
|
|
20
|
-
wcgw-1.5.3.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
21
|
-
wcgw-1.5.3.dist-info/entry_points.txt,sha256=eKo1omwbAggWlQ0l7GKoR7uV1-j16nk9tK0BhC2Oz_E,120
|
|
22
|
-
wcgw-1.5.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|