meshagent-cli 0.33.2__tar.gz → 0.34.0__tar.gz
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.
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/PKG-INFO +13 -13
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/queue.py +58 -9
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/queue_test.py +112 -0
- meshagent_cli-0.34.0/meshagent/cli/version.py +1 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/webserver.py +9 -3
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/webserver_test.py +123 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent_cli.egg-info/PKG-INFO +13 -13
- meshagent_cli-0.34.0/meshagent_cli.egg-info/requires.txt +29 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/pyproject.toml +12 -12
- meshagent_cli-0.33.2/meshagent/cli/version.py +0 -1
- meshagent_cli-0.33.2/meshagent_cli.egg-info/requires.txt +0 -29
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/README.md +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/__init__.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/agent.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/api_keys.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/async_typer.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/auth.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/auth_async.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/call.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/chatbot.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/cli.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/cli_mcp.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/cli_secrets.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/cli_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/codex.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/common_options.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/containers.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/containers_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/database.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/developer.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/developer_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/helper.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/helper_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/helpers.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/host.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/mailbot.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/mailboxes.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/meeting_transcriber.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/memory.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/memory_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/messaging.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/multi.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/oauth2.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/participant_token.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/port.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/port_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/process.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/process_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/projects.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/room.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/room_services.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/rooms.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/routes.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/scheduled_tasks.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/scheduled_tasks_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/services.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/services_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/sessions.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/sessions_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/storage.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/storage_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/sync.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/sync_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/task_runner.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/tui/__init__.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/tui/setup.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/tui/setup_splash_frames.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/tui/setup_test.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/voicebot.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/webhook.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent/cli/worker.py +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent_cli.egg-info/SOURCES.txt +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent_cli.egg-info/dependency_links.txt +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent_cli.egg-info/entry_points.txt +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/meshagent_cli.egg-info/top_level.txt +0 -0
- {meshagent_cli-0.33.2 → meshagent_cli-0.34.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshagent-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.34.0
|
|
4
4
|
Summary: CLI for Meshagent
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Project-URL: Documentation, https://docs.meshagent.com
|
|
@@ -19,21 +19,21 @@ Requires-Dist: rich~=14.3.0
|
|
|
19
19
|
Requires-Dist: textual<2.0,>=0.50
|
|
20
20
|
Requires-Dist: prompt-toolkit~=3.0.52
|
|
21
21
|
Provides-Extra: all
|
|
22
|
-
Requires-Dist: meshagent-agents[all]~=0.
|
|
23
|
-
Requires-Dist: meshagent-api[all]~=0.
|
|
24
|
-
Requires-Dist: meshagent-computers~=0.
|
|
25
|
-
Requires-Dist: meshagent-openai~=0.
|
|
26
|
-
Requires-Dist: meshagent-anthropic~=0.
|
|
27
|
-
Requires-Dist: meshagent-codex~=0.
|
|
28
|
-
Requires-Dist: meshagent-mcp~=0.
|
|
29
|
-
Requires-Dist: meshagent-tools~=0.
|
|
22
|
+
Requires-Dist: meshagent-agents[all]~=0.34.0; extra == "all"
|
|
23
|
+
Requires-Dist: meshagent-api[all]~=0.34.0; extra == "all"
|
|
24
|
+
Requires-Dist: meshagent-computers~=0.34.0; extra == "all"
|
|
25
|
+
Requires-Dist: meshagent-openai~=0.34.0; extra == "all"
|
|
26
|
+
Requires-Dist: meshagent-anthropic~=0.34.0; extra == "all"
|
|
27
|
+
Requires-Dist: meshagent-codex~=0.34.0; extra == "all"
|
|
28
|
+
Requires-Dist: meshagent-mcp~=0.34.0; extra == "all"
|
|
29
|
+
Requires-Dist: meshagent-tools~=0.34.0; extra == "all"
|
|
30
30
|
Requires-Dist: supabase-auth~=2.28.0; extra == "all"
|
|
31
31
|
Requires-Dist: prompt-toolkit~=3.0.52; extra == "all"
|
|
32
32
|
Provides-Extra: mcp-service
|
|
33
|
-
Requires-Dist: meshagent-agents[all]~=0.
|
|
34
|
-
Requires-Dist: meshagent-api~=0.
|
|
35
|
-
Requires-Dist: meshagent-mcp~=0.
|
|
36
|
-
Requires-Dist: meshagent-tools~=0.
|
|
33
|
+
Requires-Dist: meshagent-agents[all]~=0.34.0; extra == "mcp-service"
|
|
34
|
+
Requires-Dist: meshagent-api~=0.34.0; extra == "mcp-service"
|
|
35
|
+
Requires-Dist: meshagent-mcp~=0.34.0; extra == "mcp-service"
|
|
36
|
+
Requires-Dist: meshagent-tools~=0.34.0; extra == "mcp-service"
|
|
37
37
|
Requires-Dist: supabase-auth~=2.28.0; extra == "mcp-service"
|
|
38
38
|
|
|
39
39
|
# [Meshagent](https://www.meshagent.com)
|
|
@@ -1,26 +1,75 @@
|
|
|
1
|
-
import typer
|
|
2
|
-
from rich import print
|
|
3
|
-
from typing import Annotated, Optional
|
|
4
|
-
from meshagent.cli.common_options import ProjectIdOption, RoomOption
|
|
5
|
-
import json as _json
|
|
6
1
|
import base64
|
|
2
|
+
import json as _json
|
|
7
3
|
import mimetypes
|
|
8
4
|
from pathlib import Path
|
|
5
|
+
from typing import Annotated, Optional
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich import print
|
|
9
9
|
|
|
10
|
-
from meshagent.api.helpers import websocket_room_url
|
|
11
10
|
from meshagent.api import (
|
|
12
11
|
RoomClient,
|
|
13
|
-
WebSocketClientProtocol,
|
|
14
12
|
RoomException,
|
|
13
|
+
WebSocketClientProtocol,
|
|
15
14
|
)
|
|
15
|
+
from meshagent.api.helpers import websocket_room_url
|
|
16
|
+
from meshagent.api.room_server_client import Queue as RoomQueue
|
|
16
17
|
from meshagent.agents.mail_common import create_email_message
|
|
17
|
-
from meshagent.cli.helper import resolve_project_id, resolve_room
|
|
18
18
|
from meshagent.cli import async_typer
|
|
19
|
-
from meshagent.cli.
|
|
19
|
+
from meshagent.cli.common_options import OutputFormatOption, ProjectIdOption, RoomOption
|
|
20
|
+
from meshagent.cli.helper import (
|
|
21
|
+
get_client,
|
|
22
|
+
print_json_table,
|
|
23
|
+
resolve_project_id,
|
|
24
|
+
resolve_room,
|
|
25
|
+
)
|
|
20
26
|
|
|
21
27
|
app = async_typer.AsyncTyper(help="Use queues in a room")
|
|
22
28
|
|
|
23
29
|
|
|
30
|
+
def _queue_rows(queues: list[RoomQueue]) -> list[dict[str, str | int]]:
|
|
31
|
+
return [{"name": item.name, "size": item.size} for item in queues]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@app.async_command("list", help="List queues in a room.")
|
|
35
|
+
async def queue_list(
|
|
36
|
+
*,
|
|
37
|
+
project_id: ProjectIdOption,
|
|
38
|
+
room: RoomOption,
|
|
39
|
+
output: OutputFormatOption = "table",
|
|
40
|
+
):
|
|
41
|
+
account_client = await get_client()
|
|
42
|
+
try:
|
|
43
|
+
project_id = await resolve_project_id(project_id=project_id)
|
|
44
|
+
room = resolve_room(room)
|
|
45
|
+
|
|
46
|
+
connection = await account_client.connect_room(project_id=project_id, room=room)
|
|
47
|
+
|
|
48
|
+
async with RoomClient(
|
|
49
|
+
protocol=WebSocketClientProtocol(
|
|
50
|
+
url=websocket_room_url(room_name=room),
|
|
51
|
+
token=connection.jwt,
|
|
52
|
+
)
|
|
53
|
+
) as client:
|
|
54
|
+
rows = _queue_rows(await client.queues.list())
|
|
55
|
+
|
|
56
|
+
if output == "json":
|
|
57
|
+
print(_json.dumps({"queues": rows}, indent=2))
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
if len(rows) == 0:
|
|
61
|
+
print("There are no queues currently in the room")
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
print_json_table(rows, "name", "size")
|
|
65
|
+
|
|
66
|
+
except RoomException as e:
|
|
67
|
+
print(e)
|
|
68
|
+
raise typer.Exit(1)
|
|
69
|
+
finally:
|
|
70
|
+
await account_client.close()
|
|
71
|
+
|
|
72
|
+
|
|
24
73
|
@app.async_command("send", help="Send a JSON message to a room queue.")
|
|
25
74
|
async def send(
|
|
26
75
|
*,
|
|
@@ -150,6 +150,50 @@ async def _run_size(
|
|
|
150
150
|
return account_client, printed
|
|
151
151
|
|
|
152
152
|
|
|
153
|
+
async def _run_queue_list(
|
|
154
|
+
*,
|
|
155
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
156
|
+
room_name: str,
|
|
157
|
+
queues: list[_FakeQueue],
|
|
158
|
+
output: str = "table",
|
|
159
|
+
) -> tuple[_FakeAccountClient, list[str], list[dict[str, object]]]:
|
|
160
|
+
account_client = _FakeAccountClient()
|
|
161
|
+
printed: list[str] = []
|
|
162
|
+
table_calls: list[dict[str, object]] = []
|
|
163
|
+
|
|
164
|
+
async def _fake_get_client() -> _FakeAccountClient:
|
|
165
|
+
return account_client
|
|
166
|
+
|
|
167
|
+
async def _fake_resolve_project_id(*, project_id):
|
|
168
|
+
del project_id
|
|
169
|
+
return "project-1"
|
|
170
|
+
|
|
171
|
+
def _fake_resolve_room(room: str) -> str:
|
|
172
|
+
return room
|
|
173
|
+
|
|
174
|
+
def _fake_print(*args, **kwargs) -> None:
|
|
175
|
+
del kwargs
|
|
176
|
+
printed.append(" ".join(str(arg) for arg in args))
|
|
177
|
+
|
|
178
|
+
def _fake_print_json_table(records: list[dict[str, object]], *cols: str) -> None:
|
|
179
|
+
table_calls.append({"records": records, "cols": cols})
|
|
180
|
+
|
|
181
|
+
monkeypatch.setattr(queue, "get_client", _fake_get_client)
|
|
182
|
+
monkeypatch.setattr(queue, "resolve_project_id", _fake_resolve_project_id)
|
|
183
|
+
monkeypatch.setattr(queue, "resolve_room", _fake_resolve_room)
|
|
184
|
+
monkeypatch.setattr(queue, "print_json_table", _fake_print_json_table)
|
|
185
|
+
_FakeRoomClient.default_queues = queues
|
|
186
|
+
monkeypatch.setattr(queue, "RoomClient", _FakeRoomClient)
|
|
187
|
+
monkeypatch.setattr(queue, "print", _fake_print)
|
|
188
|
+
|
|
189
|
+
await queue.queue_list(
|
|
190
|
+
project_id=None,
|
|
191
|
+
room=room_name,
|
|
192
|
+
output=output,
|
|
193
|
+
)
|
|
194
|
+
return account_client, printed, table_calls
|
|
195
|
+
|
|
196
|
+
|
|
153
197
|
@pytest.mark.asyncio
|
|
154
198
|
async def test_send_mail_queues_base64_email_payload(
|
|
155
199
|
monkeypatch: pytest.MonkeyPatch,
|
|
@@ -242,6 +286,74 @@ async def test_send_mail_allows_empty_body(
|
|
|
242
286
|
assert list(email_message.iter_attachments()) == []
|
|
243
287
|
|
|
244
288
|
|
|
289
|
+
@pytest.mark.asyncio
|
|
290
|
+
async def test_queue_list_prints_queue_rows(
|
|
291
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
292
|
+
) -> None:
|
|
293
|
+
account_client, printed, table_calls = await _run_queue_list(
|
|
294
|
+
monkeypatch=monkeypatch,
|
|
295
|
+
room_name="demo-room",
|
|
296
|
+
queues=[
|
|
297
|
+
_FakeQueue(name="jobs", size=3),
|
|
298
|
+
_FakeQueue(name="other", size=1),
|
|
299
|
+
],
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
assert account_client.connect_calls == [
|
|
303
|
+
{"project_id": "project-1", "room": "demo-room"}
|
|
304
|
+
]
|
|
305
|
+
assert account_client.closed is True
|
|
306
|
+
assert printed == []
|
|
307
|
+
assert table_calls == [
|
|
308
|
+
{
|
|
309
|
+
"records": [
|
|
310
|
+
{"name": "jobs", "size": 3},
|
|
311
|
+
{"name": "other", "size": 1},
|
|
312
|
+
],
|
|
313
|
+
"cols": ("name", "size"),
|
|
314
|
+
}
|
|
315
|
+
]
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
@pytest.mark.asyncio
|
|
319
|
+
async def test_queue_list_prints_empty_room_message(
|
|
320
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
321
|
+
) -> None:
|
|
322
|
+
account_client, printed, table_calls = await _run_queue_list(
|
|
323
|
+
monkeypatch=monkeypatch,
|
|
324
|
+
room_name="demo-room",
|
|
325
|
+
queues=[],
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
assert account_client.connect_calls == [
|
|
329
|
+
{"project_id": "project-1", "room": "demo-room"}
|
|
330
|
+
]
|
|
331
|
+
assert account_client.closed is True
|
|
332
|
+
assert printed == ["There are no queues currently in the room"]
|
|
333
|
+
assert table_calls == []
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
@pytest.mark.asyncio
|
|
337
|
+
async def test_queue_list_json_output(
|
|
338
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
339
|
+
) -> None:
|
|
340
|
+
account_client, printed, table_calls = await _run_queue_list(
|
|
341
|
+
monkeypatch=monkeypatch,
|
|
342
|
+
room_name="demo-room",
|
|
343
|
+
queues=[_FakeQueue(name="jobs", size=3)],
|
|
344
|
+
output="json",
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
assert account_client.connect_calls == [
|
|
348
|
+
{"project_id": "project-1", "room": "demo-room"}
|
|
349
|
+
]
|
|
350
|
+
assert account_client.closed is True
|
|
351
|
+
assert printed == [
|
|
352
|
+
'{\n "queues": [\n {\n "name": "jobs",\n "size": 3\n }\n ]\n}'
|
|
353
|
+
]
|
|
354
|
+
assert table_calls == []
|
|
355
|
+
|
|
356
|
+
|
|
245
357
|
@pytest.mark.asyncio
|
|
246
358
|
async def test_size_prints_matching_queue_size(
|
|
247
359
|
monkeypatch: pytest.MonkeyPatch,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.34.0"
|
|
@@ -34,6 +34,7 @@ from meshagent.api.helpers import meshagent_base_url, websocket_room_url
|
|
|
34
34
|
from meshagent.api.specs.service import (
|
|
35
35
|
AgentSpec,
|
|
36
36
|
ANNOTATION_AGENT_TYPE,
|
|
37
|
+
ANNOTATION_SERVICE_ID,
|
|
37
38
|
ContainerMountSpec,
|
|
38
39
|
EnvironmentVariable,
|
|
39
40
|
PortSpec,
|
|
@@ -275,15 +276,17 @@ async def _upsert_domain_route(
|
|
|
275
276
|
domain: str,
|
|
276
277
|
room_name: str,
|
|
277
278
|
port: str,
|
|
279
|
+
service_id: str,
|
|
278
280
|
) -> None:
|
|
279
281
|
_warn_if_non_meshagent_app_domain(domain)
|
|
282
|
+
route_annotations = {ANNOTATION_SERVICE_ID: service_id}
|
|
280
283
|
try:
|
|
281
284
|
await client.create_route(
|
|
282
285
|
project_id=project_id,
|
|
283
286
|
domain=domain,
|
|
284
287
|
room_name=room_name,
|
|
285
288
|
port=port,
|
|
286
|
-
annotations=
|
|
289
|
+
annotations=route_annotations,
|
|
287
290
|
)
|
|
288
291
|
except ConflictError:
|
|
289
292
|
existing = await client.get_route(project_id=project_id, domain=domain)
|
|
@@ -293,7 +296,9 @@ async def _upsert_domain_route(
|
|
|
293
296
|
f"Refusing to change it to room {room_name}. "
|
|
294
297
|
"Use `meshagent route update` if you want to repoint it."
|
|
295
298
|
)
|
|
296
|
-
|
|
299
|
+
updated_annotations = dict(existing.annotations)
|
|
300
|
+
updated_annotations[ANNOTATION_SERVICE_ID] = service_id
|
|
301
|
+
if existing.port == port and existing.annotations == updated_annotations:
|
|
297
302
|
print(f"[green]Route already configured:[/] {domain} -> {room_name}:{port}")
|
|
298
303
|
return
|
|
299
304
|
await client.update_route(
|
|
@@ -301,7 +306,7 @@ async def _upsert_domain_route(
|
|
|
301
306
|
domain=domain,
|
|
302
307
|
room_name=room_name,
|
|
303
308
|
port=port,
|
|
304
|
-
annotations=
|
|
309
|
+
annotations=updated_annotations,
|
|
305
310
|
)
|
|
306
311
|
print(f"[green]Updated route:[/] {domain} -> {room_name}:{port}")
|
|
307
312
|
else:
|
|
@@ -2057,6 +2062,7 @@ async def deploy(
|
|
|
2057
2062
|
domain=domain,
|
|
2058
2063
|
room_name=cast(str, room_name),
|
|
2059
2064
|
port=str(resolved_web_port),
|
|
2065
|
+
service_id=resolved_service_name,
|
|
2060
2066
|
)
|
|
2061
2067
|
|
|
2062
2068
|
finally:
|
|
@@ -4,10 +4,133 @@ from pathlib import Path
|
|
|
4
4
|
import pytest
|
|
5
5
|
import typer
|
|
6
6
|
from aiohttp.test_utils import TestClient, TestServer
|
|
7
|
+
from meshagent.api.client import ConflictError, Route
|
|
8
|
+
from meshagent.api.specs.service import ANNOTATION_SERVICE_ID
|
|
7
9
|
|
|
8
10
|
from meshagent.cli import webserver
|
|
9
11
|
|
|
10
12
|
|
|
13
|
+
class _FakeRouteClient:
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
*,
|
|
17
|
+
existing_route: Route | None = None,
|
|
18
|
+
create_conflict: bool = False,
|
|
19
|
+
) -> None:
|
|
20
|
+
self._existing_route = existing_route
|
|
21
|
+
self._create_conflict = create_conflict
|
|
22
|
+
self.created_routes: list[dict[str, object]] = []
|
|
23
|
+
self.updated_routes: list[dict[str, object]] = []
|
|
24
|
+
|
|
25
|
+
async def create_route(
|
|
26
|
+
self,
|
|
27
|
+
*,
|
|
28
|
+
project_id: str,
|
|
29
|
+
domain: str,
|
|
30
|
+
room_name: str,
|
|
31
|
+
port: str,
|
|
32
|
+
annotations: dict[str, str],
|
|
33
|
+
) -> None:
|
|
34
|
+
self.created_routes.append(
|
|
35
|
+
{
|
|
36
|
+
"project_id": project_id,
|
|
37
|
+
"domain": domain,
|
|
38
|
+
"room_name": room_name,
|
|
39
|
+
"port": port,
|
|
40
|
+
"annotations": annotations,
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
if self._create_conflict:
|
|
44
|
+
raise ConflictError("route already exists")
|
|
45
|
+
|
|
46
|
+
async def get_route(self, *, project_id: str, domain: str) -> Route:
|
|
47
|
+
assert self._existing_route is not None
|
|
48
|
+
assert project_id
|
|
49
|
+
assert domain
|
|
50
|
+
return self._existing_route
|
|
51
|
+
|
|
52
|
+
async def update_route(
|
|
53
|
+
self,
|
|
54
|
+
*,
|
|
55
|
+
project_id: str,
|
|
56
|
+
domain: str,
|
|
57
|
+
room_name: str,
|
|
58
|
+
port: str,
|
|
59
|
+
annotations: dict[str, str],
|
|
60
|
+
) -> None:
|
|
61
|
+
self.updated_routes.append(
|
|
62
|
+
{
|
|
63
|
+
"project_id": project_id,
|
|
64
|
+
"domain": domain,
|
|
65
|
+
"room_name": room_name,
|
|
66
|
+
"port": port,
|
|
67
|
+
"annotations": annotations,
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@pytest.mark.asyncio
|
|
73
|
+
async def test_upsert_domain_route_creates_route_with_service_id_annotation() -> None:
|
|
74
|
+
client = _FakeRouteClient()
|
|
75
|
+
|
|
76
|
+
await webserver._upsert_domain_route(
|
|
77
|
+
client=client,
|
|
78
|
+
project_id="project-123",
|
|
79
|
+
domain="site.meshagent.app",
|
|
80
|
+
room_name="demo-room",
|
|
81
|
+
port="8000",
|
|
82
|
+
service_id="demo-webserver",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
assert client.created_routes == [
|
|
86
|
+
{
|
|
87
|
+
"project_id": "project-123",
|
|
88
|
+
"domain": "site.meshagent.app",
|
|
89
|
+
"room_name": "demo-room",
|
|
90
|
+
"port": "8000",
|
|
91
|
+
"annotations": {ANNOTATION_SERVICE_ID: "demo-webserver"},
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
assert client.updated_routes == []
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@pytest.mark.asyncio
|
|
98
|
+
async def test_upsert_domain_route_backfills_service_id_annotation_when_port_matches() -> (
|
|
99
|
+
None
|
|
100
|
+
):
|
|
101
|
+
client = _FakeRouteClient(
|
|
102
|
+
create_conflict=True,
|
|
103
|
+
existing_route=Route(
|
|
104
|
+
domain="site.meshagent.app",
|
|
105
|
+
room_name="demo-room",
|
|
106
|
+
port="8000",
|
|
107
|
+
annotations={"meshagent.custom": "keep-me"},
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
await webserver._upsert_domain_route(
|
|
112
|
+
client=client,
|
|
113
|
+
project_id="project-123",
|
|
114
|
+
domain="site.meshagent.app",
|
|
115
|
+
room_name="demo-room",
|
|
116
|
+
port="8000",
|
|
117
|
+
service_id="demo-webserver",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
assert client.updated_routes == [
|
|
121
|
+
{
|
|
122
|
+
"project_id": "project-123",
|
|
123
|
+
"domain": "site.meshagent.app",
|
|
124
|
+
"room_name": "demo-room",
|
|
125
|
+
"port": "8000",
|
|
126
|
+
"annotations": {
|
|
127
|
+
"meshagent.custom": "keep-me",
|
|
128
|
+
ANNOTATION_SERVICE_ID: "demo-webserver",
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
|
|
11
134
|
async def _start_static_test_client(
|
|
12
135
|
*,
|
|
13
136
|
route_path: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshagent-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.34.0
|
|
4
4
|
Summary: CLI for Meshagent
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Project-URL: Documentation, https://docs.meshagent.com
|
|
@@ -19,21 +19,21 @@ Requires-Dist: rich~=14.3.0
|
|
|
19
19
|
Requires-Dist: textual<2.0,>=0.50
|
|
20
20
|
Requires-Dist: prompt-toolkit~=3.0.52
|
|
21
21
|
Provides-Extra: all
|
|
22
|
-
Requires-Dist: meshagent-agents[all]~=0.
|
|
23
|
-
Requires-Dist: meshagent-api[all]~=0.
|
|
24
|
-
Requires-Dist: meshagent-computers~=0.
|
|
25
|
-
Requires-Dist: meshagent-openai~=0.
|
|
26
|
-
Requires-Dist: meshagent-anthropic~=0.
|
|
27
|
-
Requires-Dist: meshagent-codex~=0.
|
|
28
|
-
Requires-Dist: meshagent-mcp~=0.
|
|
29
|
-
Requires-Dist: meshagent-tools~=0.
|
|
22
|
+
Requires-Dist: meshagent-agents[all]~=0.34.0; extra == "all"
|
|
23
|
+
Requires-Dist: meshagent-api[all]~=0.34.0; extra == "all"
|
|
24
|
+
Requires-Dist: meshagent-computers~=0.34.0; extra == "all"
|
|
25
|
+
Requires-Dist: meshagent-openai~=0.34.0; extra == "all"
|
|
26
|
+
Requires-Dist: meshagent-anthropic~=0.34.0; extra == "all"
|
|
27
|
+
Requires-Dist: meshagent-codex~=0.34.0; extra == "all"
|
|
28
|
+
Requires-Dist: meshagent-mcp~=0.34.0; extra == "all"
|
|
29
|
+
Requires-Dist: meshagent-tools~=0.34.0; extra == "all"
|
|
30
30
|
Requires-Dist: supabase-auth~=2.28.0; extra == "all"
|
|
31
31
|
Requires-Dist: prompt-toolkit~=3.0.52; extra == "all"
|
|
32
32
|
Provides-Extra: mcp-service
|
|
33
|
-
Requires-Dist: meshagent-agents[all]~=0.
|
|
34
|
-
Requires-Dist: meshagent-api~=0.
|
|
35
|
-
Requires-Dist: meshagent-mcp~=0.
|
|
36
|
-
Requires-Dist: meshagent-tools~=0.
|
|
33
|
+
Requires-Dist: meshagent-agents[all]~=0.34.0; extra == "mcp-service"
|
|
34
|
+
Requires-Dist: meshagent-api~=0.34.0; extra == "mcp-service"
|
|
35
|
+
Requires-Dist: meshagent-mcp~=0.34.0; extra == "mcp-service"
|
|
36
|
+
Requires-Dist: meshagent-tools~=0.34.0; extra == "mcp-service"
|
|
37
37
|
Requires-Dist: supabase-auth~=2.28.0; extra == "mcp-service"
|
|
38
38
|
|
|
39
39
|
# [Meshagent](https://www.meshagent.com)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
typer~=0.21
|
|
2
|
+
fastmcp~=2.8
|
|
3
|
+
opentelemetry-distro~=0.59b0
|
|
4
|
+
opentelemetry-exporter-otlp-proto-http~=1.33
|
|
5
|
+
art~=6.5
|
|
6
|
+
pydantic-yaml~=1.5
|
|
7
|
+
pathspec~=0.12.1
|
|
8
|
+
rich~=14.3.0
|
|
9
|
+
textual<2.0,>=0.50
|
|
10
|
+
prompt-toolkit~=3.0.52
|
|
11
|
+
|
|
12
|
+
[all]
|
|
13
|
+
meshagent-agents[all]~=0.34.0
|
|
14
|
+
meshagent-api[all]~=0.34.0
|
|
15
|
+
meshagent-computers~=0.34.0
|
|
16
|
+
meshagent-openai~=0.34.0
|
|
17
|
+
meshagent-anthropic~=0.34.0
|
|
18
|
+
meshagent-codex~=0.34.0
|
|
19
|
+
meshagent-mcp~=0.34.0
|
|
20
|
+
meshagent-tools~=0.34.0
|
|
21
|
+
supabase-auth~=2.28.0
|
|
22
|
+
prompt-toolkit~=3.0.52
|
|
23
|
+
|
|
24
|
+
[mcp-service]
|
|
25
|
+
meshagent-agents[all]~=0.34.0
|
|
26
|
+
meshagent-api~=0.34.0
|
|
27
|
+
meshagent-mcp~=0.34.0
|
|
28
|
+
meshagent-tools~=0.34.0
|
|
29
|
+
supabase-auth~=2.28.0
|
|
@@ -26,23 +26,23 @@ dynamic = ["version", "readme"]
|
|
|
26
26
|
[project.optional-dependencies]
|
|
27
27
|
|
|
28
28
|
all = [
|
|
29
|
-
"meshagent-agents[all]~=0.
|
|
30
|
-
"meshagent-api[all]~=0.
|
|
31
|
-
"meshagent-computers~=0.
|
|
32
|
-
"meshagent-openai~=0.
|
|
33
|
-
"meshagent-anthropic~=0.
|
|
34
|
-
"meshagent-codex~=0.
|
|
35
|
-
"meshagent-mcp~=0.
|
|
36
|
-
"meshagent-tools~=0.
|
|
29
|
+
"meshagent-agents[all]~=0.34.0",
|
|
30
|
+
"meshagent-api[all]~=0.34.0",
|
|
31
|
+
"meshagent-computers~=0.34.0",
|
|
32
|
+
"meshagent-openai~=0.34.0",
|
|
33
|
+
"meshagent-anthropic~=0.34.0",
|
|
34
|
+
"meshagent-codex~=0.34.0",
|
|
35
|
+
"meshagent-mcp~=0.34.0",
|
|
36
|
+
"meshagent-tools~=0.34.0",
|
|
37
37
|
"supabase-auth~=2.28.0",
|
|
38
38
|
"prompt-toolkit~=3.0.52"
|
|
39
39
|
]
|
|
40
40
|
|
|
41
41
|
mcp-service = [
|
|
42
|
-
"meshagent-agents[all]~=0.
|
|
43
|
-
"meshagent-api~=0.
|
|
44
|
-
"meshagent-mcp~=0.
|
|
45
|
-
"meshagent-tools~=0.
|
|
42
|
+
"meshagent-agents[all]~=0.34.0",
|
|
43
|
+
"meshagent-api~=0.34.0",
|
|
44
|
+
"meshagent-mcp~=0.34.0",
|
|
45
|
+
"meshagent-tools~=0.34.0",
|
|
46
46
|
"supabase-auth~=2.28.0"
|
|
47
47
|
]
|
|
48
48
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.33.2"
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
typer~=0.21
|
|
2
|
-
fastmcp~=2.8
|
|
3
|
-
opentelemetry-distro~=0.59b0
|
|
4
|
-
opentelemetry-exporter-otlp-proto-http~=1.33
|
|
5
|
-
art~=6.5
|
|
6
|
-
pydantic-yaml~=1.5
|
|
7
|
-
pathspec~=0.12.1
|
|
8
|
-
rich~=14.3.0
|
|
9
|
-
textual<2.0,>=0.50
|
|
10
|
-
prompt-toolkit~=3.0.52
|
|
11
|
-
|
|
12
|
-
[all]
|
|
13
|
-
meshagent-agents[all]~=0.33.2
|
|
14
|
-
meshagent-api[all]~=0.33.2
|
|
15
|
-
meshagent-computers~=0.33.2
|
|
16
|
-
meshagent-openai~=0.33.2
|
|
17
|
-
meshagent-anthropic~=0.33.2
|
|
18
|
-
meshagent-codex~=0.33.2
|
|
19
|
-
meshagent-mcp~=0.33.2
|
|
20
|
-
meshagent-tools~=0.33.2
|
|
21
|
-
supabase-auth~=2.28.0
|
|
22
|
-
prompt-toolkit~=3.0.52
|
|
23
|
-
|
|
24
|
-
[mcp-service]
|
|
25
|
-
meshagent-agents[all]~=0.33.2
|
|
26
|
-
meshagent-api~=0.33.2
|
|
27
|
-
meshagent-mcp~=0.33.2
|
|
28
|
-
meshagent-tools~=0.33.2
|
|
29
|
-
supabase-auth~=2.28.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|