controlmesh 0.17.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- controlmesh/_Dockerfile.sandbox +48 -0
- controlmesh/__init__.py +3 -0
- controlmesh/__main__.py +474 -0
- controlmesh/_banner.txt +14 -0
- controlmesh/_config_example.json +238 -0
- controlmesh/_home_defaults/RULES.md +74 -0
- controlmesh/_home_defaults/config/RULES-all-clis.md +185 -0
- controlmesh/_home_defaults/config/RULES-claude-only.md +133 -0
- controlmesh/_home_defaults/config/RULES-codex-only.md +141 -0
- controlmesh/_home_defaults/config/RULES-gemini-only.md +139 -0
- controlmesh/_home_defaults/workspace/JOIN_NOTIFICATION.md +9 -0
- controlmesh/_home_defaults/workspace/RULES.md +91 -0
- controlmesh/_home_defaults/workspace/cron_tasks/RULES-all-clis.md +153 -0
- controlmesh/_home_defaults/workspace/cron_tasks/RULES-claude-only.md +88 -0
- controlmesh/_home_defaults/workspace/cron_tasks/RULES-codex-only.md +111 -0
- controlmesh/_home_defaults/workspace/cron_tasks/RULES-gemini-only.md +100 -0
- controlmesh/_home_defaults/workspace/memory_system/MAINMEMORY.md +13 -0
- controlmesh/_home_defaults/workspace/memory_system/RULES.md +51 -0
- controlmesh/_home_defaults/workspace/output_to_user/RULES.md +20 -0
- controlmesh/_home_defaults/workspace/skills/RULES.md +44 -0
- controlmesh/_home_defaults/workspace/skills/skill-creator/LICENSE.txt +202 -0
- controlmesh/_home_defaults/workspace/skills/skill-creator/SKILL.md +357 -0
- controlmesh/_home_defaults/workspace/skills/skill-creator/references/output-patterns.md +82 -0
- controlmesh/_home_defaults/workspace/skills/skill-creator/references/workflows.md +28 -0
- controlmesh/_home_defaults/workspace/skills/skill-creator/scripts/init_skill.py +304 -0
- controlmesh/_home_defaults/workspace/skills/skill-creator/scripts/package_skill.py +111 -0
- controlmesh/_home_defaults/workspace/skills/skill-creator/scripts/quick_validate.py +107 -0
- controlmesh/_home_defaults/workspace/tasks/RULES.md +21 -0
- controlmesh/_home_defaults/workspace/telegram_files/RULES.md +43 -0
- controlmesh/_home_defaults/workspace/tools/RULES.md +48 -0
- controlmesh/_home_defaults/workspace/tools/_tool_shared.py +71 -0
- controlmesh/_home_defaults/workspace/tools/agent_tools/RULES.md +249 -0
- controlmesh/_home_defaults/workspace/tools/agent_tools/ask_agent.py +83 -0
- controlmesh/_home_defaults/workspace/tools/agent_tools/ask_agent_async.py +114 -0
- controlmesh/_home_defaults/workspace/tools/agent_tools/create_agent.py +391 -0
- controlmesh/_home_defaults/workspace/tools/agent_tools/edit_shared_knowledge.py +84 -0
- controlmesh/_home_defaults/workspace/tools/agent_tools/list_agents.py +72 -0
- controlmesh/_home_defaults/workspace/tools/agent_tools/remove_agent.py +70 -0
- controlmesh/_home_defaults/workspace/tools/cron_tools/RULES-all-clis.md +156 -0
- controlmesh/_home_defaults/workspace/tools/cron_tools/RULES-claude-only.md +106 -0
- controlmesh/_home_defaults/workspace/tools/cron_tools/RULES-codex-only.md +118 -0
- controlmesh/_home_defaults/workspace/tools/cron_tools/RULES-gemini-only.md +109 -0
- controlmesh/_home_defaults/workspace/tools/cron_tools/_shared.py +233 -0
- controlmesh/_home_defaults/workspace/tools/cron_tools/cron_add.py +455 -0
- controlmesh/_home_defaults/workspace/tools/cron_tools/cron_edit.py +500 -0
- controlmesh/_home_defaults/workspace/tools/cron_tools/cron_list.py +83 -0
- controlmesh/_home_defaults/workspace/tools/cron_tools/cron_remove.py +153 -0
- controlmesh/_home_defaults/workspace/tools/cron_tools/cron_time.py +103 -0
- controlmesh/_home_defaults/workspace/tools/media_tools/RULES.md +35 -0
- controlmesh/_home_defaults/workspace/tools/media_tools/file_info.py +99 -0
- controlmesh/_home_defaults/workspace/tools/media_tools/list_files.py +93 -0
- controlmesh/_home_defaults/workspace/tools/media_tools/process_video.py +278 -0
- controlmesh/_home_defaults/workspace/tools/media_tools/read_document.py +115 -0
- controlmesh/_home_defaults/workspace/tools/media_tools/transcribe_audio.py +156 -0
- controlmesh/_home_defaults/workspace/tools/task_tools/RULES.md +93 -0
- controlmesh/_home_defaults/workspace/tools/task_tools/_shared.py +72 -0
- controlmesh/_home_defaults/workspace/tools/task_tools/ask_parent.py +66 -0
- controlmesh/_home_defaults/workspace/tools/task_tools/cancel_task.py +53 -0
- controlmesh/_home_defaults/workspace/tools/task_tools/create_task.py +114 -0
- controlmesh/_home_defaults/workspace/tools/task_tools/delete_task.py +54 -0
- controlmesh/_home_defaults/workspace/tools/task_tools/list_tasks.py +63 -0
- controlmesh/_home_defaults/workspace/tools/task_tools/resume_task.py +67 -0
- controlmesh/_home_defaults/workspace/tools/user_tools/RULES.md +31 -0
- controlmesh/_home_defaults/workspace/tools/webhook_tools/RULES-all-clis.md +318 -0
- controlmesh/_home_defaults/workspace/tools/webhook_tools/RULES-claude-only.md +236 -0
- controlmesh/_home_defaults/workspace/tools/webhook_tools/RULES-codex-only.md +258 -0
- controlmesh/_home_defaults/workspace/tools/webhook_tools/RULES-gemini-only.md +244 -0
- controlmesh/_home_defaults/workspace/tools/webhook_tools/_shared.py +61 -0
- controlmesh/_home_defaults/workspace/tools/webhook_tools/webhook_add.py +442 -0
- controlmesh/_home_defaults/workspace/tools/webhook_tools/webhook_edit.py +282 -0
- controlmesh/_home_defaults/workspace/tools/webhook_tools/webhook_list.py +73 -0
- controlmesh/_home_defaults/workspace/tools/webhook_tools/webhook_remove.py +83 -0
- controlmesh/_home_defaults/workspace/tools/webhook_tools/webhook_rotate_token.py +124 -0
- controlmesh/_home_defaults/workspace/tools/webhook_tools/webhook_test.py +195 -0
- controlmesh/_plugins/__init__.py +1 -0
- controlmesh/_plugins/feishu_auth_kit/VENDORED_FROM.md +13 -0
- controlmesh/_plugins/feishu_auth_kit/__init__.py +4 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/__init__.py +161 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/agent_runtime.py +397 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/app_registration.py +220 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/cardkit.py +127 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/claude_adapter.py +57 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/cli.py +1289 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/client.py +155 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/device_flow.py +127 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/domains.py +83 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/message_context.py +162 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/models.py +48 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/native_agent_tools.py +166 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/native_contract.py +259 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/orchestration.py +447 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/owner_policy.py +111 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/probe.py +52 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/runtime_cards.py +219 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/scopes.py +127 -0
- controlmesh/_plugins/feishu_auth_kit/feishu_auth_kit/token_store.py +147 -0
- controlmesh/_plugins/feishu_auth_kit/runner.py +10 -0
- controlmesh/agents_runtime/__init__.py +12 -0
- controlmesh/agents_runtime/context.py +32 -0
- controlmesh/agents_runtime/manager.py +116 -0
- controlmesh/agents_runtime/progress.py +166 -0
- controlmesh/agents_runtime/results.py +59 -0
- controlmesh/agents_runtime/tools.py +166 -0
- controlmesh/api/__init__.py +24 -0
- controlmesh/api/admin_read.py +214 -0
- controlmesh/api/catalog_http.py +63 -0
- controlmesh/api/crypto.py +50 -0
- controlmesh/api/server.py +665 -0
- controlmesh/background/__init__.py +8 -0
- controlmesh/background/models.py +55 -0
- controlmesh/background/observer.py +279 -0
- controlmesh/bus/__init__.py +16 -0
- controlmesh/bus/adapters.py +303 -0
- controlmesh/bus/bus.py +221 -0
- controlmesh/bus/cron_sanitize.py +27 -0
- controlmesh/bus/envelope.py +90 -0
- controlmesh/bus/lock_pool.py +59 -0
- controlmesh/case_pack/__init__.py +15 -0
- controlmesh/case_pack/__main__.py +58 -0
- controlmesh/case_pack/io.py +14 -0
- controlmesh/case_pack/lint.py +192 -0
- controlmesh/case_pack/models.py +93 -0
- controlmesh/case_pack/render.py +93 -0
- controlmesh/cleanup/__init__.py +5 -0
- controlmesh/cleanup/observer.py +149 -0
- controlmesh/cli/__init__.py +33 -0
- controlmesh/cli/auth.py +432 -0
- controlmesh/cli/base.py +228 -0
- controlmesh/cli/claude_provider.py +219 -0
- controlmesh/cli/coalescer.py +149 -0
- controlmesh/cli/codex_cache.py +126 -0
- controlmesh/cli/codex_cache_observer.py +36 -0
- controlmesh/cli/codex_discovery.py +149 -0
- controlmesh/cli/codex_events.py +315 -0
- controlmesh/cli/codex_hooks.py +230 -0
- controlmesh/cli/codex_provider.py +293 -0
- controlmesh/cli/executor.py +336 -0
- controlmesh/cli/factory.py +32 -0
- controlmesh/cli/gemini_cache.py +61 -0
- controlmesh/cli/gemini_cache_observer.py +54 -0
- controlmesh/cli/gemini_events.py +215 -0
- controlmesh/cli/gemini_provider.py +659 -0
- controlmesh/cli/gemini_utils.py +455 -0
- controlmesh/cli/init_wizard.py +810 -0
- controlmesh/cli/model_cache.py +259 -0
- controlmesh/cli/openai_agents_provider.py +349 -0
- controlmesh/cli/param_resolver.py +137 -0
- controlmesh/cli/process_registry.py +257 -0
- controlmesh/cli/service.py +406 -0
- controlmesh/cli/stream_events.py +196 -0
- controlmesh/cli/timeout_controller.py +223 -0
- controlmesh/cli/types.py +78 -0
- controlmesh/cli_commands/__init__.py +1 -0
- controlmesh/cli_commands/agents.py +318 -0
- controlmesh/cli_commands/api_cmd.py +170 -0
- controlmesh/cli_commands/auth.py +1357 -0
- controlmesh/cli_commands/docker.py +515 -0
- controlmesh/cli_commands/feishu.py +93 -0
- controlmesh/cli_commands/install.py +91 -0
- controlmesh/cli_commands/lifecycle.py +288 -0
- controlmesh/cli_commands/runtime.py +212 -0
- controlmesh/cli_commands/service.py +92 -0
- controlmesh/cli_commands/status.py +214 -0
- controlmesh/cli_commands/tasks.py +137 -0
- controlmesh/commands.py +52 -0
- controlmesh/config.py +605 -0
- controlmesh/config_reload.py +226 -0
- controlmesh/cron/__init__.py +6 -0
- controlmesh/cron/dependency_queue.py +163 -0
- controlmesh/cron/execution.py +298 -0
- controlmesh/cron/manager.py +207 -0
- controlmesh/cron/observer.py +412 -0
- controlmesh/cron/policy.py +119 -0
- controlmesh/errors.py +37 -0
- controlmesh/files/__init__.py +1 -0
- controlmesh/files/allowed_roots.py +27 -0
- controlmesh/files/browser.py +28 -0
- controlmesh/files/image_processor.py +76 -0
- controlmesh/files/prompt.py +64 -0
- controlmesh/files/storage.py +86 -0
- controlmesh/files/tags.py +157 -0
- controlmesh/gateways/__init__.py +9 -0
- controlmesh/gateways/config.py +95 -0
- controlmesh/gateways/types.py +8 -0
- controlmesh/heartbeat/__init__.py +5 -0
- controlmesh/heartbeat/observer.py +346 -0
- controlmesh/history/__init__.py +6 -0
- controlmesh/history/catalog.py +325 -0
- controlmesh/history/index.py +1341 -0
- controlmesh/history/models.py +39 -0
- controlmesh/history/store.py +52 -0
- controlmesh/i18n/__init__.py +82 -0
- controlmesh/i18n/de/chat.toml +459 -0
- controlmesh/i18n/de/cli.toml +286 -0
- controlmesh/i18n/de/commands.toml +29 -0
- controlmesh/i18n/de/wizard.toml +182 -0
- controlmesh/i18n/en/chat.toml +458 -0
- controlmesh/i18n/en/cli.toml +286 -0
- controlmesh/i18n/en/commands.toml +29 -0
- controlmesh/i18n/en/wizard.toml +182 -0
- controlmesh/i18n/es/chat.toml +459 -0
- controlmesh/i18n/es/cli.toml +286 -0
- controlmesh/i18n/es/commands.toml +29 -0
- controlmesh/i18n/es/wizard.toml +182 -0
- controlmesh/i18n/fr/chat.toml +459 -0
- controlmesh/i18n/fr/cli.toml +286 -0
- controlmesh/i18n/fr/commands.toml +29 -0
- controlmesh/i18n/fr/wizard.toml +182 -0
- controlmesh/i18n/loader.py +131 -0
- controlmesh/i18n/nl/chat.toml +459 -0
- controlmesh/i18n/nl/cli.toml +286 -0
- controlmesh/i18n/nl/commands.toml +29 -0
- controlmesh/i18n/nl/wizard.toml +182 -0
- controlmesh/i18n/pt/chat.toml +459 -0
- controlmesh/i18n/pt/cli.toml +286 -0
- controlmesh/i18n/pt/commands.toml +29 -0
- controlmesh/i18n/pt/wizard.toml +182 -0
- controlmesh/i18n/ru/chat.toml +459 -0
- controlmesh/i18n/ru/cli.toml +286 -0
- controlmesh/i18n/ru/commands.toml +29 -0
- controlmesh/i18n/ru/wizard.toml +182 -0
- controlmesh/infra/__init__.py +22 -0
- controlmesh/infra/atomic_io.py +51 -0
- controlmesh/infra/base_observer.py +55 -0
- controlmesh/infra/base_task_observer.py +68 -0
- controlmesh/infra/boot_id.py +83 -0
- controlmesh/infra/docker.py +460 -0
- controlmesh/infra/docker_extras.py +292 -0
- controlmesh/infra/env_secrets.py +109 -0
- controlmesh/infra/file_watcher.py +73 -0
- controlmesh/infra/fs.py +57 -0
- controlmesh/infra/inflight.py +113 -0
- controlmesh/infra/install.py +99 -0
- controlmesh/infra/json_store.py +33 -0
- controlmesh/infra/pidlock.py +145 -0
- controlmesh/infra/platform.py +17 -0
- controlmesh/infra/process_tree.py +231 -0
- controlmesh/infra/recovery.py +104 -0
- controlmesh/infra/restart.py +63 -0
- controlmesh/infra/service.py +65 -0
- controlmesh/infra/service_base.py +122 -0
- controlmesh/infra/service_linux.py +258 -0
- controlmesh/infra/service_logs.py +101 -0
- controlmesh/infra/service_macos.py +238 -0
- controlmesh/infra/service_windows.py +309 -0
- controlmesh/infra/startup_state.py +89 -0
- controlmesh/infra/task_runner.py +132 -0
- controlmesh/infra/updater.py +450 -0
- controlmesh/infra/version.py +186 -0
- controlmesh/integrations/__init__.py +2 -0
- controlmesh/integrations/feishu_auth_kit.py +139 -0
- controlmesh/log_context.py +69 -0
- controlmesh/logging_config.py +137 -0
- controlmesh/memory/__init__.py +80 -0
- controlmesh/memory/commands.py +94 -0
- controlmesh/memory/dreaming.py +435 -0
- controlmesh/memory/models.py +167 -0
- controlmesh/memory/promotion.py +195 -0
- controlmesh/memory/search.py +245 -0
- controlmesh/memory/store.py +97 -0
- controlmesh/messenger/__init__.py +28 -0
- controlmesh/messenger/callback_router.py +98 -0
- controlmesh/messenger/capabilities.py +48 -0
- controlmesh/messenger/commands.py +67 -0
- controlmesh/messenger/feishu/__init__.py +6 -0
- controlmesh/messenger/feishu/auth/__init__.py +84 -0
- controlmesh/messenger/feishu/auth/app_info.py +173 -0
- controlmesh/messenger/feishu/auth/auth_cards.py +107 -0
- controlmesh/messenger/feishu/auth/brand.py +93 -0
- controlmesh/messenger/feishu/auth/card_auth.py +148 -0
- controlmesh/messenger/feishu/auth/card_auth_context.py +38 -0
- controlmesh/messenger/feishu/auth/card_auth_runner.py +266 -0
- controlmesh/messenger/feishu/auth/device_flow.py +168 -0
- controlmesh/messenger/feishu/auth/errors.py +92 -0
- controlmesh/messenger/feishu/auth/feishu_card_sender.py +62 -0
- controlmesh/messenger/feishu/auth/native_auth_all_runner.py +239 -0
- controlmesh/messenger/feishu/auth/orchestration_runner.py +458 -0
- controlmesh/messenger/feishu/auth/runtime_auth.py +148 -0
- controlmesh/messenger/feishu/auth/runtime_continuation.py +80 -0
- controlmesh/messenger/feishu/auth/token_store.py +86 -0
- controlmesh/messenger/feishu/auth/uat_client.py +164 -0
- controlmesh/messenger/feishu/bot.py +1834 -0
- controlmesh/messenger/feishu/bundled_runtime.py +84 -0
- controlmesh/messenger/feishu/card_stream.py +440 -0
- controlmesh/messenger/feishu/id_map.py +100 -0
- controlmesh/messenger/feishu/inbound.py +105 -0
- controlmesh/messenger/feishu/long_connection.py +317 -0
- controlmesh/messenger/feishu/media.py +311 -0
- controlmesh/messenger/feishu/media_meta.py +143 -0
- controlmesh/messenger/feishu/message_context.py +304 -0
- controlmesh/messenger/feishu/native_tools/__init__.py +29 -0
- controlmesh/messenger/feishu/native_tools/agent_runtime.py +23 -0
- controlmesh/messenger/feishu/native_tools/client.py +67 -0
- controlmesh/messenger/feishu/native_tools/executor.py +518 -0
- controlmesh/messenger/feishu/progress_preview.py +171 -0
- controlmesh/messenger/feishu/sender.py +111 -0
- controlmesh/messenger/feishu/settings_card.py +492 -0
- controlmesh/messenger/feishu/startup.py +53 -0
- controlmesh/messenger/feishu/tool_auth.py +147 -0
- controlmesh/messenger/feishu/transport.py +177 -0
- controlmesh/messenger/matrix/__init__.py +1 -0
- controlmesh/messenger/matrix/bot.py +1174 -0
- controlmesh/messenger/matrix/buttons.py +142 -0
- controlmesh/messenger/matrix/credentials.py +103 -0
- controlmesh/messenger/matrix/file_browser.py +113 -0
- controlmesh/messenger/matrix/formatting.py +126 -0
- controlmesh/messenger/matrix/id_map.py +69 -0
- controlmesh/messenger/matrix/media.py +189 -0
- controlmesh/messenger/matrix/sender.py +230 -0
- controlmesh/messenger/matrix/startup.py +83 -0
- controlmesh/messenger/matrix/streaming.py +109 -0
- controlmesh/messenger/matrix/transport.py +256 -0
- controlmesh/messenger/matrix/typing.py +64 -0
- controlmesh/messenger/multi.py +163 -0
- controlmesh/messenger/notifications.py +41 -0
- controlmesh/messenger/protocol.py +72 -0
- controlmesh/messenger/registry.py +127 -0
- controlmesh/messenger/send_opts.py +14 -0
- controlmesh/messenger/telegram/__init__.py +1 -0
- controlmesh/messenger/telegram/abort.py +92 -0
- controlmesh/messenger/telegram/app.py +1600 -0
- controlmesh/messenger/telegram/buttons.py +140 -0
- controlmesh/messenger/telegram/callbacks.py +120 -0
- controlmesh/messenger/telegram/chat_tracker.py +135 -0
- controlmesh/messenger/telegram/controlmesh_images/logo_text.png +0 -0
- controlmesh/messenger/telegram/controlmesh_images/welcome.png +0 -0
- controlmesh/messenger/telegram/dedup.py +78 -0
- controlmesh/messenger/telegram/edit_streaming.py +422 -0
- controlmesh/messenger/telegram/file_browser.py +123 -0
- controlmesh/messenger/telegram/formatting.py +209 -0
- controlmesh/messenger/telegram/handlers.py +205 -0
- controlmesh/messenger/telegram/media.py +265 -0
- controlmesh/messenger/telegram/message_dispatch.py +241 -0
- controlmesh/messenger/telegram/middleware.py +416 -0
- controlmesh/messenger/telegram/sender.py +294 -0
- controlmesh/messenger/telegram/startup.py +149 -0
- controlmesh/messenger/telegram/streaming.py +170 -0
- controlmesh/messenger/telegram/topic.py +101 -0
- controlmesh/messenger/telegram/transport.py +328 -0
- controlmesh/messenger/telegram/typing.py +56 -0
- controlmesh/messenger/telegram/upgrade_handler.py +212 -0
- controlmesh/messenger/telegram/welcome.py +142 -0
- controlmesh/messenger/weixin/__init__.py +25 -0
- controlmesh/messenger/weixin/api.py +191 -0
- controlmesh/messenger/weixin/auth_state.py +41 -0
- controlmesh/messenger/weixin/auth_store.py +239 -0
- controlmesh/messenger/weixin/bot.py +300 -0
- controlmesh/messenger/weixin/id_map.py +61 -0
- controlmesh/messenger/weixin/runtime.py +227 -0
- controlmesh/messenger/weixin/runtime_state.py +99 -0
- controlmesh/messenger/weixin/transport.py +31 -0
- controlmesh/multiagent/__init__.py +8 -0
- controlmesh/multiagent/bus.py +446 -0
- controlmesh/multiagent/commands.py +111 -0
- controlmesh/multiagent/health.py +55 -0
- controlmesh/multiagent/internal_api.py +540 -0
- controlmesh/multiagent/models.py +101 -0
- controlmesh/multiagent/registry.py +109 -0
- controlmesh/multiagent/shared_knowledge.py +129 -0
- controlmesh/multiagent/stack.py +74 -0
- controlmesh/multiagent/supervisor.py +676 -0
- controlmesh/orchestrator/__init__.py +6 -0
- controlmesh/orchestrator/commands.py +816 -0
- controlmesh/orchestrator/core.py +1021 -0
- controlmesh/orchestrator/directives.py +65 -0
- controlmesh/orchestrator/flows.py +896 -0
- controlmesh/orchestrator/hooks.py +115 -0
- controlmesh/orchestrator/injection.py +273 -0
- controlmesh/orchestrator/lifecycle.py +207 -0
- controlmesh/orchestrator/observers.py +230 -0
- controlmesh/orchestrator/providers.py +221 -0
- controlmesh/orchestrator/registry.py +82 -0
- controlmesh/orchestrator/selectors/__init__.py +1 -0
- controlmesh/orchestrator/selectors/agent_router.py +50 -0
- controlmesh/orchestrator/selectors/capability_registry.py +232 -0
- controlmesh/orchestrator/selectors/cron_selector.py +222 -0
- controlmesh/orchestrator/selectors/model_selector.py +431 -0
- controlmesh/orchestrator/selectors/models.py +28 -0
- controlmesh/orchestrator/selectors/session_selector.py +157 -0
- controlmesh/orchestrator/selectors/settings_selector.py +438 -0
- controlmesh/orchestrator/selectors/task_selector.py +253 -0
- controlmesh/orchestrator/selectors/utils.py +14 -0
- controlmesh/run.py +86 -0
- controlmesh/runtime/__init__.py +6 -0
- controlmesh/runtime/models.py +27 -0
- controlmesh/runtime/store.py +52 -0
- controlmesh/security/__init__.py +11 -0
- controlmesh/security/content.py +98 -0
- controlmesh/security/paths.py +52 -0
- controlmesh/session/__init__.py +8 -0
- controlmesh/session/key.py +81 -0
- controlmesh/session/manager.py +735 -0
- controlmesh/session/named.py +391 -0
- controlmesh/tasks/__init__.py +1 -0
- controlmesh/tasks/hub.py +645 -0
- controlmesh/tasks/models.py +135 -0
- controlmesh/tasks/registry.py +269 -0
- controlmesh/tasks/task_policy.py +169 -0
- controlmesh/team/__init__.py +45 -0
- controlmesh/team/api.py +279 -0
- controlmesh/team/contracts.py +399 -0
- controlmesh/team/execution.py +2054 -0
- controlmesh/team/live.py +479 -0
- controlmesh/team/models.py +1428 -0
- controlmesh/team/orchestrator.py +31 -0
- controlmesh/team/phases.py +88 -0
- controlmesh/team/presentation.py +61 -0
- controlmesh/team/runtime_attachment.py +408 -0
- controlmesh/team/runtime_control.py +502 -0
- controlmesh/team/state/__init__.py +5 -0
- controlmesh/team/state/base.py +52 -0
- controlmesh/team/state/dispatch.py +123 -0
- controlmesh/team/state/events.py +65 -0
- controlmesh/team/state/mailbox.py +92 -0
- controlmesh/team/state/manifest.py +29 -0
- controlmesh/team/state/phase.py +29 -0
- controlmesh/team/state/recovery.py +111 -0
- controlmesh/team/state/runtime.py +307 -0
- controlmesh/team/state/snapshot.py +224 -0
- controlmesh/team/state/store.py +318 -0
- controlmesh/team/state/tasks.py +180 -0
- controlmesh/text/__init__.py +0 -0
- controlmesh/text/response_format.py +168 -0
- controlmesh/text/tool_event_format.py +54 -0
- controlmesh/utils/__init__.py +0 -0
- controlmesh/utils/quiet_hours.py +42 -0
- controlmesh/webhook/__init__.py +6 -0
- controlmesh/webhook/auth.py +172 -0
- controlmesh/webhook/manager.py +100 -0
- controlmesh/webhook/models.py +143 -0
- controlmesh/webhook/observer.py +308 -0
- controlmesh/webhook/server.py +171 -0
- controlmesh/workspace/__init__.py +43 -0
- controlmesh/workspace/cron_tasks.py +234 -0
- controlmesh/workspace/init.py +577 -0
- controlmesh/workspace/loader.py +26 -0
- controlmesh/workspace/paths.py +270 -0
- controlmesh/workspace/rules_selector.py +279 -0
- controlmesh/workspace/skill_sync.py +430 -0
- controlmesh-0.17.0.dist-info/METADATA +354 -0
- controlmesh-0.17.0.dist-info/RECORD +444 -0
- controlmesh-0.17.0.dist-info/WHEEL +4 -0
- controlmesh-0.17.0.dist-info/entry_points.txt +2 -0
- controlmesh-0.17.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Dockerfile.sandbox -- lightweight sandbox for controlmesh CLI execution.
|
|
2
|
+
# Based on Node 22 (Debian bookworm) with Python 3, Claude Code CLI,
|
|
3
|
+
# and Codex CLI pre-installed. The container runs "sleep infinity" so
|
|
4
|
+
# controlmesh can `docker exec` into it on demand.
|
|
5
|
+
|
|
6
|
+
FROM node:22-bookworm-slim
|
|
7
|
+
|
|
8
|
+
ENV DEBIAN_FRONTEND=noninteractive \
|
|
9
|
+
LANG=en_US.UTF-8 \
|
|
10
|
+
LC_ALL=en_US.UTF-8 \
|
|
11
|
+
PIP_BREAK_SYSTEM_PACKAGES=1
|
|
12
|
+
|
|
13
|
+
# System packages: Python, build tools, Git, locale support
|
|
14
|
+
RUN apt-get update \
|
|
15
|
+
&& apt-get install -y --no-install-recommends \
|
|
16
|
+
python3 python3-pip python3-venv python3-dev \
|
|
17
|
+
build-essential git curl ca-certificates sudo locales \
|
|
18
|
+
&& sed -i '/en_US.UTF-8/s/^# //' /etc/locale.gen && locale-gen \
|
|
19
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
20
|
+
|
|
21
|
+
# Chrome/Chromium runtime dependencies for browser-based skills
|
|
22
|
+
# (e.g. patchright, playwright). Only shared libraries -- no browser binary.
|
|
23
|
+
RUN apt-get update \
|
|
24
|
+
&& apt-get install -y --no-install-recommends \
|
|
25
|
+
libasound2 libatk-bridge2.0-0 libatk1.0-0 libcairo2 libcups2 \
|
|
26
|
+
libdbus-1-3 libdrm2 libexpat1 libgbm1 libglib2.0-0 libnspr4 \
|
|
27
|
+
libnss3 libpango-1.0-0 libx11-6 libx11-xcb1 libxcb1 \
|
|
28
|
+
libxcomposite1 libxdamage1 libxext6 libxfixes3 libxkbcommon0 \
|
|
29
|
+
libxrandr2 libxshmfence1 \
|
|
30
|
+
fonts-liberation fonts-noto-color-emoji \
|
|
31
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
32
|
+
|
|
33
|
+
# Allow pip installs without the "externally managed" guard
|
|
34
|
+
RUN rm -f /usr/lib/python*/EXTERNALLY-MANAGED
|
|
35
|
+
|
|
36
|
+
# Install Claude Code CLI and Codex CLI
|
|
37
|
+
RUN npm install -g @anthropic-ai/claude-code @openai/codex @google/gemini-cli
|
|
38
|
+
|
|
39
|
+
# Prepare a writable data directory
|
|
40
|
+
RUN mkdir -p /data && chown node:node /data
|
|
41
|
+
|
|
42
|
+
# Let the `node` user run sudo without a password (useful for apt inside sandbox)
|
|
43
|
+
RUN echo "node ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/node
|
|
44
|
+
|
|
45
|
+
USER node
|
|
46
|
+
WORKDIR /workspace
|
|
47
|
+
|
|
48
|
+
CMD ["sleep", "infinity"]
|
controlmesh/__init__.py
ADDED
controlmesh/__main__.py
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
"""Entry point: python -m controlmesh."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import shutil
|
|
10
|
+
import signal
|
|
11
|
+
import sys
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
|
|
17
|
+
# Re-exports from cli_commands — referenced by main() dispatch and by
|
|
18
|
+
# tests that patch controlmesh.__main__.<name>.
|
|
19
|
+
from controlmesh.cli_commands.agents import cmd_agents as _cmd_agents
|
|
20
|
+
from controlmesh.cli_commands.api_cmd import cmd_api as _cmd_api
|
|
21
|
+
from controlmesh.cli_commands.auth import cmd_auth as _cmd_auth
|
|
22
|
+
from controlmesh.cli_commands.docker import cmd_docker as _cmd_docker
|
|
23
|
+
from controlmesh.cli_commands.feishu import cmd_feishu as _cmd_feishu
|
|
24
|
+
from controlmesh.cli_commands.install import cmd_install as _cmd_install
|
|
25
|
+
from controlmesh.cli_commands.lifecycle import (
|
|
26
|
+
cmd_restart as _cmd_restart,
|
|
27
|
+
)
|
|
28
|
+
from controlmesh.cli_commands.lifecycle import (
|
|
29
|
+
start_bot as _start_bot,
|
|
30
|
+
)
|
|
31
|
+
from controlmesh.cli_commands.lifecycle import (
|
|
32
|
+
stop_bot as _stop_bot,
|
|
33
|
+
)
|
|
34
|
+
from controlmesh.cli_commands.lifecycle import (
|
|
35
|
+
uninstall as _uninstall,
|
|
36
|
+
)
|
|
37
|
+
from controlmesh.cli_commands.lifecycle import (
|
|
38
|
+
upgrade as _upgrade,
|
|
39
|
+
)
|
|
40
|
+
from controlmesh.cli_commands.runtime import cmd_runtime as _cmd_runtime
|
|
41
|
+
from controlmesh.cli_commands.service import cmd_service as _cmd_service
|
|
42
|
+
from controlmesh.cli_commands.status import (
|
|
43
|
+
print_status as _print_status,
|
|
44
|
+
)
|
|
45
|
+
from controlmesh.cli_commands.status import (
|
|
46
|
+
print_usage as _print_usage,
|
|
47
|
+
)
|
|
48
|
+
from controlmesh.cli_commands.tasks import cmd_tasks as _cmd_tasks
|
|
49
|
+
from controlmesh.config import (
|
|
50
|
+
DEFAULT_EMPTY_GEMINI_API_KEY,
|
|
51
|
+
AgentConfig,
|
|
52
|
+
deep_merge_config,
|
|
53
|
+
)
|
|
54
|
+
from controlmesh.i18n import t_rich
|
|
55
|
+
from controlmesh.infra.json_store import atomic_json_save
|
|
56
|
+
from controlmesh.infra.version import get_current_version
|
|
57
|
+
from controlmesh.workspace.init import init_workspace
|
|
58
|
+
from controlmesh.workspace.paths import resolve_paths
|
|
59
|
+
|
|
60
|
+
logger = logging.getLogger(__name__)
|
|
61
|
+
|
|
62
|
+
_console = Console()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
# Config helpers
|
|
67
|
+
# ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _is_configured() -> bool:
|
|
71
|
+
"""Check if bot has a valid configuration."""
|
|
72
|
+
paths = resolve_paths()
|
|
73
|
+
if not paths.config_path.exists():
|
|
74
|
+
return False
|
|
75
|
+
try:
|
|
76
|
+
data = json.loads(paths.config_path.read_text(encoding="utf-8"))
|
|
77
|
+
except (json.JSONDecodeError, OSError):
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
transports = data.get("transports", [])
|
|
81
|
+
if not transports:
|
|
82
|
+
transports = [data.get("transport", "telegram")]
|
|
83
|
+
for t in transports:
|
|
84
|
+
checker = _IS_CONFIGURED_CHECKS.get(t, _is_configured_telegram)
|
|
85
|
+
if not checker(data):
|
|
86
|
+
return False
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _is_configured_telegram(data: dict[str, object]) -> bool:
|
|
91
|
+
token = data.get("telegram_token", "")
|
|
92
|
+
users = data.get("allowed_user_ids", [])
|
|
93
|
+
return bool(token) and not str(token).startswith("YOUR_") and bool(users)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _is_configured_matrix(data: dict[str, object]) -> bool:
|
|
97
|
+
mx = data.get("matrix", {})
|
|
98
|
+
if not isinstance(mx, dict):
|
|
99
|
+
return False
|
|
100
|
+
return bool(mx.get("homeserver")) and bool(mx.get("user_id"))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _is_configured_feishu(data: dict[str, object]) -> bool:
|
|
104
|
+
fs = data.get("feishu", {})
|
|
105
|
+
if not isinstance(fs, dict):
|
|
106
|
+
return False
|
|
107
|
+
return (
|
|
108
|
+
fs.get("mode", "bot_only") == "bot_only"
|
|
109
|
+
and fs.get("brand", "feishu") == "feishu"
|
|
110
|
+
and bool(fs.get("app_id"))
|
|
111
|
+
and bool(fs.get("app_secret"))
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _is_configured_weixin(data: dict[str, object]) -> bool:
|
|
116
|
+
wx = data.get("weixin", {})
|
|
117
|
+
if not isinstance(wx, dict):
|
|
118
|
+
return False
|
|
119
|
+
if wx.get("mode", "ilink") != "ilink":
|
|
120
|
+
return False
|
|
121
|
+
if not bool(wx.get("enabled", False)):
|
|
122
|
+
return False
|
|
123
|
+
|
|
124
|
+
raw_home = data.get("controlmesh_home") or os.environ.get(
|
|
125
|
+
"CONTROLMESH_HOME",
|
|
126
|
+
"~/.controlmesh",
|
|
127
|
+
)
|
|
128
|
+
controlmesh_home = Path(str(raw_home)).expanduser()
|
|
129
|
+
relative_path = str(wx.get("credentials_path", "weixin_store/credentials.json"))
|
|
130
|
+
from controlmesh.messenger.weixin.auth_store import WeixinCredentialStore
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
WeixinCredentialStore(controlmesh_home, relative_path=relative_path).load_credentials()
|
|
134
|
+
is not None
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
_IS_CONFIGURED_CHECKS: dict[str, Callable[[dict[str, object]], bool]] = {
|
|
139
|
+
"telegram": _is_configured_telegram,
|
|
140
|
+
"matrix": _is_configured_matrix,
|
|
141
|
+
"feishu": _is_configured_feishu,
|
|
142
|
+
"weixin": _is_configured_weixin,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def load_config() -> AgentConfig:
|
|
147
|
+
"""Load, auto-create, and smart-merge the bot config.
|
|
148
|
+
|
|
149
|
+
Resolution order:
|
|
150
|
+
1. ``~/.controlmesh/config/config.json`` (canonical location)
|
|
151
|
+
2. Copy from ``config.example.json`` in the framework root on first start
|
|
152
|
+
3. Fall back to Pydantic defaults if example file is missing
|
|
153
|
+
|
|
154
|
+
On every load the config is deep-merged with current Pydantic defaults
|
|
155
|
+
so that new fields from framework updates are added without destroying
|
|
156
|
+
user settings.
|
|
157
|
+
"""
|
|
158
|
+
paths = resolve_paths()
|
|
159
|
+
config_path = paths.config_path
|
|
160
|
+
|
|
161
|
+
first_start = not config_path.exists()
|
|
162
|
+
|
|
163
|
+
if first_start:
|
|
164
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
165
|
+
example = paths.config_example_path
|
|
166
|
+
if example.is_file():
|
|
167
|
+
shutil.copy2(example, config_path)
|
|
168
|
+
logger.info("Created config from config.example.json at %s", config_path)
|
|
169
|
+
else:
|
|
170
|
+
defaults = AgentConfig().model_dump(mode="json")
|
|
171
|
+
defaults["gemini_api_key"] = DEFAULT_EMPTY_GEMINI_API_KEY
|
|
172
|
+
defaults.pop("api", None) # Beta: only written by `controlmesh api enable`
|
|
173
|
+
atomic_json_save(config_path, defaults)
|
|
174
|
+
logger.info("Created default config at %s", config_path)
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
user_data: dict[str, object] = json.loads(config_path.read_text(encoding="utf-8"))
|
|
178
|
+
except (json.JSONDecodeError, OSError):
|
|
179
|
+
logger.exception("Failed to parse config at %s", config_path)
|
|
180
|
+
sys.exit(1)
|
|
181
|
+
|
|
182
|
+
normalized_existing = False
|
|
183
|
+
if user_data.get("gemini_api_key") is None:
|
|
184
|
+
user_data["gemini_api_key"] = DEFAULT_EMPTY_GEMINI_API_KEY
|
|
185
|
+
normalized_existing = True
|
|
186
|
+
|
|
187
|
+
defaults = AgentConfig().model_dump(mode="json")
|
|
188
|
+
defaults["gemini_api_key"] = DEFAULT_EMPTY_GEMINI_API_KEY
|
|
189
|
+
defaults.pop("api", None) # Beta: only written by `controlmesh api enable`
|
|
190
|
+
merged, changed = deep_merge_config(user_data, defaults)
|
|
191
|
+
changed = changed or normalized_existing
|
|
192
|
+
configured_home = user_data.get("controlmesh_home")
|
|
193
|
+
default_home = defaults.get("controlmesh_home")
|
|
194
|
+
env_selected_home = os.environ.get("CONTROLMESH_HOME")
|
|
195
|
+
if not configured_home or (env_selected_home and configured_home == default_home):
|
|
196
|
+
resolved_home = str(paths.controlmesh_home)
|
|
197
|
+
if merged.get("controlmesh_home") != resolved_home:
|
|
198
|
+
merged["controlmesh_home"] = resolved_home
|
|
199
|
+
changed = True
|
|
200
|
+
|
|
201
|
+
if changed:
|
|
202
|
+
atomic_json_save(config_path, merged)
|
|
203
|
+
logger.info("Extended config with new default fields")
|
|
204
|
+
|
|
205
|
+
init_workspace(paths)
|
|
206
|
+
return AgentConfig.model_validate(merged)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# ---------------------------------------------------------------------------
|
|
210
|
+
# Bot lifecycle
|
|
211
|
+
# ---------------------------------------------------------------------------
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _validate_transports(config: AgentConfig) -> None:
|
|
215
|
+
"""Run transport-specific config validators for all active transports."""
|
|
216
|
+
for t in config.transports:
|
|
217
|
+
validator = _TRANSPORT_VALIDATORS.get(t)
|
|
218
|
+
if validator:
|
|
219
|
+
validator(config)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
async def run_bot(config: AgentConfig) -> int:
|
|
223
|
+
"""Validate config and run the bot via AgentSupervisor.
|
|
224
|
+
|
|
225
|
+
The supervisor manages the main agent and dynamically created sub-agents
|
|
226
|
+
from ``agents.json``. If no sub-agents are defined, the supervisor runs
|
|
227
|
+
only the main agent — behaviour is identical to the old single-bot path.
|
|
228
|
+
|
|
229
|
+
Returns the exit code from the bot (``0`` = clean, ``42`` = restart requested).
|
|
230
|
+
"""
|
|
231
|
+
paths = resolve_paths(controlmesh_home=config.controlmesh_home)
|
|
232
|
+
_validate_transports(config)
|
|
233
|
+
|
|
234
|
+
from controlmesh.infra.pidlock import acquire_lock, release_lock
|
|
235
|
+
from controlmesh.multiagent.supervisor import AgentSupervisor
|
|
236
|
+
|
|
237
|
+
acquire_lock(pid_file=paths.controlmesh_home / "bot.pid", kill_existing=True)
|
|
238
|
+
|
|
239
|
+
supervisor = AgentSupervisor(config)
|
|
240
|
+
exit_code = 0
|
|
241
|
+
loop = asyncio.get_running_loop()
|
|
242
|
+
current_task = asyncio.current_task()
|
|
243
|
+
installed_signals: list[signal.Signals] = []
|
|
244
|
+
|
|
245
|
+
def _request_shutdown() -> None:
|
|
246
|
+
if current_task is not None and not current_task.done():
|
|
247
|
+
current_task.cancel()
|
|
248
|
+
|
|
249
|
+
if current_task is not None and sys.platform != "win32":
|
|
250
|
+
for sig in (signal.SIGTERM, signal.SIGINT):
|
|
251
|
+
try:
|
|
252
|
+
loop.add_signal_handler(sig, _request_shutdown)
|
|
253
|
+
except (NotImplementedError, RuntimeError, ValueError):
|
|
254
|
+
continue
|
|
255
|
+
installed_signals.append(sig)
|
|
256
|
+
|
|
257
|
+
try:
|
|
258
|
+
exit_code = await supervisor.start()
|
|
259
|
+
except asyncio.CancelledError:
|
|
260
|
+
logger.info("Termination signal received, shutting down gracefully...")
|
|
261
|
+
except KeyboardInterrupt:
|
|
262
|
+
logger.info("Shutting down...")
|
|
263
|
+
finally:
|
|
264
|
+
for sig in installed_signals:
|
|
265
|
+
loop.remove_signal_handler(sig)
|
|
266
|
+
await supervisor.stop_all()
|
|
267
|
+
release_lock(pid_file=paths.controlmesh_home / "bot.pid")
|
|
268
|
+
return exit_code
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
# Backward-compat alias for external scripts that call run_telegram().
|
|
272
|
+
run_telegram = run_bot
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _validate_telegram_config(config: AgentConfig) -> None:
|
|
276
|
+
"""Validate Telegram transport requirements."""
|
|
277
|
+
missing_token = not config.telegram_token or config.telegram_token.startswith("YOUR_")
|
|
278
|
+
needs_users = not config.allowed_user_ids
|
|
279
|
+
if missing_token or needs_users:
|
|
280
|
+
_console.print(t_rich("config.incomplete"))
|
|
281
|
+
sys.exit(1)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _validate_matrix_config(config: AgentConfig) -> None:
|
|
285
|
+
"""Validate Matrix transport requirements."""
|
|
286
|
+
m = config.matrix
|
|
287
|
+
hint = t_rich("config.onboarding_hint")
|
|
288
|
+
if not m.homeserver:
|
|
289
|
+
_console.print(t_rich("config.matrix_no_homeserver", hint=hint))
|
|
290
|
+
sys.exit(1)
|
|
291
|
+
if not m.user_id:
|
|
292
|
+
_console.print(t_rich("config.matrix_no_user", hint=hint))
|
|
293
|
+
sys.exit(1)
|
|
294
|
+
if not m.password and not m.access_token:
|
|
295
|
+
_console.print(t_rich("config.matrix_no_auth", hint=hint))
|
|
296
|
+
sys.exit(1)
|
|
297
|
+
if not m.allowed_rooms and not m.allowed_users:
|
|
298
|
+
_console.print(t_rich("config.matrix_no_target", hint=hint))
|
|
299
|
+
sys.exit(1)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _validate_feishu_config(config: AgentConfig) -> None:
|
|
303
|
+
"""Validate Feishu bot-only transport requirements."""
|
|
304
|
+
fs = config.feishu
|
|
305
|
+
if fs.mode != "bot_only":
|
|
306
|
+
_console.print("Feishu cut 1 supports only feishu.mode='bot_only'.")
|
|
307
|
+
sys.exit(1)
|
|
308
|
+
if fs.brand != "feishu":
|
|
309
|
+
_console.print("Feishu cut 1 supports only feishu.brand='feishu'.")
|
|
310
|
+
sys.exit(1)
|
|
311
|
+
if not fs.app_id:
|
|
312
|
+
_console.print("Feishu transport requires an existing Feishu self-built app.")
|
|
313
|
+
_console.print("Missing field: feishu.app_id.")
|
|
314
|
+
_console.print("Run `controlmesh auth feishu setup` for zero-app onboarding guidance.")
|
|
315
|
+
sys.exit(1)
|
|
316
|
+
if not fs.app_secret:
|
|
317
|
+
_console.print("Feishu transport requires an existing Feishu self-built app.")
|
|
318
|
+
_console.print("Missing field: feishu.app_secret.")
|
|
319
|
+
_console.print("Run `controlmesh auth feishu setup` for zero-app onboarding guidance.")
|
|
320
|
+
sys.exit(1)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _validate_weixin_config(config: AgentConfig) -> None:
|
|
324
|
+
"""Validate Weixin iLink transport requirements."""
|
|
325
|
+
wx = config.weixin
|
|
326
|
+
if wx.mode != "ilink":
|
|
327
|
+
_console.print("Weixin cut 1 supports only weixin.mode='ilink'.")
|
|
328
|
+
sys.exit(1)
|
|
329
|
+
if not wx.enabled:
|
|
330
|
+
_console.print("Weixin iLink transport is disabled by default; set weixin.enabled=true.")
|
|
331
|
+
sys.exit(1)
|
|
332
|
+
|
|
333
|
+
from controlmesh.messenger.weixin.auth_store import WeixinCredentialStore
|
|
334
|
+
|
|
335
|
+
store = WeixinCredentialStore(config.controlmesh_home, relative_path=wx.credentials_path)
|
|
336
|
+
if store.load_credentials() is None:
|
|
337
|
+
_console.print(f"Weixin iLink transport requires stored QR credentials at {store.path}.")
|
|
338
|
+
sys.exit(1)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
_TRANSPORT_VALIDATORS: dict[str, Callable[[AgentConfig], None]] = {
|
|
342
|
+
"telegram": _validate_telegram_config,
|
|
343
|
+
"matrix": _validate_matrix_config,
|
|
344
|
+
"feishu": _validate_feishu_config,
|
|
345
|
+
"weixin": _validate_weixin_config,
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
# ---------------------------------------------------------------------------
|
|
350
|
+
# CLI command handlers
|
|
351
|
+
# ---------------------------------------------------------------------------
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _cmd_status() -> None:
|
|
355
|
+
"""Show bot status or hint to configure."""
|
|
356
|
+
from rich.panel import Panel
|
|
357
|
+
|
|
358
|
+
_console.print()
|
|
359
|
+
if _is_configured():
|
|
360
|
+
_print_status()
|
|
361
|
+
else:
|
|
362
|
+
_console.print(
|
|
363
|
+
Panel(
|
|
364
|
+
t_rich("status.not_configured"),
|
|
365
|
+
title="[bold]Status[/bold]",
|
|
366
|
+
border_style="yellow",
|
|
367
|
+
padding=(1, 2),
|
|
368
|
+
),
|
|
369
|
+
)
|
|
370
|
+
_console.print()
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _cmd_setup(verbose: bool) -> None:
|
|
374
|
+
"""Run onboarding (with smart reset if already configured), then start."""
|
|
375
|
+
from controlmesh.cli.init_wizard import run_onboarding, run_smart_reset
|
|
376
|
+
|
|
377
|
+
_stop_bot()
|
|
378
|
+
paths = resolve_paths()
|
|
379
|
+
if _is_configured():
|
|
380
|
+
run_smart_reset(paths.controlmesh_home)
|
|
381
|
+
service_installed = run_onboarding()
|
|
382
|
+
if service_installed:
|
|
383
|
+
return
|
|
384
|
+
_start_bot(verbose)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def _default_action(verbose: bool) -> None:
|
|
388
|
+
"""Auto-onboarding if unconfigured, then start bot."""
|
|
389
|
+
if not _is_configured():
|
|
390
|
+
from controlmesh.cli.init_wizard import run_onboarding
|
|
391
|
+
|
|
392
|
+
service_installed = run_onboarding()
|
|
393
|
+
if service_installed:
|
|
394
|
+
return
|
|
395
|
+
_start_bot(verbose)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def _print_version() -> None:
|
|
399
|
+
"""Print the installed ControlMesh version."""
|
|
400
|
+
_console.print(get_current_version())
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# ---------------------------------------------------------------------------
|
|
404
|
+
# Entry point
|
|
405
|
+
# ---------------------------------------------------------------------------
|
|
406
|
+
|
|
407
|
+
_COMMANDS: dict[str, str] = {
|
|
408
|
+
"help": "help",
|
|
409
|
+
"status": "status",
|
|
410
|
+
"stop": "stop",
|
|
411
|
+
"restart": "restart",
|
|
412
|
+
"upgrade": "upgrade",
|
|
413
|
+
"uninstall": "uninstall",
|
|
414
|
+
"onboarding": "setup",
|
|
415
|
+
"reset": "setup",
|
|
416
|
+
"service": "service",
|
|
417
|
+
"docker": "docker",
|
|
418
|
+
"api": "api",
|
|
419
|
+
"agents": "agents",
|
|
420
|
+
"install": "install",
|
|
421
|
+
"auth": "auth",
|
|
422
|
+
"runtime": "runtime",
|
|
423
|
+
"tasks": "tasks",
|
|
424
|
+
"feishu": "feishu",
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
_Action = Callable[[], None]
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def main() -> None:
|
|
431
|
+
"""CLI entry point."""
|
|
432
|
+
args = sys.argv[1:]
|
|
433
|
+
commands = [a for a in args if not a.startswith("-")]
|
|
434
|
+
verbose = "--verbose" in args
|
|
435
|
+
show_version = "--version" in args or ("-v" in args and not commands and not verbose)
|
|
436
|
+
|
|
437
|
+
if "--help" in args or "-h" in args:
|
|
438
|
+
commands.append("help")
|
|
439
|
+
|
|
440
|
+
if show_version:
|
|
441
|
+
_print_version()
|
|
442
|
+
return
|
|
443
|
+
|
|
444
|
+
# Resolve first matching command
|
|
445
|
+
action = next((_COMMANDS[c] for c in commands if c in _COMMANDS), None)
|
|
446
|
+
|
|
447
|
+
dispatch: dict[str, _Action] = {
|
|
448
|
+
"help": _print_usage,
|
|
449
|
+
"status": _cmd_status,
|
|
450
|
+
"stop": _stop_bot,
|
|
451
|
+
"restart": _cmd_restart,
|
|
452
|
+
"upgrade": _upgrade,
|
|
453
|
+
"uninstall": _uninstall,
|
|
454
|
+
"setup": lambda: _cmd_setup(verbose),
|
|
455
|
+
"service": lambda: _cmd_service(args),
|
|
456
|
+
"docker": lambda: _cmd_docker(args),
|
|
457
|
+
"api": lambda: _cmd_api(args),
|
|
458
|
+
"agents": lambda: _cmd_agents(args),
|
|
459
|
+
"install": lambda: _cmd_install(args),
|
|
460
|
+
"auth": lambda: _cmd_auth(args),
|
|
461
|
+
"runtime": lambda: _cmd_runtime(args),
|
|
462
|
+
"tasks": lambda: _cmd_tasks(args),
|
|
463
|
+
"feishu": lambda: _cmd_feishu(args),
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
handler = dispatch.get(action) if action else None
|
|
467
|
+
if handler is not None:
|
|
468
|
+
handler()
|
|
469
|
+
else:
|
|
470
|
+
_default_action(verbose)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
if __name__ == "__main__":
|
|
474
|
+
main()
|
controlmesh/_banner.txt
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
██████╗ ██╗ ██╗ ██████╗████████╗ ██████╗ ██████╗ ██████╗ ███████╗██╗ ██╗
|
|
2
|
+
██╔══██╗██║ ██║██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗ ██╔══██╗██╔════╝██║ ██║
|
|
3
|
+
██║ ██║██║ ██║██║ ██║ ██║ ██║██████╔╝ ██║ ██║█████╗ ██║ ██║
|
|
4
|
+
██║ ██║██║ ██║██║ ██║ ██║ ██║██╔══██╗ ██║ ██║██╔══╝ ╚██╗ ██╔╝
|
|
5
|
+
██████╔╝╚██████╔╝╚██████╗ ██║ ╚██████╔╝██║ ██║██╗██████╔╝███████╗ ╚████╔╝
|
|
6
|
+
╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚══════╝ ╚═══╝
|
|
7
|
+
|
|
8
|
+
████████╗███████╗██╗ ███████╗ ██████╗ ██████╗ █████╗ ███╗ ███╗ ██████╗ ██████╗ ████████╗
|
|
9
|
+
╚══██╔══╝██╔════╝██║ ██╔════╝██╔════╝ ██╔══██╗██╔══██╗████╗ ████║ ██╔══██╗██╔═══██╗╚══██╔══╝
|
|
10
|
+
██║ █████╗ ██║ █████╗ ██║ ███╗██████╔╝███████║██╔████╔██║ ██████╔╝██║ ██║ ██║
|
|
11
|
+
██║ ██╔══╝ ██║ ██╔══╝ ██║ ██║██╔══██╗██╔══██║██║╚██╔╝██║ ██╔══██╗██║ ██║ ██║
|
|
12
|
+
██║ ███████╗███████╗███████╗╚██████╔╝██║ ██║██║ ██║██║ ╚═╝ ██║ ██████╔╝╚██████╔╝ ██║
|
|
13
|
+
╚═╝ ╚══════╝╚══════╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝
|
|
14
|
+
|