meshagent-cli 0.21.0__py3-none-any.whl → 0.23.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.
- meshagent/cli/agent.py +8 -2
- meshagent/cli/call.py +15 -28
- meshagent/cli/chatbot.py +580 -76
- meshagent/cli/cli.py +3 -3
- meshagent/cli/helper.py +40 -2
- meshagent/cli/helpers.py +0 -3
- meshagent/cli/host.py +4 -0
- meshagent/cli/mailbot.py +137 -76
- meshagent/cli/meeting_transcriber.py +19 -11
- meshagent/cli/messaging.py +1 -4
- meshagent/cli/multi.py +53 -98
- meshagent/cli/oauth2.py +164 -35
- meshagent/cli/room.py +6 -2
- meshagent/cli/services.py +238 -15
- meshagent/cli/sync.py +434 -0
- meshagent/cli/task_runner.py +625 -78
- meshagent/cli/version.py +1 -1
- meshagent/cli/voicebot.py +54 -34
- meshagent/cli/worker.py +151 -75
- {meshagent_cli-0.21.0.dist-info → meshagent_cli-0.23.0.dist-info}/METADATA +13 -11
- meshagent_cli-0.23.0.dist-info/RECORD +45 -0
- {meshagent_cli-0.21.0.dist-info → meshagent_cli-0.23.0.dist-info}/WHEEL +1 -1
- meshagent_cli-0.21.0.dist-info/RECORD +0 -44
- {meshagent_cli-0.21.0.dist-info → meshagent_cli-0.23.0.dist-info}/entry_points.txt +0 -0
- {meshagent_cli-0.21.0.dist-info → meshagent_cli-0.23.0.dist-info}/top_level.txt +0 -0
meshagent/cli/services.py
CHANGED
|
@@ -9,7 +9,7 @@ from aiohttp import ClientResponseError
|
|
|
9
9
|
import pathlib
|
|
10
10
|
from meshagent.cli import async_typer
|
|
11
11
|
from meshagent.api.services import well_known_service_path
|
|
12
|
-
from meshagent.api.specs.service import ServiceSpec
|
|
12
|
+
from meshagent.api.specs.service import ServiceSpec, ServiceTemplateSpec
|
|
13
13
|
from meshagent.api.keys import parse_api_key
|
|
14
14
|
|
|
15
15
|
import asyncio
|
|
@@ -35,6 +35,7 @@ from meshagent.api import (
|
|
|
35
35
|
)
|
|
36
36
|
from meshagent.cli.common_options import OutputFormatOption
|
|
37
37
|
|
|
38
|
+
from pydantic import RootModel
|
|
38
39
|
from pydantic_yaml import parse_yaml_raw_as
|
|
39
40
|
|
|
40
41
|
|
|
@@ -44,6 +45,32 @@ from meshagent.cli.call import _make_call
|
|
|
44
45
|
app = async_typer.AsyncTyper(help="Manage services for your project")
|
|
45
46
|
|
|
46
47
|
|
|
48
|
+
class ServiceTemplateValues(RootModel[dict[str, str]]):
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _load_template_values(
|
|
53
|
+
values_file: Optional[str],
|
|
54
|
+
values: Optional[list[str]] = None,
|
|
55
|
+
) -> dict[str, str]:
|
|
56
|
+
template_values: dict[str, str] = {}
|
|
57
|
+
|
|
58
|
+
if values_file is not None:
|
|
59
|
+
with open(str(pathlib.Path(values_file).expanduser().resolve()), "rb") as f:
|
|
60
|
+
template_values = parse_yaml_raw_as(ServiceTemplateValues, f.read()).root
|
|
61
|
+
|
|
62
|
+
if values:
|
|
63
|
+
for item in values:
|
|
64
|
+
if "=" not in item:
|
|
65
|
+
raise typer.BadParameter("Template values must be key=value")
|
|
66
|
+
key, value = item.split("=", 1)
|
|
67
|
+
if not key:
|
|
68
|
+
raise typer.BadParameter("Template values must include a key")
|
|
69
|
+
template_values[key] = value
|
|
70
|
+
|
|
71
|
+
return template_values
|
|
72
|
+
|
|
73
|
+
|
|
47
74
|
@app.async_command("create")
|
|
48
75
|
async def service_create(
|
|
49
76
|
*,
|
|
@@ -173,6 +200,213 @@ async def service_update(
|
|
|
173
200
|
await client.close()
|
|
174
201
|
|
|
175
202
|
|
|
203
|
+
@app.async_command("validate")
|
|
204
|
+
async def service_validate(
|
|
205
|
+
*,
|
|
206
|
+
file: Annotated[
|
|
207
|
+
str,
|
|
208
|
+
typer.Option("--file", "-f", help="File path to a service definition"),
|
|
209
|
+
],
|
|
210
|
+
):
|
|
211
|
+
"""Validate a service spec from a YAML file."""
|
|
212
|
+
try:
|
|
213
|
+
with open(str(pathlib.Path(file).expanduser().resolve()), "rb") as f:
|
|
214
|
+
spec = parse_yaml_raw_as(ServiceSpec, f.read())
|
|
215
|
+
except Exception as exc:
|
|
216
|
+
print(f"[red]Invalid service spec: {exc}[/red]")
|
|
217
|
+
raise typer.Exit(code=1)
|
|
218
|
+
|
|
219
|
+
print(f"[green]Service spec is valid:[/] {spec.metadata.name}")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@app.async_command("create-template")
|
|
223
|
+
async def service_create_template(
|
|
224
|
+
*,
|
|
225
|
+
project_id: ProjectIdOption,
|
|
226
|
+
file: Annotated[
|
|
227
|
+
str,
|
|
228
|
+
typer.Option("--file", "-f", help="File path to a service template"),
|
|
229
|
+
],
|
|
230
|
+
values: Annotated[
|
|
231
|
+
Optional[str],
|
|
232
|
+
typer.Option("--values-file", help="File path to template values"),
|
|
233
|
+
] = None,
|
|
234
|
+
value: Annotated[
|
|
235
|
+
Optional[list[str]],
|
|
236
|
+
typer.Option(
|
|
237
|
+
"--value",
|
|
238
|
+
"-v",
|
|
239
|
+
help="Template value override (key=value)",
|
|
240
|
+
),
|
|
241
|
+
] = None,
|
|
242
|
+
room: Annotated[
|
|
243
|
+
Optional[str],
|
|
244
|
+
typer.Option("--room", help="The name of a room to create the service for"),
|
|
245
|
+
] = None,
|
|
246
|
+
):
|
|
247
|
+
"""Create a service from a ServiceTemplate spec."""
|
|
248
|
+
client = await get_client()
|
|
249
|
+
try:
|
|
250
|
+
project_id = await resolve_project_id(project_id)
|
|
251
|
+
|
|
252
|
+
with open(str(pathlib.Path(file).expanduser().resolve()), "rb") as f:
|
|
253
|
+
template = f.read()
|
|
254
|
+
|
|
255
|
+
template_values = _load_template_values(values, value)
|
|
256
|
+
|
|
257
|
+
try:
|
|
258
|
+
if room is None:
|
|
259
|
+
service = await client.create_service_from_template(
|
|
260
|
+
project_id=project_id, template=template, values=template_values
|
|
261
|
+
)
|
|
262
|
+
else:
|
|
263
|
+
service = await client.create_room_service_from_template(
|
|
264
|
+
project_id=project_id,
|
|
265
|
+
template=template,
|
|
266
|
+
values=template_values,
|
|
267
|
+
room_name=room,
|
|
268
|
+
)
|
|
269
|
+
except ClientResponseError as exc:
|
|
270
|
+
if exc.status == 409:
|
|
271
|
+
print(
|
|
272
|
+
f"[red]Service name already in use: {template.metadata.name}[/red]"
|
|
273
|
+
)
|
|
274
|
+
raise typer.Exit(code=1)
|
|
275
|
+
raise
|
|
276
|
+
else:
|
|
277
|
+
service_id = service.id or ""
|
|
278
|
+
print(f"[green]Created service:[/] {service_id}")
|
|
279
|
+
|
|
280
|
+
finally:
|
|
281
|
+
await client.close()
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@app.async_command("update-template")
|
|
285
|
+
async def service_update_template(
|
|
286
|
+
*,
|
|
287
|
+
project_id: ProjectIdOption,
|
|
288
|
+
id: Optional[str] = None,
|
|
289
|
+
file: Annotated[
|
|
290
|
+
str,
|
|
291
|
+
typer.Option("--file", "-f", help="File path to a service template"),
|
|
292
|
+
],
|
|
293
|
+
values: Annotated[
|
|
294
|
+
Optional[str],
|
|
295
|
+
typer.Option("--values-file", help="File path to template values"),
|
|
296
|
+
] = None,
|
|
297
|
+
value: Annotated[
|
|
298
|
+
Optional[list[str]],
|
|
299
|
+
typer.Option(
|
|
300
|
+
"--value",
|
|
301
|
+
"-v",
|
|
302
|
+
help="Template value override (key=value)",
|
|
303
|
+
),
|
|
304
|
+
] = None,
|
|
305
|
+
create: Annotated[
|
|
306
|
+
Optional[bool],
|
|
307
|
+
typer.Option(
|
|
308
|
+
help="create the service if it does not exist",
|
|
309
|
+
),
|
|
310
|
+
] = False,
|
|
311
|
+
room: Annotated[
|
|
312
|
+
Optional[str],
|
|
313
|
+
typer.Option("--room", help="The name of a room to update the service for"),
|
|
314
|
+
] = None,
|
|
315
|
+
):
|
|
316
|
+
"""Update a service using a ServiceTemplate spec."""
|
|
317
|
+
client = await get_client()
|
|
318
|
+
try:
|
|
319
|
+
project_id = await resolve_project_id(project_id)
|
|
320
|
+
|
|
321
|
+
with open(str(pathlib.Path(file).expanduser().resolve()), "rb") as f:
|
|
322
|
+
template = f.read()
|
|
323
|
+
|
|
324
|
+
template_values = _load_template_values(values, value)
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
if id is None:
|
|
328
|
+
if room is None:
|
|
329
|
+
services = await client.list_services(project_id=project_id)
|
|
330
|
+
else:
|
|
331
|
+
services = await client.list_room_services(
|
|
332
|
+
project_id=project_id, room_name=room
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
for s in services:
|
|
336
|
+
if s.metadata.name == template.metadata.name:
|
|
337
|
+
id = s.id
|
|
338
|
+
|
|
339
|
+
if id is None and not create:
|
|
340
|
+
print("[red]pass a service id or specify --create[/red]")
|
|
341
|
+
raise typer.Exit(code=1)
|
|
342
|
+
|
|
343
|
+
if id is None:
|
|
344
|
+
if room is None:
|
|
345
|
+
service = await client.create_service_from_template(
|
|
346
|
+
project_id=project_id,
|
|
347
|
+
template=template,
|
|
348
|
+
values=template_values,
|
|
349
|
+
)
|
|
350
|
+
else:
|
|
351
|
+
service = await client.create_room_service_from_template(
|
|
352
|
+
project_id=project_id,
|
|
353
|
+
template=template,
|
|
354
|
+
values=template_values,
|
|
355
|
+
room_name=room,
|
|
356
|
+
)
|
|
357
|
+
id = service.id
|
|
358
|
+
else:
|
|
359
|
+
if room is None:
|
|
360
|
+
service = await client.update_service_from_template(
|
|
361
|
+
project_id=project_id,
|
|
362
|
+
service_id=id,
|
|
363
|
+
template=template,
|
|
364
|
+
values=template_values,
|
|
365
|
+
)
|
|
366
|
+
else:
|
|
367
|
+
service = await client.update_room_service_from_template(
|
|
368
|
+
project_id=project_id,
|
|
369
|
+
service_id=id,
|
|
370
|
+
template=template,
|
|
371
|
+
values=template_values,
|
|
372
|
+
room_name=room,
|
|
373
|
+
)
|
|
374
|
+
if service.id is not None:
|
|
375
|
+
id = service.id
|
|
376
|
+
|
|
377
|
+
except ClientResponseError as exc:
|
|
378
|
+
if exc.status == 409:
|
|
379
|
+
print(
|
|
380
|
+
f"[red]Service name already in use: {template.metadata.name}[/red]"
|
|
381
|
+
)
|
|
382
|
+
raise typer.Exit(code=1)
|
|
383
|
+
raise
|
|
384
|
+
else:
|
|
385
|
+
print(f"[green]Updated service:[/] {id}")
|
|
386
|
+
|
|
387
|
+
finally:
|
|
388
|
+
await client.close()
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
@app.async_command("validate-template")
|
|
392
|
+
async def service_validate_template(
|
|
393
|
+
*,
|
|
394
|
+
file: Annotated[
|
|
395
|
+
str,
|
|
396
|
+
typer.Option("--file", "-f", help="File path to a service template"),
|
|
397
|
+
],
|
|
398
|
+
):
|
|
399
|
+
"""Validate a service template from a YAML file."""
|
|
400
|
+
try:
|
|
401
|
+
with open(str(pathlib.Path(file).expanduser().resolve()), "rb") as f:
|
|
402
|
+
template = parse_yaml_raw_as(ServiceTemplateSpec, f.read())
|
|
403
|
+
except Exception as exc:
|
|
404
|
+
print(f"[red]Invalid service template: {exc}[/red]")
|
|
405
|
+
raise typer.Exit(code=1)
|
|
406
|
+
|
|
407
|
+
print(f"[green]Service template is valid:[/] {template.metadata.name}")
|
|
408
|
+
|
|
409
|
+
|
|
176
410
|
@app.async_command("run")
|
|
177
411
|
async def service_run(
|
|
178
412
|
*,
|
|
@@ -291,7 +525,7 @@ async def service_run(
|
|
|
291
525
|
|
|
292
526
|
sys.stdout.write("\n")
|
|
293
527
|
|
|
294
|
-
for p in spec.ports:
|
|
528
|
+
for p in spec.ports or []:
|
|
295
529
|
print(f"[bold green]Connecting port {p.num}...[/bold green]")
|
|
296
530
|
|
|
297
531
|
for endpoint in p.endpoints:
|
|
@@ -370,9 +604,7 @@ async def service_list(
|
|
|
370
604
|
)
|
|
371
605
|
|
|
372
606
|
if o == "json":
|
|
373
|
-
print(
|
|
374
|
-
{"services": [svc.model_dump(mode="json") for svc in services]}
|
|
375
|
-
).model_dump_json(indent=2)
|
|
607
|
+
print({"services": [svc.model_dump(mode="json") for svc in services]})
|
|
376
608
|
else:
|
|
377
609
|
print_json_table(
|
|
378
610
|
[
|
|
@@ -398,21 +630,12 @@ async def service_delete(
|
|
|
398
630
|
*,
|
|
399
631
|
project_id: ProjectIdOption,
|
|
400
632
|
service_id: Annotated[str, typer.Argument(help="ID of the service to delete")],
|
|
401
|
-
room: Annotated[
|
|
402
|
-
Optional[str],
|
|
403
|
-
typer.Option("--room", help="The name of a room to delete the service for"),
|
|
404
|
-
] = None,
|
|
405
633
|
):
|
|
406
634
|
"""Delete a service."""
|
|
407
635
|
client = await get_client()
|
|
408
636
|
try:
|
|
409
637
|
project_id = await resolve_project_id(project_id)
|
|
410
|
-
|
|
411
|
-
await client.delete_service(project_id=project_id, service_id=service_id)
|
|
412
|
-
else:
|
|
413
|
-
await client.delete_service(
|
|
414
|
-
project_id=project_id, service_id=service_id, room_name=room
|
|
415
|
-
)
|
|
638
|
+
await client.delete_service(project_id=project_id, service_id=service_id)
|
|
416
639
|
print(f"[green]Service {service_id} deleted.[/]")
|
|
417
640
|
finally:
|
|
418
641
|
await client.close()
|