meshagent-cli 0.7.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.
Files changed (42) hide show
  1. meshagent/cli/agent.py +23 -13
  2. meshagent/cli/api_keys.py +4 -4
  3. meshagent/cli/async_typer.py +52 -4
  4. meshagent/cli/call.py +27 -36
  5. meshagent/cli/chatbot.py +1559 -177
  6. meshagent/cli/cli.py +23 -22
  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 +101 -12
  15. meshagent/cli/helpers.py +65 -11
  16. meshagent/cli/host.py +41 -0
  17. meshagent/cli/mailbot.py +1104 -79
  18. meshagent/cli/mailboxes.py +223 -0
  19. meshagent/cli/meeting_transcriber.py +29 -15
  20. meshagent/cli/messaging.py +7 -10
  21. meshagent/cli/multi.py +357 -0
  22. meshagent/cli/oauth2.py +192 -40
  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 +24 -212
  27. meshagent/cli/rooms.py +214 -0
  28. meshagent/cli/services.py +269 -37
  29. meshagent/cli/sessions.py +5 -5
  30. meshagent/cli/storage.py +5 -5
  31. meshagent/cli/sync.py +434 -0
  32. meshagent/cli/task_runner.py +1317 -0
  33. meshagent/cli/version.py +1 -1
  34. meshagent/cli/voicebot.py +544 -98
  35. meshagent/cli/webhook.py +7 -7
  36. meshagent/cli/worker.py +1403 -0
  37. {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.23.0.dist-info}/METADATA +15 -13
  38. meshagent_cli-0.23.0.dist-info/RECORD +45 -0
  39. {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.23.0.dist-info}/WHEEL +1 -1
  40. meshagent_cli-0.7.0.dist-info/RECORD +0 -36
  41. {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.23.0.dist-info}/entry_points.txt +0 -0
  42. {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.23.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,223 @@
1
+ # meshagent/cli/mailboxes.py
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated, Optional
6
+
7
+ import typer
8
+ from aiohttp import ClientResponseError
9
+ from rich import print
10
+
11
+ from meshagent.cli import async_typer
12
+ from meshagent.cli.common_options import ProjectIdOption, OutputFormatOption
13
+ from meshagent.cli.helper import get_client, print_json_table, resolve_project_id
14
+
15
+ app = async_typer.AsyncTyper(help="Manage mailboxes for your project")
16
+
17
+
18
+ @app.async_command("create")
19
+ async def mailbox_create(
20
+ *,
21
+ project_id: ProjectIdOption,
22
+ address: Annotated[
23
+ str,
24
+ typer.Option(
25
+ "--address",
26
+ "-a",
27
+ help="Mailbox email address (unique per project)",
28
+ ),
29
+ ],
30
+ room: Annotated[
31
+ str,
32
+ typer.Option(
33
+ "--room",
34
+ "-r",
35
+ help="Room name to route inbound mail into",
36
+ ),
37
+ ],
38
+ queue: Annotated[
39
+ str,
40
+ typer.Option(
41
+ "--queue",
42
+ "-q",
43
+ help="Queue name to deliver inbound messages to",
44
+ ),
45
+ ],
46
+ public: Annotated[
47
+ bool,
48
+ typer.Option(
49
+ "--public",
50
+ help="Queue name to deliver inbound messages to",
51
+ ),
52
+ ] = False,
53
+ ):
54
+ """Create a mailbox attached to the project."""
55
+ client = await get_client()
56
+ try:
57
+ project_id = await resolve_project_id(project_id)
58
+
59
+ try:
60
+ await client.create_mailbox(
61
+ project_id=project_id,
62
+ address=address,
63
+ room=room,
64
+ queue=queue,
65
+ public=public,
66
+ )
67
+ except ClientResponseError as exc:
68
+ # Common patterns: 409 conflict on duplicate address, 400 validation, etc.
69
+ if exc.status == 409:
70
+ print(f"[red]Mailbox address already in use:[/] {address}")
71
+ raise typer.Exit(code=1)
72
+ raise
73
+ else:
74
+ print(f"[green]Created mailbox:[/] {address}")
75
+ finally:
76
+ await client.close()
77
+
78
+
79
+ @app.async_command("update")
80
+ async def mailbox_update(
81
+ *,
82
+ project_id: ProjectIdOption,
83
+ address: Annotated[
84
+ str,
85
+ typer.Argument(help="Mailbox email address to update"),
86
+ ],
87
+ room: Annotated[
88
+ Optional[str],
89
+ typer.Option(
90
+ "--room",
91
+ "-r",
92
+ help="Room name to route inbound mail into",
93
+ ),
94
+ ] = None,
95
+ queue: Annotated[
96
+ Optional[str],
97
+ typer.Option(
98
+ "--queue",
99
+ "-q",
100
+ help="Queue name to deliver inbound messages to",
101
+ ),
102
+ ] = None,
103
+ public: Annotated[
104
+ bool,
105
+ typer.Option(
106
+ "--public",
107
+ help="Queue name to deliver inbound messages to",
108
+ ),
109
+ ] = False,
110
+ ):
111
+ """Update a mailbox routing configuration."""
112
+ client = await get_client()
113
+ try:
114
+ project_id = await resolve_project_id(project_id)
115
+
116
+ # Keep parity with other CLIs: allow partial update by reading existing first
117
+ if room is None or queue is None:
118
+ try:
119
+ mb = await client.get_mailbox(project_id=project_id, address=address)
120
+ except ClientResponseError as exc:
121
+ if exc.status == 404:
122
+ print(f"[red]Mailbox not found:[/] {address}")
123
+ raise typer.Exit(code=1)
124
+ raise
125
+ room = room or mb.room
126
+ queue = queue or mb.queue
127
+
128
+ try:
129
+ await client.update_mailbox(
130
+ project_id=project_id,
131
+ address=address,
132
+ room=room,
133
+ queue=queue,
134
+ public=public,
135
+ )
136
+ except ClientResponseError as exc:
137
+ if exc.status == 404:
138
+ print(f"[red]Mailbox not found:[/] {address}")
139
+ raise typer.Exit(code=1)
140
+ raise
141
+ else:
142
+ print(f"[green]Updated mailbox:[/] {address}")
143
+ finally:
144
+ await client.close()
145
+
146
+
147
+ @app.async_command("show")
148
+ async def mailbox_show(
149
+ *,
150
+ project_id: ProjectIdOption,
151
+ address: Annotated[str, typer.Argument(help="Mailbox address to show")],
152
+ ):
153
+ """Show mailbox details."""
154
+ client = await get_client()
155
+ try:
156
+ project_id = await resolve_project_id(project_id)
157
+ try:
158
+ mb = await client.get_mailbox(project_id=project_id, address=address)
159
+ except ClientResponseError as exc:
160
+ if exc.status == 404:
161
+ print(f"[red]Mailbox not found:[/] {address}")
162
+ raise typer.Exit(code=1)
163
+ raise
164
+ print(mb.model_dump(mode="json"))
165
+ finally:
166
+ await client.close()
167
+
168
+
169
+ @app.async_command("list")
170
+ async def mailbox_list(
171
+ *,
172
+ project_id: ProjectIdOption,
173
+ o: OutputFormatOption = "table",
174
+ ):
175
+ """List mailboxes for the project."""
176
+ client = await get_client()
177
+ try:
178
+ project_id = await resolve_project_id(project_id)
179
+ mailboxes = await client.list_mailboxes(project_id=project_id)
180
+
181
+ if o == "json":
182
+ # Keep your existing conventions: wrap in an object.
183
+ print({"mailboxes": [mb.model_dump(mode="json") for mb in mailboxes]})
184
+ else:
185
+ print_json_table(
186
+ [
187
+ {
188
+ "address": mb.address,
189
+ "room": mb.room,
190
+ "queue": mb.queue,
191
+ "public": mb.public,
192
+ }
193
+ for mb in mailboxes
194
+ ],
195
+ "address",
196
+ "room",
197
+ "queue",
198
+ )
199
+ finally:
200
+ await client.close()
201
+
202
+
203
+ @app.async_command("delete")
204
+ async def mailbox_delete(
205
+ *,
206
+ project_id: ProjectIdOption,
207
+ address: Annotated[str, typer.Argument(help="Mailbox address to delete")],
208
+ ):
209
+ """Delete a mailbox."""
210
+ client = await get_client()
211
+ try:
212
+ project_id = await resolve_project_id(project_id)
213
+ try:
214
+ await client.delete_mailbox(project_id=project_id, address=address)
215
+ except ClientResponseError as exc:
216
+ if exc.status == 404:
217
+ print(f"[red]Mailbox not found:[/] {address}")
218
+ raise typer.Exit(code=1)
219
+ raise
220
+ else:
221
+ print(f"[green]Mailbox deleted:[/] {address}")
222
+ finally:
223
+ await client.close()
@@ -12,7 +12,7 @@ from meshagent.cli.helper import (
12
12
  resolve_room,
13
13
  resolve_key,
14
14
  )
15
-
15
+ import os
16
16
  from meshagent.api import RequiredSchema
17
17
  from meshagent.api.services import ServiceHost
18
18
 
@@ -22,9 +22,11 @@ app = async_typer.AsyncTyper(help="Join a meeting transcriber to a room")
22
22
  @app.async_command("join")
23
23
  async def join(
24
24
  *,
25
- project_id: ProjectIdOption = None,
25
+ project_id: ProjectIdOption,
26
26
  room: RoomOption,
27
- agent_name: Annotated[str, typer.Option(..., help="Name of the agent")],
27
+ agent_name: Annotated[
28
+ Optional[str], typer.Option(..., help="Name of the agent")
29
+ ] = None,
28
30
  key: Annotated[
29
31
  str,
30
32
  typer.Option("--key", help="an api key to sign the token with"),
@@ -47,16 +49,24 @@ async def join(
47
49
  project_id = await resolve_project_id(project_id=project_id)
48
50
  room = resolve_room(room)
49
51
 
50
- token = ParticipantToken(
51
- name=agent_name,
52
- )
52
+ jwt = os.getenv("MESHAGENT_TOKEN")
53
+ if jwt is None:
54
+ if agent_name is None:
55
+ print(
56
+ "[bold red]--agent-name must be specified when the MESHAGENT_TOKEN environment variable is not set[/bold red]"
57
+ )
58
+ raise typer.Exit(1)
59
+
60
+ token = ParticipantToken(
61
+ name=agent_name,
62
+ )
53
63
 
54
- token.add_api_grant(ApiScope.agent_default())
64
+ token.add_api_grant(ApiScope.agent_default())
55
65
 
56
- token.add_role_grant(role="agent")
57
- token.add_room_grant(room)
66
+ token.add_role_grant(role="agent")
67
+ token.add_room_grant(room)
58
68
 
59
- jwt = token.to_jwt(api_key=key)
69
+ jwt = token.to_jwt(api_key=key)
60
70
 
61
71
  print("[bold green]Connecting to room...[/bold green]", flush=True)
62
72
  async with RoomClient(
@@ -70,7 +80,6 @@ async def join(
70
80
  requirements.append(RequiredSchema(name="transcript"))
71
81
 
72
82
  bot = MeetingTranscriber(
73
- name=agent_name,
74
83
  requires=requirements,
75
84
  )
76
85
 
@@ -93,9 +102,15 @@ async def join(
93
102
  async def service(
94
103
  *,
95
104
  agent_name: Annotated[str, typer.Option(..., help="Name of the agent")],
96
- host: Annotated[Optional[str], typer.Option()] = None,
97
- port: Annotated[Optional[int], typer.Option()] = None,
98
- path: Annotated[str, typer.Option()] = "/agent",
105
+ host: Annotated[
106
+ Optional[str], typer.Option(help="Host to bind the service on")
107
+ ] = None,
108
+ port: Annotated[
109
+ Optional[int], typer.Option(help="Port to bind the service on")
110
+ ] = None,
111
+ path: Annotated[
112
+ str, typer.Option(help="HTTP path to mount the service at")
113
+ ] = "/agent",
99
114
  ):
100
115
  try:
101
116
  from meshagent.livekit.agents.meeting_transcriber import MeetingTranscriber
@@ -117,7 +132,6 @@ async def service(
117
132
  class CustomMeetingTranscriber(MeetingTranscriber):
118
133
  def __init__(self):
119
134
  super().__init__(
120
- name=agent_name,
121
135
  requires=requirements,
122
136
  )
123
137
 
@@ -16,13 +16,13 @@ from meshagent.cli.helper import (
16
16
  resolve_room,
17
17
  )
18
18
 
19
- app = async_typer.AsyncTyper()
19
+ app = async_typer.AsyncTyper(help="Send and receive messages in a room")
20
20
 
21
21
 
22
- @app.async_command("list-participants")
22
+ @app.async_command("list", help="List messaging-enabled participants")
23
23
  async def messaging_list_participants_command(
24
24
  *,
25
- project_id: ProjectIdOption = None,
25
+ project_id: ProjectIdOption,
26
26
  room: RoomOption,
27
27
  ):
28
28
  """
@@ -46,7 +46,6 @@ async def messaging_list_participants_command(
46
46
  ) as client:
47
47
  # Must enable before we can see who else is enabled
48
48
  await client.messaging.enable()
49
- await client.messaging.start()
50
49
 
51
50
  participants = client.messaging.get_participants()
52
51
  output = []
@@ -61,10 +60,10 @@ async def messaging_list_participants_command(
61
60
  await account_client.close()
62
61
 
63
62
 
64
- @app.async_command("send")
63
+ @app.async_command("send", help="Send a direct message to a participant")
65
64
  async def messaging_send_command(
66
65
  *,
67
- project_id: ProjectIdOption = None,
66
+ project_id: ProjectIdOption,
68
67
  room: RoomOption,
69
68
  to_participant_id: Annotated[
70
69
  str, typer.Option(..., help="Participant ID to send a message to")
@@ -92,7 +91,6 @@ async def messaging_send_command(
92
91
  ) as client:
93
92
  # Create and enable messaging
94
93
  await client.messaging.enable()
95
- await client.messaging.start()
96
94
 
97
95
  # Find the participant we want to message
98
96
  participant = None
@@ -120,10 +118,10 @@ async def messaging_send_command(
120
118
  await account_client.close()
121
119
 
122
120
 
123
- @app.async_command("broadcast")
121
+ @app.async_command("broadcast", help="Broadcast a message to all participants")
124
122
  async def messaging_broadcast_command(
125
123
  *,
126
- project_id: ProjectIdOption = None,
124
+ project_id: ProjectIdOption,
127
125
  room: RoomOption,
128
126
  data: Annotated[str, typer.Option(..., help="JSON message to broadcast")],
129
127
  ):
@@ -147,7 +145,6 @@ async def messaging_broadcast_command(
147
145
  ) as client:
148
146
  # Create and enable messaging
149
147
  await client.messaging.enable()
150
- await client.messaging.start()
151
148
 
152
149
  # Broadcast the message
153
150
  await client.messaging.broadcast_message(