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.
Files changed (173) hide show
  1. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/PKG-INFO +17 -17
  2. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/projects.py +75 -2
  3. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/projects_test.py +96 -0
  4. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/routes.py +273 -14
  5. meshagent_cli-0.49.0/meshagent/cli/routes_test.py +352 -0
  6. meshagent_cli-0.49.0/meshagent/cli/version.py +1 -0
  7. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent_cli.egg-info/PKG-INFO +17 -17
  8. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent_cli.egg-info/requires.txt +16 -16
  9. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/pyproject.toml +16 -16
  10. meshagent_cli-0.48.0/meshagent/cli/routes_test.py +0 -119
  11. meshagent_cli-0.48.0/meshagent/cli/version.py +0 -1
  12. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/LICENSE +0 -0
  13. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/README.md +0 -0
  14. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/__init__.py +0 -0
  15. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/_filedrop.py +0 -0
  16. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/agent.py +0 -0
  17. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/agent_cli_options_test.py +0 -0
  18. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/agent_package_cli.py +0 -0
  19. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/agent_package_cli_test.py +0 -0
  20. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/agents.py +0 -0
  21. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/agents_test.py +0 -0
  22. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/ask.py +0 -0
  23. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/ask_test.py +0 -0
  24. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/async_typer.py +0 -0
  25. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/async_typer_test.py +0 -0
  26. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/auth.py +0 -0
  27. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/auth_async.py +0 -0
  28. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/auth_async_test.py +0 -0
  29. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/auth_test.py +0 -0
  30. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/call.py +0 -0
  31. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/chatbot.py +0 -0
  32. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/cli.py +0 -0
  33. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/cli_mcp.py +0 -0
  34. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/cli_test.py +0 -0
  35. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/common_options.py +0 -0
  36. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/config.py +0 -0
  37. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/config_test.py +0 -0
  38. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/containers.py +0 -0
  39. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/containers_test.py +0 -0
  40. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create.py +0 -0
  41. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/__init__.py +0 -0
  42. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/backend-agent/server.py +0 -0
  43. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/contact-form/server.py +0 -0
  44. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/slack-channel/channel.py +0 -0
  45. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/slack-channel/scripts/configure-slack.py +0 -0
  46. {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
  47. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/slack-channel/server.py +0 -0
  48. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/task-queue-dashboard/server.py +0 -0
  49. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/telegram-channel/channel.py +0 -0
  50. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/telegram-channel/scripts/configure-telegram.py +0 -0
  51. {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
  52. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/telegram-channel/server.py +0 -0
  53. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/twilio-channel/channel.py +0 -0
  54. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/twilio-channel/server.py +0 -0
  55. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/webserver/server.py +0 -0
  56. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/whatsapp-channel/channel.py +0 -0
  57. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_project_templates/python/whatsapp-channel/server.py +0 -0
  58. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/create_test.py +0 -0
  59. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/dataset.py +0 -0
  60. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/dataset_test.py +0 -0
  61. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/developer.py +0 -0
  62. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/developer_test.py +0 -0
  63. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/doctor.py +0 -0
  64. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/doctor_dockerfiles/__init__.py +0 -0
  65. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/doctor_templates/__init__.py +0 -0
  66. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/doctor_test.py +0 -0
  67. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/feeds.py +0 -0
  68. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/feeds_test.py +0 -0
  69. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/helper.py +0 -0
  70. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/helper_test.py +0 -0
  71. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/helpers.py +0 -0
  72. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/host.py +0 -0
  73. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/iam.py +0 -0
  74. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/iam_test.py +0 -0
  75. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/image.py +0 -0
  76. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/image_test.py +0 -0
  77. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/launch.py +0 -0
  78. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/launch_test.py +0 -0
  79. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/llm.py +0 -0
  80. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/llm_test.py +0 -0
  81. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/local_settings.py +0 -0
  82. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/local_settings_test.py +0 -0
  83. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/mailbot.py +0 -0
  84. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/mailbot_test.py +0 -0
  85. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/mailboxes.py +0 -0
  86. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/mailboxes_test.py +0 -0
  87. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/meeting_transcriber.py +0 -0
  88. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/memory.py +0 -0
  89. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/memory_test.py +0 -0
  90. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/meshagent_images.py +0 -0
  91. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/meshagent_images_test.py +0 -0
  92. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/messaging.py +0 -0
  93. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/messaging_test.py +0 -0
  94. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/mounts.py +0 -0
  95. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/multi.py +0 -0
  96. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/oci_archive.py +0 -0
  97. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/oci_archive_test.py +0 -0
  98. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/participant_token.py +0 -0
  99. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/port.py +0 -0
  100. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/port_test.py +0 -0
  101. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/preamble_rules.py +0 -0
  102. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/preamble_rules_test.py +0 -0
  103. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/process.py +0 -0
  104. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/process_live_test.py +0 -0
  105. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/process_test.py +0 -0
  106. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/queue.py +0 -0
  107. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/queue_test.py +0 -0
  108. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/registry.py +0 -0
  109. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/registry_test.py +0 -0
  110. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/room.py +0 -0
  111. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/room_connect.py +0 -0
  112. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/room_connect_test.py +0 -0
  113. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/room_services.py +0 -0
  114. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/room_services_test.py +0 -0
  115. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/rooms.py +0 -0
  116. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/rooms_test.py +0 -0
  117. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/root_commands.py +0 -0
  118. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/root_commands_test.py +0 -0
  119. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sample_channel_http_test.py +0 -0
  120. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sample_channel_live_e2e_test.py +0 -0
  121. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/scheduled_tasks.py +0 -0
  122. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/scheduled_tasks_test.py +0 -0
  123. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/secrets.py +0 -0
  124. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/secrets_test.py +0 -0
  125. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/service_accounts.py +0 -0
  126. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/service_accounts_test.py +0 -0
  127. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/services.py +0 -0
  128. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/services_test.py +0 -0
  129. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sessions.py +0 -0
  130. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sessions_test.py +0 -0
  131. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sqlite.py +0 -0
  132. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sqlite_test.py +0 -0
  133. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/storage.py +0 -0
  134. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/storage_test.py +0 -0
  135. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/subscriptions.py +0 -0
  136. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sync.py +0 -0
  137. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/sync_test.py +0 -0
  138. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/task_runner.py +0 -0
  139. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/task_runner_test.py +0 -0
  140. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/test.py +0 -0
  141. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/testing.py +0 -0
  142. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/thread_inspect.py +0 -0
  143. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/thread_inspect_test.py +0 -0
  144. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/thread_sidebar.py +0 -0
  145. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tool_call_summary.py +0 -0
  146. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tool_call_summary_test.py +0 -0
  147. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tool_integrations.py +0 -0
  148. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tool_integrations_test.py +0 -0
  149. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/__init__.py +0 -0
  150. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/auth_switch.py +0 -0
  151. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/auth_switch_test.py +0 -0
  152. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/create.py +0 -0
  153. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/deploy_room.py +0 -0
  154. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/deploy_room_test.py +0 -0
  155. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/project_activate.py +0 -0
  156. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/project_activate_test.py +0 -0
  157. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/setup.py +0 -0
  158. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/setup_splash_frames.py +0 -0
  159. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/tui/setup_test.py +0 -0
  160. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/version_check.py +0 -0
  161. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/version_check_test.py +0 -0
  162. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/voicebot.py +0 -0
  163. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/volumes.py +0 -0
  164. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/volumes_test.py +0 -0
  165. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/webserver.py +0 -0
  166. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/webserver_test.py +0 -0
  167. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/worker.py +0 -0
  168. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent/cli/worker_test.py +0 -0
  169. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent_cli.egg-info/SOURCES.txt +0 -0
  170. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent_cli.egg-info/dependency_links.txt +0 -0
  171. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent_cli.egg-info/entry_points.txt +0 -0
  172. {meshagent_cli-0.48.0 → meshagent_cli-0.49.0}/meshagent_cli.egg-info/top_level.txt +0 -0
  173. {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.48.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.48.0
26
- Requires-Dist: meshagent-codex==0.48.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.48.0; extra == "all"
36
- Requires-Dist: meshagent-api[all]==0.48.0; extra == "all"
37
- Requires-Dist: meshagent-commoncrawl==0.48.0; extra == "all"
38
- Requires-Dist: meshagent-scrapy==0.48.0; extra == "all"
39
- Requires-Dist: meshagent-computers==0.48.0; extra == "all"
40
- Requires-Dist: meshagent-openai==0.48.0; extra == "all"
41
- Requires-Dist: meshagent-anthropic==0.48.0; extra == "all"
42
- Requires-Dist: meshagent-otel==0.48.0; extra == "all"
43
- Requires-Dist: meshagent-mcp==0.48.0; extra == "all"
44
- Requires-Dist: meshagent-tools==0.48.0; extra == "all"
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.48.0; extra == "mcp-service"
49
- Requires-Dist: meshagent-api==0.48.0; extra == "mcp-service"
50
- Requires-Dist: meshagent-mcp==0.48.0; extra == "mcp-service"
51
- Requires-Dist: meshagent-tools==0.48.0; extra == "mcp-service"
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 typing import Annotated
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
- if domain is None or port is None:
154
- raise typer.BadParameter("Provide --file or both --domain and --port")
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": [{"path": "/", "pathType": "prefix", "targetPort": port}],
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
- port = port or route.port
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": [{"path": "/", "pathType": "prefix", "targetPort": port}],
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()