meshagent-cli 0.22.2__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-cli might be problematic. Click here for more details.

Files changed (45) hide show
  1. meshagent/cli/__init__.py +3 -0
  2. meshagent/cli/agent.py +273 -0
  3. meshagent/cli/api_keys.py +102 -0
  4. meshagent/cli/async_typer.py +79 -0
  5. meshagent/cli/auth.py +30 -0
  6. meshagent/cli/auth_async.py +295 -0
  7. meshagent/cli/call.py +215 -0
  8. meshagent/cli/chatbot.py +1983 -0
  9. meshagent/cli/cli.py +187 -0
  10. meshagent/cli/cli_mcp.py +408 -0
  11. meshagent/cli/cli_secrets.py +414 -0
  12. meshagent/cli/common_options.py +47 -0
  13. meshagent/cli/containers.py +725 -0
  14. meshagent/cli/database.py +997 -0
  15. meshagent/cli/developer.py +70 -0
  16. meshagent/cli/exec.py +397 -0
  17. meshagent/cli/helper.py +236 -0
  18. meshagent/cli/helpers.py +185 -0
  19. meshagent/cli/host.py +41 -0
  20. meshagent/cli/mailbot.py +1295 -0
  21. meshagent/cli/mailboxes.py +223 -0
  22. meshagent/cli/meeting_transcriber.py +138 -0
  23. meshagent/cli/messaging.py +157 -0
  24. meshagent/cli/multi.py +357 -0
  25. meshagent/cli/oauth2.py +341 -0
  26. meshagent/cli/participant_token.py +63 -0
  27. meshagent/cli/port.py +70 -0
  28. meshagent/cli/projects.py +105 -0
  29. meshagent/cli/queue.py +91 -0
  30. meshagent/cli/room.py +26 -0
  31. meshagent/cli/rooms.py +214 -0
  32. meshagent/cli/services.py +722 -0
  33. meshagent/cli/sessions.py +26 -0
  34. meshagent/cli/storage.py +813 -0
  35. meshagent/cli/sync.py +434 -0
  36. meshagent/cli/task_runner.py +1317 -0
  37. meshagent/cli/version.py +1 -0
  38. meshagent/cli/voicebot.py +624 -0
  39. meshagent/cli/webhook.py +100 -0
  40. meshagent/cli/worker.py +1403 -0
  41. meshagent_cli-0.22.2.dist-info/METADATA +49 -0
  42. meshagent_cli-0.22.2.dist-info/RECORD +45 -0
  43. meshagent_cli-0.22.2.dist-info/WHEEL +5 -0
  44. meshagent_cli-0.22.2.dist-info/entry_points.txt +2 -0
  45. meshagent_cli-0.22.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,3 @@
1
+ from .version import __version__
2
+
3
+ __all__ = [__version__]
meshagent/cli/agent.py ADDED
@@ -0,0 +1,273 @@
1
+ import typer
2
+ from rich import print
3
+ from typing import Annotated, Optional
4
+ from meshagent.cli.common_options import ProjectIdOption, RoomOption
5
+ import json
6
+ import asyncio
7
+
8
+ from meshagent.api.helpers import meshagent_base_url, websocket_room_url
9
+ from meshagent.api import (
10
+ RoomClient,
11
+ WebSocketClientProtocol,
12
+ RoomException,
13
+ TextResponse,
14
+ JsonResponse,
15
+ )
16
+ from meshagent.cli.helper import resolve_project_id
17
+ from meshagent.cli import async_typer
18
+ from meshagent.cli.helper import get_client, resolve_room
19
+
20
+ app = async_typer.AsyncTyper(help="Interact with agents and toolkits in a room")
21
+
22
+
23
+ @app.async_command("ask", help="Send a request to an agent")
24
+ async def ask(
25
+ *,
26
+ project_id: ProjectIdOption,
27
+ room: RoomOption,
28
+ agent: Annotated[str, typer.Option(..., help="Agent name to ask")],
29
+ input: Annotated[str, typer.Option(..., help="JSON string with tool arguments")],
30
+ timeout: Annotated[
31
+ Optional[int],
32
+ typer.Option(
33
+ ..., help="How long to wait for the agent if the agent is not in the room"
34
+ ),
35
+ ] = 30,
36
+ ):
37
+ """Wait for an agent to join, then send it an ask request."""
38
+
39
+ account_client = await get_client()
40
+ try:
41
+ project_id = await resolve_project_id(project_id=project_id)
42
+ room = resolve_room(room)
43
+
44
+ connection = await account_client.connect_room(project_id=project_id, room=room)
45
+
46
+ print("[bold green]Connecting to room...[/bold green]")
47
+ async with RoomClient(
48
+ protocol=WebSocketClientProtocol(
49
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
50
+ token=connection.jwt,
51
+ )
52
+ ) as client:
53
+ found = timeout == 0
54
+ for i in range(30):
55
+ if found:
56
+ break
57
+
58
+ if i == 1:
59
+ print("[magenta]Waiting for agent...[/magenta]")
60
+
61
+ agents = await client.agents.list_agents()
62
+ await asyncio.sleep(1)
63
+
64
+ for a in agents:
65
+ if a.name == agent:
66
+ found = True
67
+ break
68
+
69
+ if not found:
70
+ print("[red]Timed out waiting for agent to join the room[/red]")
71
+ raise typer.Exit(1)
72
+
73
+ print("[magenta]Asking agent...[/magenta]")
74
+
75
+ response = await client.agents.ask(agent=agent, arguments=json.loads(input))
76
+ if isinstance(response, TextResponse):
77
+ print(response.text)
78
+ elif isinstance(response, JsonResponse):
79
+ print(json.dumps(response.json))
80
+ else:
81
+ print(response)
82
+ except RoomException as e:
83
+ print(f"[red]{e}[/red]")
84
+ finally:
85
+ await account_client.close()
86
+
87
+
88
+ @app.async_command("invoke-tool", help="Invoke a specific tool from a toolkit")
89
+ async def invoke_tool(
90
+ *,
91
+ project_id: ProjectIdOption,
92
+ room: RoomOption,
93
+ toolkit: Annotated[str, typer.Option(..., help="Toolkit name")],
94
+ tool: Annotated[str, typer.Option(..., help="Tool name")],
95
+ arguments: Annotated[
96
+ str, typer.Option(..., help="JSON string with arguments for the tool")
97
+ ],
98
+ participant_id: Annotated[
99
+ Optional[str],
100
+ typer.Option(..., help="Optional participant ID to invoke the tool on"),
101
+ ] = None,
102
+ on_behalf_of_id: Annotated[
103
+ Optional[str], typer.Option(..., help="Optional 'on_behalf_of' participant ID")
104
+ ] = None,
105
+ caller_context: Annotated[
106
+ Optional[str], typer.Option(..., help="Optional JSON for caller context")
107
+ ] = None,
108
+ timeout: Annotated[
109
+ Optional[int],
110
+ typer.Option(
111
+ ...,
112
+ help="How long to wait for the toolkit if the toolkit is not in the room",
113
+ ),
114
+ ] = 30,
115
+ ):
116
+ """
117
+ Invoke a specific tool from a given toolkit with arguments.
118
+ """
119
+ account_client = await get_client()
120
+ try:
121
+ project_id = await resolve_project_id(project_id=project_id)
122
+ room = resolve_room(room)
123
+
124
+ connection = await account_client.connect_room(project_id=project_id, room=room)
125
+
126
+ print("[bold green]Connecting to room...[/bold green]")
127
+ async with RoomClient(
128
+ protocol=WebSocketClientProtocol(
129
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
130
+ token=connection.jwt,
131
+ )
132
+ ) as client:
133
+ found = timeout == 0
134
+ for i in range(timeout):
135
+ if found:
136
+ break
137
+
138
+ if i == 1:
139
+ print("[magenta]Waiting for toolkit...[/magenta]")
140
+
141
+ agents = await client.agents.list_toolkits(
142
+ participant_id=participant_id
143
+ )
144
+ await asyncio.sleep(1)
145
+
146
+ for a in agents:
147
+ if a.name == toolkit:
148
+ found = True
149
+ break
150
+
151
+ if not found:
152
+ print("[red]Timed out waiting for toolkit to join the room[/red]")
153
+ raise typer.Exit(1)
154
+
155
+ print("[bold green]Invoking tool...[/bold green]")
156
+ parsed_context = json.loads(caller_context) if caller_context else None
157
+ response = await client.agents.invoke_tool(
158
+ toolkit=toolkit,
159
+ tool=tool,
160
+ arguments=json.loads(arguments),
161
+ participant_id=participant_id,
162
+ on_behalf_of_id=on_behalf_of_id,
163
+ caller_context=parsed_context,
164
+ )
165
+ # The response is presumably a dictionary or similar
166
+ print(response.to_json())
167
+ except RoomException as e:
168
+ print(f"[red]{e}[/red]")
169
+ finally:
170
+ await account_client.close()
171
+
172
+
173
+ @app.async_command("list-agents", help="List agents currently in the room")
174
+ async def list_agents_command(
175
+ *,
176
+ project_id: ProjectIdOption,
177
+ room: RoomOption,
178
+ ):
179
+ """
180
+ List all agents available in the room.
181
+ """
182
+ account_client = await get_client()
183
+ try:
184
+ project_id = await resolve_project_id(project_id=project_id)
185
+ room = resolve_room(room)
186
+
187
+ connection = await account_client.connect_room(project_id=project_id, room=room)
188
+
189
+ print("[bold green]Connecting to room...[/bold green]")
190
+ async with RoomClient(
191
+ protocol=WebSocketClientProtocol(
192
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
193
+ token=connection.jwt,
194
+ )
195
+ ) as client:
196
+ print("[bold green]Fetching list of agents...[/bold green]")
197
+ agents = await client.agents.list_agents()
198
+ # Format the output as JSON
199
+ output = []
200
+ for agent in agents:
201
+ output.append(
202
+ {
203
+ "name": agent.name,
204
+ "title": agent.title,
205
+ "description": agent.description,
206
+ "supports_tools": agent.supports_tools,
207
+ "labels": agent.labels,
208
+ }
209
+ )
210
+ print(json.dumps(output, indent=2))
211
+
212
+ finally:
213
+ await account_client.close()
214
+
215
+
216
+ @app.async_command(
217
+ "list-toolkits", help="List toolkits (and tools) available in the room"
218
+ )
219
+ async def list_toolkits_command(
220
+ *,
221
+ project_id: ProjectIdOption,
222
+ room: RoomOption,
223
+ role: str = "user",
224
+ participant_id: Annotated[
225
+ Optional[str], typer.Option(..., help="Optional participant ID")
226
+ ] = None,
227
+ ):
228
+ """
229
+ List all toolkits (and tools within them) available in the room.
230
+ """
231
+ account_client = await get_client()
232
+ try:
233
+ project_id = await resolve_project_id(project_id=project_id)
234
+ room = resolve_room(room)
235
+ connection = await account_client.connect_room(project_id=project_id, room=room)
236
+
237
+ print("[bold green]Connecting to room...[/bold green]")
238
+ async with RoomClient(
239
+ protocol=WebSocketClientProtocol(
240
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
241
+ token=connection.jwt,
242
+ )
243
+ ) as client:
244
+ print("[bold green]Fetching list of toolkits...[/bold green]")
245
+ toolkits = await client.agents.list_toolkits(participant_id=participant_id)
246
+
247
+ # Format and output as JSON
248
+ output = []
249
+ for tk in toolkits:
250
+ output.append(
251
+ {
252
+ "name": tk.name,
253
+ "title": tk.title,
254
+ "description": tk.description,
255
+ "thumbnail_url": tk.thumbnail_url,
256
+ "tools": [
257
+ {
258
+ "name": tool.name,
259
+ "title": tool.title,
260
+ "description": tool.description,
261
+ "input_schema": tool.input_schema,
262
+ "thumbnail_url": tool.thumbnail_url,
263
+ "defs": tool.defs,
264
+ "supports_context": tool.supports_context,
265
+ }
266
+ for tool in tk.tools
267
+ ],
268
+ }
269
+ )
270
+ print(json.dumps(output, indent=2))
271
+
272
+ finally:
273
+ await account_client.close()
@@ -0,0 +1,102 @@
1
+ import json
2
+ from rich import print
3
+
4
+ from meshagent.cli.common_options import ProjectIdOption
5
+ from meshagent.cli import async_typer
6
+ from meshagent.cli.helper import (
7
+ get_client,
8
+ print_json_table,
9
+ resolve_project_id,
10
+ set_active_api_key,
11
+ )
12
+ from meshagent.cli.common_options import OutputFormatOption
13
+ from typing import Annotated
14
+ import typer
15
+
16
+ app = async_typer.AsyncTyper(help="Manage or activate api-keys for your project")
17
+
18
+
19
+ @app.async_command("list")
20
+ async def list(
21
+ *,
22
+ project_id: ProjectIdOption,
23
+ o: OutputFormatOption = "table",
24
+ ):
25
+ project_id = await resolve_project_id(project_id=project_id)
26
+ client = await get_client()
27
+ keys = (await client.list_api_keys(project_id=project_id))["keys"]
28
+
29
+ if len(keys) > 0:
30
+ if o == "json":
31
+ sanitized_keys = [
32
+ {k: v for k, v in key.items() if k != "created_by"} for key in keys
33
+ ]
34
+ print(json.dumps({"api-keys": sanitized_keys}, indent=2))
35
+ else:
36
+ print_json_table(keys, "id", "name", "description")
37
+ else:
38
+ print("There are not currently any API keys in the project")
39
+ await client.close()
40
+
41
+
42
+ @app.async_command("create")
43
+ async def create(
44
+ *,
45
+ project_id: ProjectIdOption,
46
+ name: str,
47
+ description: Annotated[
48
+ str, typer.Option(..., help="a description for the api key")
49
+ ] = "",
50
+ activate: Annotated[
51
+ bool,
52
+ typer.Option(
53
+ ..., help="use this key by default for commands that accept an API key"
54
+ ),
55
+ ] = False,
56
+ silent: Annotated[bool, typer.Option(..., help="do not print api key")] = False,
57
+ ):
58
+ project_id = await resolve_project_id(project_id=project_id)
59
+
60
+ client = await get_client()
61
+ api_key = await client.create_api_key(
62
+ project_id=project_id, name=name, description=description
63
+ )
64
+ if not silent:
65
+ if not activate:
66
+ print(
67
+ "[green]This is your token. Save it for later, you will not be able to get the value again:[/green]\n"
68
+ )
69
+ print(api_key["value"])
70
+ print(
71
+ "[green]\nNote: you can use the --activate flag to save a key in your local project settings when creating a key.[/green]\n"
72
+ )
73
+ else:
74
+ print("[green]This is your token:[/green]\n")
75
+ print(api_key["value"])
76
+
77
+ await client.close()
78
+ if activate:
79
+ await set_active_api_key(project_id=project_id, key=api_key["value"])
80
+ print(
81
+ "[green]your api key has been activated and will be used automatically with commands that require a key[/green]\n"
82
+ )
83
+
84
+
85
+ @app.async_command("activate")
86
+ async def activate(
87
+ *,
88
+ project_id: ProjectIdOption,
89
+ key: str,
90
+ ):
91
+ project_id = await resolve_project_id(project_id=project_id)
92
+ if activate:
93
+ await set_active_api_key(project_id=project_id, key=key)
94
+
95
+
96
+ @app.async_command("delete")
97
+ async def delete(*, project_id: ProjectIdOption, id: str):
98
+ project_id = await resolve_project_id(project_id=project_id)
99
+
100
+ client = await get_client()
101
+ await client.delete_api_key(project_id=project_id, id=id)
102
+ await client.close()
@@ -0,0 +1,79 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import inspect
5
+ import threading
6
+ from functools import partial, wraps
7
+ from typing import Any, Callable, TypeVar
8
+
9
+ from typer import Typer
10
+
11
+ T = TypeVar("T")
12
+
13
+
14
+ def _run_coroutine_sync(
15
+ coro: "asyncio.Future[T] | asyncio.coroutines.Coroutine[Any, Any, T]",
16
+ ) -> T:
17
+ """
18
+ Run an awaitable from sync code.
19
+
20
+ - If we're not currently in an event loop, use asyncio.run().
21
+ - If we ARE in a running loop (e.g. inside an agent / notebook / ASGI app),
22
+ run asyncio.run() in a separate thread and block for the result.
23
+
24
+ This avoids: RuntimeError: asyncio.run() cannot be called from a running event loop
25
+ """
26
+ try:
27
+ asyncio.get_running_loop()
28
+ in_running_loop = True
29
+ except RuntimeError:
30
+ in_running_loop = False
31
+
32
+ if not in_running_loop:
33
+ return asyncio.run(coro) # type: ignore[arg-type]
34
+
35
+ result: dict[str, Any] = {}
36
+ done = threading.Event()
37
+
38
+ def _worker() -> None:
39
+ try:
40
+ result["value"] = asyncio.run(coro) # type: ignore[arg-type]
41
+ except BaseException as e:
42
+ result["error"] = e
43
+ finally:
44
+ done.set()
45
+
46
+ t = threading.Thread(target=_worker, daemon=True)
47
+ t.start()
48
+ done.wait()
49
+
50
+ if "error" in result:
51
+ raise result["error"]
52
+ return result["value"] # type: ignore[return-value]
53
+
54
+
55
+ class AsyncTyper(Typer):
56
+ @staticmethod
57
+ def maybe_run_async(decorator: Callable[..., Any], func: Callable[..., Any]) -> Any:
58
+ if inspect.iscoroutinefunction(func):
59
+
60
+ @wraps(func)
61
+ def runner(*args: Any, **kwargs: Any) -> Any:
62
+ return _run_coroutine_sync(func(*args, **kwargs))
63
+
64
+ decorator(runner)
65
+ else:
66
+ decorator(func)
67
+ return func
68
+
69
+ def callback(self, *args: Any, **kwargs: Any) -> Any:
70
+ decorator = super().callback(*args, **kwargs)
71
+ return partial(self.maybe_run_async, decorator)
72
+
73
+ def command(self, *args: Any, **kwargs: Any) -> Any:
74
+ decorator = super().command(*args, **kwargs)
75
+ return partial(self.maybe_run_async, decorator)
76
+
77
+ # keep your existing name if you prefer
78
+ def async_command(self, *args: Any, **kwargs: Any) -> Any:
79
+ return self.command(*args, **kwargs)
meshagent/cli/auth.py ADDED
@@ -0,0 +1,30 @@
1
+ import typer
2
+
3
+ from meshagent.cli import async_typer
4
+ from meshagent.cli import auth_async
5
+ from meshagent.cli.helper import get_active_project
6
+
7
+ app = async_typer.AsyncTyper(help="Authenticate to meshagent")
8
+
9
+
10
+ @app.async_command("login")
11
+ async def login():
12
+ await auth_async.login()
13
+
14
+ project_id = await get_active_project()
15
+ if project_id is None:
16
+ print(
17
+ "You have been logged in, but you haven"
18
+ 't activated a project yet, list your projects with "meshagent project list" and then activate one with "meshagent project activate PROJECT_ID"'
19
+ )
20
+
21
+
22
+ @app.async_command("logout")
23
+ async def logout():
24
+ await auth_async.logout()
25
+
26
+
27
+ @app.async_command("whoami")
28
+ async def whoami():
29
+ _, s = await auth_async.session()
30
+ typer.echo(s.user.email if s else "Not logged in")