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.

@@ -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()