meshagent-cli 0.7.0__py3-none-any.whl → 0.23.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.
Files changed (42) hide show
  1. meshagent/cli/agent.py +23 -13
  2. meshagent/cli/api_keys.py +4 -4
  3. meshagent/cli/async_typer.py +52 -4
  4. meshagent/cli/call.py +27 -36
  5. meshagent/cli/chatbot.py +1559 -177
  6. meshagent/cli/cli.py +23 -22
  7. meshagent/cli/cli_mcp.py +92 -28
  8. meshagent/cli/cli_secrets.py +10 -10
  9. meshagent/cli/common_options.py +19 -4
  10. meshagent/cli/containers.py +164 -16
  11. meshagent/cli/database.py +997 -0
  12. meshagent/cli/developer.py +3 -3
  13. meshagent/cli/exec.py +22 -6
  14. meshagent/cli/helper.py +101 -12
  15. meshagent/cli/helpers.py +65 -11
  16. meshagent/cli/host.py +41 -0
  17. meshagent/cli/mailbot.py +1104 -79
  18. meshagent/cli/mailboxes.py +223 -0
  19. meshagent/cli/meeting_transcriber.py +29 -15
  20. meshagent/cli/messaging.py +7 -10
  21. meshagent/cli/multi.py +357 -0
  22. meshagent/cli/oauth2.py +192 -40
  23. meshagent/cli/participant_token.py +5 -3
  24. meshagent/cli/port.py +70 -0
  25. meshagent/cli/queue.py +2 -2
  26. meshagent/cli/room.py +24 -212
  27. meshagent/cli/rooms.py +214 -0
  28. meshagent/cli/services.py +269 -37
  29. meshagent/cli/sessions.py +5 -5
  30. meshagent/cli/storage.py +5 -5
  31. meshagent/cli/sync.py +434 -0
  32. meshagent/cli/task_runner.py +1317 -0
  33. meshagent/cli/version.py +1 -1
  34. meshagent/cli/voicebot.py +544 -98
  35. meshagent/cli/webhook.py +7 -7
  36. meshagent/cli/worker.py +1403 -0
  37. {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.23.0.dist-info}/METADATA +15 -13
  38. meshagent_cli-0.23.0.dist-info/RECORD +45 -0
  39. {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.23.0.dist-info}/WHEEL +1 -1
  40. meshagent_cli-0.7.0.dist-info/RECORD +0 -36
  41. {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.23.0.dist-info}/entry_points.txt +0 -0
  42. {meshagent_cli-0.7.0.dist-info → meshagent_cli-0.23.0.dist-info}/top_level.txt +0 -0
meshagent/cli/multi.py ADDED
@@ -0,0 +1,357 @@
1
+ import typer
2
+ from meshagent.cli import async_typer
3
+ from meshagent.cli.host import run_services, set_deferred, service_specs, get_service
4
+ from meshagent.cli.common_options import ProjectIdOption
5
+ from typing import Annotated, Optional
6
+
7
+ import importlib.util
8
+ from pathlib import Path
9
+ import click
10
+ import shlex
11
+
12
+ from rich import print
13
+
14
+ from meshagent.agents import Agent
15
+
16
+ from typer.main import get_command
17
+
18
+ from meshagent.cli.common_options import RoomOption
19
+ from meshagent.cli.helper import (
20
+ get_client,
21
+ resolve_project_id,
22
+ )
23
+ from aiohttp import ClientResponseError
24
+ import asyncio
25
+
26
+ from meshagent.api import RoomClient
27
+
28
+ from meshagent.api.helpers import meshagent_base_url, websocket_room_url
29
+ from meshagent.api.websocket_protocol import WebSocketClientProtocol
30
+
31
+ from meshagent.cli.chatbot import service as chatbot_service
32
+ from meshagent.cli.worker import service as worker_service
33
+ from meshagent.cli.mailbot import service as mailbot_service
34
+ from meshagent.cli.voicebot import service as voicebot_service
35
+
36
+ from meshagent.cli.chatbot import join as chatbot_join
37
+ from meshagent.cli.worker import join as worker_join
38
+ from meshagent.cli.mailbot import join as mailbot_join
39
+ from meshagent.cli.voicebot import join as voicebot_join
40
+
41
+ import yaml
42
+
43
+
44
+ app = async_typer.AsyncTyper(help="Connect agents and tools to a room")
45
+
46
+ cli_service = async_typer.AsyncTyper(help="Add agents to a team")
47
+
48
+ cli_service.command("chatbot")(chatbot_service)
49
+ cli_service.command("worker")(worker_service)
50
+ cli_service.command("mailbot")(mailbot_service)
51
+ cli_service.command("voicebot")(voicebot_service)
52
+
53
+ cli_join = async_typer.AsyncTyper(help="Add agents to a team")
54
+ cli_join.command("chatbot")(chatbot_join)
55
+ cli_join.command("worker")(worker_join)
56
+ cli_join.command("mailbot")(mailbot_join)
57
+ cli_join.command("voicebot")(voicebot_join)
58
+
59
+
60
+ @cli_service.async_command("python")
61
+ async def python(
62
+ *,
63
+ module: str,
64
+ host: Annotated[
65
+ Optional[str], typer.Option(help="Host to bind the service on")
66
+ ] = None,
67
+ port: Annotated[
68
+ Optional[int], typer.Option(help="Port to bind the service on")
69
+ ] = None,
70
+ path: Annotated[
71
+ Optional[str],
72
+ typer.Option(help="A path to add the service at"),
73
+ ] = None,
74
+ identity: Annotated[
75
+ Optional[str],
76
+ typer.Option(help="The desired identity for the service"),
77
+ ] = None,
78
+ name: Annotated[
79
+ str, typer.Option(help="Entry-point name in the Python module")
80
+ ] = "main",
81
+ ):
82
+ service = get_service(host=host, port=port)
83
+
84
+ if path is None:
85
+ path = "/agent"
86
+ i = 0
87
+ while service.has_path(path):
88
+ i += 1
89
+ path = f"/agent{i}"
90
+
91
+ module = import_from_path(module)
92
+ service.add_path(path=path, identity=identity, cls=getattr(module, name or "main"))
93
+
94
+
95
+ def execute_via_root(app, line: str, *, prog_name="meshagent") -> int:
96
+ cmd = get_command(app)
97
+ try:
98
+ cmd.main(args=shlex.split(line), prog_name=prog_name, standalone_mode=False)
99
+ return 0
100
+ except click.ClickException as e:
101
+ e.show()
102
+ return e.exit_code
103
+
104
+
105
+ subcommand_help = """a list of sub commands to run, seperated by semicolons
106
+
107
+ available sub commands:
108
+
109
+ chatbot ...;
110
+ mailbot ...;
111
+ worker ...;
112
+ voicebot ...;
113
+ python path-to-python-file.py --name=NameOfModule;
114
+
115
+ chatbot, worker, and mailbot command arguments mirror those of the respective meshagent chatbot service, meshagent mailbot service, meshagent voicebot service, and meshagent worker service commands.
116
+ """
117
+
118
+
119
+ def build_spec(
120
+ *,
121
+ command: Annotated[str, typer.Option("-c", help=subcommand_help)],
122
+ service_name: Annotated[str, typer.Option("--service-name", help="service name")],
123
+ service_description: Annotated[
124
+ Optional[str], typer.Option("--service-description", help="service description")
125
+ ] = None,
126
+ service_title: Annotated[
127
+ Optional[str],
128
+ typer.Option("--service-title", help="a display name for the service"),
129
+ ] = None,
130
+ ):
131
+ for c in command.split(";"):
132
+ if execute_via_root(cli_service, c, prog_name="meshagent") != 0:
133
+ print(f"[red]{c} failed[/red]")
134
+ raise typer.Exit(1)
135
+
136
+ specs = service_specs()
137
+ if len(specs) == 0:
138
+ print("[red]found no services, specify at least one agent or tool to run[/red]")
139
+ raise typer.Exit(1)
140
+
141
+ if len(specs) > 1:
142
+ print(
143
+ "[red]found multiple services leave host and port empty or use the same port for each command[/red]"
144
+ )
145
+ raise typer.Exit(1)
146
+
147
+ spec = specs[0]
148
+ spec.metadata.annotations = {
149
+ "meshagent.service.id": service_name,
150
+ }
151
+ for port in spec.ports:
152
+ port.num = "*"
153
+
154
+ spec.metadata.name = service_name
155
+ spec.metadata.description = service_description
156
+ spec.container.image = (
157
+ "us-central1-docker.pkg.dev/meshagent-public/images/cli:{SERVER_VERSION}-esgz"
158
+ )
159
+ spec.container.command = (
160
+ f'meshagent multi service -c "{command.replace('"', '\\"')}"'
161
+ )
162
+
163
+
164
+ @app.async_command("spec")
165
+ async def spec(
166
+ command: Annotated[str, typer.Option("-c", help=subcommand_help)],
167
+ service_name: Annotated[str, typer.Option("--service-name", help="service name")],
168
+ service_description: Annotated[
169
+ Optional[str], typer.Option("--service-description", help="service description")
170
+ ] = None,
171
+ service_title: Annotated[
172
+ Optional[str],
173
+ typer.Option("--service-title", help="a display name for the service"),
174
+ ] = None,
175
+ ):
176
+ set_deferred(True)
177
+
178
+ spec = build_spec(
179
+ command=command,
180
+ service_name=service_name,
181
+ service_description=service_description,
182
+ service_title=service_title,
183
+ )
184
+
185
+ print(yaml.dump(spec.model_dump(mode="json", exclude_none=True), sort_keys=False))
186
+
187
+
188
+ @app.async_command("deploy")
189
+ async def deploy(
190
+ project_id: ProjectIdOption,
191
+ command: Annotated[str, typer.Option("-c", help=subcommand_help)],
192
+ service_name: Annotated[str, typer.Option("--service-name", help="service name")],
193
+ service_description: Annotated[
194
+ Optional[str], typer.Option("--service-description", help="service description")
195
+ ] = None,
196
+ service_title: Annotated[
197
+ Optional[str],
198
+ typer.Option("--service-title", help="a display name for the service"),
199
+ ] = None,
200
+ room: Annotated[
201
+ Optional[str],
202
+ typer.Option("--room", help="The name of a room to create the service for"),
203
+ ] = None,
204
+ ):
205
+ project_id = await resolve_project_id(project_id)
206
+
207
+ client = await get_client()
208
+ try:
209
+ set_deferred(True)
210
+
211
+ spec = build_spec(
212
+ command=command,
213
+ service_name=service_name,
214
+ service_description=service_description,
215
+ service_title=service_title,
216
+ )
217
+
218
+ spec.container.secrets = []
219
+
220
+ id = None
221
+ try:
222
+ if id is None:
223
+ if room is None:
224
+ services = await client.list_services(project_id=project_id)
225
+ else:
226
+ services = await client.list_room_services(
227
+ project_id=project_id, room_name=room
228
+ )
229
+
230
+ for s in services:
231
+ if s.metadata.name == spec.metadata.name:
232
+ id = s.id
233
+
234
+ if id is None:
235
+ if room is None:
236
+ id = await client.create_service(
237
+ project_id=project_id, service=spec
238
+ )
239
+ else:
240
+ id = await client.create_room_service(
241
+ project_id=project_id, service=spec, room_name=room
242
+ )
243
+
244
+ else:
245
+ spec.id = id
246
+ if room is None:
247
+ await client.update_service(
248
+ project_id=project_id, service_id=id, service=spec
249
+ )
250
+ else:
251
+ await client.update_room_service(
252
+ project_id=project_id,
253
+ service_id=id,
254
+ service=spec,
255
+ room_name=room,
256
+ )
257
+
258
+ except ClientResponseError as exc:
259
+ if exc.status == 409:
260
+ print(f"[red]Service name already in use: {spec.metadata.name}[/red]")
261
+ raise typer.Exit(code=1)
262
+ raise
263
+ else:
264
+ print(f"[green]Deployed service:[/] {id}")
265
+
266
+ finally:
267
+ await client.close()
268
+
269
+
270
+ @app.async_command("service")
271
+ async def host(
272
+ host: Annotated[
273
+ Optional[str], typer.Option(help="Host to bind the service on")
274
+ ] = None,
275
+ port: Annotated[
276
+ Optional[int], typer.Option(help="Port to bind the service on")
277
+ ] = None,
278
+ command: Annotated[str, typer.Option("-c", help=subcommand_help)] = [],
279
+ ):
280
+ set_deferred(True)
281
+
282
+ for c in command.split(";"):
283
+ if execute_via_root(cli_service, c, prog_name="meshagent") != 0:
284
+ print(f"[red]{c} failed[/red]")
285
+ raise typer.Exit(1)
286
+
287
+ await run_services()
288
+
289
+
290
+ def import_from_path(path: str, module_name: str | None = None):
291
+ path = Path(path)
292
+ module_name = module_name or path.stem
293
+
294
+ spec = importlib.util.spec_from_file_location(module_name, path)
295
+ if spec is None or spec.loader is None:
296
+ raise ImportError(f"Cannot load spec for {path}")
297
+
298
+ module = importlib.util.module_from_spec(spec)
299
+ spec.loader.exec_module(module)
300
+ return module
301
+
302
+
303
+ @app.async_command("join")
304
+ async def join(
305
+ *,
306
+ project_id: ProjectIdOption,
307
+ command: Annotated[str, typer.Option("-c", help=subcommand_help)] = [],
308
+ port: Annotated[
309
+ int,
310
+ typer.Option(
311
+ "--port",
312
+ "-p",
313
+ help=(
314
+ "a port number to run the agent on (will set MESHAGENT_PORT environment variable when launching the service)"
315
+ ),
316
+ ),
317
+ ] = None,
318
+ room: RoomOption,
319
+ key: Annotated[
320
+ str,
321
+ typer.Option("--key", help="an api key to sign the token with"),
322
+ ] = None,
323
+ ):
324
+ set_deferred(True)
325
+
326
+ if room is None:
327
+ print("[bold red]--room is required[/bold red]")
328
+ raise typer.Exit(-1)
329
+
330
+ for c in command.split(";"):
331
+ execute_via_root(cli_join, c + f" --room={room}", prog_name="meshagent")
332
+
333
+ from meshagent.cli.host import agents
334
+
335
+ try:
336
+
337
+ async def run_agent(agent: Agent, jwt: str):
338
+ nonlocal room
339
+
340
+ async with RoomClient(
341
+ protocol=WebSocketClientProtocol(
342
+ url=websocket_room_url(
343
+ room_name=room, base_url=meshagent_base_url()
344
+ ),
345
+ token=jwt,
346
+ )
347
+ ) as room:
348
+ await agent.start(room=room)
349
+ await room.protocol.wait_for_close()
350
+ await agent.stop()
351
+
352
+ await asyncio.gather(
353
+ *([asyncio.create_task(run_agent(agent, jwt)) for agent, jwt in agents])
354
+ )
355
+
356
+ except KeyboardInterrupt:
357
+ pass
meshagent/cli/oauth2.py CHANGED
@@ -9,25 +9,52 @@ from meshagent.api import RoomClient, WebSocketClientProtocol
9
9
  from meshagent.api.helpers import meshagent_base_url, websocket_room_url
10
10
  from rich import print
11
11
  from typing import Annotated, Optional
12
+ from pathlib import Path
12
13
  import typer
13
14
  import json
15
+ import sys
14
16
 
15
17
  app = async_typer.AsyncTyper(help="OAuth2 test commands")
16
18
 
17
19
 
18
- @app.async_command("request")
20
+ def _read_bytes(*, input_path: str) -> bytes:
21
+ if input_path == "-":
22
+ return sys.stdin.buffer.read()
23
+ return Path(input_path).expanduser().resolve().read_bytes()
24
+
25
+
26
+ def _write_bytes(*, output_path: str, data: bytes) -> None:
27
+ if output_path == "-":
28
+ sys.stdout.buffer.write(data)
29
+ sys.stdout.buffer.flush()
30
+ return
31
+ Path(output_path).expanduser().resolve().write_bytes(data)
32
+
33
+
34
+ @app.async_command("oauth")
19
35
  async def oauth2(
20
36
  *,
21
- project_id: ProjectIdOption = None,
37
+ project_id: ProjectIdOption,
22
38
  room: RoomOption,
23
- from_participant_id: Annotated[str, typer.Option()],
24
- client_id: Annotated[str, typer.Option()],
25
- authorization_endpoint: Annotated[str, typer.Option()],
26
- token_endpoint: Annotated[str, typer.Option()],
27
- scopes: Annotated[Optional[str], typer.Option()] = None,
28
- client_secret: Annotated[Optional[str], typer.Option()],
29
- redirect_uri: Annotated[Optional[str], typer.Option()],
30
- pkce: Annotated[bool, typer.Option()] = True,
39
+ from_participant_id: Annotated[
40
+ str,
41
+ typer.Option(..., help="Participant ID to request the token from"),
42
+ ],
43
+ client_id: Annotated[str, typer.Option(..., help="OAuth client ID")],
44
+ authorization_endpoint: Annotated[
45
+ str, typer.Option(..., help="OAuth authorization endpoint URL")
46
+ ],
47
+ token_endpoint: Annotated[str, typer.Option(..., help="OAuth token endpoint URL")],
48
+ scopes: Annotated[
49
+ Optional[str], typer.Option(help="Comma-separated OAuth scopes")
50
+ ] = None,
51
+ client_secret: Annotated[
52
+ Optional[str], typer.Option(help="OAuth client secret (if required)")
53
+ ],
54
+ redirect_uri: Annotated[
55
+ Optional[str], typer.Option(help="Redirect URI for the OAuth flow")
56
+ ],
57
+ pkce: Annotated[bool, typer.Option(help="Use PKCE (recommended)")] = True,
31
58
  ):
32
59
  """
33
60
  Run an OAuth2 request test between two participants in the same room.
@@ -69,29 +96,157 @@ async def oauth2(
69
96
  await account_client.close()
70
97
 
71
98
 
99
+ @app.async_command("request")
100
+ async def secret_request(
101
+ *,
102
+ project_id: ProjectIdOption,
103
+ room: RoomOption,
104
+ from_participant_id: Annotated[
105
+ str,
106
+ typer.Option(..., help="Participant ID to request the secret from"),
107
+ ],
108
+ url: Annotated[str, typer.Option(..., help="Secret URL identifier")],
109
+ mime_type: Annotated[
110
+ str, typer.Option("--type", help="Secret MIME type")
111
+ ] = "application/octet-stream",
112
+ delegate_to: Annotated[
113
+ Optional[str],
114
+ typer.Option(help="Delegate secret to this participant name"),
115
+ ] = None,
116
+ timeout: Annotated[int, typer.Option(help="Timeout in seconds")] = 300,
117
+ out: Annotated[
118
+ str,
119
+ typer.Option(
120
+ "--out",
121
+ "-o",
122
+ help="Output file path, or '-' for stdout",
123
+ ),
124
+ ] = "-",
125
+ ):
126
+ """Request a secret from another participant."""
127
+
128
+ account_client = await get_client()
129
+ try:
130
+ project_id = await resolve_project_id(project_id=project_id)
131
+ jwt_consumer = await account_client.connect_room(
132
+ project_id=project_id, room=room
133
+ )
134
+
135
+ async with RoomClient(
136
+ protocol=WebSocketClientProtocol(
137
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
138
+ token=jwt_consumer.jwt,
139
+ )
140
+ ) as consumer:
141
+ typer.echo(
142
+ f"Requesting secret from participant {from_participant_id}...",
143
+ err=True,
144
+ )
145
+ secret = await consumer.secrets.request_secret(
146
+ url=url,
147
+ type=mime_type,
148
+ timeout=timeout,
149
+ from_participant_id=from_participant_id,
150
+ delegate_to=delegate_to,
151
+ )
152
+
153
+ _write_bytes(output_path=out, data=secret)
154
+ if out != "-":
155
+ typer.echo(f"Wrote {len(secret)} bytes to {out}", err=True)
156
+
157
+ finally:
158
+ await account_client.close()
159
+
160
+
72
161
  @app.async_command("get")
73
- async def get(
162
+ async def secret_get(
74
163
  *,
75
- project_id: ProjectIdOption = None,
164
+ project_id: ProjectIdOption,
76
165
  room: RoomOption,
77
- delegated_to: Annotated[str, typer.Option()],
78
- client_id: Annotated[str, typer.Option()],
79
- authorization_endpoint: Annotated[str, typer.Option()],
80
- token_endpoint: Annotated[str, typer.Option()],
81
- scopes: Annotated[Optional[str], typer.Option()] = None,
82
- client_secret: Annotated[Optional[str], typer.Option()],
83
- redirect_uri: Annotated[Optional[str], typer.Option()],
84
- pkce: Annotated[bool, typer.Option()] = True,
166
+ secret_id: Annotated[str, typer.Option(..., help="Secret ID")],
167
+ delegated_to: Annotated[
168
+ Optional[str],
169
+ typer.Option(help="Fetch a secret delegated to this participant name"),
170
+ ] = None,
171
+ out: Annotated[
172
+ str,
173
+ typer.Option(
174
+ "--out",
175
+ "-o",
176
+ help="Output file path, or '-' for stdout",
177
+ ),
178
+ ] = "-",
85
179
  ):
86
- """
87
- Run an OAuth2 request test between two participants in the same room.
88
- One will act as the consumer, the other as the provider.
89
- """
180
+ """Get a stored secret by secret id."""
90
181
 
91
182
  account_client = await get_client()
92
183
  try:
93
184
  project_id = await resolve_project_id(project_id=project_id)
185
+ jwt_consumer = await account_client.connect_room(
186
+ project_id=project_id, room=room
187
+ )
188
+
189
+ async with RoomClient(
190
+ protocol=WebSocketClientProtocol(
191
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
192
+ token=jwt_consumer.jwt,
193
+ )
194
+ ) as consumer:
195
+ resp = await consumer.secrets.get_secret(
196
+ secret_id=secret_id,
197
+ delegated_to=delegated_to,
198
+ )
199
+
200
+ if resp is None:
201
+ typer.echo("Secret not found", err=True)
202
+ raise typer.Exit(1)
203
+
204
+ typer.echo(
205
+ f"Got secret name={resp.name} mime_type={resp.mime_type} bytes={len(resp.data)}",
206
+ err=True,
207
+ )
208
+ _write_bytes(output_path=out, data=resp.data)
209
+ if out != "-":
210
+ typer.echo(f"Wrote {len(resp.data)} bytes to {out}", err=True)
211
+
212
+ finally:
213
+ await account_client.close()
214
+
94
215
 
216
+ @app.async_command("set")
217
+ async def secret_set(
218
+ *,
219
+ project_id: ProjectIdOption,
220
+ room: RoomOption,
221
+ secret_id: Annotated[str, typer.Option(..., help="Secret ID")],
222
+ mime_type: Annotated[
223
+ Optional[str],
224
+ typer.Option("--type", help="Secret MIME type"),
225
+ ] = None,
226
+ name: Annotated[
227
+ Optional[str],
228
+ typer.Option(help="Optional secret name"),
229
+ ] = None,
230
+ delegated_to: Annotated[
231
+ Optional[str],
232
+ typer.Option(help="Store a secret delegated to this participant name"),
233
+ ] = None,
234
+ input_path: Annotated[
235
+ str,
236
+ typer.Option(
237
+ "--in",
238
+ "-i",
239
+ help="Input file path, or '-' for stdin",
240
+ ),
241
+ ] = "-",
242
+ ):
243
+ """Set/store a secret (bytes from stdin or file)."""
244
+
245
+ secret_bytes = _read_bytes(input_path=input_path)
246
+
247
+ account_client = await get_client()
248
+ try:
249
+ project_id = await resolve_project_id(project_id=project_id)
95
250
  jwt_consumer = await account_client.connect_room(
96
251
  project_id=project_id, room=room
97
252
  )
@@ -102,20 +257,15 @@ async def get(
102
257
  token=jwt_consumer.jwt,
103
258
  )
104
259
  ) as consumer:
105
- print("[green]Requesting OAuth token from consumer side...[/green]")
106
- token = await consumer.secrets.get_offline_oauth_token(
107
- oauth=OAuthClientConfig(
108
- client_id=client_id,
109
- authorization_endpoint=authorization_endpoint,
110
- token_endpoint=token_endpoint,
111
- scopes=scopes.split(",") if scopes is not None else scopes,
112
- client_secret=client_secret,
113
- no_pkce=not pkce,
114
- ),
260
+ await consumer.secrets.set_secret(
261
+ secret_id=secret_id,
262
+ type=mime_type,
263
+ name=name,
115
264
  delegated_to=delegated_to,
265
+ data=secret_bytes,
116
266
  )
117
267
 
118
- print(f"[bold cyan]Got access token:[/bold cyan] {token}")
268
+ typer.echo(f"Stored {len(secret_bytes)} bytes", err=True)
119
269
 
120
270
  finally:
121
271
  await account_client.close()
@@ -124,7 +274,7 @@ async def get(
124
274
  @app.async_command("list")
125
275
  async def list(
126
276
  *,
127
- project_id: ProjectIdOption = None,
277
+ project_id: ProjectIdOption,
128
278
  room: RoomOption,
129
279
  ):
130
280
  """
@@ -145,7 +295,7 @@ async def list(
145
295
  token=jwt_consumer.jwt,
146
296
  )
147
297
  ) as consumer:
148
- secrets = await consumer.secrets.list_user_secrets()
298
+ secrets = await consumer.secrets.list_secrets()
149
299
  output = []
150
300
  for s in secrets:
151
301
  output.append(s.model_dump(mode="json"))
@@ -159,10 +309,12 @@ async def list(
159
309
  @app.async_command("delete")
160
310
  async def delete(
161
311
  *,
162
- project_id: ProjectIdOption = None,
312
+ project_id: ProjectIdOption,
163
313
  room: RoomOption,
164
314
  id: str,
165
- delegated_to: Annotated[Optional[str], typer.Option()] = None,
315
+ delegated_to: Annotated[
316
+ str, typer.Option(help="The value of the delegated_to field of the secret")
317
+ ],
166
318
  ):
167
319
  """
168
320
  delete a secret
@@ -182,7 +334,7 @@ async def delete(
182
334
  token=jwt_consumer.jwt,
183
335
  )
184
336
  ) as consumer:
185
- await consumer.secrets.delete_user_secret(id=id, delegated_to=delegated_to)
337
+ await consumer.secrets.delete_secret(id=id, delegated_to=delegated_to)
186
338
  print("deleted secret")
187
339
 
188
340
  finally:
@@ -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 = None,
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