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.
- meshagent/cli/agent.py +15 -11
- meshagent/cli/api_keys.py +4 -4
- meshagent/cli/async_typer.py +52 -4
- meshagent/cli/call.py +12 -8
- meshagent/cli/chatbot.py +1007 -129
- meshagent/cli/cli.py +21 -20
- meshagent/cli/cli_mcp.py +92 -28
- meshagent/cli/cli_secrets.py +10 -10
- meshagent/cli/common_options.py +19 -4
- meshagent/cli/containers.py +164 -16
- meshagent/cli/database.py +997 -0
- meshagent/cli/developer.py +3 -3
- meshagent/cli/exec.py +22 -6
- meshagent/cli/helper.py +62 -11
- meshagent/cli/helpers.py +66 -9
- meshagent/cli/host.py +37 -0
- meshagent/cli/mailbot.py +1004 -40
- meshagent/cli/mailboxes.py +223 -0
- meshagent/cli/meeting_transcriber.py +10 -4
- meshagent/cli/messaging.py +7 -7
- meshagent/cli/multi.py +402 -0
- meshagent/cli/oauth2.py +44 -21
- meshagent/cli/participant_token.py +5 -3
- meshagent/cli/port.py +70 -0
- meshagent/cli/queue.py +2 -2
- meshagent/cli/room.py +20 -212
- meshagent/cli/rooms.py +214 -0
- meshagent/cli/services.py +32 -23
- meshagent/cli/sessions.py +5 -5
- meshagent/cli/storage.py +5 -5
- meshagent/cli/task_runner.py +770 -0
- meshagent/cli/version.py +1 -1
- meshagent/cli/voicebot.py +502 -76
- meshagent/cli/webhook.py +7 -7
- meshagent/cli/worker.py +1327 -0
- {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.21.0.dist-info}/METADATA +13 -13
- meshagent_cli-0.21.0.dist-info/RECORD +44 -0
- meshagent_cli-0.7.0.dist-info/RECORD +0 -36
- {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.21.0.dist-info}/WHEEL +0 -0
- {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.21.0.dist-info}/entry_points.txt +0 -0
- {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.21.0.dist-info}/top_level.txt +0 -0
meshagent/cli/oauth2.py
CHANGED
|
@@ -18,16 +18,27 @@ app = async_typer.AsyncTyper(help="OAuth2 test commands")
|
|
|
18
18
|
@app.async_command("request")
|
|
19
19
|
async def oauth2(
|
|
20
20
|
*,
|
|
21
|
-
project_id: ProjectIdOption
|
|
21
|
+
project_id: ProjectIdOption,
|
|
22
22
|
room: RoomOption,
|
|
23
|
-
from_participant_id: Annotated[
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
from_participant_id: Annotated[
|
|
24
|
+
str,
|
|
25
|
+
typer.Option(..., help="Participant ID to request the token from"),
|
|
26
|
+
],
|
|
27
|
+
client_id: Annotated[str, typer.Option(..., help="OAuth client ID")],
|
|
28
|
+
authorization_endpoint: Annotated[
|
|
29
|
+
str, typer.Option(..., help="OAuth authorization endpoint URL")
|
|
30
|
+
],
|
|
31
|
+
token_endpoint: Annotated[str, typer.Option(..., help="OAuth token endpoint URL")],
|
|
32
|
+
scopes: Annotated[
|
|
33
|
+
Optional[str], typer.Option(help="Comma-separated OAuth scopes")
|
|
34
|
+
] = None,
|
|
35
|
+
client_secret: Annotated[
|
|
36
|
+
Optional[str], typer.Option(help="OAuth client secret (if required)")
|
|
37
|
+
],
|
|
38
|
+
redirect_uri: Annotated[
|
|
39
|
+
Optional[str], typer.Option(help="Redirect URI for the OAuth flow")
|
|
40
|
+
],
|
|
41
|
+
pkce: Annotated[bool, typer.Option(help="Use PKCE (recommended)")] = True,
|
|
31
42
|
):
|
|
32
43
|
"""
|
|
33
44
|
Run an OAuth2 request test between two participants in the same room.
|
|
@@ -72,16 +83,26 @@ async def oauth2(
|
|
|
72
83
|
@app.async_command("get")
|
|
73
84
|
async def get(
|
|
74
85
|
*,
|
|
75
|
-
project_id: ProjectIdOption
|
|
86
|
+
project_id: ProjectIdOption,
|
|
76
87
|
room: RoomOption,
|
|
77
|
-
delegated_to: Annotated[
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
88
|
+
delegated_to: Annotated[
|
|
89
|
+
str, typer.Option(..., help="Participant ID to delegate the token to")
|
|
90
|
+
],
|
|
91
|
+
client_id: Annotated[str, typer.Option(..., help="OAuth client ID")],
|
|
92
|
+
authorization_endpoint: Annotated[
|
|
93
|
+
str, typer.Option(..., help="OAuth authorization endpoint URL")
|
|
94
|
+
],
|
|
95
|
+
token_endpoint: Annotated[str, typer.Option(..., help="OAuth token endpoint URL")],
|
|
96
|
+
scopes: Annotated[
|
|
97
|
+
Optional[str], typer.Option(help="Comma-separated OAuth scopes")
|
|
98
|
+
] = None,
|
|
99
|
+
client_secret: Annotated[
|
|
100
|
+
Optional[str], typer.Option(help="OAuth client secret (if required)")
|
|
101
|
+
],
|
|
102
|
+
redirect_uri: Annotated[
|
|
103
|
+
Optional[str], typer.Option(help="Redirect URI for the OAuth flow")
|
|
104
|
+
],
|
|
105
|
+
pkce: Annotated[bool, typer.Option(help="Use PKCE (recommended)")] = True,
|
|
85
106
|
):
|
|
86
107
|
"""
|
|
87
108
|
Run an OAuth2 request test between two participants in the same room.
|
|
@@ -124,7 +145,7 @@ async def get(
|
|
|
124
145
|
@app.async_command("list")
|
|
125
146
|
async def list(
|
|
126
147
|
*,
|
|
127
|
-
project_id: ProjectIdOption
|
|
148
|
+
project_id: ProjectIdOption,
|
|
128
149
|
room: RoomOption,
|
|
129
150
|
):
|
|
130
151
|
"""
|
|
@@ -159,10 +180,12 @@ async def list(
|
|
|
159
180
|
@app.async_command("delete")
|
|
160
181
|
async def delete(
|
|
161
182
|
*,
|
|
162
|
-
project_id: ProjectIdOption
|
|
183
|
+
project_id: ProjectIdOption,
|
|
163
184
|
room: RoomOption,
|
|
164
185
|
id: str,
|
|
165
|
-
delegated_to: Annotated[
|
|
186
|
+
delegated_to: Annotated[
|
|
187
|
+
str, typer.Option(help="The value of the delegated_to field of the secret")
|
|
188
|
+
],
|
|
166
189
|
):
|
|
167
190
|
"""
|
|
168
191
|
delete a secret
|
|
@@ -10,13 +10,13 @@ from meshagent.api.participant_token import ParticipantTokenSpec
|
|
|
10
10
|
from pydantic_yaml import parse_yaml_raw_as
|
|
11
11
|
from meshagent.cli.common_options import ProjectIdOption
|
|
12
12
|
|
|
13
|
-
app = async_typer.AsyncTyper()
|
|
13
|
+
app = async_typer.AsyncTyper(help="Generate participant tokens (JWTs)")
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
@app.async_command("generate")
|
|
16
|
+
@app.async_command("generate", help="Generate a participant token (JWT) from a spec")
|
|
17
17
|
async def generate(
|
|
18
18
|
*,
|
|
19
|
-
project_id: ProjectIdOption
|
|
19
|
+
project_id: ProjectIdOption,
|
|
20
20
|
output: Annotated[
|
|
21
21
|
Optional[str],
|
|
22
22
|
typer.Option("--output", "-o", help="File path to a file"),
|
|
@@ -30,6 +30,8 @@ async def generate(
|
|
|
30
30
|
typer.Option("--key", help="an api key to sign the token with"),
|
|
31
31
|
] = None,
|
|
32
32
|
):
|
|
33
|
+
"""Generate a signed participant token (JWT) from a YAML spec."""
|
|
34
|
+
|
|
33
35
|
project_id = await resolve_project_id(project_id=project_id)
|
|
34
36
|
key = await resolve_key(project_id=project_id, key=key)
|
|
35
37
|
|
meshagent/cli/port.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from meshagent.cli import async_typer
|
|
11
|
+
from meshagent.cli.common_options import ProjectIdOption
|
|
12
|
+
from meshagent.cli.helper import get_client, resolve_project_id
|
|
13
|
+
|
|
14
|
+
from meshagent.api.port_forward import port_forward
|
|
15
|
+
|
|
16
|
+
app = async_typer.AsyncTyper(help="Port forwarding into room containers")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@app.async_command("forward", help="Forward a container port to localhost")
|
|
20
|
+
async def forward(
|
|
21
|
+
*,
|
|
22
|
+
project_id: ProjectIdOption,
|
|
23
|
+
room: Annotated[
|
|
24
|
+
str,
|
|
25
|
+
typer.Option(
|
|
26
|
+
"--room",
|
|
27
|
+
"-r",
|
|
28
|
+
help="Room name containing the target container",
|
|
29
|
+
),
|
|
30
|
+
],
|
|
31
|
+
container_id: Annotated[
|
|
32
|
+
str,
|
|
33
|
+
typer.Option(
|
|
34
|
+
"--container-id",
|
|
35
|
+
"-c",
|
|
36
|
+
help="Container ID to port-forward into",
|
|
37
|
+
),
|
|
38
|
+
],
|
|
39
|
+
port: Annotated[
|
|
40
|
+
str,
|
|
41
|
+
typer.Option(
|
|
42
|
+
"--port",
|
|
43
|
+
"-p",
|
|
44
|
+
help="Port mapping in the form LOCAL:REMOTE",
|
|
45
|
+
),
|
|
46
|
+
],
|
|
47
|
+
):
|
|
48
|
+
"""Create a local TCP listener forwarding into a room container."""
|
|
49
|
+
|
|
50
|
+
client = await get_client()
|
|
51
|
+
try:
|
|
52
|
+
project_id = await resolve_project_id(project_id)
|
|
53
|
+
|
|
54
|
+
connection = await client.connect_room(project_id=project_id, room=room)
|
|
55
|
+
|
|
56
|
+
ports = port.split(":")
|
|
57
|
+
|
|
58
|
+
handler = await port_forward(
|
|
59
|
+
listen_port=int(ports[0]),
|
|
60
|
+
port=int(ports[1]),
|
|
61
|
+
container_id=container_id,
|
|
62
|
+
token=connection.jwt,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
await asyncio.sleep(10000)
|
|
66
|
+
|
|
67
|
+
await handler.close()
|
|
68
|
+
|
|
69
|
+
finally:
|
|
70
|
+
await client.close()
|
meshagent/cli/queue.py
CHANGED
|
@@ -20,7 +20,7 @@ app = async_typer.AsyncTyper(help="Use queues in a room")
|
|
|
20
20
|
@app.async_command("send")
|
|
21
21
|
async def send(
|
|
22
22
|
*,
|
|
23
|
-
project_id: ProjectIdOption
|
|
23
|
+
project_id: ProjectIdOption,
|
|
24
24
|
room: RoomOption,
|
|
25
25
|
queue: Annotated[str, typer.Option(..., help="Queue name")],
|
|
26
26
|
json: Optional[str] = typer.Option(..., help="a JSON message to send to the queue"),
|
|
@@ -60,7 +60,7 @@ async def send(
|
|
|
60
60
|
@app.async_command("receive")
|
|
61
61
|
async def receive(
|
|
62
62
|
*,
|
|
63
|
-
project_id: ProjectIdOption
|
|
63
|
+
project_id: ProjectIdOption,
|
|
64
64
|
room: RoomOption,
|
|
65
65
|
queue: Annotated[str, typer.Option(..., help="Queue name")],
|
|
66
66
|
):
|
meshagent/cli/room.py
CHANGED
|
@@ -1,214 +1,22 @@
|
|
|
1
|
-
# rooms_cli.py (add to the same module or import into your CLI package)
|
|
2
|
-
|
|
3
|
-
import typer
|
|
4
|
-
from rich import print
|
|
5
|
-
from typing import Annotated, Optional
|
|
6
|
-
import json
|
|
7
|
-
|
|
8
1
|
from meshagent.cli import async_typer
|
|
9
|
-
from meshagent.cli
|
|
10
|
-
from meshagent.cli
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
2
|
+
from meshagent.cli import database
|
|
3
|
+
from meshagent.cli import queue
|
|
4
|
+
from meshagent.cli import agent
|
|
5
|
+
from meshagent.cli import messaging
|
|
6
|
+
from meshagent.cli import storage
|
|
7
|
+
from meshagent.cli import developer
|
|
8
|
+
from meshagent.cli import cli_secrets
|
|
9
|
+
from meshagent.cli import containers
|
|
10
|
+
|
|
11
|
+
app = async_typer.AsyncTyper(help="Operate within a room")
|
|
12
|
+
|
|
13
|
+
app.add_typer(agent.app, name="agents", help="Interact with agents and toolkits")
|
|
14
|
+
app.add_typer(cli_secrets.app, name="secret", help="Manage secrets for your project")
|
|
15
|
+
app.add_typer(queue.app, name="queue", help="Use queues in a room")
|
|
16
|
+
app.add_typer(messaging.app, name="messaging", help="Send and receive messages")
|
|
17
|
+
app.add_typer(storage.app, name="storage", help="Manage storage for a room")
|
|
18
|
+
app.add_typer(developer.app, name="developer", help="Developer utilities for a room")
|
|
19
|
+
app.add_typer(database.app, name="database", help="Manage database tables in a room")
|
|
20
|
+
app.add_typer(
|
|
21
|
+
containers.app, name="container", help="Manage containers and images in a room"
|
|
14
22
|
)
|
|
15
|
-
from meshagent.api import RoomException
|
|
16
|
-
|
|
17
|
-
app = async_typer.AsyncTyper()
|
|
18
|
-
|
|
19
|
-
# ---------------------------
|
|
20
|
-
# Helpers
|
|
21
|
-
# ---------------------------
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
async def _resolve_room_id_or_fail(
|
|
25
|
-
account_client, *, project_id: str, room_id: Optional[str], room_name: Optional[str]
|
|
26
|
-
) -> str:
|
|
27
|
-
"""
|
|
28
|
-
If room_id is provided, return it.
|
|
29
|
-
Else, resolve via room_name -> account_client.get_room(...).id
|
|
30
|
-
"""
|
|
31
|
-
if room_id:
|
|
32
|
-
return room_id
|
|
33
|
-
if not room_name:
|
|
34
|
-
raise RoomException("You must provide either --id or --name.")
|
|
35
|
-
room = await account_client.get_room(project_id=project_id, name=room_name)
|
|
36
|
-
return room.id
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _maybe_parse_json(label: str, s: Optional[str]):
|
|
40
|
-
if s is None:
|
|
41
|
-
return None
|
|
42
|
-
try:
|
|
43
|
-
return json.loads(s)
|
|
44
|
-
except json.JSONDecodeError as e:
|
|
45
|
-
raise RoomException(f"Invalid {label} JSON: {e}") from e
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# ---------------------------
|
|
49
|
-
# Commands
|
|
50
|
-
# ---------------------------
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@app.async_command("create")
|
|
54
|
-
async def room_create_command(
|
|
55
|
-
*,
|
|
56
|
-
project_id: ProjectIdOption = None,
|
|
57
|
-
name: Annotated[str, typer.Option(..., help="Room name")],
|
|
58
|
-
if_not_exists: Annotated[
|
|
59
|
-
bool, typer.Option(help="Do not error if the room already exists")
|
|
60
|
-
] = False,
|
|
61
|
-
metadata: Annotated[
|
|
62
|
-
Optional[str], typer.Option(help="Optional JSON object for room metadata")
|
|
63
|
-
] = None,
|
|
64
|
-
):
|
|
65
|
-
"""
|
|
66
|
-
Create a room in the project.
|
|
67
|
-
"""
|
|
68
|
-
account_client = await get_client()
|
|
69
|
-
try:
|
|
70
|
-
project_id = await resolve_project_id(project_id=project_id)
|
|
71
|
-
|
|
72
|
-
meta_obj = _maybe_parse_json("metadata", metadata)
|
|
73
|
-
|
|
74
|
-
print(f"[bold green]Creating room {name}[/bold green]")
|
|
75
|
-
room = await account_client.create_room(
|
|
76
|
-
project_id=project_id,
|
|
77
|
-
name=name,
|
|
78
|
-
if_not_exists=if_not_exists,
|
|
79
|
-
metadata=meta_obj,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
print(
|
|
83
|
-
json.dumps(
|
|
84
|
-
{"id": room.id, "name": room.name, "metadata": room.metadata}, indent=2
|
|
85
|
-
)
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
except RoomException as ex:
|
|
89
|
-
print(f"[red]{ex}[/red]")
|
|
90
|
-
raise typer.Exit(1)
|
|
91
|
-
finally:
|
|
92
|
-
await account_client.close()
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@app.async_command("delete")
|
|
96
|
-
async def room_delete_command(
|
|
97
|
-
*,
|
|
98
|
-
project_id: ProjectIdOption = None,
|
|
99
|
-
id: Annotated[Optional[str], typer.Option(help="Room ID (preferred)")] = None,
|
|
100
|
-
name: Optional[str] = None,
|
|
101
|
-
):
|
|
102
|
-
"""
|
|
103
|
-
Delete a room by ID (or by name if --name is supplied).
|
|
104
|
-
"""
|
|
105
|
-
account_client = await get_client()
|
|
106
|
-
try:
|
|
107
|
-
project_id = await resolve_project_id(project_id=project_id)
|
|
108
|
-
room_name = resolve_room(name) if name else None
|
|
109
|
-
rid = await _resolve_room_id_or_fail(
|
|
110
|
-
account_client, project_id=project_id, room_id=id, room_name=room_name
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
print(f"[bold yellow]Deleting room id={rid}...[/bold yellow]")
|
|
114
|
-
await account_client.delete_room(project_id=project_id, room_id=rid)
|
|
115
|
-
print("[bold cyan]Room deleted.[/bold cyan]")
|
|
116
|
-
except RoomException as ex:
|
|
117
|
-
print(f"[red]{ex}[/red]")
|
|
118
|
-
raise typer.Exit(1)
|
|
119
|
-
finally:
|
|
120
|
-
await account_client.close()
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
@app.async_command("update")
|
|
124
|
-
async def room_update_command(
|
|
125
|
-
*,
|
|
126
|
-
project_id: ProjectIdOption = None,
|
|
127
|
-
id: Annotated[Optional[str], typer.Option(help="Room ID (preferred)")] = None,
|
|
128
|
-
name: Optional[str] = None,
|
|
129
|
-
new_name: Annotated[str, typer.Option(..., help="New room name")],
|
|
130
|
-
):
|
|
131
|
-
"""
|
|
132
|
-
Update a room's name (ID is preferred; name will be resolved to ID if needed).
|
|
133
|
-
"""
|
|
134
|
-
account_client = await get_client()
|
|
135
|
-
try:
|
|
136
|
-
project_id = await resolve_project_id(project_id=project_id)
|
|
137
|
-
room_name = resolve_room(name) if name else None
|
|
138
|
-
rid = await _resolve_room_id_or_fail(
|
|
139
|
-
account_client, project_id=project_id, room_id=id, room_name=room_name
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
print(
|
|
143
|
-
f"[bold green]Updating room id={rid} -> name='{new_name}'...[/bold green]"
|
|
144
|
-
)
|
|
145
|
-
await account_client.update_room(
|
|
146
|
-
project_id=project_id, room_id=rid, name=new_name
|
|
147
|
-
)
|
|
148
|
-
print("[bold cyan]Room updated.[/bold cyan]")
|
|
149
|
-
except RoomException as ex:
|
|
150
|
-
print(f"[red]{ex}[/red]")
|
|
151
|
-
raise typer.Exit(1)
|
|
152
|
-
finally:
|
|
153
|
-
await account_client.close()
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
@app.async_command("list")
|
|
157
|
-
async def room_list_command(
|
|
158
|
-
*,
|
|
159
|
-
project_id: ProjectIdOption = None,
|
|
160
|
-
limit: Annotated[
|
|
161
|
-
int, typer.Option(help="Max rooms to return", min=1, max=500)
|
|
162
|
-
] = 50,
|
|
163
|
-
offset: Annotated[int, typer.Option(help="Offset for pagination", min=0)] = 0,
|
|
164
|
-
order_by: Annotated[
|
|
165
|
-
str, typer.Option(help='Order by column (e.g. "room_name", "created_at")')
|
|
166
|
-
] = "room_name",
|
|
167
|
-
):
|
|
168
|
-
"""
|
|
169
|
-
List rooms in the project.
|
|
170
|
-
"""
|
|
171
|
-
account_client = await get_client()
|
|
172
|
-
try:
|
|
173
|
-
project_id = await resolve_project_id(project_id=project_id)
|
|
174
|
-
print("[bold green]Fetching rooms...[/bold green]")
|
|
175
|
-
|
|
176
|
-
rooms = await account_client.list_rooms(
|
|
177
|
-
project_id=project_id,
|
|
178
|
-
limit=limit,
|
|
179
|
-
offset=offset,
|
|
180
|
-
order_by=order_by,
|
|
181
|
-
)
|
|
182
|
-
output = [{"id": r.id, "name": r.name, "metadata": r.metadata} for r in rooms]
|
|
183
|
-
print(json.dumps(output, indent=2))
|
|
184
|
-
except RoomException as ex:
|
|
185
|
-
print(f"[red]{ex}[/red]")
|
|
186
|
-
raise typer.Exit(1)
|
|
187
|
-
finally:
|
|
188
|
-
await account_client.close()
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
@app.async_command("get")
|
|
192
|
-
async def room_get_command(
|
|
193
|
-
*,
|
|
194
|
-
project_id: ProjectIdOption = None,
|
|
195
|
-
name: Optional[str] = None,
|
|
196
|
-
):
|
|
197
|
-
"""
|
|
198
|
-
Get a single room by name (handy for resolving the ID).
|
|
199
|
-
"""
|
|
200
|
-
account_client = await get_client()
|
|
201
|
-
try:
|
|
202
|
-
project_id = await resolve_project_id(project_id=project_id)
|
|
203
|
-
room_name = resolve_room(name)
|
|
204
|
-
|
|
205
|
-
print(f"[bold green]Fetching room '{room_name}'...[/bold green]")
|
|
206
|
-
r = await account_client.get_room(project_id=project_id, name=room_name)
|
|
207
|
-
print(
|
|
208
|
-
json.dumps({"id": r.id, "name": r.name, "metadata": r.metadata}, indent=2)
|
|
209
|
-
)
|
|
210
|
-
except RoomException as ex:
|
|
211
|
-
print(f"[red]{ex}[/red]")
|
|
212
|
-
raise typer.Exit(1)
|
|
213
|
-
finally:
|
|
214
|
-
await account_client.close()
|
meshagent/cli/rooms.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# rooms_cli.py (add to the same module or import into your CLI package)
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich import print
|
|
5
|
+
from typing import Annotated, Optional
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
from meshagent.cli import async_typer
|
|
9
|
+
from meshagent.cli.common_options import ProjectIdOption
|
|
10
|
+
from meshagent.cli.helper import (
|
|
11
|
+
get_client,
|
|
12
|
+
resolve_project_id,
|
|
13
|
+
resolve_room,
|
|
14
|
+
)
|
|
15
|
+
from meshagent.api import RoomException
|
|
16
|
+
|
|
17
|
+
app = async_typer.AsyncTyper(help="Create, list, and manage rooms in a project")
|
|
18
|
+
|
|
19
|
+
# ---------------------------
|
|
20
|
+
# Helpers
|
|
21
|
+
# ---------------------------
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
async def _resolve_room_id_or_fail(
|
|
25
|
+
account_client, *, project_id: str, room_id: Optional[str], room_name: Optional[str]
|
|
26
|
+
) -> str:
|
|
27
|
+
"""
|
|
28
|
+
If room_id is provided, return it.
|
|
29
|
+
Else, resolve via room_name -> account_client.get_room(...).id
|
|
30
|
+
"""
|
|
31
|
+
if room_id:
|
|
32
|
+
return room_id
|
|
33
|
+
if not room_name:
|
|
34
|
+
raise RoomException("You must provide either --id or --name.")
|
|
35
|
+
room = await account_client.get_room(project_id=project_id, name=room_name)
|
|
36
|
+
return room.id
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _maybe_parse_json(label: str, s: Optional[str]):
|
|
40
|
+
if s is None:
|
|
41
|
+
return None
|
|
42
|
+
try:
|
|
43
|
+
return json.loads(s)
|
|
44
|
+
except json.JSONDecodeError as e:
|
|
45
|
+
raise RoomException(f"Invalid {label} JSON: {e}") from e
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ---------------------------
|
|
49
|
+
# Commands
|
|
50
|
+
# ---------------------------
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@app.async_command("create")
|
|
54
|
+
async def room_create_command(
|
|
55
|
+
*,
|
|
56
|
+
project_id: ProjectIdOption,
|
|
57
|
+
name: Annotated[str, typer.Option(..., help="Room name")],
|
|
58
|
+
if_not_exists: Annotated[
|
|
59
|
+
bool, typer.Option(help="Do not error if the room already exists")
|
|
60
|
+
] = False,
|
|
61
|
+
metadata: Annotated[
|
|
62
|
+
Optional[str], typer.Option(help="Optional JSON object for room metadata")
|
|
63
|
+
] = None,
|
|
64
|
+
):
|
|
65
|
+
"""
|
|
66
|
+
Create a room in the project.
|
|
67
|
+
"""
|
|
68
|
+
account_client = await get_client()
|
|
69
|
+
try:
|
|
70
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
71
|
+
|
|
72
|
+
meta_obj = _maybe_parse_json("metadata", metadata)
|
|
73
|
+
|
|
74
|
+
print(f"[bold green]Creating room {name}[/bold green]")
|
|
75
|
+
room = await account_client.create_room(
|
|
76
|
+
project_id=project_id,
|
|
77
|
+
name=name,
|
|
78
|
+
if_not_exists=if_not_exists,
|
|
79
|
+
metadata=meta_obj,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
print(
|
|
83
|
+
json.dumps(
|
|
84
|
+
{"id": room.id, "name": room.name, "metadata": room.metadata}, indent=2
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
except RoomException as ex:
|
|
89
|
+
print(f"[red]{ex}[/red]")
|
|
90
|
+
raise typer.Exit(1)
|
|
91
|
+
finally:
|
|
92
|
+
await account_client.close()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@app.async_command("delete")
|
|
96
|
+
async def room_delete_command(
|
|
97
|
+
*,
|
|
98
|
+
project_id: ProjectIdOption,
|
|
99
|
+
id: Annotated[Optional[str], typer.Option(help="Room ID (preferred)")] = None,
|
|
100
|
+
name: Optional[str] = None,
|
|
101
|
+
):
|
|
102
|
+
"""
|
|
103
|
+
Delete a room by ID (or by name if --name is supplied).
|
|
104
|
+
"""
|
|
105
|
+
account_client = await get_client()
|
|
106
|
+
try:
|
|
107
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
108
|
+
room_name = resolve_room(name) if name else None
|
|
109
|
+
rid = await _resolve_room_id_or_fail(
|
|
110
|
+
account_client, project_id=project_id, room_id=id, room_name=room_name
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
print(f"[bold yellow]Deleting room id={rid}...[/bold yellow]")
|
|
114
|
+
await account_client.delete_room(project_id=project_id, room_id=rid)
|
|
115
|
+
print("[bold cyan]Room deleted.[/bold cyan]")
|
|
116
|
+
except RoomException as ex:
|
|
117
|
+
print(f"[red]{ex}[/red]")
|
|
118
|
+
raise typer.Exit(1)
|
|
119
|
+
finally:
|
|
120
|
+
await account_client.close()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@app.async_command("update")
|
|
124
|
+
async def room_update_command(
|
|
125
|
+
*,
|
|
126
|
+
project_id: ProjectIdOption,
|
|
127
|
+
id: Annotated[Optional[str], typer.Option(help="Room ID (preferred)")] = None,
|
|
128
|
+
name: Optional[str] = None,
|
|
129
|
+
new_name: Annotated[str, typer.Option(..., help="New room name")],
|
|
130
|
+
):
|
|
131
|
+
"""
|
|
132
|
+
Update a room's name (ID is preferred; name will be resolved to ID if needed).
|
|
133
|
+
"""
|
|
134
|
+
account_client = await get_client()
|
|
135
|
+
try:
|
|
136
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
137
|
+
room_name = resolve_room(name) if name else None
|
|
138
|
+
rid = await _resolve_room_id_or_fail(
|
|
139
|
+
account_client, project_id=project_id, room_id=id, room_name=room_name
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
print(
|
|
143
|
+
f"[bold green]Updating room id={rid} -> name='{new_name}'...[/bold green]"
|
|
144
|
+
)
|
|
145
|
+
await account_client.update_room(
|
|
146
|
+
project_id=project_id, room_id=rid, name=new_name
|
|
147
|
+
)
|
|
148
|
+
print("[bold cyan]Room updated.[/bold cyan]")
|
|
149
|
+
except RoomException as ex:
|
|
150
|
+
print(f"[red]{ex}[/red]")
|
|
151
|
+
raise typer.Exit(1)
|
|
152
|
+
finally:
|
|
153
|
+
await account_client.close()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@app.async_command("list")
|
|
157
|
+
async def room_list_command(
|
|
158
|
+
*,
|
|
159
|
+
project_id: ProjectIdOption,
|
|
160
|
+
limit: Annotated[
|
|
161
|
+
int, typer.Option(help="Max rooms to return", min=1, max=500)
|
|
162
|
+
] = 50,
|
|
163
|
+
offset: Annotated[int, typer.Option(help="Offset for pagination", min=0)] = 0,
|
|
164
|
+
order_by: Annotated[
|
|
165
|
+
str, typer.Option(help='Order by column (e.g. "room_name", "created_at")')
|
|
166
|
+
] = "room_name",
|
|
167
|
+
):
|
|
168
|
+
"""
|
|
169
|
+
List rooms in the project.
|
|
170
|
+
"""
|
|
171
|
+
account_client = await get_client()
|
|
172
|
+
try:
|
|
173
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
174
|
+
print("[bold green]Fetching rooms...[/bold green]")
|
|
175
|
+
|
|
176
|
+
rooms = await account_client.list_rooms(
|
|
177
|
+
project_id=project_id,
|
|
178
|
+
limit=limit,
|
|
179
|
+
offset=offset,
|
|
180
|
+
order_by=order_by,
|
|
181
|
+
)
|
|
182
|
+
output = [{"id": r.id, "name": r.name, "metadata": r.metadata} for r in rooms]
|
|
183
|
+
print(json.dumps(output, indent=2))
|
|
184
|
+
except RoomException as ex:
|
|
185
|
+
print(f"[red]{ex}[/red]")
|
|
186
|
+
raise typer.Exit(1)
|
|
187
|
+
finally:
|
|
188
|
+
await account_client.close()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@app.async_command("get")
|
|
192
|
+
async def room_get_command(
|
|
193
|
+
*,
|
|
194
|
+
project_id: ProjectIdOption,
|
|
195
|
+
name: Optional[str] = None,
|
|
196
|
+
):
|
|
197
|
+
"""
|
|
198
|
+
Get a single room by name (handy for resolving the ID).
|
|
199
|
+
"""
|
|
200
|
+
account_client = await get_client()
|
|
201
|
+
try:
|
|
202
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
203
|
+
room_name = resolve_room(name)
|
|
204
|
+
|
|
205
|
+
print(f"[bold green]Fetching room '{room_name}'...[/bold green]")
|
|
206
|
+
r = await account_client.get_room(project_id=project_id, name=room_name)
|
|
207
|
+
print(
|
|
208
|
+
json.dumps({"id": r.id, "name": r.name, "metadata": r.metadata}, indent=2)
|
|
209
|
+
)
|
|
210
|
+
except RoomException as ex:
|
|
211
|
+
print(f"[red]{ex}[/red]")
|
|
212
|
+
raise typer.Exit(1)
|
|
213
|
+
finally:
|
|
214
|
+
await account_client.close()
|