wcgw 2.8.10__py3-none-any.whl → 3.0.1__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/py.typed ADDED
File without changes
wcgw/relay/client.py ADDED
@@ -0,0 +1,95 @@
1
+ import importlib.metadata
2
+ import os
3
+ import time
4
+ import traceback
5
+ import uuid
6
+ from typing import Optional
7
+
8
+ import rich
9
+ import typer
10
+ import websockets
11
+ from typer import Typer
12
+ from websockets.sync.client import connect as syncconnect
13
+
14
+ from ..client.bash_state.bash_state import BashState
15
+ from ..client.tools import Context, curr_cost, default_enc, get_tool_output
16
+ from ..types_ import Mdata
17
+
18
+
19
+ def register_client(server_url: str, client_uuid: str = "") -> None:
20
+ global default_enc, curr_cost
21
+ # Generate a unique UUID for this client
22
+ if not client_uuid:
23
+ client_uuid = str(uuid.uuid4())
24
+
25
+ # Create the WebSocket connection and context
26
+ the_console = rich.console.Console(style="magenta", highlight=False, markup=False)
27
+ with BashState(
28
+ the_console, os.getcwd(), None, None, None, None, True, None
29
+ ) as bash_state:
30
+ context = Context(bash_state=bash_state, console=the_console)
31
+
32
+ try:
33
+ with syncconnect(f"{server_url}/{client_uuid}") as websocket:
34
+ server_version = str(websocket.recv())
35
+ print(f"Server version: {server_version}")
36
+ client_version = importlib.metadata.version("wcgw")
37
+ websocket.send(client_version)
38
+
39
+ print(f"Connected. Share this user id with the chatbot: {client_uuid}")
40
+ while True:
41
+ # Wait to receive data from the server
42
+ message = websocket.recv()
43
+ mdata = Mdata.model_validate_json(message)
44
+ if isinstance(mdata.data, str):
45
+ raise Exception(mdata)
46
+ try:
47
+ outputs, cost = get_tool_output(
48
+ context,
49
+ mdata.data,
50
+ default_enc,
51
+ 0.0,
52
+ lambda x, y: ("", 0),
53
+ 8000,
54
+ )
55
+ output = outputs[0]
56
+ curr_cost += cost
57
+ print(f"{curr_cost=}")
58
+ except Exception as e:
59
+ output = f"GOT EXCEPTION while calling tool. Error: {e}"
60
+ context.console.print(traceback.format_exc())
61
+ assert isinstance(output, str)
62
+ websocket.send(output)
63
+
64
+ except (websockets.ConnectionClosed, ConnectionError, OSError):
65
+ print(f"Connection closed for UUID: {client_uuid}, retrying")
66
+ time.sleep(0.5)
67
+ register_client(server_url, client_uuid)
68
+
69
+
70
+ run = Typer(pretty_exceptions_show_locals=False, no_args_is_help=True)
71
+
72
+
73
+ @run.command()
74
+ def app(
75
+ server_url: str = "",
76
+ client_uuid: Optional[str] = None,
77
+ version: bool = typer.Option(False, "--version", "-v"),
78
+ ) -> None:
79
+ if version:
80
+ version_ = importlib.metadata.version("wcgw")
81
+ print(f"wcgw version: {version_}")
82
+ exit()
83
+ if not server_url:
84
+ server_url = os.environ.get("WCGW_RELAY_SERVER", "")
85
+ if not server_url:
86
+ print(
87
+ "Error: Please provide relay server url using --server_url or WCGW_RELAY_SERVER environment variable"
88
+ )
89
+ print(
90
+ "\tNOTE: you need to run a relay server first, author doesn't host a relay server anymore."
91
+ )
92
+ print("\thttps://github.com/rusiaaman/wcgw/blob/main/openai.md")
93
+ print("\tExample `--server-url=ws://localhost:8000/v1/register`")
94
+ raise typer.Exit(1)
95
+ register_client(server_url, client_uuid or "")
wcgw/relay/serve.py CHANGED
@@ -15,13 +15,10 @@ from pydantic import BaseModel
15
15
 
16
16
  from ..types_ import (
17
17
  BashCommand,
18
- BashInteraction,
19
18
  ContextSave,
20
19
  FileEdit,
21
- FileEditFindReplace,
22
20
  Initialize,
23
21
  ReadFiles,
24
- ResetShell,
25
22
  WriteIfEmpty,
26
23
  )
27
24
 
@@ -29,10 +26,7 @@ from ..types_ import (
29
26
  class Mdata(BaseModel):
30
27
  data: (
31
28
  BashCommand
32
- | BashInteraction
33
29
  | WriteIfEmpty
34
- | ResetShell
35
- | FileEditFindReplace
36
30
  | FileEdit
37
31
  | ReadFiles
38
32
  | Initialize
@@ -164,37 +158,7 @@ async def file_edit_find_replace(
164
158
  raise fastapi.HTTPException(status_code=500, detail="Timeout error")
165
159
 
166
160
 
167
- class ResetShellWithUUID(ResetShell):
168
- user_id: UUID
169
-
170
-
171
- @app.post("/v1/reset_shell")
172
- async def reset_shell(reset_shell: ResetShellWithUUID) -> str:
173
- user_id = reset_shell.user_id
174
- if user_id not in clients:
175
- return "Failure: id not found, ask the user to check it."
176
-
177
- results: Optional[str] = None
178
-
179
- def put_results(result: str) -> None:
180
- nonlocal results
181
- results = result
182
-
183
- gpts[user_id] = put_results
184
-
185
- await clients[user_id](Mdata(data=reset_shell, user_id=user_id))
186
-
187
- start_time = time.time()
188
- while time.time() - start_time < 30:
189
- if results is not None:
190
- return results
191
- await asyncio.sleep(0.1)
192
-
193
- raise fastapi.HTTPException(status_code=500, detail="Timeout error")
194
-
195
-
196
- class CommandWithUUID(BaseModel):
197
- command: str
161
+ class CommandWithUUID(BashCommand):
198
162
  user_id: UUID
199
163
 
200
164
 
@@ -212,40 +176,11 @@ async def bash_command(command: CommandWithUUID) -> str:
212
176
 
213
177
  gpts[user_id] = put_results
214
178
 
215
- await clients[user_id](
216
- Mdata(data=BashCommand(command=command.command), user_id=user_id)
217
- )
218
-
219
- start_time = time.time()
220
- while time.time() - start_time < 30:
221
- if results is not None:
222
- return results
223
- await asyncio.sleep(0.1)
224
-
225
- raise fastapi.HTTPException(status_code=500, detail="Timeout error")
226
-
227
-
228
- class BashInteractionWithUUID(BashInteraction):
229
- user_id: UUID
230
-
231
-
232
- @app.post("/v1/bash_interaction")
233
- async def bash_interaction(bash_interaction: BashInteractionWithUUID) -> str:
234
- user_id = bash_interaction.user_id
235
- if user_id not in clients:
236
- return "Failure: id not found, ask the user to check it."
237
-
238
- results: Optional[str] = None
239
-
240
- def put_results(result: str) -> None:
241
- nonlocal results
242
- results = result
243
-
244
- gpts[user_id] = put_results
245
-
246
179
  await clients[user_id](
247
180
  Mdata(
248
- data=bash_interaction,
181
+ data=BashCommand(
182
+ action=command.action, wait_for_seconds=command.wait_for_seconds
183
+ ),
249
184
  user_id=user_id,
250
185
  )
251
186
  )
wcgw/types_.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import os
2
- from enum import Enum
3
- from typing import Any, Literal, Optional, Sequence, Union
2
+ from typing import Any, Literal, Optional, Protocol, Sequence, Union
4
3
 
5
4
  from pydantic import BaseModel as PydanticBaseModel
6
5
 
@@ -13,16 +12,23 @@ class NoExtraArgs(PydanticBaseModel):
13
12
  BaseModel = NoExtraArgs
14
13
 
15
14
 
16
- class Modes(str, Enum):
17
- wcgw = "wcgw"
18
- architect = "architect"
19
- code_writer = "code_writer"
15
+ Modes = Literal["wcgw", "architect", "code_writer"]
20
16
 
21
17
 
22
18
  class CodeWriterMode(BaseModel):
23
19
  allowed_globs: Literal["all"] | list[str]
24
20
  allowed_commands: Literal["all"] | list[str]
25
21
 
22
+ def __post_init__(self) -> None:
23
+ # Patch frequently wrong output trading off accuracy
24
+ # in rare case there's a file named 'all' or a command named 'all'
25
+ if len(self.allowed_commands) == 1:
26
+ if self.allowed_commands[0] == "all":
27
+ self.allowed_commands = "all"
28
+ if len(self.allowed_globs) == 1:
29
+ if self.allowed_globs[0] == "all":
30
+ self.allowed_globs = "all"
31
+
26
32
  def update_relative_globs(self, workspace_root: str) -> None:
27
33
  """Update globs if they're relative paths"""
28
34
  if self.allowed_globs != "all":
@@ -36,6 +42,12 @@ ModesConfig = Union[Literal["wcgw", "architect"], CodeWriterMode]
36
42
 
37
43
 
38
44
  class Initialize(BaseModel):
45
+ type: Literal[
46
+ "first_call",
47
+ "user_asked_mode_change",
48
+ "reset_shell",
49
+ "user_asked_change_workspace",
50
+ ]
39
51
  any_workspace_path: str
40
52
  initial_files_to_read: list[str]
41
53
  task_id_to_resume: str
@@ -61,21 +73,34 @@ class Initialize(BaseModel):
61
73
  return self.code_writer_config
62
74
 
63
75
 
64
- class BashCommand(BaseModel):
76
+ class Command(BaseModel):
65
77
  command: str
66
- wait_for_seconds: Optional[int] = None
78
+
79
+
80
+ class StatusCheck(BaseModel):
81
+ status_check: Literal[True]
82
+
83
+
84
+ class SendText(BaseModel):
85
+ send_text: str
67
86
 
68
87
 
69
88
  Specials = Literal[
70
- "Key-up", "Key-down", "Key-left", "Key-right", "Enter", "Ctrl-c", "Ctrl-d", "Ctrl-z"
89
+ "Enter", "Key-up", "Key-down", "Key-left", "Key-right", "Ctrl-c", "Ctrl-d"
71
90
  ]
72
91
 
73
92
 
74
- class BashInteraction(BaseModel):
75
- send_text: Optional[str] = None
76
- send_specials: Optional[Sequence[Specials]] = None
77
- send_ascii: Optional[Sequence[int]] = None
78
- wait_for_seconds: Optional[int] = None
93
+ class SendSpecials(BaseModel):
94
+ send_specials: Sequence[Specials]
95
+
96
+
97
+ class SendAscii(BaseModel):
98
+ send_ascii: Sequence[int]
99
+
100
+
101
+ class BashCommand(BaseModel):
102
+ action: Command | StatusCheck | SendText | SendSpecials | SendAscii
103
+ wait_for_seconds: Optional[float] = None
79
104
 
80
105
 
81
106
  class ReadImage(BaseModel):
@@ -91,62 +116,31 @@ class ReadFiles(BaseModel):
91
116
  file_paths: list[str]
92
117
 
93
118
 
94
- class FileEditFindReplace(BaseModel):
95
- file_path: str
96
- find_lines: str
97
- replace_with_lines: str
98
-
99
-
100
- class ResetShell(BaseModel):
101
- should_reset: Literal[True]
102
-
103
-
104
119
  class FileEdit(BaseModel):
105
120
  file_path: str
106
121
  file_edit_using_search_replace_blocks: str
107
122
 
108
123
 
109
- class GetScreenInfo(BaseModel):
110
- docker_image_id: str
111
-
112
-
113
- class ScreenShot(BaseModel):
114
- take_after_delay_seconds: int
115
-
116
-
117
- class MouseMove(BaseModel):
118
- x: int
119
- y: int
120
- do_left_click_on_move: bool
121
-
122
-
123
- class LeftClickDrag(BaseModel):
124
- x: int
125
- y: int
126
-
127
-
128
- class MouseButton(BaseModel):
129
- button_type: Literal[
130
- "left_click",
131
- "right_click",
132
- "middle_click",
133
- "double_click",
134
- "scroll_up",
135
- "scroll_down",
136
- ]
137
-
138
-
139
- class Mouse(BaseModel):
140
- action: MouseButton | LeftClickDrag | MouseMove
141
-
142
-
143
- class Keyboard(BaseModel):
144
- action: Literal["key", "type"]
145
- text: str
146
-
147
-
148
124
  class ContextSave(BaseModel):
149
125
  id: str
150
126
  project_root_path: str
151
127
  description: str
152
128
  relevant_file_globs: list[str]
129
+
130
+
131
+ class Console(Protocol):
132
+ def print(self, msg: str, *args: Any, **kwargs: Any) -> None: ...
133
+
134
+ def log(self, msg: str, *args: Any, **kwargs: Any) -> None: ...
135
+
136
+
137
+ class Mdata(PydanticBaseModel):
138
+ data: (
139
+ BashCommand
140
+ | WriteIfEmpty
141
+ | FileEdit
142
+ | str
143
+ | ReadFiles
144
+ | Initialize
145
+ | ContextSave
146
+ )
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wcgw
3
- Version: 2.8.10
3
+ Version: 3.0.1
4
4
  Summary: Shell and coding agent on claude and chatgpt
5
5
  Project-URL: Homepage, https://github.com/rusiaaman/wcgw
6
6
  Author-email: Aman Rusia <gapypi@arcfu.com>
7
7
  License-File: LICENSE
8
- Requires-Python: <3.13,>=3.11
8
+ Requires-Python: >=3.11
9
9
  Requires-Dist: anthropic>=0.39.0
10
10
  Requires-Dist: fastapi>=0.115.0
11
11
  Requires-Dist: openai>=1.46.0
@@ -22,7 +22,6 @@ Requires-Dist: syntax-checker>=0.3.0
22
22
  Requires-Dist: tokenizers>=0.21.0
23
23
  Requires-Dist: toml>=0.10.2
24
24
  Requires-Dist: typer>=0.12.5
25
- Requires-Dist: types-pexpect>=4.9.0.20240806
26
25
  Requires-Dist: uvicorn>=0.31.0
27
26
  Requires-Dist: websockets>=13.1
28
27
  Description-Content-Type: text/markdown
@@ -103,6 +102,8 @@ First install `uv` using homebrew `brew install uv`
103
102
 
104
103
  Then update `claude_desktop_config.json` (~/Library/Application Support/Claude/claude_desktop_config.json)
105
104
 
105
+ If it doesn't exist, you can just `touch ~/Library/Application\ Support/Claude/claude_desktop_config.json` or create the file.
106
+
106
107
  ```json
107
108
  {
108
109
  "mcpServers": {
@@ -1,30 +1,33 @@
1
- wcgw/__init__.py,sha256=sQZHC74HPFyE9XqWZDCVmgdBXVK1xkHrMtMSLBZ5BOY,90
2
- wcgw/types_.py,sha256=D3518a2azSKeW3D-ACYWJwsaqo7Oj-8BRRR2IhCUtNU,3414
1
+ wcgw/__init__.py,sha256=qUofQOAXCGcWr2u_B8U-MIMhhYaBUpUwNDcscvRmYfo,90
2
+ wcgw/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ wcgw/types_.py,sha256=KFyfLFrctU9jr3zoQR3nlAMlP17hu91bM7ZeUTdTZ74,3624
3
4
  wcgw/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
5
  wcgw/client/common.py,sha256=OCH7Tx64jojz3M3iONUrGMadE07W21DiZs5sOxWX1Qc,1456
5
- wcgw/client/computer_use.py,sha256=35NKAlMrxwD0TBlMMRnbCwz4g8TBRGOlcy-cmS-yJ_A,15247
6
6
  wcgw/client/diff-instructions.txt,sha256=tmJ9Fu9XdO_72lYXQQNY9RZyx91bjxrXJf9d_KBz57k,1611
7
7
  wcgw/client/memory.py,sha256=8LdYsOhvCOoC1kfvDr85kNy07WnhPMvE6B2FRM2w85Y,2902
8
- wcgw/client/modes.py,sha256=FkDJIgjKrlJEufLq3abWfqV25BdF2pH-HnoHafy9LrA,10484
9
- wcgw/client/sys_utils.py,sha256=GajPntKhaTUMn6EOmopENWZNR2G_BJyuVbuot0x6veI,1376
10
- wcgw/client/tools.py,sha256=c-LipooLb6XBF9l-qwJlzBLzznlqQQuCXRB9HSZJFT0,53450
11
- wcgw/client/file_ops/diff_edit.py,sha256=OlJCpPSE_3T41q9H0yDORm6trjm3w6zh1EkuPTxik2A,16832
8
+ wcgw/client/modes.py,sha256=FjIQOjT5oI7dk9VG0oRemN1A6EHBvHnuSQGKuRbguJI,10352
9
+ wcgw/client/tool_prompts.py,sha256=RzLO3XGMg2VJfURG1BiQzZ6IdDWXfH2syqM1N3w0770,3980
10
+ wcgw/client/tools.py,sha256=5zEr83vowHOM6vj0Uw4RaKVD01ZqNp0cXSm0FqzV9JY,24178
11
+ wcgw/client/bash_state/bash_state.py,sha256=DND9vNIfFee-Djif3YZhIsnaXamI3TzXE-AhISnICZY,26476
12
+ wcgw/client/encoder/__init__.py,sha256=Y-8f43I6gMssUCWpX5rLYiAFv3D-JPRs4uNEejPlke8,1514
13
+ wcgw/client/file_ops/diff_edit.py,sha256=sIwXSSkWYff_Dp3oHfefqtSuFqLrxbhKvzkCoLuLqDE,18679
12
14
  wcgw/client/file_ops/search_replace.py,sha256=Napa7IWaYPGMNdttunKyRDkb90elZE7r23B_o_htRxo,5585
13
- wcgw/client/mcp_server/Readme.md,sha256=I8N4dHkTUVGNQ63BQkBMBhCCBTgqGOSF_pUR6iOEiUk,2495
14
- wcgw/client/mcp_server/__init__.py,sha256=hyPPwO9cabAJsOMWhKyat9yl7OlSmIobaoAZKHu3DMc,381
15
- wcgw/client/mcp_server/server.py,sha256=zMVa2nR2hapQZAtR34POch3VLVEEnJ6SQ8iWYLDxJrU,13199
16
- wcgw/client/repo_ops/display_tree.py,sha256=5FD4hfMkM2cIZnXlu7WfJswJLthj0SkuHlkGH6dpWQU,4632
15
+ wcgw/client/mcp_server/Readme.md,sha256=2Z88jj1mf9daYGW1CWaldcJ0moy8owDumhR2glBY3A8,109
16
+ wcgw/client/mcp_server/__init__.py,sha256=mm7xhBIPwJpRT3u-Qsj4cKVMpVyucJoKRlbMP_gRRB0,343
17
+ wcgw/client/mcp_server/server.py,sha256=ayK6qbzCveoQW7RO80m10cAIS3m-hvxzd15XhjiyxmE,5055
18
+ wcgw/client/repo_ops/display_tree.py,sha256=E5q9mrGBb57NyvudSmRIG-fj4FUqupbzjmARpX8X0XY,4166
17
19
  wcgw/client/repo_ops/path_prob.py,sha256=SWf0CDn37rtlsYRQ51ufSxay-heaQoVIhr1alB9tZ4M,2144
18
20
  wcgw/client/repo_ops/paths_model.vocab,sha256=M1pXycYDQehMXtpp-qAgU7rtzeBbCOiJo4qcYFY0kqk,315087
19
21
  wcgw/client/repo_ops/paths_tokens.model,sha256=jiwwE4ae8ADKuTZISutXuM5Wfyc_FBmN5rxTjoNnCos,1569052
20
22
  wcgw/client/repo_ops/repo_context.py,sha256=5NqRxBY0K-SBFXJ0Ybt7llzYOBD8pRkTpruMMJHWxv4,4336
21
- wcgw/relay/serve.py,sha256=Z5EwtaCAtKFBSnUw4mPYw0sze3Coc4Fa8gObRRG_bT0,9525
23
+ wcgw/relay/client.py,sha256=BUeEKUsWts8RpYxXwXcyFyjBJhOCS-CxThAlL_-VCOI,3618
24
+ wcgw/relay/serve.py,sha256=Vl7Nb68-F910LwHrTElfCNwajF37CfgObUIAdwhrRsI,7886
22
25
  wcgw/relay/static/privacy.txt,sha256=s9qBdbx2SexCpC_z33sg16TptmAwDEehMCLz4L50JLc,529
23
26
  wcgw_cli/__init__.py,sha256=TNxXsTPgb52OhakIda9wTRh91cqoBqgQRx5TxjzQQFU,21
24
27
  wcgw_cli/__main__.py,sha256=wcCrL4PjG51r5wVKqJhcoJPTLfHW0wNbD31DrUN0MWI,28
25
- wcgw_cli/anthropic_client.py,sha256=ZMFHD6g6h_PAReL8tubI8LnxVeRA3hcFLGNitjt9XhQ,24047
26
- wcgw_cli/cli.py,sha256=GEje9ZBIaD5_-HK3zxZCGYaeDF8bfFxImloOR3O66Fw,1019
27
- wcgw_cli/openai_client.py,sha256=wp4XDf3t3W6XG5LHgr6bFckePyty24BGtsOEjOrIrk0,17955
28
+ wcgw_cli/anthropic_client.py,sha256=gQc1opw-N5ecqcORbvvHGBW1Ac-Soe7c7APJD25HIfo,19887
29
+ wcgw_cli/cli.py,sha256=-7FBe_lahKyUOhf65iurTA1M1gXXXAiT0OVKQVcZKKo,948
30
+ wcgw_cli/openai_client.py,sha256=o4vfhcfGKqoWYWnn5YxNw4muKyDnFEiPmWpZpufDGMA,16021
28
31
  wcgw_cli/openai_utils.py,sha256=xGOb3W5ALrIozV7oszfGYztpj0FnXdD7jAxm5lEIVKY,2439
29
32
  mcp_wcgw/__init__.py,sha256=fKCgOdN7cn7gR3YGFaGyV5Goe8A2sEyllLcsRkN0i-g,2601
30
33
  mcp_wcgw/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -48,8 +51,8 @@ mcp_wcgw/shared/memory.py,sha256=dBsOghxHz8-tycdSVo9kSujbsC8xb_tYsGmuJobuZnw,281
48
51
  mcp_wcgw/shared/progress.py,sha256=ymxOsb8XO5Mhlop7fRfdbmvPodANj7oq6O4dD0iUcnw,1048
49
52
  mcp_wcgw/shared/session.py,sha256=e44a0LQOW8gwdLs9_DE9oDsxqW2U8mXG3d5KT95bn5o,10393
50
53
  mcp_wcgw/shared/version.py,sha256=d2LZii-mgsPIxpshjkXnOTUmk98i0DT4ff8VpA_kAvE,111
51
- wcgw-2.8.10.dist-info/METADATA,sha256=-bjtkmro5UQVMBYYzxbakEIADO-VOpONetLtwxb1vX4,13054
52
- wcgw-2.8.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
53
- wcgw-2.8.10.dist-info/entry_points.txt,sha256=vd3tj1_Kzfp55LscJ8-6WFMM5hm9cWTfNGFCrWBnH3Q,124
54
- wcgw-2.8.10.dist-info/licenses/LICENSE,sha256=BvY8xqjOfc3X2qZpGpX3MZEmF-4Dp0LqgKBbT6L_8oI,11142
55
- wcgw-2.8.10.dist-info/RECORD,,
54
+ wcgw-3.0.1.dist-info/METADATA,sha256=e7K24TS29NAaGKsuxY6-zH-rpw4YzcDrS4tSdmSE3Js,13130
55
+ wcgw-3.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
56
+ wcgw-3.0.1.dist-info/entry_points.txt,sha256=vd3tj1_Kzfp55LscJ8-6WFMM5hm9cWTfNGFCrWBnH3Q,124
57
+ wcgw-3.0.1.dist-info/licenses/LICENSE,sha256=BvY8xqjOfc3X2qZpGpX3MZEmF-4Dp0LqgKBbT6L_8oI,11142
58
+ wcgw-3.0.1.dist-info/RECORD,,