sunholo 0.60.5__py3-none-any.whl → 0.60.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.
- sunholo/cli/chat_vac.py +142 -30
- sunholo/cli/run_proxy.py +7 -62
- {sunholo-0.60.5.dist-info → sunholo-0.60.8.dist-info}/METADATA +2 -2
- {sunholo-0.60.5.dist-info → sunholo-0.60.8.dist-info}/RECORD +8 -8
- {sunholo-0.60.5.dist-info → sunholo-0.60.8.dist-info}/LICENSE.txt +0 -0
- {sunholo-0.60.5.dist-info → sunholo-0.60.8.dist-info}/WHEEL +0 -0
- {sunholo-0.60.5.dist-info → sunholo-0.60.8.dist-info}/entry_points.txt +0 -0
- {sunholo-0.60.5.dist-info → sunholo-0.60.8.dist-info}/top_level.txt +0 -0
sunholo/cli/chat_vac.py
CHANGED
|
@@ -7,6 +7,8 @@ from .run_proxy import clean_proxy_list, start_proxy, stop_proxy
|
|
|
7
7
|
|
|
8
8
|
import uuid
|
|
9
9
|
import sys
|
|
10
|
+
import subprocess
|
|
11
|
+
import json
|
|
10
12
|
|
|
11
13
|
from rich import print
|
|
12
14
|
from .sun_rich import console
|
|
@@ -14,6 +16,7 @@ from .sun_rich import console
|
|
|
14
16
|
from rich.prompt import Prompt
|
|
15
17
|
from rich.panel import Panel
|
|
16
18
|
from rich.text import Text
|
|
19
|
+
from rich.table import Table
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
def get_service_url(vac_name, project, region):
|
|
@@ -28,9 +31,8 @@ def get_service_url(vac_name, project, region):
|
|
|
28
31
|
|
|
29
32
|
return url
|
|
30
33
|
|
|
31
|
-
def stream_chat_session(
|
|
34
|
+
def stream_chat_session(service_url, service_name):
|
|
32
35
|
|
|
33
|
-
service_url = get_service_url(service_name, project, region)
|
|
34
36
|
user_id = generate_user_id()
|
|
35
37
|
chat_history = []
|
|
36
38
|
while True:
|
|
@@ -91,10 +93,9 @@ def stream_chat_session(service_name, project, region):
|
|
|
91
93
|
console.print()
|
|
92
94
|
console.rule()
|
|
93
95
|
|
|
94
|
-
def headless_mode(service_name, user_input,
|
|
96
|
+
def headless_mode(service_url, service_name, user_input, chat_history=None):
|
|
95
97
|
chat_history = chat_history or []
|
|
96
98
|
|
|
97
|
-
service_url = get_service_url(service_name, project, region)
|
|
98
99
|
user_id = generate_user_id()
|
|
99
100
|
session_id = str(uuid.uuid4())
|
|
100
101
|
|
|
@@ -144,34 +145,132 @@ def headless_mode(service_name, user_input, project, region, chat_history=None):
|
|
|
144
145
|
|
|
145
146
|
|
|
146
147
|
def vac_command(args):
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
148
|
+
if args.action == 'list':
|
|
149
|
+
list_cloud_run_services(args.project, args.region)
|
|
150
|
+
return
|
|
151
|
+
elif args.action == 'get-url':
|
|
152
|
+
service_url = get_cloud_run_service_url(args.project, args.region, args.vac_name)
|
|
153
|
+
if service_url:
|
|
154
|
+
console.print(service_url)
|
|
155
|
+
return
|
|
156
|
+
elif args.action == 'chat':
|
|
154
157
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
if not args.no_proxy:
|
|
159
|
+
try:
|
|
160
|
+
service_url = get_service_url(args.vac_name, args.project, args.region)
|
|
161
|
+
except ValueError as e:
|
|
162
|
+
console.print(f"[bold red]ERROR: Could not start {args.vac_name} proxy URL: {str(e)}[/bold red]")
|
|
163
|
+
sys.exit(1)
|
|
164
|
+
else:
|
|
165
|
+
service_url = get_cloud_run_service_url(args.project, args.region, args.vac_name)
|
|
166
|
+
console.print(f"Not using a proxy, connecting directly to {service_url}")
|
|
167
|
+
|
|
168
|
+
agent_name = load_config_key("agent", args.vac_name, kind="vacConfig")
|
|
161
169
|
|
|
162
|
-
if
|
|
163
|
-
|
|
170
|
+
if args.headless:
|
|
171
|
+
headless_mode(service_url, args.vac_name, args.user_input, args.chat_history)
|
|
164
172
|
else:
|
|
165
|
-
|
|
173
|
+
display_name = load_config_key("display_name", vector_name=args.vac_name, kind="vacConfig")
|
|
174
|
+
description = load_config_key("description", vector_name=args.vac_name, kind="vacConfig")
|
|
175
|
+
|
|
176
|
+
if agent_name == "langserve":
|
|
177
|
+
subtitle = f"{service_url}/{args.vac_name}/playground/"
|
|
178
|
+
else:
|
|
179
|
+
subtitle = f"{agent_name} - {service_url}/vac/{args.vac_name}"
|
|
166
180
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
181
|
+
print(
|
|
182
|
+
Panel(description or "Starting VAC chat session",
|
|
183
|
+
title=display_name or args.vac_name,
|
|
184
|
+
subtitle=subtitle)
|
|
185
|
+
)
|
|
172
186
|
|
|
173
|
-
|
|
187
|
+
stream_chat_session(service_url, args.vac_name)
|
|
188
|
+
|
|
174
189
|
stop_proxy(agent_name)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def list_cloud_run_services(project, region):
|
|
193
|
+
"""
|
|
194
|
+
Lists all Cloud Run services the user has access to in a specific project and region.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
project (str): The GCP project ID.
|
|
198
|
+
region (str): The region of the Cloud Run services.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
# point or star?
|
|
202
|
+
with console.status("[bold orange]Listing Cloud Run Services[/bold orange]", spinner="star") as status:
|
|
203
|
+
try:
|
|
204
|
+
result = subprocess.run(
|
|
205
|
+
["gcloud", "run", "services", "list", "--project", project, "--region", region, "--format=json"],
|
|
206
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30
|
|
207
|
+
)
|
|
208
|
+
if result.returncode != 0:
|
|
209
|
+
status.stop()
|
|
210
|
+
console.print(f"[bold red]ERROR: Unable to list Cloud Run services: {result.stderr.decode()}[/bold red]")
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
services = json.loads(result.stdout.decode())
|
|
214
|
+
if not services:
|
|
215
|
+
status.stop()
|
|
216
|
+
console.print("[bold red]No Cloud Run services found.[/bold red]")
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
proxies = clean_proxy_list()
|
|
220
|
+
status.stop()
|
|
221
|
+
|
|
222
|
+
table = Table(title="VAC Cloud Run Services")
|
|
223
|
+
table.add_column("Service Name")
|
|
224
|
+
table.add_column("Region")
|
|
225
|
+
table.add_column("URL")
|
|
226
|
+
table.add_column("Proxied")
|
|
227
|
+
table.add_column("Port")
|
|
228
|
+
|
|
229
|
+
for service in services:
|
|
230
|
+
service_name = service['metadata']['name']
|
|
231
|
+
service_url = service['status']['url']
|
|
232
|
+
if service_name in proxies:
|
|
233
|
+
proxied = "Yes"
|
|
234
|
+
proxy_port = proxies[service_name]['port']
|
|
235
|
+
else:
|
|
236
|
+
proxied = "No"
|
|
237
|
+
proxy_port = "-"
|
|
238
|
+
table.add_row(service_name, region, service_url, proxied, str(proxy_port))
|
|
239
|
+
|
|
240
|
+
console.print(table)
|
|
241
|
+
except Exception as e:
|
|
242
|
+
status.stop()
|
|
243
|
+
console.print(f"[bold red]ERROR: An unexpected error occurred: {e}[/bold red]")
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def get_cloud_run_service_url(project, region, service_name):
|
|
247
|
+
"""
|
|
248
|
+
Retrieves the URL of a specific Cloud Run service in a given project and region.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
project (str): The GCP project ID.
|
|
252
|
+
region (str): The region of the Cloud Run service.
|
|
253
|
+
service_name (str): The name of the Cloud Run service.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
str: The URL of the Cloud Run service, or an error message if not found.
|
|
257
|
+
"""
|
|
258
|
+
try:
|
|
259
|
+
result = subprocess.run(
|
|
260
|
+
["gcloud", "run", "services", "describe", service_name, "--project", project, "--region", region, "--format=json"],
|
|
261
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30
|
|
262
|
+
)
|
|
263
|
+
if result.returncode != 0:
|
|
264
|
+
console.print(f"[bold red]ERROR: Unable to get Cloud Run service URL: {result.stderr.decode()}[/bold red]")
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
service = json.loads(result.stdout.decode())
|
|
268
|
+
service_url = service['status']['url']
|
|
269
|
+
return service_url
|
|
270
|
+
except Exception as e:
|
|
271
|
+
console.print(f"[bold red]ERROR: An unexpected error occurred: {e}[/bold red]")
|
|
272
|
+
return None
|
|
273
|
+
|
|
175
274
|
|
|
176
275
|
|
|
177
276
|
def setup_vac_subparser(subparsers):
|
|
@@ -182,8 +281,21 @@ def setup_vac_subparser(subparsers):
|
|
|
182
281
|
subparsers: The subparsers object from argparse.ArgumentParser().
|
|
183
282
|
"""
|
|
184
283
|
vac_parser = subparsers.add_parser('vac', help='Interact with deployed VAC services.')
|
|
185
|
-
vac_parser.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
284
|
+
vac_subparsers = vac_parser.add_subparsers(dest='action', help='VAC subcommands')
|
|
285
|
+
|
|
286
|
+
# Subcommand for listing VAC services
|
|
287
|
+
list_parser = vac_subparsers.add_parser('list', help='List all VAC services.')
|
|
288
|
+
|
|
289
|
+
# Subcommand for getting the URL of a specific VAC service
|
|
290
|
+
get_url_parser = vac_subparsers.add_parser('get-url', help='Get the URL of a specific VAC service.')
|
|
291
|
+
get_url_parser.add_argument('vac_name', help='Name of the VAC service.')
|
|
292
|
+
|
|
293
|
+
# Subcommand for interacting with a VAC service
|
|
294
|
+
chat_parser = vac_subparsers.add_parser('chat', help='Interact with a VAC service.')
|
|
295
|
+
chat_parser.add_argument('vac_name', help='Name of the VAC service.')
|
|
296
|
+
chat_parser.add_argument('user_input', help='User input for the VAC service when in headless mode.', nargs='?', default=None)
|
|
297
|
+
chat_parser.add_argument('--headless', action='store_true', help='Run in headless mode.')
|
|
298
|
+
chat_parser.add_argument('--chat_history', help='Chat history for headless mode (as JSON string).', default=None)
|
|
299
|
+
chat_parser.add_argument('--no-proxy', action='store_true', help='Do not use the proxy and connect directly to the VAC service.')
|
|
300
|
+
|
|
189
301
|
vac_parser.set_defaults(func=vac_command)
|
sunholo/cli/run_proxy.py
CHANGED
|
@@ -109,7 +109,7 @@ def start_proxy(service_name, region, project, port=None):
|
|
|
109
109
|
proxies = clean_proxy_list()
|
|
110
110
|
|
|
111
111
|
if service_name in proxies:
|
|
112
|
-
print(f"Proxy for service {service_name} is already running on port {proxies[service_name]['port']}.")
|
|
112
|
+
console.print(f"Proxy for service [bold orange]'{service_name}'[/bold orange] is already running on port {proxies[service_name]['port']}.")
|
|
113
113
|
return
|
|
114
114
|
|
|
115
115
|
if not port:
|
|
@@ -130,7 +130,7 @@ def start_proxy(service_name, region, project, port=None):
|
|
|
130
130
|
}
|
|
131
131
|
save_proxies(proxies)
|
|
132
132
|
|
|
133
|
-
print(f"Proxy for {service_name} setup complete on port {port}")
|
|
133
|
+
console.print(f"Proxy for [bold orange]'{service_name}'[/bold orange] setup complete on port {port}")
|
|
134
134
|
list_proxies()
|
|
135
135
|
|
|
136
136
|
return f"http://127.0.0.1:{port}"
|
|
@@ -154,11 +154,11 @@ def stop_proxy(service_name):
|
|
|
154
154
|
os.kill(pid, signal.SIGTERM)
|
|
155
155
|
del proxies[service_name]
|
|
156
156
|
save_proxies(proxies)
|
|
157
|
-
print(f"Proxy for {service_name} stopped.")
|
|
157
|
+
console.print(f"Proxy for [bold orange]'{service_name}'[bold orange] stopped.")
|
|
158
158
|
except ProcessLookupError:
|
|
159
|
-
print(f"No process found with PID: {pid}")
|
|
159
|
+
console.print(f"No process found with PID: {pid}")
|
|
160
160
|
except Exception as e:
|
|
161
|
-
print(f"Error stopping proxy for {service_name}: {e}")
|
|
161
|
+
console.print(f"[bold red]Error stopping proxy for {service_name}: {e}[/bold red]")
|
|
162
162
|
|
|
163
163
|
list_proxies()
|
|
164
164
|
|
|
@@ -172,69 +172,16 @@ def stop_all_proxies():
|
|
|
172
172
|
pid = info["pid"]
|
|
173
173
|
try:
|
|
174
174
|
os.kill(pid, signal.SIGTERM)
|
|
175
|
-
print(f"Proxy for {service_name} stopped.")
|
|
175
|
+
print(f"Proxy for [bold orange]'{service_name}'[/bold orange] stopped.")
|
|
176
176
|
except ProcessLookupError:
|
|
177
177
|
print(f"No process found with PID: {pid}")
|
|
178
178
|
except Exception as e:
|
|
179
|
-
print(f"Error stopping proxy for {service_name}: {e}")
|
|
179
|
+
print(f"Error stopping proxy for [bold orange]'{service_name}'[/bold orange]: {e}")
|
|
180
180
|
|
|
181
181
|
save_proxies({})
|
|
182
182
|
|
|
183
183
|
list_proxies()
|
|
184
184
|
|
|
185
|
-
def list_cloud_run_services(project, region):
|
|
186
|
-
"""
|
|
187
|
-
Lists all Cloud Run services the user has access to in a specific project and region.
|
|
188
|
-
|
|
189
|
-
Args:
|
|
190
|
-
project (str): The GCP project ID.
|
|
191
|
-
region (str): The region of the Cloud Run services.
|
|
192
|
-
"""
|
|
193
|
-
|
|
194
|
-
# point or star?
|
|
195
|
-
with console.status("[bold orange]Listing Cloud Run Services[/bold orange]", spinner="star") as status:
|
|
196
|
-
try:
|
|
197
|
-
result = subprocess.run(
|
|
198
|
-
["gcloud", "run", "services", "list", "--project", project, "--region", region, "--format=json"],
|
|
199
|
-
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30
|
|
200
|
-
)
|
|
201
|
-
if result.returncode != 0:
|
|
202
|
-
status.stop()
|
|
203
|
-
console.print(f"[bold red]ERROR: Unable to list Cloud Run services: {result.stderr.decode()}[/bold red]")
|
|
204
|
-
return
|
|
205
|
-
|
|
206
|
-
services = json.loads(result.stdout.decode())
|
|
207
|
-
if not services:
|
|
208
|
-
status.stop()
|
|
209
|
-
console.print("[bold red]No Cloud Run services found.[/bold red]")
|
|
210
|
-
return
|
|
211
|
-
|
|
212
|
-
proxies = clean_proxy_list()
|
|
213
|
-
status.stop()
|
|
214
|
-
|
|
215
|
-
table = Table(title="VAC Cloud Run Services")
|
|
216
|
-
table.add_column("Service Name")
|
|
217
|
-
table.add_column("Region")
|
|
218
|
-
table.add_column("URL")
|
|
219
|
-
table.add_column("Proxied")
|
|
220
|
-
table.add_column("Port")
|
|
221
|
-
|
|
222
|
-
for service in services:
|
|
223
|
-
service_name = service['metadata']['name']
|
|
224
|
-
service_url = service['status']['url']
|
|
225
|
-
if service_name in proxies:
|
|
226
|
-
proxied = "Yes"
|
|
227
|
-
proxy_port = proxies[service_name]['port']
|
|
228
|
-
else:
|
|
229
|
-
proxied = "No"
|
|
230
|
-
proxy_port = "-"
|
|
231
|
-
table.add_row(service_name, region, service_url, proxied, str(proxy_port))
|
|
232
|
-
|
|
233
|
-
console.print(table)
|
|
234
|
-
except Exception as e:
|
|
235
|
-
status.stop()
|
|
236
|
-
console.print(f"[bold red]ERROR: An unexpected error occurred: {e}[/bold red]")
|
|
237
|
-
|
|
238
185
|
def list_proxies():
|
|
239
186
|
"""
|
|
240
187
|
Lists all running proxies.
|
|
@@ -283,7 +230,5 @@ def setup_proxy_subparser(subparsers):
|
|
|
283
230
|
stop_all_parser = proxy_subparsers.add_parser('stop-all', help='Stop all running proxies.')
|
|
284
231
|
stop_all_parser.set_defaults(func=lambda args: stop_all_proxies())
|
|
285
232
|
|
|
286
|
-
list_services_parser = proxy_subparsers.add_parser('list-vacs', help='List all Cloud Run VAC services.')
|
|
287
|
-
list_services_parser.set_defaults(func=lambda args: list_cloud_run_services(args.project, args.region))
|
|
288
233
|
|
|
289
234
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.60.
|
|
3
|
+
Version: 0.60.8
|
|
4
4
|
Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
|
|
5
5
|
Home-page: https://github.com/sunholo-data/sunholo-py
|
|
6
|
-
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.60.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.60.8.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -32,13 +32,13 @@ sunholo/chunker/pdfs.py,sha256=daCZ1xjn1YvxlifIyxskWNpLJLe-Q9D_Jq12MWx3tZo,2473
|
|
|
32
32
|
sunholo/chunker/publish.py,sha256=PoT8q3XJeFCg10WrLkYhuaaXIrGVkvUD3-R9IfoWoH4,2703
|
|
33
33
|
sunholo/chunker/splitter.py,sha256=FLkDhkePkg_zGQpFBK13Cznw575D-Rf9pcaCpc1HUxY,6726
|
|
34
34
|
sunholo/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
-
sunholo/cli/chat_vac.py,sha256=
|
|
35
|
+
sunholo/cli/chat_vac.py,sha256=FvQTgFA4h0vfPSXvAx0_KCe8DUr2u_Wv1Md8-p6BkHk,11361
|
|
36
36
|
sunholo/cli/cli.py,sha256=LWA5OveHx3ocy9KD1XwAwosBFrTUwifArIbltYgpul4,2420
|
|
37
37
|
sunholo/cli/cli_init.py,sha256=JMZ9AX2cPDZ-_mv3adiv2ToFVNyRPtjk9Biszl1kiR0,2358
|
|
38
38
|
sunholo/cli/configs.py,sha256=QUM9DvKOdZmEQRM5uI3Nh887T0YDiSMr7O240zTLqws,4546
|
|
39
39
|
sunholo/cli/deploy.py,sha256=zxdwUsRTRMC8U5vyRv0JiKBLFn84Ug_Tc88-_h9hJSs,1609
|
|
40
40
|
sunholo/cli/merge_texts.py,sha256=U9vdMwKmcPoc6iPOWX5MKSxn49dNGbNzVLw8ui5PhEU,1823
|
|
41
|
-
sunholo/cli/run_proxy.py,sha256=
|
|
41
|
+
sunholo/cli/run_proxy.py,sha256=53bRzm6IVf5SB07fiwqKpJrGQBTt3xAWi-669w6VYSU,7591
|
|
42
42
|
sunholo/cli/sun_rich.py,sha256=UpMqeJ0C8i0pkue1AHnnyyX0bFJ9zZeJ7HBR6yhuA8A,54
|
|
43
43
|
sunholo/components/__init__.py,sha256=RJGNEihwvRIiDScKis04RHJv4yZGI1UpXlOmuCptNZI,208
|
|
44
44
|
sunholo/components/llm.py,sha256=T4we3tGmqUj4tPwxQr9M6AXv_BALqZV_dRSvINan-oU,10374
|
|
@@ -96,9 +96,9 @@ sunholo/utils/parsers.py,sha256=OrHmASqIbI45atVOhiGodgLvnfrzkvVzyHnSvAXD89I,3841
|
|
|
96
96
|
sunholo/utils/user_ids.py,sha256=SQd5_H7FE7vcTZp9AQuQDWBXd4FEEd7TeVMQe1H4Ny8,292
|
|
97
97
|
sunholo/vertex/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
98
|
sunholo/vertex/init_vertex.py,sha256=JDMUaBRdednzbKF-5p33qqLit2LMsvgvWW-NRz0AqO0,1801
|
|
99
|
-
sunholo-0.60.
|
|
100
|
-
sunholo-0.60.
|
|
101
|
-
sunholo-0.60.
|
|
102
|
-
sunholo-0.60.
|
|
103
|
-
sunholo-0.60.
|
|
104
|
-
sunholo-0.60.
|
|
99
|
+
sunholo-0.60.8.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
|
|
100
|
+
sunholo-0.60.8.dist-info/METADATA,sha256=TN1eHOBtuzd5fypSsbUpXwexrvfQ12ZPPE8IkRYq_A8,8057
|
|
101
|
+
sunholo-0.60.8.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
102
|
+
sunholo-0.60.8.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
|
|
103
|
+
sunholo-0.60.8.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
|
|
104
|
+
sunholo-0.60.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|