meshagent-cli 0.0.36__py3-none-any.whl → 0.0.38__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 -1
- meshagent/cli/agent.py +139 -70
- meshagent/cli/api_keys.py +27 -9
- meshagent/cli/auth.py +9 -4
- meshagent/cli/auth_async.py +40 -17
- meshagent/cli/call.py +45 -36
- meshagent/cli/chatbot.py +127 -73
- meshagent/cli/cli.py +32 -19
- meshagent/cli/cli_mcp.py +183 -79
- meshagent/cli/cli_secrets.py +50 -20
- meshagent/cli/developer.py +19 -9
- meshagent/cli/helper.py +59 -39
- meshagent/cli/messaging.py +54 -31
- meshagent/cli/otel.py +36 -32
- meshagent/cli/participant_token.py +21 -12
- meshagent/cli/projects.py +3 -2
- meshagent/cli/services.py +50 -29
- meshagent/cli/sessions.py +9 -3
- meshagent/cli/storage.py +222 -94
- meshagent/cli/tty.py +24 -28
- meshagent/cli/version.py +1 -1
- meshagent/cli/voicebot.py +57 -34
- meshagent/cli/webhook.py +14 -5
- meshagent_cli-0.0.38.dist-info/METADATA +35 -0
- meshagent_cli-0.0.38.dist-info/RECORD +29 -0
- meshagent_cli-0.0.36.dist-info/METADATA +0 -28
- meshagent_cli-0.0.36.dist-info/RECORD +0 -29
- {meshagent_cli-0.0.36.dist-info → meshagent_cli-0.0.38.dist-info}/WHEEL +0 -0
- {meshagent_cli-0.0.36.dist-info → meshagent_cli-0.0.38.dist-info}/entry_points.txt +0 -0
- {meshagent_cli-0.0.36.dist-info → meshagent_cli-0.0.38.dist-info}/top_level.txt +0 -0
meshagent/cli/cli_mcp.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
from mcp.client.session import ClientSession
|
|
3
2
|
from mcp.client.sse import sse_client
|
|
4
3
|
from mcp.client.stdio import stdio_client, StdioServerParameters
|
|
@@ -8,14 +7,19 @@ from rich import print
|
|
|
8
7
|
from typing import Annotated, Optional, List
|
|
9
8
|
|
|
10
9
|
from meshagent.api.helpers import meshagent_base_url, websocket_room_url
|
|
11
|
-
from meshagent.api import RoomClient,
|
|
10
|
+
from meshagent.api import RoomClient, WebSocketClientProtocol, RoomException
|
|
12
11
|
from meshagent.cli import async_typer
|
|
13
|
-
from meshagent.cli.helper import
|
|
12
|
+
from meshagent.cli.helper import (
|
|
13
|
+
get_client,
|
|
14
|
+
resolve_project_id,
|
|
15
|
+
resolve_api_key,
|
|
16
|
+
resolve_token_jwt,
|
|
17
|
+
resolve_room,
|
|
18
|
+
)
|
|
14
19
|
|
|
15
|
-
from meshagent.tools.hosting import RemoteToolkit
|
|
20
|
+
from meshagent.tools.hosting import RemoteToolkit
|
|
16
21
|
|
|
17
22
|
from meshagent.mcp import MCPToolkit
|
|
18
|
-
from pathlib import Path
|
|
19
23
|
|
|
20
24
|
from meshagent.api.services import ServiceHost
|
|
21
25
|
import os
|
|
@@ -25,9 +29,20 @@ import shlex
|
|
|
25
29
|
|
|
26
30
|
app = async_typer.AsyncTyper()
|
|
27
31
|
|
|
32
|
+
|
|
28
33
|
@app.async_command("sse")
|
|
29
|
-
async def sse(
|
|
30
|
-
|
|
34
|
+
async def sse(
|
|
35
|
+
*,
|
|
36
|
+
project_id: str = None,
|
|
37
|
+
room: Annotated[str, typer.Option()],
|
|
38
|
+
token_path: Annotated[Optional[str], typer.Option()] = None,
|
|
39
|
+
api_key_id: Annotated[Optional[str], typer.Option()] = None,
|
|
40
|
+
name: Annotated[str, typer.Option(..., help="Participant name")] = "cli",
|
|
41
|
+
role: str = "tool",
|
|
42
|
+
url: Annotated[str, typer.Option()],
|
|
43
|
+
toolkit_name: Annotated[Optional[str], typer.Option()] = None,
|
|
44
|
+
):
|
|
45
|
+
if toolkit_name is None:
|
|
31
46
|
toolkit_name = "mcp"
|
|
32
47
|
|
|
33
48
|
account_client = await get_client()
|
|
@@ -36,20 +51,40 @@ async def sse(*, project_id: str = None, room: Annotated[str, typer.Option()], t
|
|
|
36
51
|
api_key_id = await resolve_api_key(project_id, api_key_id)
|
|
37
52
|
|
|
38
53
|
room = resolve_room(room)
|
|
39
|
-
jwt = await resolve_token_jwt(
|
|
40
|
-
|
|
54
|
+
jwt = await resolve_token_jwt(
|
|
55
|
+
project_id=project_id,
|
|
56
|
+
api_key_id=api_key_id,
|
|
57
|
+
token_path=token_path,
|
|
58
|
+
name=name,
|
|
59
|
+
role=role,
|
|
60
|
+
room=room,
|
|
61
|
+
)
|
|
62
|
+
|
|
41
63
|
print("[bold green]Connecting to room...[/bold green]")
|
|
42
|
-
async with RoomClient(
|
|
43
|
-
|
|
64
|
+
async with RoomClient(
|
|
65
|
+
protocol=WebSocketClientProtocol(
|
|
66
|
+
url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
|
|
67
|
+
token=jwt,
|
|
68
|
+
)
|
|
69
|
+
) as client:
|
|
44
70
|
async with sse_client(url) as (read_stream, write_stream):
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
71
|
+
async with ClientSession(
|
|
72
|
+
read_stream=read_stream, write_stream=write_stream
|
|
73
|
+
) as session:
|
|
48
74
|
mcp_tools_response = await session.list_tools()
|
|
49
|
-
|
|
50
|
-
toolkit = MCPToolkit(
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
|
|
76
|
+
toolkit = MCPToolkit(
|
|
77
|
+
name=toolkit_name,
|
|
78
|
+
session=session,
|
|
79
|
+
tools=mcp_tools_response.tools,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
remote_toolkit = RemoteToolkit(
|
|
83
|
+
name=toolkit.name,
|
|
84
|
+
tools=toolkit.tools,
|
|
85
|
+
title=toolkit.title,
|
|
86
|
+
description=toolkit.description,
|
|
87
|
+
)
|
|
53
88
|
|
|
54
89
|
await remote_toolkit.start(room=client)
|
|
55
90
|
try:
|
|
@@ -57,15 +92,26 @@ async def sse(*, project_id: str = None, room: Annotated[str, typer.Option()], t
|
|
|
57
92
|
except KeyboardInterrupt:
|
|
58
93
|
await remote_toolkit.stop()
|
|
59
94
|
|
|
60
|
-
|
|
61
95
|
except RoomException as e:
|
|
62
96
|
print(f"[red]{e}[/red]")
|
|
63
97
|
finally:
|
|
64
98
|
await account_client.close()
|
|
65
99
|
|
|
100
|
+
|
|
66
101
|
@app.async_command("stdio")
|
|
67
|
-
async def stdio(
|
|
68
|
-
|
|
102
|
+
async def stdio(
|
|
103
|
+
*,
|
|
104
|
+
project_id: str = None,
|
|
105
|
+
room: Annotated[str, typer.Option()],
|
|
106
|
+
token_path: Annotated[Optional[str], typer.Option()] = None,
|
|
107
|
+
api_key_id: Annotated[Optional[str], typer.Option()] = None,
|
|
108
|
+
name: Annotated[str, typer.Option(..., help="Participant name")] = "cli",
|
|
109
|
+
role: str = "tool",
|
|
110
|
+
command: Annotated[str, typer.Option()],
|
|
111
|
+
toolkit_name: Annotated[Optional[str], typer.Option()] = None,
|
|
112
|
+
env: Annotated[List[str], typer.Option("--env", "-e", help="KEY=VALUE")] = [],
|
|
113
|
+
):
|
|
114
|
+
if toolkit_name is None:
|
|
69
115
|
toolkit_name = "mcp"
|
|
70
116
|
|
|
71
117
|
account_client = await get_client()
|
|
@@ -74,26 +120,50 @@ async def stdio(*, project_id: str = None, room: Annotated[str, typer.Option()],
|
|
|
74
120
|
api_key_id = await resolve_api_key(project_id, api_key_id)
|
|
75
121
|
|
|
76
122
|
room = resolve_room(room)
|
|
77
|
-
jwt = await resolve_token_jwt(
|
|
123
|
+
jwt = await resolve_token_jwt(
|
|
124
|
+
project_id=project_id,
|
|
125
|
+
api_key_id=api_key_id,
|
|
126
|
+
token_path=token_path,
|
|
127
|
+
name=name,
|
|
128
|
+
role=role,
|
|
129
|
+
room=room,
|
|
130
|
+
)
|
|
78
131
|
|
|
79
132
|
print("[bold green]Connecting to room...[/bold green]")
|
|
80
|
-
async with RoomClient(
|
|
81
|
-
|
|
133
|
+
async with RoomClient(
|
|
134
|
+
protocol=WebSocketClientProtocol(
|
|
135
|
+
url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
|
|
136
|
+
token=jwt,
|
|
137
|
+
)
|
|
138
|
+
) as client:
|
|
82
139
|
parsed_command = shlex.split(command)
|
|
83
140
|
|
|
84
|
-
async with
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
141
|
+
async with (
|
|
142
|
+
stdio_client(
|
|
143
|
+
StdioServerParameters(
|
|
144
|
+
command=parsed_command[0], # Executable
|
|
145
|
+
args=parsed_command[1:], # Optional command line arguments
|
|
146
|
+
env=_kv_to_dict(env), # Optional environment variables
|
|
147
|
+
)
|
|
148
|
+
) as (read_stream, write_stream)
|
|
149
|
+
):
|
|
150
|
+
async with ClientSession(
|
|
151
|
+
read_stream=read_stream, write_stream=write_stream
|
|
152
|
+
) as session:
|
|
92
153
|
mcp_tools_response = await session.list_tools()
|
|
93
|
-
|
|
94
|
-
toolkit = MCPToolkit(
|
|
95
|
-
|
|
96
|
-
|
|
154
|
+
|
|
155
|
+
toolkit = MCPToolkit(
|
|
156
|
+
name=toolkit_name,
|
|
157
|
+
session=session,
|
|
158
|
+
tools=mcp_tools_response.tools,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
remote_toolkit = RemoteToolkit(
|
|
162
|
+
name=toolkit.name,
|
|
163
|
+
tools=toolkit.tools,
|
|
164
|
+
title=toolkit.title,
|
|
165
|
+
description=toolkit.description,
|
|
166
|
+
)
|
|
97
167
|
|
|
98
168
|
await remote_toolkit.start(room=client)
|
|
99
169
|
try:
|
|
@@ -101,107 +171,141 @@ async def stdio(*, project_id: str = None, room: Annotated[str, typer.Option()],
|
|
|
101
171
|
except KeyboardInterrupt:
|
|
102
172
|
await remote_toolkit.stop()
|
|
103
173
|
|
|
104
|
-
|
|
105
174
|
except RoomException as e:
|
|
106
175
|
print(f"[red]{e}[/red]")
|
|
107
176
|
finally:
|
|
108
177
|
await account_client.close()
|
|
109
178
|
|
|
110
|
-
@app.async_command("http-proxy")
|
|
111
|
-
async def stdio_host(*, command: Annotated[str, typer.Option()], host: Annotated[Optional[str], typer.Option()] = None, port: Annotated[Optional[int], typer.Option()] = None, path: Annotated[Optional[str], typer.Option()] = None, name: Annotated[Optional[str], typer.Option()] = None, env: Annotated[List[str], typer.Option("--env", "-e", help="KEY=VALUE")] = []):
|
|
112
179
|
|
|
180
|
+
@app.async_command("http-proxy")
|
|
181
|
+
async def stdio_host(
|
|
182
|
+
*,
|
|
183
|
+
command: Annotated[str, typer.Option()],
|
|
184
|
+
host: Annotated[Optional[str], typer.Option()] = None,
|
|
185
|
+
port: Annotated[Optional[int], typer.Option()] = None,
|
|
186
|
+
path: Annotated[Optional[str], typer.Option()] = None,
|
|
187
|
+
name: Annotated[Optional[str], typer.Option()] = None,
|
|
188
|
+
env: Annotated[List[str], typer.Option("--env", "-e", help="KEY=VALUE")] = [],
|
|
189
|
+
):
|
|
113
190
|
from fastmcp import FastMCP, Client
|
|
114
191
|
from fastmcp.client.transports import StdioTransport
|
|
115
192
|
|
|
116
193
|
parsed_command = shlex.split(command)
|
|
117
|
-
|
|
194
|
+
|
|
118
195
|
# Create a client that connects to the original server
|
|
119
196
|
proxy_client = Client(
|
|
120
|
-
transport
|
|
197
|
+
transport=StdioTransport(
|
|
198
|
+
parsed_command[0], parsed_command[1:], _kv_to_dict(env)
|
|
199
|
+
),
|
|
121
200
|
)
|
|
122
201
|
|
|
123
|
-
if name
|
|
202
|
+
if name is None:
|
|
124
203
|
name = "Stdio-to-Streamable Http Proxy"
|
|
125
204
|
|
|
126
205
|
# Create a proxy server that connects to the client and exposes its capabilities
|
|
127
206
|
proxy = FastMCP.as_proxy(proxy_client, name=name)
|
|
128
|
-
if path
|
|
207
|
+
if path is None:
|
|
129
208
|
path = "/mcp"
|
|
130
|
-
|
|
131
|
-
await proxy.run_async(transport='streamable-http', host=host, port=port, path=path)
|
|
132
209
|
|
|
210
|
+
await proxy.run_async(transport="streamable-http", host=host, port=port, path=path)
|
|
133
211
|
|
|
134
|
-
@app.async_command("sse-proxy")
|
|
135
|
-
async def stdio_host(*, command: Annotated[str, typer.Option()], host: Annotated[Optional[str], typer.Option()] = None, port: Annotated[Optional[int], typer.Option()] = None, path: Annotated[Optional[str], typer.Option()] = None, name: Annotated[Optional[str], typer.Option()] = None, env: Annotated[List[str], typer.Option("--env", "-e", help="KEY=VALUE")] = []):
|
|
136
212
|
|
|
213
|
+
@app.async_command("sse-proxy")
|
|
214
|
+
async def sse_proxy(
|
|
215
|
+
*,
|
|
216
|
+
command: Annotated[str, typer.Option()],
|
|
217
|
+
host: Annotated[Optional[str], typer.Option()] = None,
|
|
218
|
+
port: Annotated[Optional[int], typer.Option()] = None,
|
|
219
|
+
path: Annotated[Optional[str], typer.Option()] = None,
|
|
220
|
+
name: Annotated[Optional[str], typer.Option()] = None,
|
|
221
|
+
env: Annotated[List[str], typer.Option("--env", "-e", help="KEY=VALUE")] = [],
|
|
222
|
+
):
|
|
137
223
|
from fastmcp import FastMCP, Client
|
|
138
224
|
from fastmcp.client.transports import StdioTransport
|
|
139
225
|
|
|
140
226
|
parsed_command = shlex.split(command)
|
|
141
|
-
|
|
227
|
+
|
|
142
228
|
# Create a client that connects to the original server
|
|
143
229
|
proxy_client = Client(
|
|
144
|
-
transport
|
|
230
|
+
transport=StdioTransport(
|
|
231
|
+
parsed_command[0], parsed_command[1:], _kv_to_dict(env)
|
|
232
|
+
),
|
|
145
233
|
)
|
|
146
234
|
|
|
147
|
-
if name
|
|
235
|
+
if name is None:
|
|
148
236
|
name = "Stdio-to-SSE Proxy"
|
|
149
237
|
|
|
150
238
|
# Create a proxy server that connects to the client and exposes its capabilities
|
|
151
239
|
proxy = FastMCP.as_proxy(proxy_client, name=name)
|
|
152
|
-
if path
|
|
240
|
+
if path is None:
|
|
153
241
|
path = "/sse"
|
|
154
|
-
|
|
155
|
-
await proxy.run_async(transport=
|
|
242
|
+
|
|
243
|
+
await proxy.run_async(transport="sse", host=host, port=port, path=path)
|
|
244
|
+
|
|
156
245
|
|
|
157
246
|
@app.async_command("stdio-service")
|
|
158
|
-
async def
|
|
247
|
+
async def stdio_service(
|
|
248
|
+
*,
|
|
249
|
+
command: Annotated[str, typer.Option()],
|
|
250
|
+
host: Annotated[Optional[str], typer.Option()] = None,
|
|
251
|
+
port: Annotated[Optional[int], typer.Option()] = None,
|
|
252
|
+
webhook_secret: Annotated[Optional[str], typer.Option()] = None,
|
|
253
|
+
path: Annotated[Optional[str], typer.Option()] = None,
|
|
254
|
+
toolkit_name: Annotated[Optional[str], typer.Option()] = None,
|
|
255
|
+
env: Annotated[List[str], typer.Option("--env", "-e", help="KEY=VALUE")] = [],
|
|
256
|
+
):
|
|
159
257
|
try:
|
|
160
|
-
|
|
161
258
|
parsed_command = shlex.split(command)
|
|
162
|
-
|
|
163
|
-
async with stdio_client(StdioServerParameters(
|
|
164
|
-
command=parsed_command[0], # Executable
|
|
165
|
-
args=parsed_command[1:], # Optional command line arguments
|
|
166
|
-
env=_kv_to_dict(env), # Optional environment variables
|
|
167
|
-
)) as (read_stream, write_stream):
|
|
168
|
-
|
|
169
|
-
async with ClientSession(read_stream=read_stream, write_stream=write_stream) as session:
|
|
170
259
|
|
|
260
|
+
async with (
|
|
261
|
+
stdio_client(
|
|
262
|
+
StdioServerParameters(
|
|
263
|
+
command=parsed_command[0], # Executable
|
|
264
|
+
args=parsed_command[1:], # Optional command line arguments
|
|
265
|
+
env=_kv_to_dict(env), # Optional environment variables
|
|
266
|
+
)
|
|
267
|
+
) as (read_stream, write_stream)
|
|
268
|
+
):
|
|
269
|
+
async with ClientSession(
|
|
270
|
+
read_stream=read_stream, write_stream=write_stream
|
|
271
|
+
) as session:
|
|
171
272
|
mcp_tools_response = await session.list_tools()
|
|
172
|
-
|
|
173
|
-
if toolkit_name
|
|
273
|
+
|
|
274
|
+
if toolkit_name is None:
|
|
174
275
|
toolkit_name = "mcp"
|
|
175
276
|
|
|
176
|
-
toolkit = MCPToolkit(
|
|
177
|
-
|
|
178
|
-
|
|
277
|
+
toolkit = MCPToolkit(
|
|
278
|
+
name=toolkit_name, session=session, tools=mcp_tools_response.tools
|
|
279
|
+
)
|
|
179
280
|
|
|
281
|
+
if port is None:
|
|
180
282
|
port = int(os.getenv("MESHAGENT_PORT", "8080"))
|
|
181
283
|
|
|
182
|
-
if host
|
|
183
|
-
|
|
284
|
+
if host is None:
|
|
184
285
|
host = "0.0.0.0"
|
|
185
286
|
|
|
186
287
|
service_host = ServiceHost(
|
|
187
|
-
host=host,
|
|
188
|
-
port=port,
|
|
189
|
-
webhook_secret=webhook_secret
|
|
288
|
+
host=host, port=port, webhook_secret=webhook_secret
|
|
190
289
|
)
|
|
191
290
|
|
|
192
|
-
if path
|
|
291
|
+
if path is None:
|
|
193
292
|
path = "/service"
|
|
194
293
|
|
|
195
|
-
print(
|
|
294
|
+
print(
|
|
295
|
+
f"[bold green]Starting service host on {host}:{port}{path}...[/bold green]"
|
|
296
|
+
)
|
|
297
|
+
|
|
196
298
|
@service_host.path(path=path)
|
|
197
299
|
class CustomToolkit(RemoteToolkit):
|
|
198
300
|
def __init__(self):
|
|
199
|
-
super().__init__(
|
|
301
|
+
super().__init__(
|
|
302
|
+
name=toolkit.name,
|
|
303
|
+
tools=toolkit.tools,
|
|
304
|
+
title=toolkit.title,
|
|
305
|
+
description=toolkit.description,
|
|
306
|
+
)
|
|
200
307
|
|
|
201
|
-
|
|
202
308
|
await service_host.run()
|
|
203
309
|
|
|
204
|
-
|
|
205
310
|
except RoomException as e:
|
|
206
311
|
print(f"[red]{e}[/red]")
|
|
207
|
-
|
meshagent/cli/cli_secrets.py
CHANGED
|
@@ -7,7 +7,11 @@ from typing import Annotated, Dict, Optional
|
|
|
7
7
|
|
|
8
8
|
from meshagent.cli import async_typer
|
|
9
9
|
from meshagent.cli.helper import get_client, print_json_table, resolve_project_id
|
|
10
|
-
from meshagent.api.accounts_client import
|
|
10
|
+
from meshagent.api.accounts_client import (
|
|
11
|
+
PullSecret,
|
|
12
|
+
KeysSecret,
|
|
13
|
+
SecretLike,
|
|
14
|
+
) # or wherever you defined them
|
|
11
15
|
|
|
12
16
|
# --------------------------------------------------------------------------
|
|
13
17
|
# App Definition
|
|
@@ -19,6 +23,7 @@ secrets_app = async_typer.AsyncTyper(help="Manage secrets for your project.")
|
|
|
19
23
|
# Utility helpers
|
|
20
24
|
# --------------------------------------------------------------------------
|
|
21
25
|
|
|
26
|
+
|
|
22
27
|
def _parse_kv_inline(source: str | None) -> Dict[str, str]:
|
|
23
28
|
"""
|
|
24
29
|
Parse a space-separated list of `key=value` tokens into a dict.
|
|
@@ -39,7 +44,10 @@ def _parse_kv_inline(source: str | None) -> Dict[str, str]:
|
|
|
39
44
|
# Subcommand group: "keys"
|
|
40
45
|
# e.g.: meshagent secrets keys create --name <NAME> --data ...
|
|
41
46
|
# --------------------------------------------------------------------------
|
|
42
|
-
keys_app = async_typer.AsyncTyper(
|
|
47
|
+
keys_app = async_typer.AsyncTyper(
|
|
48
|
+
help="Create or update environment-based key-value secrets."
|
|
49
|
+
)
|
|
50
|
+
|
|
43
51
|
|
|
44
52
|
@keys_app.async_command("create")
|
|
45
53
|
async def create_keys_secret(
|
|
@@ -52,7 +60,7 @@ async def create_keys_secret(
|
|
|
52
60
|
"--data",
|
|
53
61
|
help="Format: key=value key2=value (space-separated)",
|
|
54
62
|
),
|
|
55
|
-
]
|
|
63
|
+
],
|
|
56
64
|
):
|
|
57
65
|
"""
|
|
58
66
|
Create a new 'keys' secret (opaque env-vars).
|
|
@@ -86,7 +94,7 @@ async def update_keys_secret(
|
|
|
86
94
|
"--data",
|
|
87
95
|
help="Format: key=value key2=value (space-separated)",
|
|
88
96
|
),
|
|
89
|
-
]
|
|
97
|
+
],
|
|
90
98
|
):
|
|
91
99
|
"""
|
|
92
100
|
Update an existing 'keys' secret (opaque env-vars).
|
|
@@ -111,20 +119,27 @@ async def update_keys_secret(
|
|
|
111
119
|
# Subcommand group: "docker"
|
|
112
120
|
# e.g.: meshagent secrets docker create --name myregistry --server ...
|
|
113
121
|
# --------------------------------------------------------------------------
|
|
114
|
-
docker_app = async_typer.AsyncTyper(
|
|
122
|
+
docker_app = async_typer.AsyncTyper(
|
|
123
|
+
help="Create or update a Docker registry pull secret."
|
|
124
|
+
)
|
|
125
|
+
|
|
115
126
|
|
|
116
127
|
@docker_app.async_command("create")
|
|
117
128
|
async def create_docker_secret(
|
|
118
129
|
*,
|
|
119
130
|
project_id: Optional[str] = typer.Option(None),
|
|
120
131
|
name: Annotated[str, typer.Option(help="Secret name")],
|
|
121
|
-
server: Annotated[
|
|
132
|
+
server: Annotated[
|
|
133
|
+
str, typer.Option(help="Docker registry server, e.g. index.docker.io")
|
|
134
|
+
],
|
|
122
135
|
username: Annotated[str, typer.Option(help="Registry user name")],
|
|
123
136
|
password: Annotated[str, typer.Option(help="Registry password")],
|
|
124
137
|
email: Annotated[
|
|
125
138
|
str,
|
|
126
|
-
typer.Option(
|
|
127
|
-
|
|
139
|
+
typer.Option(
|
|
140
|
+
"--email", help="User email for Docker config", show_default=False
|
|
141
|
+
),
|
|
142
|
+
] = "none@example.com",
|
|
128
143
|
):
|
|
129
144
|
"""
|
|
130
145
|
Create a new Docker pull secret (generic).
|
|
@@ -158,8 +173,10 @@ async def update_docker_secret(
|
|
|
158
173
|
password: Annotated[str, typer.Option(help="Registry password")],
|
|
159
174
|
email: Annotated[
|
|
160
175
|
str,
|
|
161
|
-
typer.Option(
|
|
162
|
-
|
|
176
|
+
typer.Option(
|
|
177
|
+
"--email", help="User email for Docker config", show_default=False
|
|
178
|
+
),
|
|
179
|
+
] = "none@example.com",
|
|
163
180
|
):
|
|
164
181
|
"""
|
|
165
182
|
Update an existing Docker pull secret (generic).
|
|
@@ -185,7 +202,10 @@ async def update_docker_secret(
|
|
|
185
202
|
# Subcommand group: "acr"
|
|
186
203
|
# e.g.: meshagent secrets acr create --name <NAME> --server <REG>.azurecr.io ...
|
|
187
204
|
# --------------------------------------------------------------------------
|
|
188
|
-
acr_app = async_typer.AsyncTyper(
|
|
205
|
+
acr_app = async_typer.AsyncTyper(
|
|
206
|
+
help="Create or update an Azure Container Registry pull secret."
|
|
207
|
+
)
|
|
208
|
+
|
|
189
209
|
|
|
190
210
|
@acr_app.async_command("create")
|
|
191
211
|
async def create_acr_secret(
|
|
@@ -194,7 +214,7 @@ async def create_acr_secret(
|
|
|
194
214
|
name: Annotated[str, typer.Option(help="Secret name")],
|
|
195
215
|
server: Annotated[str, typer.Option(help="ACR server, e.g. myregistry.azurecr.io")],
|
|
196
216
|
username: Annotated[str, typer.Option(help="Service principal ID")],
|
|
197
|
-
password: Annotated[str, typer.Option(help="Service principal secret/password")]
|
|
217
|
+
password: Annotated[str, typer.Option(help="Service principal secret/password")],
|
|
198
218
|
):
|
|
199
219
|
"""
|
|
200
220
|
Create a new ACR pull secret (defaults email to 'none@microsoft.com').
|
|
@@ -209,13 +229,14 @@ async def create_acr_secret(
|
|
|
209
229
|
server=server,
|
|
210
230
|
username=username,
|
|
211
231
|
password=password,
|
|
212
|
-
email="none@microsoft.com",
|
|
232
|
+
email="none@microsoft.com", # Set a default for ACR usage
|
|
213
233
|
)
|
|
214
234
|
secret_id = await client.create_secret(project_id=project_id, secret=secret_obj)
|
|
215
235
|
print(f"[green]Created ACR pull secret:[/] {secret_id}")
|
|
216
236
|
finally:
|
|
217
237
|
await client.close()
|
|
218
238
|
|
|
239
|
+
|
|
219
240
|
@acr_app.async_command("update")
|
|
220
241
|
async def update_acr_secret(
|
|
221
242
|
*,
|
|
@@ -224,7 +245,7 @@ async def update_acr_secret(
|
|
|
224
245
|
name: Annotated[str, typer.Option(help="Secret name")],
|
|
225
246
|
server: Annotated[str, typer.Option(help="ACR server, e.g. myregistry.azurecr.io")],
|
|
226
247
|
username: Annotated[str, typer.Option(help="Service principal ID")],
|
|
227
|
-
password: Annotated[str, typer.Option(help="Service principal secret/password")]
|
|
248
|
+
password: Annotated[str, typer.Option(help="Service principal secret/password")],
|
|
228
249
|
):
|
|
229
250
|
"""
|
|
230
251
|
Update an existing ACR pull secret (defaults email to 'none@microsoft.com').
|
|
@@ -251,7 +272,10 @@ async def update_acr_secret(
|
|
|
251
272
|
# e.g.: meshagent secrets gar create --name <NAME> --server ...
|
|
252
273
|
# (Typically sets email='none@google.com' and username='_json_key')
|
|
253
274
|
# --------------------------------------------------------------------------
|
|
254
|
-
gar_app = async_typer.AsyncTyper(
|
|
275
|
+
gar_app = async_typer.AsyncTyper(
|
|
276
|
+
help="Create or update a Google Artifact Registry pull secret."
|
|
277
|
+
)
|
|
278
|
+
|
|
255
279
|
|
|
256
280
|
@gar_app.async_command("create")
|
|
257
281
|
async def create_gar_secret(
|
|
@@ -259,11 +283,13 @@ async def create_gar_secret(
|
|
|
259
283
|
project_id: Optional[str] = typer.Option(None),
|
|
260
284
|
name: Annotated[str, typer.Option(help="Secret name")],
|
|
261
285
|
server: Annotated[str, typer.Option(help="GAR host, e.g. us-west1-docker.pkg.dev")],
|
|
262
|
-
json_key: Annotated[
|
|
286
|
+
json_key: Annotated[
|
|
287
|
+
str, typer.Option(help="Entire GCP service account JSON as string")
|
|
288
|
+
],
|
|
263
289
|
):
|
|
264
290
|
"""
|
|
265
291
|
Create a new Google Artifact Registry pull secret.
|
|
266
|
-
|
|
292
|
+
|
|
267
293
|
By default, sets email='none@google.com', username='_json_key'
|
|
268
294
|
"""
|
|
269
295
|
client = await get_client()
|
|
@@ -283,6 +309,7 @@ async def create_gar_secret(
|
|
|
283
309
|
finally:
|
|
284
310
|
await client.close()
|
|
285
311
|
|
|
312
|
+
|
|
286
313
|
@gar_app.async_command("update")
|
|
287
314
|
async def update_gar_secret(
|
|
288
315
|
*,
|
|
@@ -290,7 +317,9 @@ async def update_gar_secret(
|
|
|
290
317
|
secret_id: Annotated[str, typer.Option(help="Existing secret ID")],
|
|
291
318
|
name: Annotated[str, typer.Option(help="Secret name")],
|
|
292
319
|
server: Annotated[str, typer.Option(help="GAR host, e.g. us-west1-docker.pkg.dev")],
|
|
293
|
-
json_key: Annotated[
|
|
320
|
+
json_key: Annotated[
|
|
321
|
+
str, typer.Option(help="Entire GCP service account JSON as string")
|
|
322
|
+
],
|
|
294
323
|
):
|
|
295
324
|
"""
|
|
296
325
|
Update an existing Google Artifact Registry pull secret.
|
|
@@ -316,6 +345,7 @@ async def update_gar_secret(
|
|
|
316
345
|
# Additional commands (shared by all secrets): list, delete
|
|
317
346
|
# --------------------------------------------------------------------------
|
|
318
347
|
|
|
348
|
+
|
|
319
349
|
@secrets_app.async_command("list")
|
|
320
350
|
async def secret_list(*, project_id: Optional[str] = None):
|
|
321
351
|
"""List all secrets in the project (typed as Docker/ACR/GAR or Keys secrets)."""
|
|
@@ -329,7 +359,7 @@ async def secret_list(*, project_id: Optional[str] = None):
|
|
|
329
359
|
rows = []
|
|
330
360
|
for s in secrets:
|
|
331
361
|
row = {
|
|
332
|
-
"id":
|
|
362
|
+
"id": s.id,
|
|
333
363
|
"name": s.name,
|
|
334
364
|
"type": s.type,
|
|
335
365
|
}
|
|
@@ -353,7 +383,7 @@ async def secret_list(*, project_id: Optional[str] = None):
|
|
|
353
383
|
async def secret_delete(
|
|
354
384
|
*,
|
|
355
385
|
project_id: Optional[str] = None,
|
|
356
|
-
secret_id: Annotated[str, typer.Argument(help="ID of the secret to delete")]
|
|
386
|
+
secret_id: Annotated[str, typer.Argument(help="ID of the secret to delete")],
|
|
357
387
|
):
|
|
358
388
|
"""Delete a secret."""
|
|
359
389
|
client = await get_client()
|
meshagent/cli/developer.py
CHANGED
|
@@ -10,33 +10,43 @@ from meshagent.api.helpers import meshagent_base_url, websocket_room_url
|
|
|
10
10
|
|
|
11
11
|
app = async_typer.AsyncTyper()
|
|
12
12
|
|
|
13
|
+
|
|
13
14
|
@app.async_command("watch")
|
|
14
15
|
async def watch_logs(
|
|
15
16
|
*,
|
|
16
|
-
project_id: Annotated[
|
|
17
|
+
project_id: Annotated[
|
|
18
|
+
Optional[str],
|
|
19
|
+
typer.Option(
|
|
20
|
+
..., help="Project ID (if not set, will try to use the active project)"
|
|
21
|
+
),
|
|
22
|
+
] = None,
|
|
17
23
|
room: Annotated[str, typer.Option(..., help="Name of the room to connect to")],
|
|
18
24
|
api_key_id: Annotated[Optional[str], typer.Option(..., help="API Key ID")] = None,
|
|
19
25
|
name: Annotated[str, typer.Option(..., help="Participant name")] = "cli",
|
|
20
|
-
role: Annotated[
|
|
26
|
+
role: Annotated[
|
|
27
|
+
str, typer.Option(..., help="Role to assign to this participant")
|
|
28
|
+
] = "user",
|
|
21
29
|
):
|
|
22
30
|
"""
|
|
23
31
|
Watch logs from the developer feed in the specified room.
|
|
24
32
|
"""
|
|
25
|
-
|
|
33
|
+
|
|
26
34
|
account_client = await get_client()
|
|
27
35
|
try:
|
|
28
36
|
# Resolve project ID (or fetch from the active project if not provided)
|
|
29
37
|
project_id = await resolve_project_id(project_id=project_id)
|
|
30
38
|
api_key_id = await resolve_api_key(project_id, api_key_id)
|
|
31
|
-
|
|
39
|
+
|
|
32
40
|
# Decrypt the project's API key
|
|
33
|
-
key = (
|
|
41
|
+
key = (
|
|
42
|
+
await account_client.decrypt_project_api_key(
|
|
43
|
+
project_id=project_id, id=api_key_id
|
|
44
|
+
)
|
|
45
|
+
)["token"]
|
|
34
46
|
|
|
35
47
|
# Build a participant token
|
|
36
48
|
token = ParticipantToken(
|
|
37
|
-
name=name,
|
|
38
|
-
project_id=project_id,
|
|
39
|
-
api_key_id=api_key_id
|
|
49
|
+
name=name, project_id=project_id, api_key_id=api_key_id
|
|
40
50
|
)
|
|
41
51
|
token.add_role_grant(role=role)
|
|
42
52
|
token.add_room_grant(room)
|
|
@@ -45,7 +55,7 @@ async def watch_logs(
|
|
|
45
55
|
async with RoomClient(
|
|
46
56
|
protocol=WebSocketClientProtocol(
|
|
47
57
|
url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
|
|
48
|
-
token=token.to_jwt(token=key)
|
|
58
|
+
token=token.to_jwt(token=key),
|
|
49
59
|
)
|
|
50
60
|
) as client:
|
|
51
61
|
# Create a developer client from the room client
|