meshagent-computers 0.0.7__py3-none-any.whl → 0.0.8__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 meshagent-computers might be problematic. Click here for more details.
- meshagent/computers/__init__.py +1 -0
- meshagent/computers/agent.py +180 -177
- meshagent/computers/utils.py +1 -1
- meshagent/computers/version.py +1 -0
- {meshagent_computers-0.0.7.dist-info → meshagent_computers-0.0.8.dist-info}/METADATA +5 -12
- {meshagent_computers-0.0.7.dist-info → meshagent_computers-0.0.8.dist-info}/RECORD +9 -8
- {meshagent_computers-0.0.7.dist-info → meshagent_computers-0.0.8.dist-info}/WHEEL +1 -1
- {meshagent_computers-0.0.7.dist-info → meshagent_computers-0.0.8.dist-info}/licenses/LICENSE +0 -0
- {meshagent_computers-0.0.7.dist-info → meshagent_computers-0.0.8.dist-info}/top_level.txt +0 -0
meshagent/computers/__init__.py
CHANGED
meshagent/computers/agent.py
CHANGED
|
@@ -1,29 +1,182 @@
|
|
|
1
|
-
from meshagent.
|
|
2
|
-
from meshagent.agents import LLMAdapter, AgentChatContext
|
|
1
|
+
from meshagent.agents import LLMAdapter
|
|
3
2
|
from meshagent.tools import Tool, Toolkit, ToolContext
|
|
4
|
-
from meshagent.
|
|
5
|
-
from meshagent.computers import Computer, Operator
|
|
3
|
+
from meshagent.computers import Computer, Operator, BrowserbaseBrowser
|
|
6
4
|
from meshagent.agents.chat import ChatBot, ChatThreadContext
|
|
7
|
-
from meshagent.api import
|
|
5
|
+
from meshagent.api import RequiredToolkit, RemoteParticipant
|
|
8
6
|
from meshagent.api.messaging import RawOutputs
|
|
7
|
+
from meshagent.tools.toolkit import register_toolkit_factory
|
|
9
8
|
|
|
10
|
-
from typing import Optional
|
|
9
|
+
from typing import Optional, Type, Callable
|
|
11
10
|
import base64
|
|
12
|
-
import json
|
|
13
11
|
import logging
|
|
14
12
|
|
|
15
13
|
logging.basicConfig()
|
|
16
14
|
logger = logging.getLogger("computer")
|
|
17
15
|
logger.setLevel(logging.INFO)
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
def make_computer_toolkit(*, operator_cls: Type[Operator], computer_cls: Type[Computer], render_screen: Callable[[bytes],None]):
|
|
18
|
+
|
|
19
|
+
operator = operator_cls()
|
|
20
|
+
computer = computer_cls()
|
|
21
|
+
started = False
|
|
22
|
+
|
|
23
|
+
class ComputerTool(Tool):
|
|
24
|
+
def __init__(self, *, operator: Operator, computer: Computer, title = "computer_call", description = "handle computer calls from computer use preview", rules = [], thumbnail_url = None, defs = None):
|
|
25
|
+
super().__init__(
|
|
26
|
+
name="computer_call",
|
|
27
|
+
# TODO: give a correct schema
|
|
28
|
+
input_schema={
|
|
29
|
+
"additionalProperties" : False,
|
|
30
|
+
"type" : "object",
|
|
31
|
+
"required" : [],
|
|
32
|
+
"properties" : {}
|
|
33
|
+
},
|
|
34
|
+
title=title,
|
|
35
|
+
description=description,
|
|
36
|
+
rules=rules,
|
|
37
|
+
thumbnail_url=thumbnail_url,
|
|
38
|
+
defs=defs,
|
|
39
|
+
|
|
40
|
+
)
|
|
41
|
+
self.computer = computer
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def options(self):
|
|
46
|
+
return {
|
|
47
|
+
"type": "computer-preview",
|
|
48
|
+
"display_width": self.computer.dimensions[0],
|
|
49
|
+
"display_height": self.computer.dimensions[1],
|
|
50
|
+
"environment": self.computer.environment,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async def execute(self, context: ToolContext, *, arguments):
|
|
54
|
+
|
|
55
|
+
nonlocal started
|
|
56
|
+
if started == False:
|
|
57
|
+
await self.computer.__aenter__()
|
|
58
|
+
started = True
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
outputs = await operator.play(computer=self.computer, item=arguments)
|
|
62
|
+
for output in outputs:
|
|
63
|
+
if output["type"] == "computer_call_output":
|
|
64
|
+
if output["output"] != None:
|
|
65
|
+
if output["output"]["type"] == "input_image":
|
|
66
|
+
|
|
67
|
+
b64 : str = output["output"]["image_url"]
|
|
68
|
+
image_data_b64 = b64.split(",", 1)
|
|
69
|
+
|
|
70
|
+
image_bytes = base64.b64decode(image_data_b64[1])
|
|
71
|
+
render_screen(image_bytes)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
nonlocal computer_toolkit
|
|
75
|
+
if len(computer_toolkit.tools) == 1:
|
|
76
|
+
# HACK: after looking at the page, add the other tools,
|
|
77
|
+
# if we add these first then the computer-use-preview mode fails if it calls them before using the computer
|
|
78
|
+
computer_toolkit.tools.extend([
|
|
79
|
+
ScreenshotTool(computer=computer),
|
|
80
|
+
GotoURL(computer=computer),
|
|
81
|
+
])
|
|
82
|
+
return RawOutputs(outputs=outputs)
|
|
83
|
+
|
|
84
|
+
class ScreenshotTool(Tool):
|
|
85
|
+
def __init__(self, computer: Computer):
|
|
86
|
+
self.computer = computer
|
|
87
|
+
|
|
88
|
+
super().__init__(
|
|
89
|
+
name="screenshot",
|
|
90
|
+
# TODO: give a correct schema
|
|
91
|
+
input_schema={
|
|
92
|
+
"additionalProperties" : False,
|
|
93
|
+
"type" : "object",
|
|
94
|
+
"required" : ["full_page","save_path"],
|
|
95
|
+
"properties" : {
|
|
96
|
+
"full_page" : {
|
|
97
|
+
"type" : "boolean"
|
|
98
|
+
},
|
|
99
|
+
"save_path" : {
|
|
100
|
+
"type" : "string",
|
|
101
|
+
"description" : "a file path to save the screenshot to (should end with .png)"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
description="take a screenshot of the current page",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
async def execute(self, context: ToolContext, save_path: str, full_page: bool):
|
|
110
|
+
nonlocal started
|
|
111
|
+
if started == False:
|
|
112
|
+
await self.computer.__aenter__()
|
|
113
|
+
started = True
|
|
114
|
+
|
|
115
|
+
screenshot_bytes = await self.computer.screenshot_bytes(full_page=full_page)
|
|
116
|
+
handle = await context.room.storage.open(path=save_path, overwrite=True)
|
|
117
|
+
await context.room.storage.write(handle=handle, data=screenshot_bytes)
|
|
118
|
+
await context.room.storage.close(handle=handle)
|
|
119
|
+
|
|
120
|
+
return f"saved screenshot to {save_path}"
|
|
121
|
+
|
|
122
|
+
class GotoURL(Tool):
|
|
123
|
+
def __init__(self, computer: Computer):
|
|
124
|
+
self.computer = computer
|
|
125
|
+
|
|
126
|
+
super().__init__(
|
|
127
|
+
name="goto",
|
|
128
|
+
description="goes to a specific URL. Make sure it starts with http:// or https://",
|
|
129
|
+
# TODO: give a correct schema
|
|
130
|
+
input_schema={
|
|
131
|
+
"additionalProperties" : False,
|
|
132
|
+
"type" : "object",
|
|
133
|
+
"required" : ["url"],
|
|
134
|
+
"properties" : {
|
|
135
|
+
"url" : {
|
|
136
|
+
"type" : "string",
|
|
137
|
+
"description": "Fully qualified URL to navigate to.",
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
async def execute(self, context: ToolContext, url: str):
|
|
145
|
+
nonlocal started
|
|
146
|
+
if started == False:
|
|
147
|
+
await self.computer.__aenter__()
|
|
148
|
+
started = True
|
|
149
|
+
|
|
150
|
+
if url.startswith("https://") == False and url.startswith("http://") == False:
|
|
151
|
+
url = "https://"+url
|
|
152
|
+
|
|
153
|
+
await self.computer.goto(url)
|
|
154
|
+
|
|
155
|
+
render_screen(await self.computer.screenshot_bytes(full_page=False))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
computer_tool = ComputerTool(computer=computer, operator=operator)
|
|
159
|
+
|
|
160
|
+
computer_toolkit = Toolkit(name="meshagent.openai.computer", tools=[
|
|
161
|
+
computer_tool
|
|
162
|
+
])
|
|
163
|
+
|
|
164
|
+
return computer_toolkit
|
|
165
|
+
|
|
166
|
+
async def make_browserbase_toolkit(context: ToolContext, requirement: RequiredToolkit):
|
|
167
|
+
return make_computer_toolkit(operator_cls=Operator(), computer_cls=BrowserbaseBrowser())
|
|
168
|
+
|
|
169
|
+
register_toolkit_factory("browserbase", make_browserbase_toolkit)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class ComputerAgent(ChatBot):
|
|
20
173
|
def __init__(self, *, name,
|
|
21
174
|
title=None,
|
|
22
175
|
description=None,
|
|
23
176
|
requires=None,
|
|
24
177
|
labels = None,
|
|
25
|
-
computer_cls:
|
|
26
|
-
operator_cls:
|
|
178
|
+
computer_cls: Type[Computer],
|
|
179
|
+
operator_cls: Type[Operator],
|
|
27
180
|
rules: Optional[list[str]] = None,
|
|
28
181
|
llm_adapter: Optional[LLMAdapter] = None,
|
|
29
182
|
toolkits: list[Toolkit] = None
|
|
@@ -48,178 +201,28 @@ class ComputerAgent[ComputerType:Computer, OperatorType:Operator](ChatBot):
|
|
|
48
201
|
self.operator_cls = operator_cls
|
|
49
202
|
|
|
50
203
|
|
|
51
|
-
async def
|
|
204
|
+
async def get_thread_toolkits(self, *, thread_context: ChatThreadContext, participant: RemoteParticipant):
|
|
52
205
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# TODO: give a correct schema
|
|
62
|
-
input_schema={
|
|
63
|
-
"additionalProperties" : False,
|
|
64
|
-
"type" : "object",
|
|
65
|
-
"required" : [],
|
|
66
|
-
"properties" : {}
|
|
67
|
-
},
|
|
68
|
-
title=title,
|
|
69
|
-
description=description,
|
|
70
|
-
rules=rules,
|
|
71
|
-
thumbnail_url=thumbnail_url,
|
|
72
|
-
defs=defs,
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
self.computer = computer
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@property
|
|
79
|
-
def options(self):
|
|
80
|
-
return {
|
|
81
|
-
"type": "computer-preview",
|
|
82
|
-
"display_width": self.computer.dimensions[0],
|
|
83
|
-
"display_height": self.computer.dimensions[1],
|
|
84
|
-
"environment": self.computer.environment,
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async def execute(self, context: ToolContext, *, arguments):
|
|
88
|
-
|
|
89
|
-
nonlocal started
|
|
90
|
-
if started == False:
|
|
91
|
-
await self.computer.__aenter__()
|
|
92
|
-
started = True
|
|
93
|
-
|
|
94
|
-
for participant in thread_context.participants:
|
|
95
|
-
await context.room.messaging.send_message(
|
|
96
|
-
to=participant,
|
|
97
|
-
type="computer_use",
|
|
98
|
-
message={
|
|
99
|
-
"arguments" : arguments
|
|
100
|
-
}
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
outputs = await operator.play(computer=self.computer, item=arguments)
|
|
104
|
-
for output in outputs:
|
|
105
|
-
if output["type"] == "computer_call_output":
|
|
106
|
-
if output["output"] != None:
|
|
107
|
-
if output["output"]["type"] == "input_image":
|
|
108
|
-
|
|
109
|
-
b64 : str = output["output"]["image_url"]
|
|
110
|
-
image_data_b64 = b64.split(",", 1)
|
|
111
|
-
|
|
112
|
-
image_bytes = base64.b64decode(image_data_b64[1])
|
|
113
|
-
|
|
114
|
-
for participant in thread_context.participants:
|
|
115
|
-
context.room.messaging.send_message_nowait(
|
|
116
|
-
to=participant,
|
|
117
|
-
type="computer_screen",
|
|
118
|
-
message={
|
|
119
|
-
},
|
|
120
|
-
attachment=image_bytes
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
nonlocal computer_toolkit
|
|
124
|
-
if len(computer_toolkit.tools) == 1:
|
|
125
|
-
# HACK: after looking at the page, add the other tools,
|
|
126
|
-
# if we add these first then the computer-use-preview mode fails if it calls them before using the computer
|
|
127
|
-
computer_toolkit.tools.extend([
|
|
128
|
-
ScreenshotTool(computer=computer),
|
|
129
|
-
GotoURL(computer=computer),
|
|
130
|
-
])
|
|
131
|
-
return RawOutputs(outputs=outputs)
|
|
132
|
-
|
|
133
|
-
class ScreenshotTool(Tool):
|
|
134
|
-
def __init__(self, computer: Computer):
|
|
135
|
-
self.computer = computer
|
|
136
|
-
|
|
137
|
-
super().__init__(
|
|
138
|
-
name="screenshot",
|
|
139
|
-
# TODO: give a correct schema
|
|
140
|
-
input_schema={
|
|
141
|
-
"additionalProperties" : False,
|
|
142
|
-
"type" : "object",
|
|
143
|
-
"required" : ["full_page","save_path"],
|
|
144
|
-
"properties" : {
|
|
145
|
-
"full_page" : {
|
|
146
|
-
"type" : "boolean"
|
|
147
|
-
},
|
|
148
|
-
"save_path" : {
|
|
149
|
-
"type" : "string",
|
|
150
|
-
"description" : "a file path to save the screenshot to (should end with .png)"
|
|
151
|
-
}
|
|
152
|
-
}
|
|
206
|
+
toolkits = await super().get_thread_toolkits(thread_context=thread_context, participant=participant)
|
|
207
|
+
|
|
208
|
+
def render_screen(image_bytes: bytes):
|
|
209
|
+
for participant in thread_context.participants:
|
|
210
|
+
self.room.messaging.send_message_nowait(
|
|
211
|
+
to=participant,
|
|
212
|
+
type="computer_screen",
|
|
213
|
+
message={
|
|
153
214
|
},
|
|
154
|
-
|
|
215
|
+
attachment=image_bytes
|
|
155
216
|
)
|
|
156
|
-
|
|
157
217
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
screenshot_bytes = await self.computer.screenshot_bytes(full_page=full_page)
|
|
165
|
-
handle = await context.room.storage.open(path=save_path, overwrite=True)
|
|
166
|
-
await context.room.storage.write(handle=handle, data=screenshot_bytes)
|
|
167
|
-
await context.room.storage.close(handle=handle)
|
|
168
|
-
|
|
169
|
-
return f"saved screenshot to {save_path}"
|
|
170
|
-
|
|
171
|
-
class GotoURL(Tool):
|
|
172
|
-
def __init__(self, computer: Computer):
|
|
173
|
-
self.computer = computer
|
|
174
|
-
|
|
175
|
-
super().__init__(
|
|
176
|
-
name="goto",
|
|
177
|
-
description="goes to a specific URL. Make sure it starts with http:// or https://",
|
|
178
|
-
# TODO: give a correct schema
|
|
179
|
-
input_schema={
|
|
180
|
-
"additionalProperties" : False,
|
|
181
|
-
"type" : "object",
|
|
182
|
-
"required" : ["url"],
|
|
183
|
-
"properties" : {
|
|
184
|
-
"url" : {
|
|
185
|
-
"type" : "string",
|
|
186
|
-
"description": "Fully qualified URL to navigate to.",
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
async def execute(self, context: ToolContext, url: str):
|
|
194
|
-
nonlocal started
|
|
195
|
-
if started == False:
|
|
196
|
-
await self.computer.__aenter__()
|
|
197
|
-
started = True
|
|
198
|
-
|
|
199
|
-
if url.startswith("https://") == False and url.startswith("http://") == False:
|
|
200
|
-
url = "https://"+url
|
|
201
|
-
|
|
202
|
-
await self.computer.goto(url)
|
|
203
|
-
|
|
204
|
-
# send an updated screen out
|
|
205
|
-
for participant in thread_context.participants:
|
|
206
|
-
context.room.messaging.send_message_nowait(
|
|
207
|
-
to=participant,
|
|
208
|
-
type="computer_screen",
|
|
209
|
-
message={
|
|
210
|
-
},
|
|
211
|
-
attachment = await self.computer.screenshot_bytes(full_page=False)
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
computer_tool = ComputerTool(computer=computer, operator=operator)
|
|
215
|
-
|
|
216
|
-
computer_toolkit = Toolkit(name="meshagent.openai.computer", tools=[
|
|
217
|
-
computer_tool
|
|
218
|
-
])
|
|
219
|
-
|
|
220
|
-
thread_context.toolkits = [
|
|
218
|
+
computer_toolkit = make_computer_toolkit(
|
|
219
|
+
operator_cls=self.operator_cls,
|
|
220
|
+
computer_cls=self.computer_cls,
|
|
221
|
+
render_screen=render_screen
|
|
222
|
+
)
|
|
223
|
+
return [
|
|
221
224
|
computer_toolkit,
|
|
222
|
-
*
|
|
225
|
+
*toolkits
|
|
223
226
|
]
|
|
224
227
|
|
|
225
228
|
|
meshagent/computers/utils.py
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.8"
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshagent-computers
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.8
|
|
4
4
|
Summary: Computer Building Blocks for Meshagent
|
|
5
|
-
|
|
6
|
-
License: Apache License 2.0
|
|
5
|
+
License-Expression: Apache-2.0
|
|
7
6
|
Project-URL: Documentation, https://meshagent.com
|
|
8
7
|
Project-URL: Website, https://meshagent.com
|
|
9
8
|
Project-URL: Source, https://github.com/meshagent
|
|
@@ -13,16 +12,10 @@ License-File: LICENSE
|
|
|
13
12
|
Requires-Dist: pytest>=8.3.4
|
|
14
13
|
Requires-Dist: pytest-asyncio>=0.24.0
|
|
15
14
|
Requires-Dist: openai>=1.66.2
|
|
16
|
-
Requires-Dist: meshagent-api>=0.0.
|
|
17
|
-
Requires-Dist: meshagent-agents>=0.0.
|
|
18
|
-
Requires-Dist: meshagent-tools>=0.0.
|
|
15
|
+
Requires-Dist: meshagent-api>=0.0.8
|
|
16
|
+
Requires-Dist: meshagent-agents>=0.0.8
|
|
17
|
+
Requires-Dist: meshagent-tools>=0.0.8
|
|
19
18
|
Requires-Dist: playwright~=1.50.0
|
|
20
19
|
Requires-Dist: browserbase~=1.2.0
|
|
21
20
|
Requires-Dist: scrapybara~=2.4.1
|
|
22
|
-
Dynamic: description-content-type
|
|
23
|
-
Dynamic: license
|
|
24
21
|
Dynamic: license-file
|
|
25
|
-
Dynamic: project-url
|
|
26
|
-
Dynamic: requires-dist
|
|
27
|
-
Dynamic: requires-python
|
|
28
|
-
Dynamic: summary
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
meshagent/computers/__init__.py,sha256=
|
|
2
|
-
meshagent/computers/agent.py,sha256=
|
|
1
|
+
meshagent/computers/__init__.py,sha256=PEzgkWE_sB6TrBA8crwWM7QaNPYWJisCKT81tk6pepQ,321
|
|
2
|
+
meshagent/computers/agent.py,sha256=AR1MDxuz7DiLul8H5aluU5z61Yt7lGqmDo6l1QKWRwA,8459
|
|
3
3
|
meshagent/computers/base_playwright.py,sha256=NhQoOzWmekBmobQ2kvN7w4I3hZeyMIuMKI2eHbG1_mk,6017
|
|
4
4
|
meshagent/computers/browserbase.py,sha256=ZT-ZDndkBD-MHD3UjxXpwpx7-_VCkkanjW1rIJr7-98,8161
|
|
5
5
|
meshagent/computers/computer.py,sha256=465GKDV3cJgzp1SB5B0xzLoftMKU0AxAuBVrZ9drBBc,1072
|
|
@@ -7,9 +7,10 @@ meshagent/computers/docker.py,sha256=BGkrlspIYlg3WViaqctyRUye-fONfF40AvLXOQuXPQ8
|
|
|
7
7
|
meshagent/computers/local_playwright.py,sha256=W7VhxIYEXn2BO3wZZVsc-hH-CU7NTssxFGO7pwz_n1I,919
|
|
8
8
|
meshagent/computers/operator.py,sha256=JTyGvoPnKOqUDaZv58p_2n5Q6m6dm8V214VMIhVEu08,2913
|
|
9
9
|
meshagent/computers/scrapybara.py,sha256=IoeV02QcBDkpW05A1NH-lc4jnBEmZ4SPsRo3nnXC3UE,7502
|
|
10
|
-
meshagent/computers/utils.py,sha256=
|
|
11
|
-
|
|
12
|
-
meshagent_computers-0.0.
|
|
13
|
-
meshagent_computers-0.0.
|
|
14
|
-
meshagent_computers-0.0.
|
|
15
|
-
meshagent_computers-0.0.
|
|
10
|
+
meshagent/computers/utils.py,sha256=N7Ltjx3HfhJ-CYectNodGsBtddk6mSTHkCH0meqHT70,2080
|
|
11
|
+
meshagent/computers/version.py,sha256=ICOxGt7ta-t2F9uXLMXVo-4VDYGM0IEvKPStCf0y8Rk,21
|
|
12
|
+
meshagent_computers-0.0.8.dist-info/licenses/LICENSE,sha256=eTt0SPW-sVNdkZe9PS_S8WfCIyLjRXRl7sUBWdlteFg,10254
|
|
13
|
+
meshagent_computers-0.0.8.dist-info/METADATA,sha256=24jshdqcrgglfb-LQfoFjt2wLgqEmiOKwvcPJW8jEG8,706
|
|
14
|
+
meshagent_computers-0.0.8.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
15
|
+
meshagent_computers-0.0.8.dist-info/top_level.txt,sha256=GlcXnHtRP6m7zlG3Df04M35OsHtNXy_DY09oFwWrH74,10
|
|
16
|
+
meshagent_computers-0.0.8.dist-info/RECORD,,
|
{meshagent_computers-0.0.7.dist-info → meshagent_computers-0.0.8.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|