meshagent-cli 0.0.38__py3-none-any.whl → 0.1.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.
Potentially problematic release.
This version of meshagent-cli might be problematic. Click here for more details.
- meshagent/cli/call.py +25 -4
- meshagent/cli/cli.py +1 -1
- meshagent/cli/services.py +296 -62
- meshagent/cli/tty.py +10 -4
- meshagent/cli/version.py +1 -1
- {meshagent_cli-0.0.38.dist-info → meshagent_cli-0.1.0.dist-info}/METADATA +8 -5
- {meshagent_cli-0.0.38.dist-info → meshagent_cli-0.1.0.dist-info}/RECORD +10 -10
- {meshagent_cli-0.0.38.dist-info → meshagent_cli-0.1.0.dist-info}/WHEEL +0 -0
- {meshagent_cli-0.0.38.dist-info → meshagent_cli-0.1.0.dist-info}/entry_points.txt +0 -0
- {meshagent_cli-0.0.38.dist-info → meshagent_cli-0.1.0.dist-info}/top_level.txt +0 -0
meshagent/cli/call.py
CHANGED
|
@@ -72,10 +72,16 @@ async def make_call(
|
|
|
72
72
|
project_id: str = None,
|
|
73
73
|
room: Annotated[str, typer.Option()],
|
|
74
74
|
api_key_id: Annotated[Optional[str], typer.Option()] = None,
|
|
75
|
-
name: Annotated[str, typer.Option(..., help="Participant name")] = "cli",
|
|
76
75
|
role: str = "agent",
|
|
77
76
|
local: Optional[bool] = None,
|
|
78
|
-
agent_name: Annotated[
|
|
77
|
+
agent_name: Annotated[
|
|
78
|
+
Optional[str], typer.Option(..., help="deprecated and unused", hidden=True)
|
|
79
|
+
] = None,
|
|
80
|
+
name: Annotated[str, typer.Option(..., help="deprecated", hidden=True)] = None,
|
|
81
|
+
participant_name: Annotated[
|
|
82
|
+
Optional[str],
|
|
83
|
+
typer.Option(..., help="the participant name to be used by the callee"),
|
|
84
|
+
] = None,
|
|
79
85
|
url: Annotated[str, typer.Option(..., help="URL the agent should call")],
|
|
80
86
|
arguments: Annotated[
|
|
81
87
|
str, typer.Option(..., help="JSON string with arguments for the call")
|
|
@@ -83,7 +89,22 @@ async def make_call(
|
|
|
83
89
|
):
|
|
84
90
|
"""
|
|
85
91
|
Instruct an agent to 'call' a given URL with specific arguments.
|
|
92
|
+
|
|
86
93
|
"""
|
|
94
|
+
|
|
95
|
+
if name is not None:
|
|
96
|
+
print("[yellow]name is deprecated and should no longer be passed[/yellow]")
|
|
97
|
+
|
|
98
|
+
if agent_name is not None:
|
|
99
|
+
print(
|
|
100
|
+
"[yellow]agent-name is deprecated and should no longer be passed, use participant-name instead[/yellow]"
|
|
101
|
+
)
|
|
102
|
+
participant_name = agent_name
|
|
103
|
+
|
|
104
|
+
if participant_name is None:
|
|
105
|
+
print("[red]--participant-name is required[/red]")
|
|
106
|
+
raise typer.Exit(1)
|
|
107
|
+
|
|
87
108
|
account_client = await get_client()
|
|
88
109
|
try:
|
|
89
110
|
project_id = await resolve_project_id(project_id=project_id)
|
|
@@ -96,7 +117,7 @@ async def make_call(
|
|
|
96
117
|
)["token"]
|
|
97
118
|
|
|
98
119
|
token = ParticipantToken(
|
|
99
|
-
name=
|
|
120
|
+
name="cli", project_id=project_id, api_key_id=api_key_id
|
|
100
121
|
)
|
|
101
122
|
token.add_role_grant(role=role)
|
|
102
123
|
token.add_room_grant(room)
|
|
@@ -130,7 +151,7 @@ async def make_call(
|
|
|
130
151
|
) as client:
|
|
131
152
|
print("[bold green]Making agent call...[/bold green]")
|
|
132
153
|
await client.agents.make_call(
|
|
133
|
-
name=
|
|
154
|
+
name=participant_name, url=url, arguments=json.loads(arguments)
|
|
134
155
|
)
|
|
135
156
|
print("[bold cyan]Call request sent successfully.[/bold cyan]")
|
|
136
157
|
|
meshagent/cli/cli.py
CHANGED
meshagent/cli/services.py
CHANGED
|
@@ -10,6 +10,7 @@ from pydantic import PositiveInt
|
|
|
10
10
|
import pydantic
|
|
11
11
|
from typing import Literal
|
|
12
12
|
from meshagent.cli import async_typer
|
|
13
|
+
from pydantic import BaseModel
|
|
13
14
|
from meshagent.cli.helper import (
|
|
14
15
|
get_client,
|
|
15
16
|
print_json_table,
|
|
@@ -24,8 +25,11 @@ from meshagent.api import (
|
|
|
24
25
|
meshagent_base_url,
|
|
25
26
|
)
|
|
26
27
|
|
|
28
|
+
from pydantic_yaml import parse_yaml_raw_as
|
|
29
|
+
|
|
27
30
|
# Pydantic basemodels
|
|
28
|
-
from meshagent.api.accounts_client import Service, Port, Services
|
|
31
|
+
from meshagent.api.accounts_client import Service, Port, Services, Endpoint
|
|
32
|
+
|
|
29
33
|
|
|
30
34
|
app = async_typer.AsyncTyper()
|
|
31
35
|
|
|
@@ -88,9 +92,17 @@ def _parse_port_spec(spec: str) -> PortSpec:
|
|
|
88
92
|
async def service_create(
|
|
89
93
|
*,
|
|
90
94
|
project_id: str = None,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
file: Annotated[
|
|
96
|
+
Optional[str],
|
|
97
|
+
typer.Option("--file", "-f", help="File path to a service definition"),
|
|
98
|
+
] = None,
|
|
99
|
+
name: Annotated[Optional[str], typer.Option(help="Friendly service name")] = None,
|
|
100
|
+
image: Annotated[
|
|
101
|
+
Optional[str], typer.Option(help="Container image reference")
|
|
102
|
+
] = None,
|
|
103
|
+
role: Annotated[
|
|
104
|
+
Optional[str], typer.Option(help="Service role (agent|tool)")
|
|
105
|
+
] = None,
|
|
94
106
|
pull_secret: Annotated[
|
|
95
107
|
Optional[str],
|
|
96
108
|
typer.Option("--pull-secret", help="Secret ID for registry"),
|
|
@@ -106,6 +118,13 @@ async def service_create(
|
|
|
106
118
|
Optional[str],
|
|
107
119
|
typer.Option("--mount", help="Path inside container to mount room storage"),
|
|
108
120
|
] = None,
|
|
121
|
+
room_storage_subpath: Annotated[
|
|
122
|
+
Optional[str],
|
|
123
|
+
typer.Option(
|
|
124
|
+
"--mount-subpath",
|
|
125
|
+
help="Restrict the container's mount to a subpath within the room storage",
|
|
126
|
+
),
|
|
127
|
+
] = None,
|
|
109
128
|
port: Annotated[
|
|
110
129
|
List[str],
|
|
111
130
|
typer.Option(
|
|
@@ -116,40 +135,50 @@ async def service_create(
|
|
|
116
135
|
' -p "num=8080 type=[mcp.sse | meshagent.callable | http | tcp] liveness=/health path=/agent participant_name=myname"'
|
|
117
136
|
),
|
|
118
137
|
),
|
|
119
|
-
] =
|
|
138
|
+
] = [],
|
|
120
139
|
):
|
|
121
140
|
"""Create a service attached to the project."""
|
|
122
141
|
client = await get_client()
|
|
123
142
|
try:
|
|
124
143
|
project_id = await resolve_project_id(project_id)
|
|
125
144
|
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
if file is not None:
|
|
146
|
+
with open(file, "rb") as f:
|
|
147
|
+
spec = parse_yaml_raw_as(ServiceSpec, f.read())
|
|
148
|
+
if spec.id is not None:
|
|
149
|
+
print("[red]id cannot be set when creating a service[/red]")
|
|
150
|
+
raise typer.Exit(code=1)
|
|
128
151
|
|
|
129
|
-
|
|
130
|
-
ps.num: Port(
|
|
131
|
-
type=ps.type,
|
|
132
|
-
liveness_path=ps.liveness,
|
|
133
|
-
participant_name=ps.participant_name,
|
|
134
|
-
path=ps.path,
|
|
135
|
-
)
|
|
136
|
-
for ps in port_specs
|
|
137
|
-
} or None
|
|
152
|
+
service_obj = spec.to_service()
|
|
138
153
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
else:
|
|
155
|
+
# ✅ validate / coerce port specs
|
|
156
|
+
port_specs: List[PortSpec] = [_parse_port_spec(s) for s in port]
|
|
157
|
+
|
|
158
|
+
ports_dict = {
|
|
159
|
+
ps.num: Port(
|
|
160
|
+
type=ps.type,
|
|
161
|
+
liveness_path=ps.liveness,
|
|
162
|
+
participant_name=ps.participant_name,
|
|
163
|
+
path=ps.path,
|
|
164
|
+
)
|
|
165
|
+
for ps in port_specs
|
|
166
|
+
} or None
|
|
167
|
+
|
|
168
|
+
service_obj = Service(
|
|
169
|
+
created_at=datetime.now(timezone.utc).isoformat(),
|
|
170
|
+
name=name,
|
|
171
|
+
role=role,
|
|
172
|
+
image=image,
|
|
173
|
+
command=command,
|
|
174
|
+
pull_secret=pull_secret,
|
|
175
|
+
room_storage_path=room_storage_path,
|
|
176
|
+
room_storage_subpath=room_storage_subpath,
|
|
177
|
+
environment=_kv_to_dict(env),
|
|
178
|
+
environment_secrets=env_secret or None,
|
|
179
|
+
runtime_secrets=_kv_to_dict(runtime_secret),
|
|
180
|
+
ports=ports_dict,
|
|
181
|
+
)
|
|
153
182
|
|
|
154
183
|
try:
|
|
155
184
|
new_id = (
|
|
@@ -157,7 +186,7 @@ async def service_create(
|
|
|
157
186
|
)["id"]
|
|
158
187
|
except ClientResponseError as exc:
|
|
159
188
|
if exc.status == 409:
|
|
160
|
-
print(f"[red]Service name already in use: {name}[/red]")
|
|
189
|
+
print(f"[red]Service name already in use: {service_obj.name}[/red]")
|
|
161
190
|
raise typer.Exit(code=1)
|
|
162
191
|
raise
|
|
163
192
|
else:
|
|
@@ -167,20 +196,221 @@ async def service_create(
|
|
|
167
196
|
await client.close()
|
|
168
197
|
|
|
169
198
|
|
|
199
|
+
@app.async_command("update")
|
|
200
|
+
async def service_update(
|
|
201
|
+
*,
|
|
202
|
+
project_id: str = None,
|
|
203
|
+
id: Optional[str] = None,
|
|
204
|
+
file: Annotated[
|
|
205
|
+
Optional[str],
|
|
206
|
+
typer.Option("--file", "-f", help="File path to a service definition"),
|
|
207
|
+
] = None,
|
|
208
|
+
name: Annotated[Optional[str], typer.Option(help="Friendly service name")] = None,
|
|
209
|
+
image: Annotated[
|
|
210
|
+
Optional[str], typer.Option(help="Container image reference")
|
|
211
|
+
] = None,
|
|
212
|
+
role: Annotated[
|
|
213
|
+
Optional[str], typer.Option(help="Service role (agent|tool)")
|
|
214
|
+
] = None,
|
|
215
|
+
pull_secret: Annotated[
|
|
216
|
+
Optional[str],
|
|
217
|
+
typer.Option("--pull-secret", help="Secret ID for registry"),
|
|
218
|
+
] = None,
|
|
219
|
+
command: Annotated[
|
|
220
|
+
Optional[str],
|
|
221
|
+
typer.Option("--command", help="Override ENTRYPOINT/CMD"),
|
|
222
|
+
] = None,
|
|
223
|
+
env: Annotated[List[str], typer.Option("--env", "-e", help="KEY=VALUE")] = [],
|
|
224
|
+
env_secret: Annotated[List[str], typer.Option("--env-secret")] = [],
|
|
225
|
+
runtime_secret: Annotated[List[str], typer.Option("--runtime-secret")] = [],
|
|
226
|
+
room_storage_path: Annotated[
|
|
227
|
+
Optional[str],
|
|
228
|
+
typer.Option("--mount", help="Path inside container to mount room storage"),
|
|
229
|
+
] = None,
|
|
230
|
+
room_storage_subpath: Annotated[
|
|
231
|
+
Optional[str],
|
|
232
|
+
typer.Option(
|
|
233
|
+
"--mount-subpath",
|
|
234
|
+
help="Restrict the container's mount to a subpath within the room storage",
|
|
235
|
+
),
|
|
236
|
+
] = None,
|
|
237
|
+
port: Annotated[
|
|
238
|
+
List[str],
|
|
239
|
+
typer.Option(
|
|
240
|
+
"--port",
|
|
241
|
+
"-p",
|
|
242
|
+
help=(
|
|
243
|
+
"Repeatable. Example:\n"
|
|
244
|
+
' -p "num=8080 type=[mcp.sse | meshagent.callable | http | tcp] liveness=/health path=/agent participant_name=myname"'
|
|
245
|
+
),
|
|
246
|
+
),
|
|
247
|
+
] = [],
|
|
248
|
+
create: Annotated[
|
|
249
|
+
Optional[bool],
|
|
250
|
+
typer.Option(
|
|
251
|
+
help="create the service if it does not exist",
|
|
252
|
+
),
|
|
253
|
+
] = False,
|
|
254
|
+
):
|
|
255
|
+
"""Create a service attached to the project."""
|
|
256
|
+
client = await get_client()
|
|
257
|
+
try:
|
|
258
|
+
project_id = await resolve_project_id(project_id)
|
|
259
|
+
|
|
260
|
+
if file is not None:
|
|
261
|
+
with open(file, "rb") as f:
|
|
262
|
+
spec = parse_yaml_raw_as(ServiceSpec, f.read())
|
|
263
|
+
if spec.id is not None:
|
|
264
|
+
id = spec.id
|
|
265
|
+
service_obj = spec.to_service()
|
|
266
|
+
|
|
267
|
+
else:
|
|
268
|
+
# ✅ validate / coerce port specs
|
|
269
|
+
port_specs: List[PortSpec] = [_parse_port_spec(s) for s in port]
|
|
270
|
+
|
|
271
|
+
ports_dict = {
|
|
272
|
+
ps.num: Port(
|
|
273
|
+
type=ps.type,
|
|
274
|
+
liveness_path=ps.liveness,
|
|
275
|
+
participant_name=ps.participant_name,
|
|
276
|
+
path=ps.path,
|
|
277
|
+
)
|
|
278
|
+
for ps in port_specs
|
|
279
|
+
} or None
|
|
280
|
+
|
|
281
|
+
service_obj = Service(
|
|
282
|
+
created_at=datetime.now(timezone.utc).isoformat(),
|
|
283
|
+
name=name,
|
|
284
|
+
role=role,
|
|
285
|
+
image=image,
|
|
286
|
+
command=command,
|
|
287
|
+
pull_secret=pull_secret,
|
|
288
|
+
room_storage_path=room_storage_path,
|
|
289
|
+
room_storage_subpath=room_storage_subpath,
|
|
290
|
+
environment=_kv_to_dict(env),
|
|
291
|
+
environment_secrets=env_secret or None,
|
|
292
|
+
runtime_secrets=_kv_to_dict(runtime_secret),
|
|
293
|
+
ports=ports_dict,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
try:
|
|
297
|
+
if id is None:
|
|
298
|
+
services = await client.list_services(project_id=project_id)
|
|
299
|
+
for s in services:
|
|
300
|
+
if s.name == service_obj.name:
|
|
301
|
+
id = s.id
|
|
302
|
+
|
|
303
|
+
if id is None and not create:
|
|
304
|
+
print("[red]pass a service id or specify --create[/red]")
|
|
305
|
+
raise typer.Exit(code=1)
|
|
306
|
+
|
|
307
|
+
if id is None:
|
|
308
|
+
id = (
|
|
309
|
+
await client.create_service(
|
|
310
|
+
project_id=project_id, service=service_obj
|
|
311
|
+
)
|
|
312
|
+
)["id"]
|
|
313
|
+
|
|
314
|
+
else:
|
|
315
|
+
await client.update_service(
|
|
316
|
+
project_id=project_id, service_id=id, service=service_obj
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
except ClientResponseError as exc:
|
|
320
|
+
if exc.status == 409:
|
|
321
|
+
print(f"[red]Service name already in use: {service_obj.name}[/red]")
|
|
322
|
+
raise typer.Exit(code=1)
|
|
323
|
+
raise
|
|
324
|
+
else:
|
|
325
|
+
print(f"[green]Updated service:[/] {id}")
|
|
326
|
+
|
|
327
|
+
finally:
|
|
328
|
+
await client.close()
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
class ServicePortEndpointSpec(pydantic.BaseModel):
|
|
332
|
+
path: str
|
|
333
|
+
identity: str
|
|
334
|
+
type: Optional[Literal["mcp.sse", "meshagent.callable", "http", "tcp"]] = None
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
class ServicePortSpec(pydantic.BaseModel):
|
|
338
|
+
num: PositiveInt
|
|
339
|
+
type: Literal["mcp.sse", "meshagent.callable", "http", "tcp"]
|
|
340
|
+
endpoints: list[ServicePortEndpointSpec] = []
|
|
341
|
+
liveness: Optional[str] = None
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class ServiceSpec(BaseModel):
|
|
345
|
+
version: Literal["v1"]
|
|
346
|
+
kind: Literal["Service"]
|
|
347
|
+
id: Optional[str] = None
|
|
348
|
+
name: str
|
|
349
|
+
command: Optional[str] = None
|
|
350
|
+
image: str
|
|
351
|
+
ports: Optional[list[ServicePortSpec]] = []
|
|
352
|
+
role: Optional[Literal["user", "tool", "agent"]] = None
|
|
353
|
+
environment: Optional[dict[str, str]] = {}
|
|
354
|
+
secrets: list[str] = []
|
|
355
|
+
pull_secret: Optional[str] = None
|
|
356
|
+
room_storage_path: Optional[str] = None
|
|
357
|
+
room_storage_subpath: Optional[str] = None
|
|
358
|
+
|
|
359
|
+
def to_service(self):
|
|
360
|
+
ports = {}
|
|
361
|
+
for p in self.ports:
|
|
362
|
+
port = Port(liveness_path=p.liveness, type=p.type, endpoints=[])
|
|
363
|
+
for endpoint in p.endpoints:
|
|
364
|
+
type = port.type
|
|
365
|
+
if endpoint.type is not None:
|
|
366
|
+
type = endpoint.type
|
|
367
|
+
|
|
368
|
+
port.endpoints.append(
|
|
369
|
+
Endpoint(
|
|
370
|
+
type=type,
|
|
371
|
+
participant_name=endpoint.identity,
|
|
372
|
+
path=endpoint.path,
|
|
373
|
+
)
|
|
374
|
+
)
|
|
375
|
+
ports[p.num] = port
|
|
376
|
+
return Service(
|
|
377
|
+
id="",
|
|
378
|
+
created_at=datetime.now(timezone.utc).isoformat(),
|
|
379
|
+
name=self.name,
|
|
380
|
+
command=self.command,
|
|
381
|
+
image=self.image,
|
|
382
|
+
ports=ports,
|
|
383
|
+
role=self.role,
|
|
384
|
+
environment=self.environment,
|
|
385
|
+
environment_secrets=self.secrets,
|
|
386
|
+
pull_secret=self.pull_secret,
|
|
387
|
+
room_storage_path=self.room_storage_path,
|
|
388
|
+
room_storage_subpath=self.room_storage_subpath,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
|
|
170
392
|
@app.async_command("test")
|
|
171
393
|
async def service_test(
|
|
172
394
|
*,
|
|
173
395
|
project_id: str = None,
|
|
174
396
|
api_key_id: Annotated[Optional[str], typer.Option()] = None,
|
|
397
|
+
file: Annotated[
|
|
398
|
+
Optional[str],
|
|
399
|
+
typer.Option("--file", "-f", help="File path to a service definition"),
|
|
400
|
+
],
|
|
175
401
|
room: Annotated[
|
|
176
|
-
str,
|
|
402
|
+
Optional[str],
|
|
177
403
|
typer.Option(
|
|
178
404
|
help="A room name to test the service in (must not be currently running)"
|
|
179
405
|
),
|
|
180
|
-
],
|
|
181
|
-
name: Annotated[str, typer.Option(help="Friendly service name")],
|
|
182
|
-
role: Annotated[
|
|
183
|
-
|
|
406
|
+
] = None,
|
|
407
|
+
name: Annotated[Optional[str], typer.Option(help="Friendly service name")] = None,
|
|
408
|
+
role: Annotated[
|
|
409
|
+
Optional[str], typer.Option(help="Service role (agent|tool)")
|
|
410
|
+
] = None,
|
|
411
|
+
image: Annotated[
|
|
412
|
+
Optional[str], typer.Option(help="Container image reference")
|
|
413
|
+
] = None,
|
|
184
414
|
pull_secret: Annotated[
|
|
185
415
|
Optional[str],
|
|
186
416
|
typer.Option("--pull-secret", help="Secret ID for registry"),
|
|
@@ -206,7 +436,7 @@ async def service_test(
|
|
|
206
436
|
' -p "num=8080 type=[mcp.sse | meshagent.callable | http | tcp] liveness=/health path=/agent participant_name=myname"'
|
|
207
437
|
),
|
|
208
438
|
),
|
|
209
|
-
] =
|
|
439
|
+
] = [],
|
|
210
440
|
timeout: Annotated[
|
|
211
441
|
Optional[int],
|
|
212
442
|
typer.Option(
|
|
@@ -221,33 +451,37 @@ async def service_test(
|
|
|
221
451
|
|
|
222
452
|
api_key_id = await resolve_api_key(project_id, api_key_id)
|
|
223
453
|
|
|
224
|
-
|
|
225
|
-
|
|
454
|
+
if file is not None:
|
|
455
|
+
with open(file, "rb") as f:
|
|
456
|
+
service_obj = parse_yaml_raw_as(ServiceSpec, f.read()).to_service()
|
|
226
457
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
458
|
+
else:
|
|
459
|
+
# ✅ validate / coerce port specs
|
|
460
|
+
port_specs: List[PortSpec] = [_parse_port_spec(s) for s in port]
|
|
461
|
+
|
|
462
|
+
ports_dict = {
|
|
463
|
+
str(ps.num): Port(
|
|
464
|
+
type=ps.type,
|
|
465
|
+
liveness_path=ps.liveness,
|
|
466
|
+
participant_name=ps.participant_name,
|
|
467
|
+
path=ps.path,
|
|
468
|
+
)
|
|
469
|
+
for ps in port_specs
|
|
470
|
+
} or None
|
|
471
|
+
|
|
472
|
+
service_obj = Service(
|
|
473
|
+
created_at=datetime.now(timezone.utc).isoformat(),
|
|
474
|
+
role=role,
|
|
475
|
+
name=name,
|
|
476
|
+
image=image,
|
|
477
|
+
command=command,
|
|
478
|
+
pull_secret=pull_secret,
|
|
479
|
+
room_storage_path=room_storage_path,
|
|
480
|
+
environment=_kv_to_dict(env),
|
|
481
|
+
environment_secrets=env_secret or None,
|
|
482
|
+
runtime_secrets=_kv_to_dict(runtime_secret),
|
|
483
|
+
ports=ports_dict,
|
|
233
484
|
)
|
|
234
|
-
for ps in port_specs
|
|
235
|
-
} or None
|
|
236
|
-
|
|
237
|
-
service_obj = Service(
|
|
238
|
-
id="",
|
|
239
|
-
created_at=datetime.now(timezone.utc).isoformat(),
|
|
240
|
-
role=role,
|
|
241
|
-
name=name,
|
|
242
|
-
image=image,
|
|
243
|
-
command=command,
|
|
244
|
-
pull_secret=pull_secret,
|
|
245
|
-
room_storage_path=room_storage_path,
|
|
246
|
-
environment=_kv_to_dict(env),
|
|
247
|
-
environment_secrets=env_secret or None,
|
|
248
|
-
runtime_secrets=_kv_to_dict(runtime_secret),
|
|
249
|
-
ports=ports_dict,
|
|
250
|
-
)
|
|
251
485
|
|
|
252
486
|
try:
|
|
253
487
|
token = ParticipantToken(
|
meshagent/cli/tty.py
CHANGED
|
@@ -3,6 +3,7 @@ import tty
|
|
|
3
3
|
import termios
|
|
4
4
|
from meshagent.api.helpers import websocket_room_url
|
|
5
5
|
from typing import Annotated, Optional
|
|
6
|
+
import os
|
|
6
7
|
|
|
7
8
|
import asyncio
|
|
8
9
|
import typer
|
|
@@ -53,13 +54,18 @@ async def tty_command(
|
|
|
53
54
|
|
|
54
55
|
# Save current terminal settings so we can restore them later.
|
|
55
56
|
old_tty_settings = termios.tcgetattr(sys.stdin)
|
|
57
|
+
|
|
56
58
|
try:
|
|
57
59
|
async with aiohttp.ClientSession() as session:
|
|
58
60
|
async with session.ws_connect(ws_url) as websocket:
|
|
59
|
-
print(f"[bold green]Connected to[/bold green] {room}")
|
|
60
|
-
|
|
61
61
|
tty.setraw(sys.stdin)
|
|
62
62
|
|
|
63
|
+
loop = asyncio.get_running_loop()
|
|
64
|
+
transport, protocol = await loop.connect_write_pipe(
|
|
65
|
+
asyncio.streams.FlowControlMixin, sys.stdout
|
|
66
|
+
)
|
|
67
|
+
writer = asyncio.StreamWriter(transport, protocol, None, loop)
|
|
68
|
+
|
|
63
69
|
async def recv_from_websocket():
|
|
64
70
|
async for message in websocket:
|
|
65
71
|
if message.type == aiohttp.WSMsgType.CLOSE:
|
|
@@ -69,8 +75,8 @@ async def tty_command(
|
|
|
69
75
|
await websocket.close()
|
|
70
76
|
|
|
71
77
|
data: bytes = message.data
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
writer.write(data)
|
|
79
|
+
await writer.drain()
|
|
74
80
|
|
|
75
81
|
async def send_to_websocket():
|
|
76
82
|
loop = asyncio.get_running_loop()
|
meshagent/cli/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.0
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshagent-cli
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.1.0
|
|
4
4
|
Summary: CLI for Meshagent
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Project-URL: Documentation, https://docs.meshagent.com
|
|
@@ -10,15 +10,18 @@ Requires-Python: >=3.12
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
Requires-Dist: typer~=0.15
|
|
12
12
|
Requires-Dist: pydantic-yaml~=1.4
|
|
13
|
-
Requires-Dist: meshagent-api~=0.
|
|
14
|
-
Requires-Dist: meshagent-agents~=0.
|
|
15
|
-
Requires-Dist: meshagent-
|
|
16
|
-
Requires-Dist: meshagent-
|
|
13
|
+
Requires-Dist: meshagent-api~=0.1
|
|
14
|
+
Requires-Dist: meshagent-agents~=0.1
|
|
15
|
+
Requires-Dist: meshagent-computers~=0.1
|
|
16
|
+
Requires-Dist: meshagent-openai~=0.1
|
|
17
|
+
Requires-Dist: meshagent-tools~=0.1
|
|
18
|
+
Requires-Dist: meshagent-mcp~=0.1
|
|
17
19
|
Requires-Dist: supabase~=2.15
|
|
18
20
|
Requires-Dist: fastmcp~=2.8
|
|
19
21
|
Requires-Dist: opentelemetry-distro~=0.54b1
|
|
20
22
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http~=1.33
|
|
21
23
|
Requires-Dist: art~=6.5
|
|
24
|
+
Requires-Dist: pydantic-yaml~=1.5
|
|
22
25
|
|
|
23
26
|
## MeshAgent CLI
|
|
24
27
|
|
|
@@ -4,9 +4,9 @@ meshagent/cli/api_keys.py,sha256=4mBqyh_WAogaMIYhRBQOCjfsSSp1RY9BqiI_6DaGLmk,496
|
|
|
4
4
|
meshagent/cli/async_typer.py,sha256=GCeSefBDbpd-V4V8LrvHGUTBMth3HspVMfFa-HUZ0cg,898
|
|
5
5
|
meshagent/cli/auth.py,sha256=tPipbOtHnsrvJ-OUOE-lyfvvIhkTIGYlkgS81hLdyB8,783
|
|
6
6
|
meshagent/cli/auth_async.py,sha256=mi2-u949M412PAMN-PCdHEiF9hGf0W3m1RAseZX07-w,4058
|
|
7
|
-
meshagent/cli/call.py,sha256=
|
|
7
|
+
meshagent/cli/call.py,sha256=IQePqq7PkAqnTVjqBah15VptgChWxV-G5ARKf0tzEBc,5295
|
|
8
8
|
meshagent/cli/chatbot.py,sha256=pK-_G3CiMbV-NC0iXzbjztu4f-ZAeXNzcmOB-HxJWQo,7928
|
|
9
|
-
meshagent/cli/cli.py,sha256=
|
|
9
|
+
meshagent/cli/cli.py,sha256=D2cIVAgYZ095XqWBrvd7Qq60yGL7pfLTIK4Y52xrKSM,6314
|
|
10
10
|
meshagent/cli/cli_mcp.py,sha256=BTTAMn3u4i1uTDItFxmxMTsSAvFaWtdI3YdZngHz11g,10641
|
|
11
11
|
meshagent/cli/cli_secrets.py,sha256=lkfR8tVjOA5KdynAhKCg5Z2EJkgrHFTX43pdB60YiU4,13767
|
|
12
12
|
meshagent/cli/developer.py,sha256=JWz-qcduCbFopQ1DNGbwrknzFt59KBLIXx8DyD6PxZM,3081
|
|
@@ -15,15 +15,15 @@ meshagent/cli/messaging.py,sha256=cr-oVAu_s8uEPUm3GELSq8yaVDnEWlt02D5V4KbA6wc,64
|
|
|
15
15
|
meshagent/cli/otel.py,sha256=1yoMGivskLV9f7M_LqCLKbttTTAPmFY5yhWXqFzvRN8,4066
|
|
16
16
|
meshagent/cli/participant_token.py,sha256=N07hblRjj0zDKxl5CQXJjIMmft5s9mWgKZKz-VZyhKw,1400
|
|
17
17
|
meshagent/cli/projects.py,sha256=WaO7M-D4ghy0nVpvwZ08ZEcybZ_e_cFe6rEoNxyAWkc,3306
|
|
18
|
-
meshagent/cli/services.py,sha256=
|
|
18
|
+
meshagent/cli/services.py,sha256=1afeMH41Kj0-1uAKB72buWnPFcgaqT4xFVO4FEpTPC0,19308
|
|
19
19
|
meshagent/cli/sessions.py,sha256=MP7XhrtkUrealdpl8IKrTR3u9sjF15sG175-Y_m7nps,800
|
|
20
20
|
meshagent/cli/storage.py,sha256=Q3ajuC6j4GLlU4jZoUW_Zsl1dOr04vzLibw2DPedJJ4,35564
|
|
21
|
-
meshagent/cli/tty.py,sha256
|
|
22
|
-
meshagent/cli/version.py,sha256=
|
|
21
|
+
meshagent/cli/tty.py,sha256=-XQE2W8_nW74vRG8jnqPofnfPB5bAvBX7_1-wMcBR1w,4207
|
|
22
|
+
meshagent/cli/version.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
|
|
23
23
|
meshagent/cli/voicebot.py,sha256=Ykn9mUhcwl03ZCPDRua6moeNyrvToGPowQfjhPM0zqA,5032
|
|
24
24
|
meshagent/cli/webhook.py,sha256=r5zct-UBQYSq3BWmnZRrHVOEHVlkY0j8uDxGVn3Pbxo,2902
|
|
25
|
-
meshagent_cli-0.0.
|
|
26
|
-
meshagent_cli-0.0.
|
|
27
|
-
meshagent_cli-0.0.
|
|
28
|
-
meshagent_cli-0.0.
|
|
29
|
-
meshagent_cli-0.0.
|
|
25
|
+
meshagent_cli-0.1.0.dist-info/METADATA,sha256=c75FMbewaNMuydlR3quvd91ja9mL_mCp1sCFJ1-xnbI,1436
|
|
26
|
+
meshagent_cli-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
27
|
+
meshagent_cli-0.1.0.dist-info/entry_points.txt,sha256=WRcGGN4vMtvC5Pgl3uRFqsJiQXNoHuLLa-TCSY3gAhQ,52
|
|
28
|
+
meshagent_cli-0.1.0.dist-info/top_level.txt,sha256=GlcXnHtRP6m7zlG3Df04M35OsHtNXy_DY09oFwWrH74,10
|
|
29
|
+
meshagent_cli-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|