meshagent-cli 0.48.0__tar.gz → 0.49.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.48.0 → meshagent_cli-0.49.0}/PKG-INFO +17 -17
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/projects.py +75 -2
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/projects_test.py +96 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/routes.py +273 -14
- meshagent_cli-0.49.0/meshagent/cli/routes_test.py +352 -0
- meshagent_cli-0.49.0/meshagent/cli/version.py +1 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent_cli.egg-info/PKG-INFO +17 -17
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent_cli.egg-info/requires.txt +16 -16
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/pyproject.toml +16 -16
- meshagent_cli-0.48.0/meshagent/cli/routes_test.py +0 -119
- meshagent_cli-0.48.0/meshagent/cli/version.py +0 -1
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/LICENSE +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/README.md +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/__init__.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/_filedrop.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/agent.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/agent_cli_options_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/agent_package_cli.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/agent_package_cli_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/agents.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/agents_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/ask.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/ask_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/async_typer.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/async_typer_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/auth.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/auth_async.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/auth_async_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/auth_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/call.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/chatbot.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/cli.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/cli_mcp.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/cli_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/common_options.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/config.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/config_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/containers.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/containers_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/__init__.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/backend-agent/server.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/contact-form/server.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/slack-channel/channel.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/slack-channel/scripts/configure-slack.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/slack-channel/scripts/setup-slack-dev-route.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/slack-channel/server.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/task-queue-dashboard/server.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/telegram-channel/channel.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/telegram-channel/scripts/configure-telegram.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/telegram-channel/scripts/create-bot-token.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/telegram-channel/server.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/twilio-channel/channel.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/twilio-channel/server.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/webserver/server.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/whatsapp-channel/channel.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/whatsapp-channel/server.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/dataset.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/dataset_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/developer.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/developer_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/doctor.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/doctor_dockerfiles/__init__.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/doctor_templates/__init__.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/doctor_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/feeds.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/feeds_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/helper.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/helper_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/helpers.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/host.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/iam.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/iam_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/image.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/image_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/launch.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/launch_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/llm.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/llm_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/local_settings.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/local_settings_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/mailbot.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/mailbot_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/mailboxes.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/mailboxes_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/meeting_transcriber.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/memory.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/memory_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/meshagent_images.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/meshagent_images_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/messaging.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/messaging_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/mounts.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/multi.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/oci_archive.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/oci_archive_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/participant_token.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/port.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/port_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/preamble_rules.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/preamble_rules_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/process.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/process_live_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/process_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/queue.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/queue_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/registry.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/registry_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/room.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/room_connect.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/room_connect_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/room_services.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/room_services_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/rooms.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/rooms_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/root_commands.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/root_commands_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sample_channel_http_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sample_channel_live_e2e_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/scheduled_tasks.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/scheduled_tasks_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/secrets.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/secrets_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/service_accounts.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/service_accounts_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/services.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/services_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sessions.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sessions_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sqlite.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sqlite_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/storage.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/storage_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/subscriptions.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sync.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sync_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/task_runner.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/task_runner_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/testing.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/thread_inspect.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/thread_inspect_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/thread_sidebar.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tool_call_summary.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tool_call_summary_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tool_integrations.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tool_integrations_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/__init__.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/auth_switch.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/auth_switch_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/create.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/deploy_room.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/deploy_room_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/project_activate.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/project_activate_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/setup.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/setup_splash_frames.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/setup_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/version_check.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/version_check_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/voicebot.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/volumes.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/volumes_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/webserver.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/webserver_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/worker.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/worker_test.py +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent_cli.egg-info/SOURCES.txt +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent_cli.egg-info/dependency_links.txt +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent_cli.egg-info/entry_points.txt +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent_cli.egg-info/top_level.txt +0 -0
- {meshagent_cli-0.48.0 → meshagent_cli-0.49.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.49.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.49.0
|
|
26
|
+
Requires-Dist: meshagent-codex==0.49.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.49.0; extra == "all"
|
|
36
|
+
Requires-Dist: meshagent-api[all]==0.49.0; extra == "all"
|
|
37
|
+
Requires-Dist: meshagent-commoncrawl==0.49.0; extra == "all"
|
|
38
|
+
Requires-Dist: meshagent-scrapy==0.49.0; extra == "all"
|
|
39
|
+
Requires-Dist: meshagent-computers==0.49.0; extra == "all"
|
|
40
|
+
Requires-Dist: meshagent-openai==0.49.0; extra == "all"
|
|
41
|
+
Requires-Dist: meshagent-anthropic==0.49.0; extra == "all"
|
|
42
|
+
Requires-Dist: meshagent-otel==0.49.0; extra == "all"
|
|
43
|
+
Requires-Dist: meshagent-mcp==0.49.0; extra == "all"
|
|
44
|
+
Requires-Dist: meshagent-tools==0.49.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.49.0; extra == "mcp-service"
|
|
49
|
+
Requires-Dist: meshagent-api==0.49.0; extra == "mcp-service"
|
|
50
|
+
Requires-Dist: meshagent-mcp==0.49.0; extra == "mcp-service"
|
|
51
|
+
Requires-Dist: meshagent-tools==0.49.0; extra == "mcp-service"
|
|
52
52
|
Requires-Dist: supabase-auth~=2.28.0; extra == "mcp-service"
|
|
53
53
|
Dynamic: license-file
|
|
54
54
|
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Annotated, Literal
|
|
4
5
|
|
|
5
6
|
import typer
|
|
7
|
+
from pydantic import BaseModel, ConfigDict
|
|
8
|
+
from pydantic_yaml import parse_yaml_raw_as
|
|
6
9
|
from rich import print
|
|
7
10
|
|
|
11
|
+
from meshagent.api import ApiScope
|
|
8
12
|
from meshagent.api.client import Meshagent, NotFoundError, ProjectInfo, ProjectsPage
|
|
9
13
|
from meshagent.cli import async_typer
|
|
14
|
+
from meshagent.cli.common_options import OutputFormatOption, ProjectIdOption
|
|
10
15
|
from meshagent.cli.helper import (
|
|
11
16
|
get_client,
|
|
12
17
|
get_active_project,
|
|
13
18
|
print_json_table,
|
|
19
|
+
resolve_project_id,
|
|
14
20
|
set_active_project,
|
|
15
21
|
)
|
|
16
|
-
from meshagent.cli.common_options import OutputFormatOption
|
|
17
22
|
|
|
18
23
|
app = async_typer.AsyncTyper(help="Manage or activate your meshagent projects")
|
|
19
24
|
|
|
@@ -25,6 +30,30 @@ class ListedProject:
|
|
|
25
30
|
is_active: bool
|
|
26
31
|
|
|
27
32
|
|
|
33
|
+
RoomAccessRole = Literal["guest", "viewer", "operator", "developer", "admin"]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ParticipantTokenRoleDefinition(BaseModel):
|
|
37
|
+
model_config = ConfigDict(extra="forbid")
|
|
38
|
+
|
|
39
|
+
api: ApiScope
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class RoomRoleDefinition(BaseModel):
|
|
43
|
+
model_config = ConfigDict(extra="forbid")
|
|
44
|
+
|
|
45
|
+
participant: ParticipantTokenRoleDefinition | None = None
|
|
46
|
+
site_user: bool = False
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class RoomRolesSpec(BaseModel):
|
|
50
|
+
model_config = ConfigDict(extra="forbid")
|
|
51
|
+
|
|
52
|
+
version: Literal["v1"]
|
|
53
|
+
kind: Literal["RoomRoles"]
|
|
54
|
+
roles: dict[RoomAccessRole, RoomRoleDefinition]
|
|
55
|
+
|
|
56
|
+
|
|
28
57
|
def _parse_listed_projects(
|
|
29
58
|
response: ProjectsPage,
|
|
30
59
|
*,
|
|
@@ -209,6 +238,50 @@ async def get(
|
|
|
209
238
|
await client.close()
|
|
210
239
|
|
|
211
240
|
|
|
241
|
+
@app.async_command(
|
|
242
|
+
"set-room-roles",
|
|
243
|
+
help="Set authoritative project room-role mappings from a YAML spec.",
|
|
244
|
+
)
|
|
245
|
+
async def set_room_roles(
|
|
246
|
+
file: Annotated[Path, typer.Argument(help="RoomRoles YAML spec")],
|
|
247
|
+
project_id: ProjectIdOption,
|
|
248
|
+
):
|
|
249
|
+
try:
|
|
250
|
+
spec = parse_yaml_raw_as(RoomRolesSpec, file.read_text())
|
|
251
|
+
except (OSError, ValueError) as error:
|
|
252
|
+
print(f"[red]Invalid room role spec: {error}[/red]")
|
|
253
|
+
raise typer.Exit(code=1) from error
|
|
254
|
+
|
|
255
|
+
client = await get_client()
|
|
256
|
+
try:
|
|
257
|
+
resolved_project_id = await resolve_project_id(project_id)
|
|
258
|
+
await client.set_project_settings_document(
|
|
259
|
+
resolved_project_id,
|
|
260
|
+
"room_roles",
|
|
261
|
+
{"roles": spec.model_dump(mode="json", exclude_none=True)["roles"]},
|
|
262
|
+
)
|
|
263
|
+
print(f"[green]Room role override updated:[/] {resolved_project_id}")
|
|
264
|
+
finally:
|
|
265
|
+
await client.close()
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@app.async_command(
|
|
269
|
+
"reset-room-roles",
|
|
270
|
+
help="Remove the project room-role override and restore built-in defaults.",
|
|
271
|
+
)
|
|
272
|
+
async def reset_room_roles(project_id: ProjectIdOption):
|
|
273
|
+
client = await get_client()
|
|
274
|
+
try:
|
|
275
|
+
resolved_project_id = await resolve_project_id(project_id)
|
|
276
|
+
await client.delete_project_settings_document(
|
|
277
|
+
resolved_project_id,
|
|
278
|
+
"room_roles",
|
|
279
|
+
)
|
|
280
|
+
print(f"[green]Room role defaults restored:[/] {resolved_project_id}")
|
|
281
|
+
finally:
|
|
282
|
+
await client.close()
|
|
283
|
+
|
|
284
|
+
|
|
212
285
|
@app.async_command(
|
|
213
286
|
"activate", help="Set the active project for subsequent CLI commands."
|
|
214
287
|
)
|
|
@@ -17,6 +17,8 @@ class _FakeClient:
|
|
|
17
17
|
self._project_rows = list(project_rows)
|
|
18
18
|
self._created_project_id = created_project_id
|
|
19
19
|
self.created_project_names: list[str] = []
|
|
20
|
+
self.settings_documents: dict[str, dict] = {}
|
|
21
|
+
self.deleted_settings_documents: list[str] = []
|
|
20
22
|
self.closed = False
|
|
21
23
|
|
|
22
24
|
async def list_projects(self) -> ProjectsPage:
|
|
@@ -48,6 +50,16 @@ class _FakeClient:
|
|
|
48
50
|
self._project_rows.append(created_row)
|
|
49
51
|
return ProjectInfo.model_validate(created_row)
|
|
50
52
|
|
|
53
|
+
async def set_project_settings_document(
|
|
54
|
+
self, project_id: str, name: str, document: dict
|
|
55
|
+
):
|
|
56
|
+
self.settings_documents[name] = document
|
|
57
|
+
return {}
|
|
58
|
+
|
|
59
|
+
async def delete_project_settings_document(self, project_id: str, name: str):
|
|
60
|
+
self.deleted_settings_documents.append(name)
|
|
61
|
+
return {}
|
|
62
|
+
|
|
51
63
|
async def close(self) -> None:
|
|
52
64
|
self.closed = True
|
|
53
65
|
|
|
@@ -375,3 +387,87 @@ async def test_activate_prints_cancel_message_when_tui_is_canceled(monkeypatch)
|
|
|
375
387
|
assert active_project_calls == []
|
|
376
388
|
assert output == ["Project activation canceled."]
|
|
377
389
|
assert client.closed is True
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
@pytest.mark.asyncio
|
|
393
|
+
async def test_set_room_roles_writes_dedicated_document(monkeypatch, tmp_path) -> None:
|
|
394
|
+
client = _FakeClient(
|
|
395
|
+
[
|
|
396
|
+
{
|
|
397
|
+
"id": "project-1",
|
|
398
|
+
"name": "Alpha",
|
|
399
|
+
}
|
|
400
|
+
]
|
|
401
|
+
)
|
|
402
|
+
spec = tmp_path / "room-roles.yaml"
|
|
403
|
+
spec.write_text(
|
|
404
|
+
"""version: v1
|
|
405
|
+
kind: RoomRoles
|
|
406
|
+
roles:
|
|
407
|
+
guest:
|
|
408
|
+
participant:
|
|
409
|
+
api:
|
|
410
|
+
messaging:
|
|
411
|
+
broadcast: false
|
|
412
|
+
list: true
|
|
413
|
+
send: false
|
|
414
|
+
site_user: false
|
|
415
|
+
"""
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
async def _fake_get_client():
|
|
419
|
+
return client
|
|
420
|
+
|
|
421
|
+
async def _fake_resolve_project_id(project_id=None):
|
|
422
|
+
return project_id or "project-1"
|
|
423
|
+
|
|
424
|
+
monkeypatch.setattr(projects, "get_client", _fake_get_client)
|
|
425
|
+
monkeypatch.setattr(projects, "resolve_project_id", _fake_resolve_project_id)
|
|
426
|
+
|
|
427
|
+
await projects.set_room_roles(file=spec, project_id="project-1")
|
|
428
|
+
|
|
429
|
+
assert client.settings_documents == {
|
|
430
|
+
"room_roles": {
|
|
431
|
+
"roles": {
|
|
432
|
+
"guest": {
|
|
433
|
+
"participant": {
|
|
434
|
+
"api": {
|
|
435
|
+
"messaging": {
|
|
436
|
+
"broadcast": False,
|
|
437
|
+
"list": True,
|
|
438
|
+
"send": False,
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
"site_user": False,
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
}
|
|
447
|
+
assert client.closed is True
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
@pytest.mark.asyncio
|
|
451
|
+
async def test_reset_room_roles_deletes_dedicated_document(monkeypatch) -> None:
|
|
452
|
+
client = _FakeClient(
|
|
453
|
+
[
|
|
454
|
+
{
|
|
455
|
+
"id": "project-1",
|
|
456
|
+
"name": "Alpha",
|
|
457
|
+
}
|
|
458
|
+
]
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
async def _fake_get_client():
|
|
462
|
+
return client
|
|
463
|
+
|
|
464
|
+
async def _fake_resolve_project_id(project_id=None):
|
|
465
|
+
return project_id or "project-1"
|
|
466
|
+
|
|
467
|
+
monkeypatch.setattr(projects, "get_client", _fake_get_client)
|
|
468
|
+
monkeypatch.setattr(projects, "resolve_project_id", _fake_resolve_project_id)
|
|
469
|
+
|
|
470
|
+
await projects.reset_room_roles(project_id="project-1")
|
|
471
|
+
|
|
472
|
+
assert client.deleted_settings_documents == ["room_roles"]
|
|
473
|
+
assert client.closed is True
|
|
@@ -14,7 +14,7 @@ from pydantic import ValidationError
|
|
|
14
14
|
from rich import print
|
|
15
15
|
|
|
16
16
|
from meshagent.api.client import ValidationErrorResponse
|
|
17
|
-
from meshagent.api.specs.service import RouteSpec
|
|
17
|
+
from meshagent.api.specs.service import RouteContentSpec, RouteCorsRule, RouteSpec
|
|
18
18
|
from meshagent.cli import async_typer
|
|
19
19
|
from meshagent.cli.common_options import ProjectIdOption, OutputFormatOption
|
|
20
20
|
from meshagent.cli.helper import (
|
|
@@ -52,6 +52,74 @@ def _parse_annotations(annotations: Optional[str]) -> Optional[dict[str, str]]:
|
|
|
52
52
|
raise typer.BadParameter("Invalid JSON for --annotations") from exc
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
def _parse_cors(cors: Optional[str]) -> Optional[list[RouteCorsRule]]:
|
|
56
|
+
if cors is None:
|
|
57
|
+
return None
|
|
58
|
+
if cors.strip() == "":
|
|
59
|
+
return []
|
|
60
|
+
try:
|
|
61
|
+
value = json.loads(cors)
|
|
62
|
+
if not isinstance(value, list):
|
|
63
|
+
raise ValueError("expected a JSON array")
|
|
64
|
+
return [RouteCorsRule.model_validate(rule) for rule in value]
|
|
65
|
+
except (json.JSONDecodeError, TypeError, ValueError, ValidationError) as exc:
|
|
66
|
+
raise typer.BadParameter("Invalid JSON for --cors") from exc
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _parse_compression(compression: Optional[str]) -> Optional[str]:
|
|
70
|
+
if compression is None:
|
|
71
|
+
return None
|
|
72
|
+
normalized = compression.strip().lower()
|
|
73
|
+
if normalized not in {"brotli", "gzip", "none"}:
|
|
74
|
+
raise typer.BadParameter(
|
|
75
|
+
"Invalid value for --compression: expected brotli, gzip, or none"
|
|
76
|
+
)
|
|
77
|
+
return normalized
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _parse_route_path(path: str) -> str:
|
|
81
|
+
if not path.startswith("/"):
|
|
82
|
+
raise typer.BadParameter("RouteSpec paths must start with /")
|
|
83
|
+
return path
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _content_options_supplied(
|
|
87
|
+
*,
|
|
88
|
+
content_path: Optional[str],
|
|
89
|
+
cors: Optional[str],
|
|
90
|
+
index: Optional[bool],
|
|
91
|
+
iap: Optional[bool],
|
|
92
|
+
compression: Optional[str],
|
|
93
|
+
) -> bool:
|
|
94
|
+
return any(
|
|
95
|
+
value is not None for value in (content_path, cors, index, iap, compression)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _route_table_row(route) -> dict[str, object]:
|
|
100
|
+
paths = route.spec.paths
|
|
101
|
+
content = [path.targetContent for path in paths if path.targetContent is not None]
|
|
102
|
+
return {
|
|
103
|
+
"domain": route.domain,
|
|
104
|
+
"backend": route.spec.room_name or route.spec.agent_name or "",
|
|
105
|
+
"path": ", ".join(path.path for path in paths),
|
|
106
|
+
"port": ", ".join(
|
|
107
|
+
str(path.targetPort) for path in paths if path.targetPort is not None
|
|
108
|
+
),
|
|
109
|
+
"content_path": ", ".join(item.subpath or "/" for item in content),
|
|
110
|
+
"index": ", ".join(str(item.index).lower() for item in content),
|
|
111
|
+
"iap": ", ".join(str(item.iap).lower() for item in content),
|
|
112
|
+
"compression": ", ".join(item.compression for item in content),
|
|
113
|
+
"cors": " | ".join(
|
|
114
|
+
json.dumps(
|
|
115
|
+
[rule.model_dump(mode="json") for rule in item.cors],
|
|
116
|
+
separators=(",", ":"),
|
|
117
|
+
)
|
|
118
|
+
for item in content
|
|
119
|
+
),
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
55
123
|
def _load_route_spec(path: str) -> RouteSpec:
|
|
56
124
|
try:
|
|
57
125
|
text = Path(path).expanduser().read_text(encoding="utf-8")
|
|
@@ -128,6 +196,34 @@ async def route_create(
|
|
|
128
196
|
help="Published port to route to",
|
|
129
197
|
),
|
|
130
198
|
] = None,
|
|
199
|
+
path: Annotated[
|
|
200
|
+
str,
|
|
201
|
+
typer.Option("--path", help="Public URL path to expose"),
|
|
202
|
+
] = "/",
|
|
203
|
+
content_path: Annotated[
|
|
204
|
+
Optional[str],
|
|
205
|
+
typer.Option(
|
|
206
|
+
"--content-path",
|
|
207
|
+
"--room-path",
|
|
208
|
+
help="Room storage subpath to serve directly",
|
|
209
|
+
),
|
|
210
|
+
] = None,
|
|
211
|
+
cors: Annotated[
|
|
212
|
+
Optional[str],
|
|
213
|
+
typer.Option("--cors", help="CORS rules as a JSON array"),
|
|
214
|
+
] = None,
|
|
215
|
+
index: Annotated[
|
|
216
|
+
Optional[bool],
|
|
217
|
+
typer.Option("--index/--no-index", help="Serve index.html for directories"),
|
|
218
|
+
] = None,
|
|
219
|
+
iap: Annotated[
|
|
220
|
+
Optional[bool],
|
|
221
|
+
typer.Option("--iap/--no-iap", help="Require identity-aware proxy access"),
|
|
222
|
+
] = None,
|
|
223
|
+
compression: Annotated[
|
|
224
|
+
Optional[str],
|
|
225
|
+
typer.Option("--compression", help="brotli, gzip, or none"),
|
|
226
|
+
] = None,
|
|
131
227
|
annotations: Annotated[
|
|
132
228
|
Optional[str],
|
|
133
229
|
typer.Option(
|
|
@@ -150,8 +246,25 @@ async def route_create(
|
|
|
150
246
|
try:
|
|
151
247
|
project_id = await resolve_project_id(project_id)
|
|
152
248
|
if file is None:
|
|
153
|
-
|
|
154
|
-
|
|
249
|
+
content_options = _content_options_supplied(
|
|
250
|
+
content_path=content_path,
|
|
251
|
+
cors=cors,
|
|
252
|
+
index=index,
|
|
253
|
+
iap=iap,
|
|
254
|
+
compression=compression,
|
|
255
|
+
)
|
|
256
|
+
if domain is None or (port is None and not content_options):
|
|
257
|
+
raise typer.BadParameter(
|
|
258
|
+
"Provide --file or --domain and one of --port or --content-path"
|
|
259
|
+
)
|
|
260
|
+
if port is not None and content_options:
|
|
261
|
+
raise typer.BadParameter(
|
|
262
|
+
"--port cannot be combined with room content options"
|
|
263
|
+
)
|
|
264
|
+
if content_options and content_path is None:
|
|
265
|
+
raise typer.BadParameter(
|
|
266
|
+
"Provide --content-path when using room content options"
|
|
267
|
+
)
|
|
155
268
|
room = resolve_room(room)
|
|
156
269
|
if room is None:
|
|
157
270
|
print(
|
|
@@ -159,12 +272,31 @@ async def route_create(
|
|
|
159
272
|
)
|
|
160
273
|
raise typer.Exit(code=1)
|
|
161
274
|
parsed_annotations = _parse_annotations(annotations) or {}
|
|
275
|
+
target: dict[str, object]
|
|
276
|
+
if content_path is not None:
|
|
277
|
+
target = {
|
|
278
|
+
"targetContent": RouteContentSpec(
|
|
279
|
+
subpath=content_path,
|
|
280
|
+
cors=_parse_cors(cors) or [],
|
|
281
|
+
index=index or False,
|
|
282
|
+
iap=iap or False,
|
|
283
|
+
compression=_parse_compression(compression) or "brotli",
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
else:
|
|
287
|
+
target = {"targetPort": port}
|
|
162
288
|
spec = RouteSpec.model_validate(
|
|
163
289
|
{
|
|
164
290
|
"metadata": {"name": domain, "annotations": parsed_annotations},
|
|
165
291
|
"domain": domain,
|
|
166
292
|
"backend": {"room": {"name": room}},
|
|
167
|
-
"paths": [
|
|
293
|
+
"paths": [
|
|
294
|
+
{
|
|
295
|
+
"path": _parse_route_path(path),
|
|
296
|
+
"pathType": "prefix",
|
|
297
|
+
**target,
|
|
298
|
+
}
|
|
299
|
+
],
|
|
168
300
|
}
|
|
169
301
|
)
|
|
170
302
|
else:
|
|
@@ -219,6 +351,34 @@ async def route_update(
|
|
|
219
351
|
help="Published port to route to",
|
|
220
352
|
),
|
|
221
353
|
] = None,
|
|
354
|
+
path: Annotated[
|
|
355
|
+
Optional[str],
|
|
356
|
+
typer.Option("--path", help="Public URL path to expose"),
|
|
357
|
+
] = None,
|
|
358
|
+
content_path: Annotated[
|
|
359
|
+
Optional[str],
|
|
360
|
+
typer.Option(
|
|
361
|
+
"--content-path",
|
|
362
|
+
"--room-path",
|
|
363
|
+
help="Room storage subpath to serve directly",
|
|
364
|
+
),
|
|
365
|
+
] = None,
|
|
366
|
+
cors: Annotated[
|
|
367
|
+
Optional[str],
|
|
368
|
+
typer.Option("--cors", help="CORS rules as a JSON array"),
|
|
369
|
+
] = None,
|
|
370
|
+
index: Annotated[
|
|
371
|
+
Optional[bool],
|
|
372
|
+
typer.Option("--index/--no-index", help="Serve index.html for directories"),
|
|
373
|
+
] = None,
|
|
374
|
+
iap: Annotated[
|
|
375
|
+
Optional[bool],
|
|
376
|
+
typer.Option("--iap/--no-iap", help="Require identity-aware proxy access"),
|
|
377
|
+
] = None,
|
|
378
|
+
compression: Annotated[
|
|
379
|
+
Optional[str],
|
|
380
|
+
typer.Option("--compression", help="brotli, gzip, or none"),
|
|
381
|
+
] = None,
|
|
222
382
|
annotations: Annotated[
|
|
223
383
|
Optional[str],
|
|
224
384
|
typer.Option(
|
|
@@ -238,6 +398,19 @@ async def route_update(
|
|
|
238
398
|
else:
|
|
239
399
|
room = resolve_room(room)
|
|
240
400
|
parsed_annotations = _parse_annotations(annotations)
|
|
401
|
+
parsed_cors = _parse_cors(cors)
|
|
402
|
+
parsed_compression = _parse_compression(compression)
|
|
403
|
+
content_options = _content_options_supplied(
|
|
404
|
+
content_path=content_path,
|
|
405
|
+
cors=cors,
|
|
406
|
+
index=index,
|
|
407
|
+
iap=iap,
|
|
408
|
+
compression=compression,
|
|
409
|
+
)
|
|
410
|
+
if port is not None and content_options:
|
|
411
|
+
raise typer.BadParameter(
|
|
412
|
+
"--port cannot be combined with room content options"
|
|
413
|
+
)
|
|
241
414
|
try:
|
|
242
415
|
route = await client.get_route(project_id=project_id, domain=domain)
|
|
243
416
|
except (ClientResponseError, ValidationErrorResponse) as exc:
|
|
@@ -250,18 +423,105 @@ async def route_update(
|
|
|
250
423
|
raise typer.Exit(code=1)
|
|
251
424
|
raise
|
|
252
425
|
room = room or route.room_name
|
|
253
|
-
|
|
426
|
+
if room == "":
|
|
427
|
+
raise typer.BadParameter("Provide --room for a room route")
|
|
254
428
|
parsed_annotations = (
|
|
255
429
|
parsed_annotations
|
|
256
430
|
if parsed_annotations is not None
|
|
257
431
|
else route.annotations
|
|
258
432
|
)
|
|
433
|
+
current_path = route.spec.paths[0] if route.spec.paths else None
|
|
434
|
+
route_path = (
|
|
435
|
+
_parse_route_path(path)
|
|
436
|
+
if path is not None
|
|
437
|
+
else current_path.path
|
|
438
|
+
if current_path is not None
|
|
439
|
+
else "/"
|
|
440
|
+
)
|
|
441
|
+
if port is not None:
|
|
442
|
+
target: dict[str, object] = {"targetPort": port}
|
|
443
|
+
strip_prefix = (
|
|
444
|
+
current_path.stripPrefix if current_path is not None else False
|
|
445
|
+
)
|
|
446
|
+
elif content_options:
|
|
447
|
+
current_content = (
|
|
448
|
+
current_path.targetContent if current_path is not None else None
|
|
449
|
+
)
|
|
450
|
+
if content_path is None and current_content is None:
|
|
451
|
+
raise typer.BadParameter(
|
|
452
|
+
"Provide --content-path when using room content options"
|
|
453
|
+
)
|
|
454
|
+
target = {
|
|
455
|
+
"targetContent": RouteContentSpec(
|
|
456
|
+
subpath=(
|
|
457
|
+
content_path
|
|
458
|
+
if content_path is not None
|
|
459
|
+
else current_content.subpath
|
|
460
|
+
if current_content is not None
|
|
461
|
+
else ""
|
|
462
|
+
),
|
|
463
|
+
cors=(
|
|
464
|
+
parsed_cors
|
|
465
|
+
if parsed_cors is not None
|
|
466
|
+
else current_content.cors
|
|
467
|
+
if current_content is not None
|
|
468
|
+
else []
|
|
469
|
+
),
|
|
470
|
+
index=(
|
|
471
|
+
index
|
|
472
|
+
if index is not None
|
|
473
|
+
else current_content.index
|
|
474
|
+
if current_content is not None
|
|
475
|
+
else False
|
|
476
|
+
),
|
|
477
|
+
iap=(
|
|
478
|
+
iap
|
|
479
|
+
if iap is not None
|
|
480
|
+
else current_content.iap
|
|
481
|
+
if current_content is not None
|
|
482
|
+
else False
|
|
483
|
+
),
|
|
484
|
+
compression=(
|
|
485
|
+
parsed_compression
|
|
486
|
+
if parsed_compression is not None
|
|
487
|
+
else current_content.compression
|
|
488
|
+
if current_content is not None
|
|
489
|
+
else "brotli"
|
|
490
|
+
),
|
|
491
|
+
)
|
|
492
|
+
}
|
|
493
|
+
strip_prefix = False
|
|
494
|
+
else:
|
|
495
|
+
if current_path is None:
|
|
496
|
+
raise typer.BadParameter(
|
|
497
|
+
"Provide --port or --content-path for a room route"
|
|
498
|
+
)
|
|
499
|
+
if current_path.targetPort is not None:
|
|
500
|
+
target = {"targetPort": current_path.targetPort}
|
|
501
|
+
elif current_path.targetContent is not None:
|
|
502
|
+
target = {"targetContent": current_path.targetContent}
|
|
503
|
+
else:
|
|
504
|
+
raise typer.BadParameter(
|
|
505
|
+
"Provide --port or --content-path for a room route"
|
|
506
|
+
)
|
|
507
|
+
strip_prefix = current_path.stripPrefix
|
|
508
|
+
paths = [
|
|
509
|
+
{
|
|
510
|
+
"path": route_path,
|
|
511
|
+
"pathType": (
|
|
512
|
+
current_path.pathType if current_path is not None else "prefix"
|
|
513
|
+
),
|
|
514
|
+
"stripPrefix": strip_prefix,
|
|
515
|
+
**target,
|
|
516
|
+
},
|
|
517
|
+
*[item.model_dump(mode="python") for item in route.spec.paths[1:]],
|
|
518
|
+
]
|
|
259
519
|
spec = RouteSpec.model_validate(
|
|
260
520
|
{
|
|
261
521
|
"metadata": {"name": domain, "annotations": parsed_annotations},
|
|
262
522
|
"domain": domain,
|
|
263
523
|
"backend": {"room": {"name": room}},
|
|
264
|
-
"paths":
|
|
524
|
+
"paths": paths,
|
|
265
525
|
}
|
|
266
526
|
)
|
|
267
527
|
|
|
@@ -353,17 +613,16 @@ async def route_list(
|
|
|
353
613
|
print({"routes": [route.model_dump(mode="json") for route in routes]})
|
|
354
614
|
else:
|
|
355
615
|
print_json_table(
|
|
356
|
-
[
|
|
357
|
-
{
|
|
358
|
-
"domain": route.domain,
|
|
359
|
-
"backend": route.spec.room_name or route.spec.agent_name or "",
|
|
360
|
-
"port": route.port,
|
|
361
|
-
}
|
|
362
|
-
for route in routes
|
|
363
|
-
],
|
|
616
|
+
[_route_table_row(route) for route in routes],
|
|
364
617
|
"domain",
|
|
365
618
|
"backend",
|
|
619
|
+
"path",
|
|
366
620
|
"port",
|
|
621
|
+
"content_path",
|
|
622
|
+
"index",
|
|
623
|
+
"iap",
|
|
624
|
+
"compression",
|
|
625
|
+
"cors",
|
|
367
626
|
)
|
|
368
627
|
finally:
|
|
369
628
|
await client.close()
|