meshagent-cli 0.45.6__tar.gz → 0.46.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.45.6/meshagent_cli.egg-info → meshagent_cli-0.46.0}/PKG-INFO +17 -17
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/containers.py +5 -1
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/containers_test.py +58 -6
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/image.py +59 -93
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/image_test.py +37 -89
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/room_services.py +27 -4
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/room_services_test.py +62 -2
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/sessions.py +10 -1
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/sessions_test.py +83 -0
- meshagent_cli-0.46.0/meshagent/cli/version.py +1 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0/meshagent_cli.egg-info}/PKG-INFO +17 -17
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent_cli.egg-info/requires.txt +16 -16
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/pyproject.toml +16 -16
- meshagent_cli-0.45.6/meshagent/cli/version.py +0 -1
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/LICENSE +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/README.md +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/__init__.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/_filedrop.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/agent.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/agent_cli_options_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/agent_package_cli.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/agent_package_cli_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/agents.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/agents_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/ask.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/ask_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/async_typer.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/async_typer_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/auth.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/auth_async.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/auth_async_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/auth_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/call.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/chatbot.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/cli.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/cli_mcp.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/cli_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/common_options.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/config.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/config_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/create.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/create_project_templates/__init__.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/create_project_templates/python/backend-agent/server.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/create_project_templates/python/contact-form/server.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/create_project_templates/python/task-queue-dashboard/server.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/create_project_templates/python/webserver/server.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/create_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/dataset.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/dataset_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/developer.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/developer_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/doctor.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/doctor_dockerfiles/__init__.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/doctor_templates/__init__.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/doctor_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/feeds.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/helper.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/helper_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/helpers.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/host.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/iam.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/iam_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/launch.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/launch_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/llm.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/llm_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/local_settings.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/local_settings_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/mailbot.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/mailbot_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/mailboxes.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/meeting_transcriber.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/memory.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/memory_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/meshagent_images.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/meshagent_images_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/messaging.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/messaging_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/multi.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/oci_archive.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/oci_archive_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/participant_token.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/port.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/port_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/preamble_rules.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/preamble_rules_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/process.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/process_live_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/process_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/projects.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/projects_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/queue.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/queue_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/registry.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/registry_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/room.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/room_connect.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/room_connect_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/rooms.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/rooms_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/root_commands.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/root_commands_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/routes.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/scheduled_tasks.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/scheduled_tasks_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/secrets.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/secrets_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/service_accounts.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/service_accounts_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/services.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/services_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/storage.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/storage_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/subscriptions.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/sync.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/sync_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/task_runner.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/task_runner_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/testing.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/thread_inspect.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/thread_inspect_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/thread_sidebar.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tool_call_summary.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tool_call_summary_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tool_integrations.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tool_integrations_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tui/__init__.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tui/auth_switch.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tui/auth_switch_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tui/create.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tui/deploy_room.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tui/deploy_room_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tui/project_activate.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tui/project_activate_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tui/setup.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tui/setup_splash_frames.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/tui/setup_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/version_check.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/version_check_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/voicebot.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/webserver.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/webserver_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/worker.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent/cli/worker_test.py +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent_cli.egg-info/SOURCES.txt +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent_cli.egg-info/dependency_links.txt +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent_cli.egg-info/entry_points.txt +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.0}/meshagent_cli.egg-info/top_level.txt +0 -0
- {meshagent_cli-0.45.6 → meshagent_cli-0.46.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.46.0
|
|
4
4
|
Summary: CLI for Meshagent
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Project-URL: Documentation, https://docs.meshagent.com
|
|
@@ -22,8 +22,8 @@ Requires-Dist: openpyxl~=3.1
|
|
|
22
22
|
Requires-Dist: xlsxwriter~=3.2
|
|
23
23
|
Requires-Dist: pathspec<2,>=1.0.3
|
|
24
24
|
Requires-Dist: zstandard~=0.25.0
|
|
25
|
-
Requires-Dist: meshagent-llm-proxy==0.
|
|
26
|
-
Requires-Dist: meshagent-codex==0.
|
|
25
|
+
Requires-Dist: meshagent-llm-proxy==0.46.0
|
|
26
|
+
Requires-Dist: meshagent-codex==0.46.0
|
|
27
27
|
Requires-Dist: rich~=14.3.0
|
|
28
28
|
Requires-Dist: sounddevice~=0.5
|
|
29
29
|
Requires-Dist: textual<9.0,>=8.2.3
|
|
@@ -32,23 +32,23 @@ Requires-Dist: prompt-toolkit~=3.0.52
|
|
|
32
32
|
Requires-Dist: ascii-magic~=2.3
|
|
33
33
|
Requires-Dist: pillow~=11.3.0
|
|
34
34
|
Provides-Extra: all
|
|
35
|
-
Requires-Dist: meshagent-agents[all]==0.
|
|
36
|
-
Requires-Dist: meshagent-api[all]==0.
|
|
37
|
-
Requires-Dist: meshagent-commoncrawl==0.
|
|
38
|
-
Requires-Dist: meshagent-scrapy==0.
|
|
39
|
-
Requires-Dist: meshagent-computers==0.
|
|
40
|
-
Requires-Dist: meshagent-openai==0.
|
|
41
|
-
Requires-Dist: meshagent-anthropic==0.
|
|
42
|
-
Requires-Dist: meshagent-otel==0.
|
|
43
|
-
Requires-Dist: meshagent-mcp==0.
|
|
44
|
-
Requires-Dist: meshagent-tools==0.
|
|
35
|
+
Requires-Dist: meshagent-agents[all]==0.46.0; extra == "all"
|
|
36
|
+
Requires-Dist: meshagent-api[all]==0.46.0; extra == "all"
|
|
37
|
+
Requires-Dist: meshagent-commoncrawl==0.46.0; extra == "all"
|
|
38
|
+
Requires-Dist: meshagent-scrapy==0.46.0; extra == "all"
|
|
39
|
+
Requires-Dist: meshagent-computers==0.46.0; extra == "all"
|
|
40
|
+
Requires-Dist: meshagent-openai==0.46.0; extra == "all"
|
|
41
|
+
Requires-Dist: meshagent-anthropic==0.46.0; extra == "all"
|
|
42
|
+
Requires-Dist: meshagent-otel==0.46.0; extra == "all"
|
|
43
|
+
Requires-Dist: meshagent-mcp==0.46.0; extra == "all"
|
|
44
|
+
Requires-Dist: meshagent-tools==0.46.0; extra == "all"
|
|
45
45
|
Requires-Dist: supabase-auth~=2.28.0; extra == "all"
|
|
46
46
|
Requires-Dist: prompt-toolkit~=3.0.52; extra == "all"
|
|
47
47
|
Provides-Extra: mcp-service
|
|
48
|
-
Requires-Dist: meshagent-agents[all]==0.
|
|
49
|
-
Requires-Dist: meshagent-api==0.
|
|
50
|
-
Requires-Dist: meshagent-mcp==0.
|
|
51
|
-
Requires-Dist: meshagent-tools==0.
|
|
48
|
+
Requires-Dist: meshagent-agents[all]==0.46.0; extra == "mcp-service"
|
|
49
|
+
Requires-Dist: meshagent-api==0.46.0; extra == "mcp-service"
|
|
50
|
+
Requires-Dist: meshagent-mcp==0.46.0; extra == "mcp-service"
|
|
51
|
+
Requires-Dist: meshagent-tools==0.46.0; extra == "mcp-service"
|
|
52
52
|
Requires-Dist: supabase-auth~=2.28.0; extra == "mcp-service"
|
|
53
53
|
Dynamic: license-file
|
|
54
54
|
|
|
@@ -538,8 +538,12 @@ async def _with_client(
|
|
|
538
538
|
|
|
539
539
|
connection = await account_client.connect_room(project_id=project_id, room=room)
|
|
540
540
|
|
|
541
|
+
room_url = connection.room_url
|
|
542
|
+
if os.getenv("MESHAGENT_ROOM_URL"):
|
|
543
|
+
room_url = websocket_room_url(room_name=room)
|
|
544
|
+
|
|
541
545
|
proto = WebSocketClientProtocol(
|
|
542
|
-
url=
|
|
546
|
+
url=room_url,
|
|
543
547
|
token=connection.jwt,
|
|
544
548
|
)
|
|
545
549
|
client_cm = RoomClient(protocol_factory=proto.create_factory())
|
|
@@ -525,6 +525,7 @@ async def test_images_inspect_renders_detail_tables(
|
|
|
525
525
|
async def test_with_client_uses_configured_websocket_room_url(
|
|
526
526
|
monkeypatch: pytest.MonkeyPatch,
|
|
527
527
|
) -> None:
|
|
528
|
+
monkeypatch.delenv("MESHAGENT_ROOM_URL", raising=False)
|
|
528
529
|
connection = SimpleNamespace(
|
|
529
530
|
jwt="jwt-token",
|
|
530
531
|
room_url="wss://room-router.meshagent.dev/custom/room-endpoint",
|
|
@@ -560,11 +561,6 @@ async def test_with_client_uses_configured_websocket_room_url(
|
|
|
560
561
|
monkeypatch.setattr(containers, "get_client", _fake_get_client)
|
|
561
562
|
monkeypatch.setattr(containers, "resolve_project_id", _fake_resolve_project_id)
|
|
562
563
|
monkeypatch.setattr(containers, "resolve_room", lambda room: f"{room}-resolved")
|
|
563
|
-
monkeypatch.setattr(
|
|
564
|
-
containers,
|
|
565
|
-
"websocket_room_url",
|
|
566
|
-
lambda room_name: f"wss://configured-router/{room_name}",
|
|
567
|
-
)
|
|
568
564
|
monkeypatch.setattr(containers, "WebSocketClientProtocol", _FakeProtocol)
|
|
569
565
|
monkeypatch.setattr(containers, "RoomClient", _FakeRoomClient)
|
|
570
566
|
|
|
@@ -579,7 +575,63 @@ async def test_with_client_uses_configured_websocket_room_url(
|
|
|
579
575
|
]
|
|
580
576
|
assert protocol_calls == [
|
|
581
577
|
{
|
|
582
|
-
"url": "wss://
|
|
578
|
+
"url": "wss://room-router.meshagent.dev/custom/room-endpoint",
|
|
579
|
+
"token": "jwt-token",
|
|
580
|
+
}
|
|
581
|
+
]
|
|
582
|
+
assert isinstance(client, _FakeRoomClient)
|
|
583
|
+
assert client.enter_calls == 1
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
@pytest.mark.asyncio
|
|
587
|
+
async def test_with_client_allows_environment_room_url_override(
|
|
588
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
589
|
+
) -> None:
|
|
590
|
+
monkeypatch.setenv("MESHAGENT_ROOM_URL", "wss://override.meshagent.test")
|
|
591
|
+
connection = SimpleNamespace(
|
|
592
|
+
jwt="jwt-token",
|
|
593
|
+
room_url="wss://room-router.meshagent.dev/custom/room-endpoint",
|
|
594
|
+
)
|
|
595
|
+
account_client = _FakeConnectAccountClient(connection=connection)
|
|
596
|
+
protocol_calls: list[dict[str, str]] = []
|
|
597
|
+
|
|
598
|
+
class _FakeProtocol:
|
|
599
|
+
def __init__(self, *, url: str, token: str) -> None:
|
|
600
|
+
protocol_calls.append({"url": url, "token": token})
|
|
601
|
+
|
|
602
|
+
def create_factory(self):
|
|
603
|
+
return lambda: self
|
|
604
|
+
|
|
605
|
+
class _FakeRoomClient:
|
|
606
|
+
def __init__(self, *, protocol_factory) -> None:
|
|
607
|
+
self.protocol = protocol_factory()
|
|
608
|
+
self.enter_calls = 0
|
|
609
|
+
|
|
610
|
+
async def __aenter__(self) -> "_FakeRoomClient":
|
|
611
|
+
self.enter_calls += 1
|
|
612
|
+
return self
|
|
613
|
+
|
|
614
|
+
async def _fake_get_client() -> _FakeConnectAccountClient:
|
|
615
|
+
return account_client
|
|
616
|
+
|
|
617
|
+
async def _fake_resolve_project_id(*, project_id):
|
|
618
|
+
return project_id
|
|
619
|
+
|
|
620
|
+
monkeypatch.setattr(containers, "get_client", _fake_get_client)
|
|
621
|
+
monkeypatch.setattr(containers, "resolve_project_id", _fake_resolve_project_id)
|
|
622
|
+
monkeypatch.setattr(containers, "resolve_room", lambda room: room)
|
|
623
|
+
monkeypatch.setattr(containers, "WebSocketClientProtocol", _FakeProtocol)
|
|
624
|
+
monkeypatch.setattr(containers, "RoomClient", _FakeRoomClient)
|
|
625
|
+
|
|
626
|
+
returned_account_client, client = await containers._with_client(
|
|
627
|
+
project_id="project-1",
|
|
628
|
+
room="room-1",
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
assert returned_account_client is account_client
|
|
632
|
+
assert protocol_calls == [
|
|
633
|
+
{
|
|
634
|
+
"url": "wss://override.meshagent.test/rooms/room-1",
|
|
583
635
|
"token": "jwt-token",
|
|
584
636
|
}
|
|
585
637
|
]
|
|
@@ -15,14 +15,12 @@ from pathlib import Path, PurePosixPath
|
|
|
15
15
|
from typing import Annotated, Literal, Optional, Protocol, TYPE_CHECKING
|
|
16
16
|
from urllib.parse import urlparse
|
|
17
17
|
|
|
18
|
-
from aiohttp import ClientTimeout
|
|
19
18
|
import typer
|
|
20
19
|
from pydantic import ValidationError
|
|
21
20
|
from pydantic_yaml import parse_yaml_raw_as
|
|
22
21
|
from rich import print
|
|
23
22
|
import yaml
|
|
24
23
|
|
|
25
|
-
from meshagent.api.http import new_client_session
|
|
26
24
|
from meshagent.cli import async_typer
|
|
27
25
|
from meshagent.cli.containers import (
|
|
28
26
|
_drain_stream_plain,
|
|
@@ -70,6 +68,7 @@ from meshagent.api.room_server_client import (
|
|
|
70
68
|
LogStream,
|
|
71
69
|
PublishedBuildImage,
|
|
72
70
|
RoomException,
|
|
71
|
+
ServicePortRuntimeState,
|
|
73
72
|
ServiceRuntimeState,
|
|
74
73
|
)
|
|
75
74
|
from meshagent.api.room_ports import RESERVED_ROOM_SERVICE_PORTS
|
|
@@ -124,13 +123,11 @@ class _DeployDomainPromptHandler(Protocol):
|
|
|
124
123
|
|
|
125
124
|
app = async_typer.AsyncTyper(help="Pack local directories as OCI images")
|
|
126
125
|
_BUILD_CONTEXT_CHUNK_SIZE = 1024 * 1024
|
|
127
|
-
_BUILD_CREATE_TIMEOUT_SECONDS = 120.0
|
|
128
126
|
_BUILD_WAIT_TIMEOUT_SECONDS = 600.0
|
|
129
127
|
_CLIENT_CLOSE_TIMEOUT_SECONDS = 2.0
|
|
130
128
|
_DEFAULT_CONTEXT_MOUNT_PATH = "/context"
|
|
131
129
|
_DEFAULT_REPOSITORY_TOKEN_TTL_SECONDS = 3600
|
|
132
130
|
_DEPLOY_CACHE_CLEANUP_TIMEOUT_SECONDS = 30.0
|
|
133
|
-
_DEPLOY_LIVENESS_REQUEST_TIMEOUT_SECONDS = 2.0
|
|
134
131
|
_DEPLOY_SERVICE_APPLY_TIMEOUT_SECONDS = 60.0
|
|
135
132
|
_DEPLOY_SERVICE_RESTART_TIMEOUT_SECONDS = 60.0
|
|
136
133
|
_DEPLOY_WAIT_TIMEOUT_SECONDS = 300.0
|
|
@@ -350,7 +347,7 @@ def default_pack_architecture() -> str:
|
|
|
350
347
|
|
|
351
348
|
|
|
352
349
|
def _meshagent_default_image_tag(*, image: str) -> str:
|
|
353
|
-
if image
|
|
350
|
+
if image in {"meshagent/buildkit:default", "meshagent/playwright:default"}:
|
|
354
351
|
return __version__
|
|
355
352
|
return f"{__version__}-nydus"
|
|
356
353
|
|
|
@@ -3217,37 +3214,6 @@ def _resolve_domain_liveness_path(
|
|
|
3217
3214
|
return service_port.liveness
|
|
3218
3215
|
|
|
3219
3216
|
|
|
3220
|
-
def _build_domain_liveness_url(*, domain: str, liveness_path: str) -> str:
|
|
3221
|
-
parsed = urlparse(domain if "://" in domain else f"https://{domain}")
|
|
3222
|
-
scheme = parsed.scheme or "https"
|
|
3223
|
-
netloc = parsed.netloc
|
|
3224
|
-
base_path = parsed.path
|
|
3225
|
-
if netloc == "":
|
|
3226
|
-
netloc = parsed.path
|
|
3227
|
-
base_path = ""
|
|
3228
|
-
if base_path != "/" and base_path.endswith("/"):
|
|
3229
|
-
base_path = base_path.rstrip("/")
|
|
3230
|
-
return f"{scheme}://{netloc}{base_path}{liveness_path}"
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
async def _probe_liveness_url(*, url: str) -> bool:
|
|
3234
|
-
try:
|
|
3235
|
-
async with new_client_session(
|
|
3236
|
-
timeout=ClientTimeout(total=_DEPLOY_LIVENESS_REQUEST_TIMEOUT_SECONDS)
|
|
3237
|
-
) as session:
|
|
3238
|
-
async with session.get(
|
|
3239
|
-
url,
|
|
3240
|
-
headers={
|
|
3241
|
-
"Accept": "*/*",
|
|
3242
|
-
"User-Agent": f"meshagent-cli/{__version__}",
|
|
3243
|
-
},
|
|
3244
|
-
) as resp:
|
|
3245
|
-
status_code = resp.status
|
|
3246
|
-
except Exception:
|
|
3247
|
-
return False
|
|
3248
|
-
return 200 <= status_code < 400 or status_code in {401, 403}
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
3217
|
async def _find_room_service_by_name(
|
|
3252
3218
|
*,
|
|
3253
3219
|
account_client,
|
|
@@ -3581,10 +3547,23 @@ async def _get_service_runtime_state(
|
|
|
3581
3547
|
client: RoomClient,
|
|
3582
3548
|
service_id: str,
|
|
3583
3549
|
) -> ServiceRuntimeState | None:
|
|
3584
|
-
service_list = await client.services.
|
|
3550
|
+
service_list = await client.services.list()
|
|
3585
3551
|
return service_list.service_states.get(service_id)
|
|
3586
3552
|
|
|
3587
3553
|
|
|
3554
|
+
def _find_service_liveness_port(
|
|
3555
|
+
*,
|
|
3556
|
+
state: ServiceRuntimeState,
|
|
3557
|
+
liveness_path: str | None,
|
|
3558
|
+
) -> ServicePortRuntimeState | None:
|
|
3559
|
+
if liveness_path is None:
|
|
3560
|
+
return None
|
|
3561
|
+
for port in state.status.ports:
|
|
3562
|
+
if port.liveness == liveness_path:
|
|
3563
|
+
return port
|
|
3564
|
+
return None
|
|
3565
|
+
|
|
3566
|
+
|
|
3588
3567
|
async def _drain_deploy_log_stream_tui(
|
|
3589
3568
|
stream: LogStream,
|
|
3590
3569
|
*,
|
|
@@ -3700,33 +3679,28 @@ async def _wait_for_deployed_service_live(
|
|
|
3700
3679
|
log_handler: Callable[[str], Awaitable[None]] | None = None,
|
|
3701
3680
|
) -> None:
|
|
3702
3681
|
active_logs: _ActiveDeployLogStream | None = None
|
|
3703
|
-
liveness_url = (
|
|
3704
|
-
_build_domain_liveness_url(domain=domain, liveness_path=liveness_path)
|
|
3705
|
-
if domain is not None and liveness_path is not None
|
|
3706
|
-
else None
|
|
3707
|
-
)
|
|
3708
3682
|
|
|
3709
3683
|
await _emit_deploy_status(
|
|
3710
3684
|
status_handler,
|
|
3711
3685
|
rich_message=f"[cyan]Waiting for service to go live:[/] {service_name} ({service_id})",
|
|
3712
3686
|
plain_message=f"Waiting for service to go live: {service_name} ({service_id})",
|
|
3713
3687
|
)
|
|
3714
|
-
if
|
|
3688
|
+
if liveness_path is not None:
|
|
3715
3689
|
await _emit_deploy_status(
|
|
3716
3690
|
status_handler,
|
|
3717
|
-
rich_message=f"[cyan]Waiting for liveness
|
|
3718
|
-
plain_message=f"Waiting for liveness
|
|
3691
|
+
rich_message=f"[cyan]Waiting for service liveness:[/] {liveness_path}",
|
|
3692
|
+
plain_message=f"Waiting for service liveness: {liveness_path}",
|
|
3719
3693
|
)
|
|
3720
3694
|
elif domain is not None:
|
|
3721
3695
|
await _emit_deploy_status(
|
|
3722
3696
|
status_handler,
|
|
3723
3697
|
rich_message=(
|
|
3724
3698
|
f"[yellow]Route created for {domain}, but the service has no HTTP "
|
|
3725
|
-
"liveness path to
|
|
3699
|
+
"liveness path to check.[/]"
|
|
3726
3700
|
),
|
|
3727
3701
|
plain_message=(
|
|
3728
3702
|
f"Route created for {domain}, but the service has no HTTP "
|
|
3729
|
-
"liveness path to
|
|
3703
|
+
"liveness path to check."
|
|
3730
3704
|
),
|
|
3731
3705
|
)
|
|
3732
3706
|
|
|
@@ -3804,7 +3778,7 @@ async def _wait_for_deployed_service_live(
|
|
|
3804
3778
|
await asyncio.sleep(_DEPLOY_WAIT_POLL_INTERVAL_SECONDS)
|
|
3805
3779
|
continue
|
|
3806
3780
|
|
|
3807
|
-
if
|
|
3781
|
+
if liveness_path is None:
|
|
3808
3782
|
await _emit_deploy_status(
|
|
3809
3783
|
status_handler,
|
|
3810
3784
|
rich_message=f"[green]Service is live:[/] {service_name} ({service_id})",
|
|
@@ -3812,11 +3786,15 @@ async def _wait_for_deployed_service_live(
|
|
|
3812
3786
|
)
|
|
3813
3787
|
return
|
|
3814
3788
|
|
|
3815
|
-
|
|
3789
|
+
liveness_port = _find_service_liveness_port(
|
|
3790
|
+
state=state,
|
|
3791
|
+
liveness_path=liveness_path,
|
|
3792
|
+
)
|
|
3793
|
+
if liveness_port is not None and liveness_port.liveness_status == "ready":
|
|
3816
3794
|
await _emit_deploy_status(
|
|
3817
3795
|
status_handler,
|
|
3818
|
-
rich_message=f"[green]
|
|
3819
|
-
plain_message=f"
|
|
3796
|
+
rich_message=f"[green]Service liveness is ready:[/] {liveness_path}",
|
|
3797
|
+
plain_message=f"Service liveness is ready: {liveness_path}",
|
|
3820
3798
|
)
|
|
3821
3799
|
return
|
|
3822
3800
|
|
|
@@ -3942,26 +3920,20 @@ async def _run_image_pack_stage(
|
|
|
3942
3920
|
)
|
|
3943
3921
|
},
|
|
3944
3922
|
)
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
size=archive_size,
|
|
3960
|
-
),
|
|
3961
|
-
timeout=_BUILD_CREATE_TIMEOUT_SECONDS,
|
|
3962
|
-
)
|
|
3963
|
-
except TimeoutError as exc:
|
|
3964
|
-
raise RuntimeError("timed out starting image build") from exc
|
|
3923
|
+
build_id = await client.containers.build(
|
|
3924
|
+
tags=[parsed_tag.value],
|
|
3925
|
+
mount_path=_DEFAULT_CONTEXT_MOUNT_PATH,
|
|
3926
|
+
context_path=_DEFAULT_CONTEXT_MOUNT_PATH,
|
|
3927
|
+
dockerfile_path=_generated_pack_dockerfile_path(
|
|
3928
|
+
mount_path=_DEFAULT_CONTEXT_MOUNT_PATH
|
|
3929
|
+
),
|
|
3930
|
+
optimize_image=True,
|
|
3931
|
+
private=False,
|
|
3932
|
+
credentials=registry_credentials,
|
|
3933
|
+
builder_name=_default_builder_name(client=client),
|
|
3934
|
+
chunks=_iter_file_chunks(archive_path),
|
|
3935
|
+
size=archive_size,
|
|
3936
|
+
)
|
|
3965
3937
|
try:
|
|
3966
3938
|
exit_code = await asyncio.wait_for(
|
|
3967
3939
|
_stream_build_job_logs_and_wait_for_exit(
|
|
@@ -4056,28 +4028,22 @@ async def _run_image_build_stage(
|
|
|
4056
4028
|
rich_message=f"[cyan]Uploading build context ({archive_size_text})...[/cyan]",
|
|
4057
4029
|
plain_message=f"Uploading build context ({archive_size_text})...",
|
|
4058
4030
|
)
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
size=archive_size,
|
|
4076
|
-
),
|
|
4077
|
-
timeout=_BUILD_CREATE_TIMEOUT_SECONDS,
|
|
4078
|
-
)
|
|
4079
|
-
except TimeoutError as exc:
|
|
4080
|
-
raise RuntimeError("timed out starting image build") from exc
|
|
4031
|
+
build_id = await client.containers.build(
|
|
4032
|
+
tags=tags,
|
|
4033
|
+
mount_path=build_inputs.pack_spec.mount_path,
|
|
4034
|
+
context_path=build_inputs.context_path,
|
|
4035
|
+
dockerfile_path=build_inputs.dockerfile_path,
|
|
4036
|
+
optimize_image=optimize,
|
|
4037
|
+
private=private,
|
|
4038
|
+
credentials=credentials,
|
|
4039
|
+
builder_name=resolved_builder_name,
|
|
4040
|
+
chunks=_iter_file_chunks_with_progress(
|
|
4041
|
+
path=archive_path,
|
|
4042
|
+
size=archive_size,
|
|
4043
|
+
status_handler=status_handler,
|
|
4044
|
+
),
|
|
4045
|
+
size=archive_size,
|
|
4046
|
+
)
|
|
4081
4047
|
await _emit_deploy_status(
|
|
4082
4048
|
status_handler,
|
|
4083
4049
|
rich_message="[cyan]Starting image build...[/cyan]",
|
|
@@ -38,6 +38,8 @@ from meshagent.cli import cli, image
|
|
|
38
38
|
from meshagent.api.room_server_client import (
|
|
39
39
|
PublishedBuildImage,
|
|
40
40
|
RoomException,
|
|
41
|
+
ServicePortRuntimeState,
|
|
42
|
+
ServiceRuntimeStatus,
|
|
41
43
|
ServiceRuntimeState,
|
|
42
44
|
)
|
|
43
45
|
from meshagent.api.specs.service import (
|
|
@@ -787,9 +789,13 @@ def test_replace_meshagent_image_vars_uses_dev_prefix(monkeypatch) -> None:
|
|
|
787
789
|
"us-central1-docker.pkg.dev/meshagent-life/meshagent-public/"
|
|
788
790
|
f"buildkit:{image.__version__}"
|
|
789
791
|
)
|
|
792
|
+
assert image.replace_meshagent_image_vars("meshagent/playwright:default") == (
|
|
793
|
+
"us-central1-docker.pkg.dev/meshagent-life/meshagent-public/"
|
|
794
|
+
f"playwright:{image.__version__}"
|
|
795
|
+
)
|
|
790
796
|
|
|
791
797
|
|
|
792
|
-
def
|
|
798
|
+
def test_replace_meshagent_image_vars_uses_nydus_for_non_exception_defaults(
|
|
793
799
|
monkeypatch,
|
|
794
800
|
) -> None:
|
|
795
801
|
monkeypatch.setattr(
|
|
@@ -2270,7 +2276,7 @@ def test_parse_meshagent_token_scope_supports_presets_and_json() -> None:
|
|
|
2270
2276
|
user_default = image._parse_meshagent_token_scope(value="userDefault")
|
|
2271
2277
|
custom = image._parse_meshagent_token_scope(value='{"queues":{"send":["jobs"]}}')
|
|
2272
2278
|
|
|
2273
|
-
assert user_default.secrets is
|
|
2279
|
+
assert user_default.secrets is None
|
|
2274
2280
|
assert user_default.admin is None
|
|
2275
2281
|
assert custom.queues is not None
|
|
2276
2282
|
assert custom.queues.send == ["jobs"]
|
|
@@ -2331,71 +2337,7 @@ async def test_validate_deploy_environment_secrets_requires_run_as() -> None:
|
|
|
2331
2337
|
|
|
2332
2338
|
|
|
2333
2339
|
@pytest.mark.asyncio
|
|
2334
|
-
|
|
2335
|
-
async def test_probe_liveness_url_accepts_reachable_statuses(
|
|
2336
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
2337
|
-
status_code: int,
|
|
2338
|
-
) -> None:
|
|
2339
|
-
class _FakeResponse:
|
|
2340
|
-
status = status_code
|
|
2341
|
-
|
|
2342
|
-
async def __aenter__(self):
|
|
2343
|
-
return self
|
|
2344
|
-
|
|
2345
|
-
async def __aexit__(self, exc_type, exc, tb) -> None:
|
|
2346
|
-
del exc_type, exc, tb
|
|
2347
|
-
|
|
2348
|
-
class _FakeSession:
|
|
2349
|
-
async def __aenter__(self):
|
|
2350
|
-
return self
|
|
2351
|
-
|
|
2352
|
-
async def __aexit__(self, exc_type, exc, tb) -> None:
|
|
2353
|
-
del exc_type, exc, tb
|
|
2354
|
-
|
|
2355
|
-
def get(self, *args, **kwargs):
|
|
2356
|
-
del args, kwargs
|
|
2357
|
-
return _FakeResponse()
|
|
2358
|
-
|
|
2359
|
-
monkeypatch.setattr(
|
|
2360
|
-
image, "new_client_session", lambda *args, **kwargs: _FakeSession()
|
|
2361
|
-
)
|
|
2362
|
-
|
|
2363
|
-
assert await image._probe_liveness_url(url="https://sheets.meshagent.dev/") is True
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
@pytest.mark.asyncio
|
|
2367
|
-
async def test_probe_liveness_url_rejects_server_errors(
|
|
2368
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
2369
|
-
) -> None:
|
|
2370
|
-
class _FakeResponse:
|
|
2371
|
-
status = 502
|
|
2372
|
-
|
|
2373
|
-
async def __aenter__(self):
|
|
2374
|
-
return self
|
|
2375
|
-
|
|
2376
|
-
async def __aexit__(self, exc_type, exc, tb) -> None:
|
|
2377
|
-
del exc_type, exc, tb
|
|
2378
|
-
|
|
2379
|
-
class _FakeSession:
|
|
2380
|
-
async def __aenter__(self):
|
|
2381
|
-
return self
|
|
2382
|
-
|
|
2383
|
-
async def __aexit__(self, exc_type, exc, tb) -> None:
|
|
2384
|
-
del exc_type, exc, tb
|
|
2385
|
-
|
|
2386
|
-
def get(self, *args, **kwargs):
|
|
2387
|
-
del args, kwargs
|
|
2388
|
-
return _FakeResponse()
|
|
2389
|
-
|
|
2390
|
-
monkeypatch.setattr(
|
|
2391
|
-
image, "new_client_session", lambda *args, **kwargs: _FakeSession()
|
|
2392
|
-
)
|
|
2393
|
-
|
|
2394
|
-
assert await image._probe_liveness_url(url="https://sheets.meshagent.dev/") is False
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
@pytest.mark.asyncio
|
|
2398
|
-
async def test_wait_for_deployed_service_live_streams_logs_and_checks_liveness(
|
|
2340
|
+
async def test_wait_for_deployed_service_live_streams_logs_and_checks_service_liveness(
|
|
2399
2341
|
monkeypatch: pytest.MonkeyPatch,
|
|
2400
2342
|
_stub_deploy_wait: dict[str, object],
|
|
2401
2343
|
) -> None:
|
|
@@ -2403,7 +2345,6 @@ async def test_wait_for_deployed_service_live_streams_logs_and_checks_liveness(
|
|
|
2403
2345
|
"prints": [],
|
|
2404
2346
|
"started_logs": [],
|
|
2405
2347
|
"stopped_logs": [],
|
|
2406
|
-
"probe_urls": [],
|
|
2407
2348
|
}
|
|
2408
2349
|
states = [
|
|
2409
2350
|
ServiceRuntimeState(service_id="service-1", state="starting"),
|
|
@@ -2416,11 +2357,29 @@ async def test_wait_for_deployed_service_live_streams_logs_and_checks_liveness(
|
|
|
2416
2357
|
service_id="service-1",
|
|
2417
2358
|
state="running",
|
|
2418
2359
|
container_id="container-1",
|
|
2360
|
+
status=ServiceRuntimeStatus(
|
|
2361
|
+
ports=[
|
|
2362
|
+
ServicePortRuntimeState(
|
|
2363
|
+
num=8000,
|
|
2364
|
+
liveness="/ready",
|
|
2365
|
+
liveness_status="not_ready",
|
|
2366
|
+
)
|
|
2367
|
+
]
|
|
2368
|
+
),
|
|
2419
2369
|
),
|
|
2420
2370
|
ServiceRuntimeState(
|
|
2421
2371
|
service_id="service-1",
|
|
2422
2372
|
state="running",
|
|
2423
2373
|
container_id="container-1",
|
|
2374
|
+
status=ServiceRuntimeStatus(
|
|
2375
|
+
ports=[
|
|
2376
|
+
ServicePortRuntimeState(
|
|
2377
|
+
num=8000,
|
|
2378
|
+
liveness="/ready",
|
|
2379
|
+
liveness_status="ready",
|
|
2380
|
+
)
|
|
2381
|
+
]
|
|
2382
|
+
),
|
|
2424
2383
|
),
|
|
2425
2384
|
]
|
|
2426
2385
|
fake_active_logs = SimpleNamespace(container_id="container-1")
|
|
@@ -2430,7 +2389,7 @@ async def test_wait_for_deployed_service_live_streams_logs_and_checks_liveness(
|
|
|
2430
2389
|
self._runtime_states = runtime_states
|
|
2431
2390
|
self._index = 0
|
|
2432
2391
|
|
|
2433
|
-
async def
|
|
2392
|
+
async def list(self):
|
|
2434
2393
|
state = self._runtime_states[
|
|
2435
2394
|
min(self._index, len(self._runtime_states) - 1)
|
|
2436
2395
|
]
|
|
@@ -2450,19 +2409,12 @@ async def test_wait_for_deployed_service_live_streams_logs_and_checks_liveness(
|
|
|
2450
2409
|
async def _fake_stop_deploy_log_stream(*, active_logs) -> None:
|
|
2451
2410
|
captured["stopped_logs"].append(active_logs)
|
|
2452
2411
|
|
|
2453
|
-
probe_results = iter([False, True])
|
|
2454
|
-
|
|
2455
|
-
async def _fake_probe_liveness_url(*, url: str) -> bool:
|
|
2456
|
-
captured["probe_urls"].append(url)
|
|
2457
|
-
return next(probe_results)
|
|
2458
|
-
|
|
2459
2412
|
monkeypatch.setattr(image.asyncio, "to_thread", _fake_to_thread)
|
|
2460
2413
|
monkeypatch.setattr(image, "_DEPLOY_WAIT_POLL_INTERVAL_SECONDS", 0)
|
|
2461
2414
|
monkeypatch.setattr(
|
|
2462
2415
|
image, "_start_deploy_log_stream", _fake_start_deploy_log_stream
|
|
2463
2416
|
)
|
|
2464
2417
|
monkeypatch.setattr(image, "_stop_deploy_log_stream", _fake_stop_deploy_log_stream)
|
|
2465
|
-
monkeypatch.setattr(image, "_probe_liveness_url", _fake_probe_liveness_url)
|
|
2466
2418
|
monkeypatch.setattr(
|
|
2467
2419
|
image,
|
|
2468
2420
|
"print",
|
|
@@ -2483,11 +2435,7 @@ async def test_wait_for_deployed_service_live_streams_logs_and_checks_liveness(
|
|
|
2483
2435
|
|
|
2484
2436
|
assert captured["started_logs"] == ["container-1"]
|
|
2485
2437
|
assert captured["stopped_logs"] == [fake_active_logs]
|
|
2486
|
-
assert captured["
|
|
2487
|
-
"https://app.meshagent.app/ready",
|
|
2488
|
-
"https://app.meshagent.app/ready",
|
|
2489
|
-
]
|
|
2490
|
-
assert any("Liveness URL responded" in message for message in captured["prints"])
|
|
2438
|
+
assert any("Service liveness is ready" in message for message in captured["prints"])
|
|
2491
2439
|
|
|
2492
2440
|
|
|
2493
2441
|
@pytest.mark.asyncio
|
|
@@ -2521,7 +2469,7 @@ async def test_wait_for_deployed_service_live_exits_when_container_restarts(
|
|
|
2521
2469
|
self._runtime_states = runtime_states
|
|
2522
2470
|
self._index = 0
|
|
2523
2471
|
|
|
2524
|
-
async def
|
|
2472
|
+
async def list(self):
|
|
2525
2473
|
state = self._runtime_states[
|
|
2526
2474
|
min(self._index, len(self._runtime_states) - 1)
|
|
2527
2475
|
]
|
|
@@ -2828,7 +2776,7 @@ async def test_deploy_image_creates_room_service_with_mounts_env_secret_and_toke
|
|
|
2828
2776
|
async def restart(self, *, service_id: str) -> None:
|
|
2829
2777
|
captured["restarted_service_id"] = service_id
|
|
2830
2778
|
|
|
2831
|
-
async def
|
|
2779
|
+
async def list(self):
|
|
2832
2780
|
return SimpleNamespace(
|
|
2833
2781
|
service_states={
|
|
2834
2782
|
"service-1": ServiceRuntimeState(
|
|
@@ -4940,7 +4888,7 @@ async def test_deploy_image_updates_existing_service_route_and_replaces_environm
|
|
|
4940
4888
|
async def restart(self, *, service_id: str) -> None:
|
|
4941
4889
|
captured["restarted_service_id"] = service_id
|
|
4942
4890
|
|
|
4943
|
-
async def
|
|
4891
|
+
async def list(self):
|
|
4944
4892
|
return SimpleNamespace(
|
|
4945
4893
|
service_states={
|
|
4946
4894
|
"service-1": ServiceRuntimeState(
|
|
@@ -5044,7 +4992,7 @@ async def test_deploy_image_updates_existing_service_route_and_replaces_environm
|
|
|
5044
4992
|
assert env_by_name["MESHAGENT_TOKEN"].token.role == "agent"
|
|
5045
4993
|
assert env_by_name["MESHAGENT_TOKEN"].token.api is not None
|
|
5046
4994
|
assert env_by_name["MESHAGENT_TOKEN"].token.api.admin is not None
|
|
5047
|
-
assert env_by_name["MESHAGENT_TOKEN"].token.api.secrets is
|
|
4995
|
+
assert env_by_name["MESHAGENT_TOKEN"].token.api.secrets is None
|
|
5048
4996
|
assert env_by_name["MESHAGENT_TOKEN"].token.api.tunnels is not None
|
|
5049
4997
|
for env_name in ("OPENAI_API_KEY", "ANTHROPIC_API_KEY"):
|
|
5050
4998
|
assert env_by_name[env_name].token is not None
|
|
@@ -5094,7 +5042,7 @@ async def test_deploy_image_sets_cookie_validation_on_private_published_ports(
|
|
|
5094
5042
|
async def restart(self, *, service_id: str) -> None:
|
|
5095
5043
|
captured["restarted_service_id"] = service_id
|
|
5096
5044
|
|
|
5097
|
-
async def
|
|
5045
|
+
async def list(self):
|
|
5098
5046
|
return SimpleNamespace(
|
|
5099
5047
|
service_states={
|
|
5100
5048
|
"service-1": ServiceRuntimeState(
|
|
@@ -5219,7 +5167,7 @@ async def test_deploy_image_preserves_existing_liveness_when_default_is_used(
|
|
|
5219
5167
|
async def restart(self, *, service_id: str) -> None:
|
|
5220
5168
|
captured["restarted_service_id"] = service_id
|
|
5221
5169
|
|
|
5222
|
-
async def
|
|
5170
|
+
async def list(self):
|
|
5223
5171
|
return SimpleNamespace(
|
|
5224
5172
|
service_states={
|
|
5225
5173
|
"service-1": ServiceRuntimeState(
|
|
@@ -5321,7 +5269,7 @@ async def test_deploy_image_liveness_flag_overrides_http_ports_only(
|
|
|
5321
5269
|
async def restart(self, *, service_id: str) -> None:
|
|
5322
5270
|
captured["restarted_service_id"] = service_id
|
|
5323
5271
|
|
|
5324
|
-
async def
|
|
5272
|
+
async def list(self):
|
|
5325
5273
|
return SimpleNamespace(
|
|
5326
5274
|
service_states={
|
|
5327
5275
|
"service-1": ServiceRuntimeState(
|
|
@@ -5413,7 +5361,7 @@ async def test_deploy_image_domain_requires_exactly_one_published_port(
|
|
|
5413
5361
|
)
|
|
5414
5362
|
|
|
5415
5363
|
class _FakeServices:
|
|
5416
|
-
async def
|
|
5364
|
+
async def list(self):
|
|
5417
5365
|
return SimpleNamespace(
|
|
5418
5366
|
service_states={
|
|
5419
5367
|
"service-1": ServiceRuntimeState(
|