meshagent-cli 0.7.0__py3-none-any.whl → 0.21.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.
Files changed (41) hide show
  1. meshagent/cli/agent.py +15 -11
  2. meshagent/cli/api_keys.py +4 -4
  3. meshagent/cli/async_typer.py +52 -4
  4. meshagent/cli/call.py +12 -8
  5. meshagent/cli/chatbot.py +1007 -129
  6. meshagent/cli/cli.py +21 -20
  7. meshagent/cli/cli_mcp.py +92 -28
  8. meshagent/cli/cli_secrets.py +10 -10
  9. meshagent/cli/common_options.py +19 -4
  10. meshagent/cli/containers.py +164 -16
  11. meshagent/cli/database.py +997 -0
  12. meshagent/cli/developer.py +3 -3
  13. meshagent/cli/exec.py +22 -6
  14. meshagent/cli/helper.py +62 -11
  15. meshagent/cli/helpers.py +66 -9
  16. meshagent/cli/host.py +37 -0
  17. meshagent/cli/mailbot.py +1004 -40
  18. meshagent/cli/mailboxes.py +223 -0
  19. meshagent/cli/meeting_transcriber.py +10 -4
  20. meshagent/cli/messaging.py +7 -7
  21. meshagent/cli/multi.py +402 -0
  22. meshagent/cli/oauth2.py +44 -21
  23. meshagent/cli/participant_token.py +5 -3
  24. meshagent/cli/port.py +70 -0
  25. meshagent/cli/queue.py +2 -2
  26. meshagent/cli/room.py +20 -212
  27. meshagent/cli/rooms.py +214 -0
  28. meshagent/cli/services.py +32 -23
  29. meshagent/cli/sessions.py +5 -5
  30. meshagent/cli/storage.py +5 -5
  31. meshagent/cli/task_runner.py +770 -0
  32. meshagent/cli/version.py +1 -1
  33. meshagent/cli/voicebot.py +502 -76
  34. meshagent/cli/webhook.py +7 -7
  35. meshagent/cli/worker.py +1327 -0
  36. {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.21.0.dist-info}/METADATA +13 -13
  37. meshagent_cli-0.21.0.dist-info/RECORD +44 -0
  38. meshagent_cli-0.7.0.dist-info/RECORD +0 -36
  39. {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.21.0.dist-info}/WHEEL +0 -0
  40. {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.21.0.dist-info}/entry_points.txt +0 -0
  41. {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.21.0.dist-info}/top_level.txt +0 -0
meshagent/cli/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.7.0"
1
+ __version__ = "0.21.0"
meshagent/cli/voicebot.py CHANGED
@@ -5,56 +5,200 @@ from meshagent.cli.common_options import ProjectIdOption, RoomOption
5
5
  from meshagent.api import RoomClient, WebSocketClientProtocol, RoomException
6
6
  from meshagent.api.helpers import meshagent_base_url, websocket_room_url
7
7
  from meshagent.cli import async_typer
8
- from meshagent.api import ParticipantToken, ApiScope
8
+ from meshagent.api import ParticipantToken, ApiScope, RemoteParticipant
9
9
  from meshagent.cli.helper import (
10
10
  get_client,
11
11
  resolve_project_id,
12
12
  resolve_room,
13
13
  resolve_key,
14
+ cleanup_args,
14
15
  )
15
16
  from typing import List
16
-
17
17
  from meshagent.api import RequiredToolkit, RequiredSchema
18
- from meshagent.api.services import ServiceHost
19
18
  from pathlib import Path
19
+ from meshagent.agents.config import RulesConfig
20
+ import logging
21
+
22
+ from meshagent.cli.host import get_service, run_services, get_deferred, service_specs
23
+ from meshagent.api.specs.service import AgentSpec, ANNOTATION_AGENT_TYPE
24
+
25
+ import yaml
20
26
 
27
+ import shlex
28
+ import sys
29
+
30
+ from meshagent.api.client import ConflictError
21
31
 
22
32
  app = async_typer.AsyncTyper(help="Join a voicebot to a room")
23
33
 
34
+ logger = logging.getLogger("voicebot")
35
+
36
+
37
+ def build_voicebot(
38
+ *,
39
+ agent_name: str,
40
+ rules: list[str],
41
+ rules_file: Optional[str] = None,
42
+ toolkits: list[str],
43
+ schemas: list[str],
44
+ auto_greet_message: Optional[str] = None,
45
+ auto_greet_prompt: Optional[str] = None,
46
+ room_rules_paths: list[str],
47
+ ):
48
+ requirements = []
49
+
50
+ for t in toolkits:
51
+ requirements.append(RequiredToolkit(name=t))
52
+
53
+ for t in schemas:
54
+ requirements.append(RequiredSchema(name=t))
55
+
56
+ if rules_file is not None:
57
+ try:
58
+ with open(Path(rules_file).resolve(), "r") as f:
59
+ rules.extend(f.read().splitlines())
60
+ except FileNotFoundError:
61
+ print(f"[yellow]rules file not found at {rules_file}[/yellow]")
62
+
63
+ try:
64
+ from meshagent.livekit.agents.voice import VoiceBot
65
+ except ImportError:
66
+
67
+ class VoiceBot:
68
+ def __init__(self, **kwargs):
69
+ raise RoomException(
70
+ "meshagent.livekit module not found, voicebots are not available"
71
+ )
72
+
73
+ class CustomVoiceBot(VoiceBot):
74
+ def __init__(self):
75
+ super().__init__(
76
+ auto_greet_message=auto_greet_message,
77
+ auto_greet_prompt=auto_greet_prompt,
78
+ name=agent_name,
79
+ requires=requirements,
80
+ rules=rules if len(rules) > 0 else None,
81
+ )
82
+
83
+ async def start(self, *, room: RoomClient):
84
+ await super().start(room=room)
85
+
86
+ if room_rules_paths is not None:
87
+ for p in room_rules_paths:
88
+ await self._load_room_rules(path=p)
89
+
90
+ async def _load_room_rules(
91
+ self,
92
+ *,
93
+ path: str,
94
+ participant: Optional[RemoteParticipant] = None,
95
+ ):
96
+ rules = []
97
+ try:
98
+ room_rules = await self.room.storage.download(path=path)
99
+
100
+ rules_txt = room_rules.data.decode()
101
+
102
+ rules_config = RulesConfig.parse(rules_txt)
103
+
104
+ if rules_config.rules is not None:
105
+ rules.extend(rules_config.rules)
106
+
107
+ if participant is not None:
108
+ client = participant.get_attribute("client")
109
+
110
+ if rules_config.client_rules is not None and client is not None:
111
+ cr = rules_config.client_rules.get(client)
112
+ if cr is not None:
113
+ rules.extend(cr)
114
+
115
+ except RoomException:
116
+ try:
117
+ logger.info("attempting to initialize rules file")
118
+ handle = await self.room.storage.open(path=path, overwrite=False)
119
+ await self.room.storage.write(
120
+ handle=handle,
121
+ data="# Add rules to this file to customize your agent's behavior, lines starting with # will be ignored.\n\n".encode(),
122
+ )
123
+ await self.room.storage.close(handle=handle)
124
+
125
+ except RoomException:
126
+ pass
127
+ logger.info(
128
+ f"unable to load rules from {path}, continuing with default rules"
129
+ )
130
+ pass
131
+
132
+ return rules
133
+
134
+ async def get_rules(self, *, participant: RemoteParticipant):
135
+ rules = [*self.rules] if self.rules is not None else []
136
+ if room_rules_paths is not None:
137
+ for p in room_rules_paths:
138
+ rules.extend(
139
+ await self._load_room_rules(participant=participant, path=p)
140
+ )
141
+
142
+ logger.info(f"voicebot using rules {rules}")
143
+
144
+ return rules
145
+
146
+ return CustomVoiceBot
147
+
24
148
 
25
149
  @app.async_command("join")
26
150
  async def make_call(
27
151
  *,
28
- project_id: ProjectIdOption = None,
152
+ project_id: ProjectIdOption,
29
153
  room: RoomOption,
30
154
  agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
31
155
  rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
32
156
  rules_file: Optional[str] = None,
157
+ require_toolkit: Annotated[
158
+ List[str],
159
+ typer.Option(
160
+ "--require-toolkit", "-rt", help="the name or url of a required toolkit"
161
+ ),
162
+ ] = [],
163
+ require_schema: Annotated[
164
+ List[str],
165
+ typer.Option(
166
+ "--require-schema", "-rs", help="the name or url of a required schema"
167
+ ),
168
+ ] = [],
33
169
  toolkit: Annotated[
34
170
  List[str],
35
- typer.Option("--toolkit", "-t", help="the name or url of a required toolkit"),
171
+ typer.Option(
172
+ "--toolkit", "-t", help="the name or url of a required toolkit", hidden=True
173
+ ),
36
174
  ] = [],
37
175
  schema: Annotated[
38
176
  List[str],
39
- typer.Option("--schema", "-s", help="the name or url of a required schema"),
177
+ typer.Option(
178
+ "--schema", "-s", help="the name or url of a required schema", hidden=True
179
+ ),
40
180
  ] = [],
41
- auto_greet_message: Annotated[Optional[str], typer.Option()] = None,
42
- auto_greet_prompt: Annotated[Optional[str], typer.Option()] = None,
181
+ auto_greet_message: Annotated[
182
+ Optional[str],
183
+ typer.Option(help="Message to send automatically when the bot joins"),
184
+ ] = None,
185
+ auto_greet_prompt: Annotated[
186
+ Optional[str],
187
+ typer.Option(help="Prompt to generate an auto-greet message"),
188
+ ] = None,
43
189
  key: Annotated[
44
190
  str,
45
191
  typer.Option("--key", help="an api key to sign the token with"),
46
192
  ] = None,
193
+ room_rules: Annotated[
194
+ List[str],
195
+ typer.Option(
196
+ "--room-rules",
197
+ "-rr",
198
+ help="a path to a rules file within the room that can be used to customize the agent's behavior",
199
+ ),
200
+ ] = [],
47
201
  ):
48
- try:
49
- from meshagent.livekit.agents.voice import VoiceBot
50
- except ImportError:
51
-
52
- class VoiceBot:
53
- def __init__(self, **kwargs):
54
- raise RoomException(
55
- "meshagent.livekit module not found, voicebots are not available"
56
- )
57
-
58
202
  key = await resolve_key(project_id=project_id, key=key)
59
203
 
60
204
  account_client = await get_client()
@@ -71,13 +215,18 @@ async def make_call(
71
215
  token.add_role_grant(role="agent")
72
216
  token.add_room_grant(room)
73
217
 
218
+ CustomVoiceBot = build_voicebot(
219
+ agent_name=agent_name,
220
+ rules=rule,
221
+ rules_file=rules_file,
222
+ toolkits=require_toolkit + toolkit,
223
+ schemas=require_schema + schema,
224
+ auto_greet_message=auto_greet_message,
225
+ auto_greet_prompt=auto_greet_prompt,
226
+ room_rules_paths=room_rules,
227
+ )
228
+
74
229
  jwt = token.to_jwt(api_key=key)
75
- if rules_file is not None:
76
- try:
77
- with open(Path(rules_file).resolve(), "r") as f:
78
- rule.extend(f.read().splitlines())
79
- except FileNotFoundError:
80
- print(f"[yellow]rules file not found at {rules_file}[/yellow]")
81
230
 
82
231
  print("[bold green]Connecting to room...[/bold green]", flush=True)
83
232
  async with RoomClient(
@@ -86,21 +235,7 @@ async def make_call(
86
235
  token=jwt,
87
236
  )
88
237
  ) as client:
89
- requirements = []
90
-
91
- for t in toolkit:
92
- requirements.append(RequiredToolkit(name=t))
93
-
94
- for t in schema:
95
- requirements.append(RequiredSchema(name=t))
96
-
97
- bot = VoiceBot(
98
- auto_greet_message=auto_greet_message,
99
- auto_greet_prompt=auto_greet_prompt,
100
- name=agent_name,
101
- requires=requirements,
102
- rules=rule if len(rule) > 0 else None,
103
- )
238
+ bot = CustomVoiceBot()
104
239
 
105
240
  await bot.start(room=client)
106
241
 
@@ -123,56 +258,347 @@ async def service(
123
258
  agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
124
259
  rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
125
260
  rules_file: Optional[str] = None,
261
+ require_toolkit: Annotated[
262
+ List[str],
263
+ typer.Option(
264
+ "--require-toolkit", "-rt", help="the name or url of a required toolkit"
265
+ ),
266
+ ] = [],
267
+ require_schema: Annotated[
268
+ List[str],
269
+ typer.Option(
270
+ "--require-schema", "-rs", help="the name or url of a required schema"
271
+ ),
272
+ ] = [],
273
+ toolkit: Annotated[
274
+ List[str],
275
+ typer.Option(
276
+ "--toolkit", "-t", help="the name or url of a required toolkit", hidden=True
277
+ ),
278
+ ] = [],
279
+ schema: Annotated[
280
+ List[str],
281
+ typer.Option(
282
+ "--schema", "-s", help="the name or url of a required schema", hidden=True
283
+ ),
284
+ ] = [],
285
+ auto_greet_message: Annotated[
286
+ Optional[str],
287
+ typer.Option(help="Message to send automatically when the bot joins"),
288
+ ] = None,
289
+ auto_greet_prompt: Annotated[
290
+ Optional[str],
291
+ typer.Option(help="Prompt to generate an auto-greet message"),
292
+ ] = None,
293
+ host: Annotated[
294
+ Optional[str], typer.Option(help="Host to bind the service on")
295
+ ] = None,
296
+ port: Annotated[
297
+ Optional[int], typer.Option(help="Port to bind the service on")
298
+ ] = None,
299
+ path: Annotated[
300
+ Optional[str], typer.Option(help="HTTP path to mount the service at")
301
+ ] = None,
302
+ room_rules: Annotated[
303
+ List[str],
304
+ typer.Option(
305
+ "--room-rules",
306
+ "-rr",
307
+ help="a path to a rules file within the room that can be used to customize the agent's behavior",
308
+ ),
309
+ ] = [],
310
+ ):
311
+ CustomVoiceBot = build_voicebot(
312
+ agent_name=agent_name,
313
+ rules=rule,
314
+ rules_file=rules_file,
315
+ toolkits=require_toolkit + toolkit,
316
+ schemas=require_schema + schema,
317
+ auto_greet_message=auto_greet_message,
318
+ auto_greet_prompt=auto_greet_prompt,
319
+ room_rules_paths=room_rules,
320
+ )
321
+
322
+ service = get_service(host=host, port=port)
323
+
324
+ service.agents.append(
325
+ AgentSpec(name=agent_name, annotations={ANNOTATION_AGENT_TYPE: "ChatBot"})
326
+ )
327
+
328
+ if path is None:
329
+ path = "/agent"
330
+ i = 0
331
+ while service.has_path(path):
332
+ i += 1
333
+ path = f"/agent{i}"
334
+
335
+ service.add_path(identity=agent_name, path=path, cls=CustomVoiceBot)
336
+
337
+ if not get_deferred():
338
+ await run_services()
339
+
340
+
341
+ @app.async_command("spec")
342
+ async def spec(
343
+ *,
344
+ service_name: Annotated[str, typer.Option("--service-name", help="service name")],
345
+ service_description: Annotated[
346
+ Optional[str], typer.Option("--service-description", help="service description")
347
+ ] = None,
348
+ service_title: Annotated[
349
+ Optional[str],
350
+ typer.Option("--service-title", help="a display name for the service"),
351
+ ] = None,
352
+ agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
353
+ rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
354
+ rules_file: Optional[str] = None,
355
+ require_toolkit: Annotated[
356
+ List[str],
357
+ typer.Option(
358
+ "--require-toolkit", "-rt", help="the name or url of a required toolkit"
359
+ ),
360
+ ] = [],
361
+ require_schema: Annotated[
362
+ List[str],
363
+ typer.Option(
364
+ "--require-schema", "-rs", help="the name or url of a required schema"
365
+ ),
366
+ ] = [],
126
367
  toolkit: Annotated[
127
368
  List[str],
128
- typer.Option("--toolkit", "-t", help="the name or url of a required toolkit"),
369
+ typer.Option(
370
+ "--toolkit", "-t", help="the name or url of a required toolkit", hidden=True
371
+ ),
129
372
  ] = [],
130
373
  schema: Annotated[
131
374
  List[str],
132
- typer.Option("--schema", "-s", help="the name or url of a required schema"),
375
+ typer.Option(
376
+ "--schema", "-s", help="the name or url of a required schema", hidden=True
377
+ ),
378
+ ] = [],
379
+ auto_greet_message: Annotated[
380
+ Optional[str],
381
+ typer.Option(help="Message to send automatically when the bot joins"),
382
+ ] = None,
383
+ auto_greet_prompt: Annotated[
384
+ Optional[str],
385
+ typer.Option(help="Prompt to generate an auto-greet message"),
386
+ ] = None,
387
+ host: Annotated[
388
+ Optional[str], typer.Option(help="Host to bind the service on")
389
+ ] = None,
390
+ port: Annotated[
391
+ Optional[int], typer.Option(help="Port to bind the service on")
392
+ ] = None,
393
+ path: Annotated[
394
+ Optional[str], typer.Option(help="HTTP path to mount the service at")
395
+ ] = None,
396
+ room_rules: Annotated[
397
+ List[str],
398
+ typer.Option(
399
+ "--room-rules",
400
+ "-rr",
401
+ help="a path to a rules file within the room that can be used to customize the agent's behavior",
402
+ ),
133
403
  ] = [],
134
- auto_greet_message: Annotated[Optional[str], typer.Option()] = None,
135
- auto_greet_prompt: Annotated[Optional[str], typer.Option()] = None,
136
- host: Annotated[Optional[str], typer.Option()] = None,
137
- port: Annotated[Optional[int], typer.Option()] = None,
138
- path: Annotated[str, typer.Option()] = "/agent",
139
404
  ):
140
- try:
141
- from meshagent.livekit.agents.voice import VoiceBot
142
- except ImportError:
405
+ CustomVoiceBot = build_voicebot(
406
+ agent_name=agent_name,
407
+ rules=rule,
408
+ rules_file=rules_file,
409
+ toolkits=require_toolkit + toolkit,
410
+ schemas=require_schema + schema,
411
+ auto_greet_message=auto_greet_message,
412
+ auto_greet_prompt=auto_greet_prompt,
413
+ room_rules_paths=room_rules,
414
+ )
143
415
 
144
- class VoiceBot:
145
- def __init__(self, **kwargs):
146
- raise RoomException(
147
- "meshagent.livekit module not found, voicebots are not available"
148
- )
416
+ service = get_service(host=host, port=port)
149
417
 
150
- requirements = []
418
+ service.agents.append(
419
+ AgentSpec(name=agent_name, annotations={ANNOTATION_AGENT_TYPE: "ChatBot"})
420
+ )
151
421
 
152
- for t in toolkit:
153
- requirements.append(RequiredToolkit(name=t))
422
+ if path is None:
423
+ path = "/agent"
424
+ i = 0
425
+ while service.has_path(path):
426
+ i += 1
427
+ path = f"/agent{i}"
154
428
 
155
- for t in schema:
156
- requirements.append(RequiredSchema(name=t))
429
+ service.add_path(identity=agent_name, path=path, cls=CustomVoiceBot)
157
430
 
158
- if rules_file is not None:
431
+ spec = service_specs()[0]
432
+ spec.metadata.annotations = {
433
+ "meshagent.service.id": service_name,
434
+ }
435
+ spec.metadata.name = service_name
436
+ spec.metadata.description = service_description
437
+ spec.container.image = (
438
+ "us-central1-docker.pkg.dev/meshagent-public/images/cli:{SERVER_VERSION}-esgz"
439
+ )
440
+ spec.container.command = shlex.join(
441
+ ["meshagent", "voicebot", "service", *cleanup_args(sys.argv[2:])]
442
+ )
443
+
444
+ print(yaml.dump(spec.model_dump(mode="json", exclude_none=True), sort_keys=False))
445
+
446
+
447
+ @app.async_command("deploy")
448
+ async def deploy(
449
+ *,
450
+ service_name: Annotated[str, typer.Option("--service-name", help="service name")],
451
+ service_description: Annotated[
452
+ Optional[str], typer.Option("--service-description", help="service description")
453
+ ] = None,
454
+ service_title: Annotated[
455
+ Optional[str],
456
+ typer.Option("--service-title", help="a display name for the service"),
457
+ ] = None,
458
+ agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
459
+ rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
460
+ rules_file: Optional[str] = None,
461
+ require_toolkit: Annotated[
462
+ List[str],
463
+ typer.Option(
464
+ "--require-toolkit", "-rt", help="the name or url of a required toolkit"
465
+ ),
466
+ ] = [],
467
+ require_schema: Annotated[
468
+ List[str],
469
+ typer.Option(
470
+ "--require-schema", "-rs", help="the name or url of a required schema"
471
+ ),
472
+ ] = [],
473
+ toolkit: Annotated[
474
+ List[str],
475
+ typer.Option(
476
+ "--toolkit", "-t", help="the name or url of a required toolkit", hidden=True
477
+ ),
478
+ ] = [],
479
+ schema: Annotated[
480
+ List[str],
481
+ typer.Option(
482
+ "--schema", "-s", help="the name or url of a required schema", hidden=True
483
+ ),
484
+ ] = [],
485
+ auto_greet_message: Annotated[
486
+ Optional[str],
487
+ typer.Option(help="Message to send automatically when the bot joins"),
488
+ ] = None,
489
+ auto_greet_prompt: Annotated[
490
+ Optional[str],
491
+ typer.Option(help="Prompt to generate an auto-greet message"),
492
+ ] = None,
493
+ host: Annotated[
494
+ Optional[str], typer.Option(help="Host to bind the service on")
495
+ ] = None,
496
+ port: Annotated[
497
+ Optional[int], typer.Option(help="Port to bind the service on")
498
+ ] = None,
499
+ path: Annotated[
500
+ Optional[str], typer.Option(help="HTTP path to mount the service at")
501
+ ] = None,
502
+ room_rules: Annotated[
503
+ List[str],
504
+ typer.Option(
505
+ "--room-rules",
506
+ "-rr",
507
+ help="a path to a rules file within the room that can be used to customize the agent's behavior",
508
+ ),
509
+ ] = [],
510
+ project_id: ProjectIdOption,
511
+ room: Annotated[
512
+ Optional[str],
513
+ typer.Option("--room", help="The name of a room to create the service for"),
514
+ ] = None,
515
+ ):
516
+ project_id = await resolve_project_id(project_id=project_id)
517
+
518
+ CustomVoiceBot = build_voicebot(
519
+ agent_name=agent_name,
520
+ rules=rule,
521
+ rules_file=rules_file,
522
+ toolkits=require_toolkit + toolkit,
523
+ schemas=require_schema + schema,
524
+ auto_greet_message=auto_greet_message,
525
+ auto_greet_prompt=auto_greet_prompt,
526
+ room_rules_paths=room_rules,
527
+ )
528
+
529
+ service = get_service(host=host, port=port)
530
+
531
+ service.agents.append(
532
+ AgentSpec(name=agent_name, annotations={ANNOTATION_AGENT_TYPE: "ChatBot"})
533
+ )
534
+
535
+ if path is None:
536
+ path = "/agent"
537
+ i = 0
538
+ while service.has_path(path):
539
+ i += 1
540
+ path = f"/agent{i}"
541
+
542
+ service.add_path(identity=agent_name, path=path, cls=CustomVoiceBot)
543
+
544
+ spec = service_specs()[0]
545
+ spec.metadata.annotations = {
546
+ "meshagent.service.id": service_name,
547
+ }
548
+ spec.metadata.name = service_name
549
+ spec.metadata.description = service_description
550
+ spec.container.image = (
551
+ "us-central1-docker.pkg.dev/meshagent-public/images/cli:{SERVER_VERSION}-esgz"
552
+ )
553
+ spec.container.command = shlex.join(
554
+ ["meshagent", "voicebot", "service", *cleanup_args(sys.argv[2:])]
555
+ )
556
+
557
+ client = await get_client()
558
+ try:
559
+ id = None
159
560
  try:
160
- with open(Path(rules_file).resolve(), "r") as f:
161
- rule.extend(f.read().splitlines())
162
- except FileNotFoundError:
163
- print(f"[yellow]rules file not found at {rules_file}[/yellow]")
561
+ if id is None:
562
+ if room is None:
563
+ services = await client.list_services(project_id=project_id)
564
+ else:
565
+ services = await client.list_room_services(
566
+ project_id=project_id, room_name=room
567
+ )
164
568
 
165
- service = ServiceHost(host=host, port=port)
569
+ for s in services:
570
+ if s.metadata.name == spec.metadata.name:
571
+ id = s.id
166
572
 
167
- @service.path(path=path)
168
- class CustomVoiceBot(VoiceBot):
169
- def __init__(self):
170
- super().__init__(
171
- auto_greet_message=auto_greet_message,
172
- auto_greet_prompt=auto_greet_prompt,
173
- name=agent_name,
174
- requires=requirements,
175
- rules=rule if len(rule) > 0 else None,
176
- )
573
+ if id is None:
574
+ if room is None:
575
+ id = await client.create_service(
576
+ project_id=project_id, service=spec
577
+ )
578
+ else:
579
+ id = await client.create_room_service(
580
+ project_id=project_id, service=spec, room_name=room
581
+ )
582
+
583
+ else:
584
+ spec.id = id
585
+ if room is None:
586
+ await client.update_service(
587
+ project_id=project_id, service_id=id, service=spec
588
+ )
589
+ else:
590
+ await client.update_room_service(
591
+ project_id=project_id,
592
+ service_id=id,
593
+ service=spec,
594
+ room_name=room,
595
+ )
177
596
 
178
- await service.run()
597
+ except ConflictError:
598
+ print(f"[red]Service name already in use: {spec.metadata.name}[/red]")
599
+ raise typer.Exit(code=1)
600
+ else:
601
+ print(f"[green]Deployed service:[/] {id}")
602
+
603
+ finally:
604
+ await client.close()
meshagent/cli/webhook.py CHANGED
@@ -7,17 +7,17 @@ from meshagent.cli.common_options import ProjectIdOption
7
7
  from meshagent.cli import async_typer
8
8
  from meshagent.cli.helper import get_client, print_json_table, resolve_project_id
9
9
 
10
- app = async_typer.AsyncTyper()
10
+ app = async_typer.AsyncTyper(help="Manage project webhooks")
11
11
 
12
12
  # ---------------------------------------------------------------------------
13
13
  # Webhook commands
14
14
  # ---------------------------------------------------------------------------
15
15
 
16
16
 
17
- @app.async_command("create")
17
+ @app.async_command("create", help="Create a webhook")
18
18
  async def webhook_create(
19
19
  *,
20
- project_id: ProjectIdOption = None,
20
+ project_id: ProjectIdOption,
21
21
  name: Annotated[str, typer.Option(help="Friendly name for the webhook")],
22
22
  url: Annotated[str, typer.Option(help="Target URL that will receive POSTs")],
23
23
  event: Annotated[
@@ -61,10 +61,10 @@ async def webhook_create(
61
61
  await client.close()
62
62
 
63
63
 
64
- @app.async_command("list")
64
+ @app.async_command("list", help="List webhooks")
65
65
  async def webhook_list(
66
66
  *,
67
- project_id: ProjectIdOption = None,
67
+ project_id: ProjectIdOption,
68
68
  ):
69
69
  """List all webhooks for the project."""
70
70
  client = await get_client()
@@ -84,10 +84,10 @@ async def webhook_list(
84
84
  await client.close()
85
85
 
86
86
 
87
- @app.async_command("delete")
87
+ @app.async_command("delete", help="Delete a webhook")
88
88
  async def webhook_delete(
89
89
  *,
90
- project_id: ProjectIdOption = None,
90
+ project_id: ProjectIdOption,
91
91
  webhook_id: Annotated[str, typer.Argument(help="ID of the webhook to delete")],
92
92
  ):
93
93
  """Delete a project webhook."""