meshagent-cli 0.0.17__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 +0 -0
- meshagent/cli/agent.py +259 -0
- meshagent/cli/api_keys.py +74 -0
- meshagent/cli/async_typer.py +31 -0
- meshagent/cli/auth.py +28 -0
- meshagent/cli/auth_async.py +115 -0
- meshagent/cli/call.py +127 -0
- meshagent/cli/cli.py +35 -0
- meshagent/cli/cli_mcp.py +113 -0
- meshagent/cli/cli_secrets.py +383 -0
- meshagent/cli/developer.py +76 -0
- meshagent/cli/helper.py +113 -0
- meshagent/cli/messaging.py +192 -0
- meshagent/cli/participant_token.py +33 -0
- meshagent/cli/projects.py +31 -0
- meshagent/cli/services.py +177 -0
- meshagent/cli/sessions.py +19 -0
- meshagent/cli/storage.py +801 -0
- meshagent/cli/version.py +1 -0
- meshagent/cli/webhook.py +89 -0
- meshagent_cli-0.0.17.dist-info/METADATA +23 -0
- meshagent_cli-0.0.17.dist-info/RECORD +25 -0
- meshagent_cli-0.0.17.dist-info/WHEEL +5 -0
- meshagent_cli-0.0.17.dist-info/entry_points.txt +2 -0
- meshagent_cli-0.0.17.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
from meshagent.cli import async_typer
|
|
2
|
+
import typer
|
|
3
|
+
from meshagent.cli.helper import get_client, resolve_project_id, resolve_api_key
|
|
4
|
+
from rich import print
|
|
5
|
+
from meshagent.api import RoomClient, ParticipantToken, WebSocketClientProtocol
|
|
6
|
+
from meshagent.api.helpers import meshagent_base_url, websocket_room_url
|
|
7
|
+
from typing import Annotated, Optional
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
app = async_typer.AsyncTyper()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@app.async_command("list")
|
|
14
|
+
async def messaging_list_participants_command(
|
|
15
|
+
*,
|
|
16
|
+
project_id: str = None,
|
|
17
|
+
room: Annotated[str, typer.Option()],
|
|
18
|
+
api_key_id: Annotated[Optional[str], typer.Option()] = None,
|
|
19
|
+
name: Annotated[str, typer.Option()] = "cli",
|
|
20
|
+
role: str = "user"
|
|
21
|
+
):
|
|
22
|
+
"""
|
|
23
|
+
List all messaging-enabled participants in the room.
|
|
24
|
+
"""
|
|
25
|
+
account_client = await get_client()
|
|
26
|
+
try:
|
|
27
|
+
# Resolve project_id if not provided
|
|
28
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
29
|
+
api_key_id = await resolve_api_key(project_id, api_key_id)
|
|
30
|
+
|
|
31
|
+
# Decrypt the API key
|
|
32
|
+
key = (await account_client.decrypt_project_api_key(project_id=project_id, id=api_key_id))["token"]
|
|
33
|
+
|
|
34
|
+
# Build participant token
|
|
35
|
+
token = ParticipantToken(
|
|
36
|
+
name=name,
|
|
37
|
+
project_id=project_id,
|
|
38
|
+
api_key_id=api_key_id
|
|
39
|
+
)
|
|
40
|
+
token.add_role_grant(role=role)
|
|
41
|
+
token.add_room_grant(room)
|
|
42
|
+
|
|
43
|
+
print("[bold green]Connecting to room...[/bold green]")
|
|
44
|
+
async with RoomClient(
|
|
45
|
+
protocol=WebSocketClientProtocol(
|
|
46
|
+
url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
|
|
47
|
+
token=token.to_jwt(token=key)
|
|
48
|
+
)
|
|
49
|
+
) as client:
|
|
50
|
+
|
|
51
|
+
# Must enable before we can see who else is enabled
|
|
52
|
+
await client.messaging.enable()
|
|
53
|
+
await client.messaging.start()
|
|
54
|
+
|
|
55
|
+
participants = client.messaging.get_participants()
|
|
56
|
+
output = []
|
|
57
|
+
for p in participants:
|
|
58
|
+
output.append({
|
|
59
|
+
"id": p.id,
|
|
60
|
+
"role": p.role,
|
|
61
|
+
"attributes": p._attributes
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
print(json.dumps(output, indent=2))
|
|
65
|
+
|
|
66
|
+
await client.messaging.stop()
|
|
67
|
+
|
|
68
|
+
finally:
|
|
69
|
+
await account_client.close()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@app.async_command("send")
|
|
73
|
+
async def messaging_send_command(
|
|
74
|
+
*,
|
|
75
|
+
project_id: str = None,
|
|
76
|
+
room: Annotated[str, typer.Option()],
|
|
77
|
+
api_key_id: Annotated[Optional[str], typer.Option()] = None,
|
|
78
|
+
name: Annotated[str, typer.Option()] = "cli",
|
|
79
|
+
role: str = "user",
|
|
80
|
+
to_participant_id: Annotated[str, typer.Option(..., help="Participant ID to send a message to")],
|
|
81
|
+
data: Annotated[str, typer.Option(..., help="JSON message to send")],
|
|
82
|
+
):
|
|
83
|
+
"""
|
|
84
|
+
Send a direct message to a single participant in the room.
|
|
85
|
+
"""
|
|
86
|
+
account_client = await get_client()
|
|
87
|
+
try:
|
|
88
|
+
# Resolve project_id if not provided
|
|
89
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
90
|
+
api_key_id = await resolve_api_key(project_id, api_key_id)
|
|
91
|
+
|
|
92
|
+
# Decrypt the API key
|
|
93
|
+
key = (await account_client.decrypt_project_api_key(project_id=project_id, id=api_key_id))["token"]
|
|
94
|
+
|
|
95
|
+
# Build participant token
|
|
96
|
+
token = ParticipantToken(
|
|
97
|
+
name=name,
|
|
98
|
+
project_id=project_id,
|
|
99
|
+
api_key_id=api_key_id
|
|
100
|
+
)
|
|
101
|
+
token.add_role_grant(role=role)
|
|
102
|
+
token.add_room_grant(room)
|
|
103
|
+
|
|
104
|
+
print("[bold green]Connecting to room...[/bold green]")
|
|
105
|
+
async with RoomClient(
|
|
106
|
+
protocol=WebSocketClientProtocol(
|
|
107
|
+
url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
|
|
108
|
+
token=token.to_jwt(token=key)
|
|
109
|
+
)
|
|
110
|
+
) as client:
|
|
111
|
+
# Create and enable messaging
|
|
112
|
+
await client.messaging.enable()
|
|
113
|
+
await client.messaging.start()
|
|
114
|
+
|
|
115
|
+
# Find the participant we want to message
|
|
116
|
+
participant = None
|
|
117
|
+
for p in client.messaging.get_participants():
|
|
118
|
+
if p.id == to_participant_id:
|
|
119
|
+
participant = p
|
|
120
|
+
break
|
|
121
|
+
|
|
122
|
+
if participant is None:
|
|
123
|
+
print(f"[bold red]Participant with ID {to_participant_id} not found or not messaging-enabled.[/bold red]")
|
|
124
|
+
else:
|
|
125
|
+
# Send the message
|
|
126
|
+
await client.messaging.send_message(
|
|
127
|
+
to=participant,
|
|
128
|
+
type="chat.message",
|
|
129
|
+
message=json.loads(data),
|
|
130
|
+
attachment=None
|
|
131
|
+
)
|
|
132
|
+
print("[bold cyan]Message sent successfully.[/bold cyan]")
|
|
133
|
+
|
|
134
|
+
await client.messaging.stop()
|
|
135
|
+
finally:
|
|
136
|
+
await account_client.close()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@app.async_command("broadcast")
|
|
141
|
+
async def messaging_broadcast_command(
|
|
142
|
+
*,
|
|
143
|
+
project_id: str = None,
|
|
144
|
+
room: Annotated[str, typer.Option()],
|
|
145
|
+
api_key_id: Annotated[Optional[str], typer.Option()] = None,
|
|
146
|
+
name: Annotated[str, typer.Option()] = "cli",
|
|
147
|
+
role: str = "user",
|
|
148
|
+
data: Annotated[str, typer.Option(..., help="JSON message to broadcast")]
|
|
149
|
+
):
|
|
150
|
+
"""
|
|
151
|
+
Broadcast a message to all messaging-enabled participants in the room.
|
|
152
|
+
"""
|
|
153
|
+
account_client = await get_client()
|
|
154
|
+
try:
|
|
155
|
+
# Resolve project_id if not provided
|
|
156
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
157
|
+
api_key_id = await resolve_api_key(project_id, api_key_id)
|
|
158
|
+
|
|
159
|
+
# Decrypt the API key
|
|
160
|
+
key = (await account_client.decrypt_project_api_key(project_id=project_id, id=api_key_id))["token"]
|
|
161
|
+
|
|
162
|
+
# Build participant token
|
|
163
|
+
token = ParticipantToken(
|
|
164
|
+
name=name,
|
|
165
|
+
project_id=project_id,
|
|
166
|
+
api_key_id=api_key_id
|
|
167
|
+
)
|
|
168
|
+
token.add_role_grant(role=role)
|
|
169
|
+
token.add_room_grant(room)
|
|
170
|
+
|
|
171
|
+
print("[bold green]Connecting to room...[/bold green]")
|
|
172
|
+
async with RoomClient(
|
|
173
|
+
protocol=WebSocketClientProtocol(
|
|
174
|
+
url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
|
|
175
|
+
token=token.to_jwt(token=key)
|
|
176
|
+
)
|
|
177
|
+
) as client:
|
|
178
|
+
# Create and enable messaging
|
|
179
|
+
await client.messaging.enable()
|
|
180
|
+
await client.messaging.start()
|
|
181
|
+
|
|
182
|
+
# Broadcast the message
|
|
183
|
+
await client.messaging.broadcast_message(
|
|
184
|
+
type="chat.broadcast",
|
|
185
|
+
message=json.loads(data),
|
|
186
|
+
attachment=None
|
|
187
|
+
)
|
|
188
|
+
print("[bold cyan]Broadcast message sent successfully.[/bold cyan]")
|
|
189
|
+
|
|
190
|
+
await client.messaging.stop()
|
|
191
|
+
finally:
|
|
192
|
+
await account_client.close()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from meshagent.cli import async_typer
|
|
2
|
+
import typer
|
|
3
|
+
from meshagent.cli.helper import get_client, print_json_table, set_active_project, resolve_project_id
|
|
4
|
+
from rich import print
|
|
5
|
+
from meshagent.api import RoomClient, ParticipantToken
|
|
6
|
+
from meshagent.cli.helper import set_active_project, get_active_project, resolve_project_id
|
|
7
|
+
from typing import Annotated, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
app = async_typer.AsyncTyper()
|
|
11
|
+
|
|
12
|
+
@app.async_command("generate")
|
|
13
|
+
async def generate(*, project_id: str = None, room: Annotated[str, typer.Option()], api_key_id: Annotated[Optional[str], typer.Option()] = None, name: Annotated[str, typer.Option()], role: str = "agent"):
|
|
14
|
+
client = await get_client()
|
|
15
|
+
try:
|
|
16
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
17
|
+
|
|
18
|
+
key = (await client.decrypt_project_api_key(project_id=project_id, id=api_key_id))["token"]
|
|
19
|
+
|
|
20
|
+
token = ParticipantToken(
|
|
21
|
+
name=name,
|
|
22
|
+
project_id=project_id,
|
|
23
|
+
api_key_id=api_key_id
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
token.add_role_grant(role=role)
|
|
27
|
+
|
|
28
|
+
token.add_room_grant(room)
|
|
29
|
+
|
|
30
|
+
print(token.to_jwt(token=key))
|
|
31
|
+
|
|
32
|
+
finally:
|
|
33
|
+
await client.close()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from meshagent.cli import async_typer
|
|
2
|
+
import typer
|
|
3
|
+
from meshagent.cli.helper import get_client, print_json_table, set_active_project
|
|
4
|
+
from rich import print
|
|
5
|
+
|
|
6
|
+
app = async_typer.AsyncTyper()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@app.async_command("list")
|
|
10
|
+
async def list():
|
|
11
|
+
client = await get_client()
|
|
12
|
+
projects = await client.list_projects()
|
|
13
|
+
print_json_table(projects["projects"], "id", "name")
|
|
14
|
+
await client.close()
|
|
15
|
+
|
|
16
|
+
@app.async_command("activate")
|
|
17
|
+
async def activate(project_id: str):
|
|
18
|
+
client = await get_client()
|
|
19
|
+
try:
|
|
20
|
+
projects = await client.list_projects()
|
|
21
|
+
projects = projects["projects"]
|
|
22
|
+
for project in projects:
|
|
23
|
+
if project["id"] == project_id:
|
|
24
|
+
await set_active_project(project_id=project_id)
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
print(f"[red]Invalid project id: {project_id}[/red]")
|
|
28
|
+
raise typer.Exit(code=1)
|
|
29
|
+
finally:
|
|
30
|
+
await client.close()
|
|
31
|
+
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# ---------------------------------------------------------------------------
|
|
2
|
+
# Imports
|
|
3
|
+
# ---------------------------------------------------------------------------
|
|
4
|
+
from meshagent.cli import async_typer
|
|
5
|
+
import typer
|
|
6
|
+
from rich import print
|
|
7
|
+
from meshagent.cli.helper import get_client, print_json_table, resolve_project_id
|
|
8
|
+
from typing import Annotated, List, Optional, Dict
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
|
+
from pydantic_yaml import parse_yaml_raw_as, to_yaml_str
|
|
11
|
+
from pydantic import PositiveInt
|
|
12
|
+
import pydantic
|
|
13
|
+
from typing import Literal
|
|
14
|
+
|
|
15
|
+
# Pydantic basemodels
|
|
16
|
+
from meshagent.api.accounts_client import Service, Port
|
|
17
|
+
|
|
18
|
+
app = async_typer.AsyncTyper()
|
|
19
|
+
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
# Utilities
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
def _kv_to_dict(pairs: List[str]) -> Dict[str, str]:
|
|
25
|
+
"""Convert ["A=1","B=2"] → {"A":"1","B":"2"}."""
|
|
26
|
+
out: Dict[str, str] = {}
|
|
27
|
+
for p in pairs:
|
|
28
|
+
if "=" not in p:
|
|
29
|
+
raise typer.BadParameter(f"'{p}' must be KEY=VALUE")
|
|
30
|
+
k, v = p.split("=", 1)
|
|
31
|
+
out[k] = v
|
|
32
|
+
return out
|
|
33
|
+
|
|
34
|
+
class PortSpec(pydantic.BaseModel):
|
|
35
|
+
"""
|
|
36
|
+
CLI schema for --port.
|
|
37
|
+
Example:
|
|
38
|
+
--port num=8080 type=webserver liveness=/health
|
|
39
|
+
"""
|
|
40
|
+
num: PositiveInt
|
|
41
|
+
type: Literal["mcp.sse", "meshagent.callable", "http", "tcp"]
|
|
42
|
+
liveness: str | None = None
|
|
43
|
+
participant_name: str | None = None
|
|
44
|
+
|
|
45
|
+
def _parse_port_spec(spec: str) -> PortSpec:
|
|
46
|
+
"""
|
|
47
|
+
Convert "num=8080 type=webserver liveness=/health" → PortSpec.
|
|
48
|
+
The user should quote the whole string if it contains spaces.
|
|
49
|
+
"""
|
|
50
|
+
tokens = spec.strip().split()
|
|
51
|
+
kv: Dict[str, str] = {}
|
|
52
|
+
for t in tokens:
|
|
53
|
+
if "=" not in t:
|
|
54
|
+
raise typer.BadParameter(f"expected key=value, got '{t}'")
|
|
55
|
+
k, v = t.split("=", 1)
|
|
56
|
+
kv[k] = v
|
|
57
|
+
try:
|
|
58
|
+
return PortSpec(**kv)
|
|
59
|
+
except pydantic.ValidationError as exc:
|
|
60
|
+
raise typer.BadParameter(str(exc))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
# Commands
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
@app.async_command("create")
|
|
68
|
+
async def service_create(
|
|
69
|
+
*,
|
|
70
|
+
project_id: str = None,
|
|
71
|
+
name: Annotated[str, typer.Option(help="Friendly service name")],
|
|
72
|
+
image: Annotated[str, typer.Option(help="Container image reference")],
|
|
73
|
+
pull_secret: Annotated[
|
|
74
|
+
Optional[str],
|
|
75
|
+
typer.Option("--pull-secret", help="Secret ID for registry"),
|
|
76
|
+
] = None,
|
|
77
|
+
command: Annotated[
|
|
78
|
+
Optional[str],
|
|
79
|
+
typer.Option("--command", help="Override ENTRYPOINT/CMD"),
|
|
80
|
+
] = None,
|
|
81
|
+
env: Annotated[List[str], typer.Option("--env", "-e", help="KEY=VALUE")] = [],
|
|
82
|
+
env_secret: Annotated[List[str], typer.Option("--env-secret")] = [],
|
|
83
|
+
runtime_secret: Annotated[List[str], typer.Option("--runtime-secret")] = [],
|
|
84
|
+
room_storage_path: Annotated[
|
|
85
|
+
Optional[str],
|
|
86
|
+
typer.Option("--mount", help="Path inside container to mount room storage"),
|
|
87
|
+
] = None,
|
|
88
|
+
port: Annotated[
|
|
89
|
+
List[str],
|
|
90
|
+
typer.Option(
|
|
91
|
+
"--port",
|
|
92
|
+
"-p",
|
|
93
|
+
help=(
|
|
94
|
+
'Repeatable. Example:\n'
|
|
95
|
+
' -p "num=8080 type=[mcp.sse | meshagent.callable | http | tcp] liveness=/health"'
|
|
96
|
+
),
|
|
97
|
+
),
|
|
98
|
+
] = [],
|
|
99
|
+
):
|
|
100
|
+
"""Create a service attached to the project."""
|
|
101
|
+
client = await get_client()
|
|
102
|
+
try:
|
|
103
|
+
project_id = await resolve_project_id(project_id)
|
|
104
|
+
|
|
105
|
+
# ✅ validate / coerce port specs
|
|
106
|
+
port_specs: List[PortSpec] = [_parse_port_spec(s) for s in port]
|
|
107
|
+
|
|
108
|
+
ports_dict = (
|
|
109
|
+
{
|
|
110
|
+
ps.num: Port(
|
|
111
|
+
type=ps.type,
|
|
112
|
+
liveness=ps.liveness,
|
|
113
|
+
participant_name=ps.participant_name,
|
|
114
|
+
)
|
|
115
|
+
for ps in port_specs
|
|
116
|
+
}
|
|
117
|
+
or None
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
service_obj = Service(
|
|
121
|
+
id="",
|
|
122
|
+
created_at=datetime.now(timezone.utc).isoformat(),
|
|
123
|
+
name=name,
|
|
124
|
+
image=image,
|
|
125
|
+
command=command,
|
|
126
|
+
pull_secret=pull_secret,
|
|
127
|
+
room_storage_path=room_storage_path,
|
|
128
|
+
environment=_kv_to_dict(env),
|
|
129
|
+
environment_secrets=env_secret or None,
|
|
130
|
+
runtime_secrets=_kv_to_dict(runtime_secret),
|
|
131
|
+
ports=ports_dict,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
new_id = (await client.create_service(project_id=project_id, service=service_obj))["id"]
|
|
135
|
+
print(f"[green]Created service:[/] {new_id}")
|
|
136
|
+
|
|
137
|
+
finally:
|
|
138
|
+
await client.close()
|
|
139
|
+
|
|
140
|
+
@app.async_command("show")
|
|
141
|
+
async def service_show(*, project_id: str = None, service_id: Annotated[str, typer.Argument(help="ID of the service to delete")],):
|
|
142
|
+
"""Show a services for the project."""
|
|
143
|
+
client = await get_client()
|
|
144
|
+
try:
|
|
145
|
+
project_id = await resolve_project_id(project_id)
|
|
146
|
+
service = await client.get_service(project_id=project_id, service_id=service_id) # → List[Service]
|
|
147
|
+
print(service.model_dump(mode="json"))
|
|
148
|
+
finally:
|
|
149
|
+
await client.close()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@app.async_command("list")
|
|
153
|
+
async def service_list(*, project_id: str = None):
|
|
154
|
+
"""List all services for the project."""
|
|
155
|
+
client = await get_client()
|
|
156
|
+
try:
|
|
157
|
+
project_id = await resolve_project_id(project_id)
|
|
158
|
+
services : list[Service] = await client.list_services(project_id=project_id) # → List[Service]
|
|
159
|
+
print_json_table([svc.model_dump(mode="json") for svc in services], "id", "name", "image", "command", "room_storage_path", "pull_secret", "environment_secrets", "ports")
|
|
160
|
+
finally:
|
|
161
|
+
await client.close()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@app.async_command("delete")
|
|
165
|
+
async def service_delete(
|
|
166
|
+
*,
|
|
167
|
+
project_id: Optional[str] = None,
|
|
168
|
+
service_id: Annotated[str, typer.Argument(help="ID of the service to delete")],
|
|
169
|
+
):
|
|
170
|
+
"""Delete a service."""
|
|
171
|
+
client = await get_client()
|
|
172
|
+
try:
|
|
173
|
+
project_id = await resolve_project_id(project_id)
|
|
174
|
+
await client.delete_service(project_id=project_id, service_id=service_id)
|
|
175
|
+
print(f"[green]Service {service_id} deleted.[/]")
|
|
176
|
+
finally:
|
|
177
|
+
await client.close()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from meshagent.cli import async_typer
|
|
2
|
+
from meshagent.cli.helper import get_client, print_json_table, set_active_project, resolve_project_id
|
|
3
|
+
|
|
4
|
+
app = async_typer.AsyncTyper()
|
|
5
|
+
|
|
6
|
+
@app.async_command("list")
|
|
7
|
+
async def list(*, project_id: str = None):
|
|
8
|
+
client = await get_client()
|
|
9
|
+
sessions = await client.list_recent_sessions(project_id=await resolve_project_id(project_id=project_id))
|
|
10
|
+
print_json_table(sessions["sessions"])
|
|
11
|
+
await client.close()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.async_command("show")
|
|
15
|
+
async def show(*, project_id: str = None, session_id: str):
|
|
16
|
+
client = await get_client()
|
|
17
|
+
events = await client.list_session_events(project_id=await resolve_project_id(project_id=project_id), session_id=session_id)
|
|
18
|
+
print_json_table(events["events"], "type", "data")
|
|
19
|
+
await client.close()
|