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.
- meshagent/cli/__init__.py +3 -0
- meshagent/cli/agent.py +273 -0
- meshagent/cli/api_keys.py +102 -0
- meshagent/cli/async_typer.py +79 -0
- meshagent/cli/auth.py +30 -0
- meshagent/cli/auth_async.py +295 -0
- meshagent/cli/call.py +215 -0
- meshagent/cli/chatbot.py +1983 -0
- meshagent/cli/cli.py +187 -0
- meshagent/cli/cli_mcp.py +408 -0
- meshagent/cli/cli_secrets.py +414 -0
- meshagent/cli/common_options.py +47 -0
- meshagent/cli/containers.py +725 -0
- meshagent/cli/database.py +997 -0
- meshagent/cli/developer.py +70 -0
- meshagent/cli/exec.py +397 -0
- meshagent/cli/helper.py +236 -0
- meshagent/cli/helpers.py +185 -0
- meshagent/cli/host.py +41 -0
- meshagent/cli/mailbot.py +1295 -0
- meshagent/cli/mailboxes.py +223 -0
- meshagent/cli/meeting_transcriber.py +138 -0
- meshagent/cli/messaging.py +157 -0
- meshagent/cli/multi.py +357 -0
- meshagent/cli/oauth2.py +341 -0
- meshagent/cli/participant_token.py +63 -0
- meshagent/cli/port.py +70 -0
- meshagent/cli/projects.py +105 -0
- meshagent/cli/queue.py +91 -0
- meshagent/cli/room.py +26 -0
- meshagent/cli/rooms.py +214 -0
- meshagent/cli/services.py +722 -0
- meshagent/cli/sessions.py +26 -0
- meshagent/cli/storage.py +813 -0
- meshagent/cli/sync.py +434 -0
- meshagent/cli/task_runner.py +1317 -0
- meshagent/cli/version.py +1 -0
- meshagent/cli/voicebot.py +624 -0
- meshagent/cli/webhook.py +100 -0
- meshagent/cli/worker.py +1403 -0
- meshagent_cli-0.22.2.dist-info/METADATA +49 -0
- meshagent_cli-0.22.2.dist-info/RECORD +45 -0
- meshagent_cli-0.22.2.dist-info/WHEEL +5 -0
- meshagent_cli-0.22.2.dist-info/entry_points.txt +2 -0
- meshagent_cli-0.22.2.dist-info/top_level.txt +1 -0
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")
|