meshagent-cli 0.7.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.
@@ -0,0 +1,3 @@
1
+ from .version import __version__
2
+
3
+ __all__ = [__version__]
meshagent/cli/agent.py ADDED
@@ -0,0 +1,263 @@
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
+ )
14
+ from meshagent.cli.helper import resolve_project_id
15
+ from meshagent.cli import async_typer
16
+ from meshagent.cli.helper import get_client, resolve_room
17
+
18
+ app = async_typer.AsyncTyper()
19
+
20
+
21
+ @app.async_command("ask")
22
+ async def ask(
23
+ *,
24
+ project_id: ProjectIdOption = None,
25
+ room: RoomOption,
26
+ agent: Annotated[str, typer.Option()],
27
+ input: Annotated[str, typer.Option()],
28
+ timeout: Annotated[
29
+ Optional[int],
30
+ typer.Option(
31
+ ..., help="How long to wait for the agent if the agent is not in the room"
32
+ ),
33
+ ] = 30,
34
+ ):
35
+ account_client = await get_client()
36
+ try:
37
+ project_id = await resolve_project_id(project_id=project_id)
38
+ room = resolve_room(room)
39
+
40
+ connection = await account_client.connect_room(project_id=project_id, room=room)
41
+
42
+ print("[bold green]Connecting to room...[/bold green]")
43
+ async with RoomClient(
44
+ protocol=WebSocketClientProtocol(
45
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
46
+ token=connection.jwt,
47
+ )
48
+ ) as client:
49
+ found = timeout == 0
50
+ for i in range(30):
51
+ if found:
52
+ break
53
+
54
+ if i == 1:
55
+ print("[magenta]Waiting for agent...[/magenta]")
56
+
57
+ agents = await client.agents.list_agents()
58
+ await asyncio.sleep(1)
59
+
60
+ for a in agents:
61
+ if a.name == agent:
62
+ found = True
63
+ break
64
+
65
+ if not found:
66
+ print("[red]Timed out waiting for agent to join the room[/red]")
67
+ raise typer.Exit(1)
68
+
69
+ print("[magenta]Asking agent...[/magenta]")
70
+
71
+ response = await client.agents.ask(agent=agent, arguments=json.loads(input))
72
+ print(json.dumps(response.json))
73
+ except RoomException as e:
74
+ print(f"[red]{e}[/red]")
75
+ finally:
76
+ await account_client.close()
77
+
78
+
79
+ @app.async_command("invoke-tool")
80
+ async def invoke_tool(
81
+ *,
82
+ project_id: ProjectIdOption = None,
83
+ room: RoomOption,
84
+ toolkit: Annotated[str, typer.Option(..., help="Toolkit name")],
85
+ tool: Annotated[str, typer.Option(..., help="Tool name")],
86
+ arguments: Annotated[
87
+ str, typer.Option(..., help="JSON string with arguments for the tool")
88
+ ],
89
+ participant_id: Annotated[
90
+ Optional[str],
91
+ typer.Option(..., help="Optional participant ID to invoke the tool on"),
92
+ ] = None,
93
+ on_behalf_of_id: Annotated[
94
+ Optional[str], typer.Option(..., help="Optional 'on_behalf_of' participant ID")
95
+ ] = None,
96
+ caller_context: Annotated[
97
+ Optional[str], typer.Option(..., help="Optional JSON for caller context")
98
+ ] = None,
99
+ timeout: Annotated[
100
+ Optional[int],
101
+ typer.Option(
102
+ ...,
103
+ help="How long to wait for the toolkit if the toolkit is not in the room",
104
+ ),
105
+ ] = 30,
106
+ ):
107
+ """
108
+ Invoke a specific tool from a given toolkit with arguments.
109
+ """
110
+ account_client = await get_client()
111
+ try:
112
+ project_id = await resolve_project_id(project_id=project_id)
113
+ room = resolve_room(room)
114
+
115
+ connection = await account_client.connect_room(project_id=project_id, room=room)
116
+
117
+ print("[bold green]Connecting to room...[/bold green]")
118
+ async with RoomClient(
119
+ protocol=WebSocketClientProtocol(
120
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
121
+ token=connection.jwt,
122
+ )
123
+ ) as client:
124
+ found = timeout == 0
125
+ for i in range(timeout):
126
+ if found:
127
+ break
128
+
129
+ if i == 1:
130
+ print("[magenta]Waiting for toolkit...[/magenta]")
131
+
132
+ agents = await client.agents.list_toolkits(
133
+ participant_id=participant_id
134
+ )
135
+ await asyncio.sleep(1)
136
+
137
+ for a in agents:
138
+ if a.name == toolkit:
139
+ found = True
140
+ break
141
+
142
+ if not found:
143
+ print("[red]Timed out waiting for toolkit to join the room[/red]")
144
+ raise typer.Exit(1)
145
+
146
+ print("[bold green]Invoking tool...[/bold green]")
147
+ parsed_context = json.loads(caller_context) if caller_context else None
148
+ response = await client.agents.invoke_tool(
149
+ toolkit=toolkit,
150
+ tool=tool,
151
+ arguments=json.loads(arguments),
152
+ participant_id=participant_id,
153
+ on_behalf_of_id=on_behalf_of_id,
154
+ caller_context=parsed_context,
155
+ )
156
+ # The response is presumably a dictionary or similar
157
+ print(response.to_json())
158
+ except RoomException as e:
159
+ print(f"[red]{e}[/red]")
160
+ finally:
161
+ await account_client.close()
162
+
163
+
164
+ @app.async_command("list-agents")
165
+ async def list_agents_command(
166
+ *,
167
+ project_id: ProjectIdOption = None,
168
+ room: RoomOption,
169
+ ):
170
+ """
171
+ List all agents available in the room.
172
+ """
173
+ account_client = await get_client()
174
+ try:
175
+ project_id = await resolve_project_id(project_id=project_id)
176
+ room = resolve_room(room)
177
+
178
+ connection = await account_client.connect_room(project_id=project_id, room=room)
179
+
180
+ print("[bold green]Connecting to room...[/bold green]")
181
+ async with RoomClient(
182
+ protocol=WebSocketClientProtocol(
183
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
184
+ token=connection.jwt,
185
+ )
186
+ ) as client:
187
+ print("[bold green]Fetching list of agents...[/bold green]")
188
+ agents = await client.agents.list_agents()
189
+ # Format the output as JSON
190
+ output = []
191
+ for agent in agents:
192
+ output.append(
193
+ {
194
+ "name": agent.name,
195
+ "title": agent.title,
196
+ "description": agent.description,
197
+ "requires": [r.to_json() for r in agent.requires],
198
+ "supports_tools": agent.supports_tools,
199
+ "labels": agent.labels,
200
+ }
201
+ )
202
+ print(json.dumps(output, indent=2))
203
+
204
+ finally:
205
+ await account_client.close()
206
+
207
+
208
+ @app.async_command("list-toolkits")
209
+ async def list_toolkits_command(
210
+ *,
211
+ project_id: ProjectIdOption = None,
212
+ room: RoomOption,
213
+ role: str = "user",
214
+ participant_id: Annotated[
215
+ Optional[str], typer.Option(..., help="Optional participant ID")
216
+ ] = None,
217
+ ):
218
+ """
219
+ List all toolkits (and tools within them) available in the room.
220
+ """
221
+ account_client = await get_client()
222
+ try:
223
+ project_id = await resolve_project_id(project_id=project_id)
224
+ room = resolve_room(room)
225
+ connection = await account_client.connect_room(project_id=project_id, room=room)
226
+
227
+ print("[bold green]Connecting to room...[/bold green]")
228
+ async with RoomClient(
229
+ protocol=WebSocketClientProtocol(
230
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
231
+ token=connection.jwt,
232
+ )
233
+ ) as client:
234
+ print("[bold green]Fetching list of toolkits...[/bold green]")
235
+ toolkits = await client.agents.list_toolkits(participant_id=participant_id)
236
+
237
+ # Format and output as JSON
238
+ output = []
239
+ for tk in toolkits:
240
+ output.append(
241
+ {
242
+ "name": tk.name,
243
+ "title": tk.title,
244
+ "description": tk.description,
245
+ "thumbnail_url": tk.thumbnail_url,
246
+ "tools": [
247
+ {
248
+ "name": tool.name,
249
+ "title": tool.title,
250
+ "description": tool.description,
251
+ "input_schema": tool.input_schema,
252
+ "thumbnail_url": tool.thumbnail_url,
253
+ "defs": tool.defs,
254
+ "supports_context": tool.supports_context,
255
+ }
256
+ for tool in tk.tools
257
+ ],
258
+ }
259
+ )
260
+ print(json.dumps(output, indent=2))
261
+
262
+ finally:
263
+ 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 = None,
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 = None,
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 = None,
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 = None, 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,31 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import inspect
5
+ from functools import partial, wraps
6
+ from typing import Any, Callable
7
+
8
+ from typer import Typer
9
+
10
+
11
+ class AsyncTyper(Typer):
12
+ @staticmethod
13
+ def maybe_run_async(decorator: Callable, func: Callable) -> Any:
14
+ if inspect.iscoroutinefunction(func):
15
+
16
+ @wraps(func)
17
+ def runner(*args: Any, **kwargs: Any) -> Any:
18
+ return asyncio.run(func(*args, **kwargs))
19
+
20
+ decorator(runner)
21
+ else:
22
+ decorator(func)
23
+ return func
24
+
25
+ def callback(self, *args: Any, **kwargs: Any) -> Any:
26
+ decorator = super().callback(*args, **kwargs)
27
+ return partial(self.maybe_run_async, decorator)
28
+
29
+ def async_command(self, *args: Any, **kwargs: Any) -> Any:
30
+ decorator = super().command(*args, **kwargs)
31
+ return partial(self.maybe_run_async, decorator)
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")