agsekit 1.7.3__tar.gz → 1.7.4__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.
- {agsekit-1.7.3/agsekit.egg-info → agsekit-1.7.4}/PKG-INFO +1 -1
- {agsekit-1.7.3 → agsekit-1.7.4/agsekit.egg-info}/PKG-INFO +1 -1
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit.egg-info/SOURCES.txt +3 -0
- agsekit-1.7.4/agsekit_cli/agent_version_sources.py +90 -0
- agsekit-1.7.4/agsekit_cli/agents_modules/aider.py +14 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/agents_modules/base.py +6 -0
- agsekit-1.7.4/agsekit_cli/agents_modules/claude.py +15 -0
- agsekit-1.7.4/agsekit_cli/agents_modules/cline.py +15 -0
- agsekit-1.7.4/agsekit_cli/agents_modules/codex.py +14 -0
- agsekit-1.7.4/agsekit_cli/agents_modules/codex_glibc.py +15 -0
- agsekit-1.7.4/agsekit_cli/agents_modules/codex_glibc_prebuilt.py +23 -0
- agsekit-1.7.4/agsekit_cli/agents_modules/forgecode.py +16 -0
- agsekit-1.7.4/agsekit_cli/agents_modules/opencode.py +15 -0
- agsekit-1.7.4/agsekit_cli/agents_modules/qwen.py +14 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/agents/codex-glibc-prebuilt.yml +4 -2
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/agents/codex-glibc.yml +3 -0
- agsekit-1.7.4/agsekit_cli/ansible/agents/codex_code_mode_host.yml +52 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/check_new_version.py +4 -4
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/daemon.py +1 -1
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/install_agents.py +51 -7
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/locales/en.json +43 -38
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/locales/ru.json +38 -33
- agsekit-1.7.4/agsekit_cli/state.py +356 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/pyproject.toml +1 -1
- agsekit-1.7.4/tests/test_agent_latest_versions.py +95 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_ansible_agent_proxychains.py +32 -1
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_daemon_command.py +2 -2
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_install_agents_command.py +125 -4
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_node_agent_installers.py +1 -1
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_state.py +88 -0
- agsekit-1.7.3/agsekit_cli/agents_modules/aider.py +0 -8
- agsekit-1.7.3/agsekit_cli/agents_modules/claude.py +0 -9
- agsekit-1.7.3/agsekit_cli/agents_modules/cline.py +0 -9
- agsekit-1.7.3/agsekit_cli/agents_modules/codex.py +0 -8
- agsekit-1.7.3/agsekit_cli/agents_modules/codex_glibc.py +0 -7
- agsekit-1.7.3/agsekit_cli/agents_modules/codex_glibc_prebuilt.py +0 -7
- agsekit-1.7.3/agsekit_cli/agents_modules/forgecode.py +0 -8
- agsekit-1.7.3/agsekit_cli/agents_modules/opencode.py +0 -9
- agsekit-1.7.3/agsekit_cli/agents_modules/qwen.py +0 -8
- agsekit-1.7.3/agsekit_cli/state.py +0 -197
- {agsekit-1.7.3 → agsekit-1.7.4}/LICENSE +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/MANIFEST.in +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/README.md +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit.egg-info/dependency_links.txt +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit.egg-info/entry_points.txt +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit.egg-info/requires.txt +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit.egg-info/top_level.txt +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/__init__.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/agent_scripts/claude-code.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/agent_scripts/codex-glibc.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/agent_scripts/codex.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/agent_scripts/proxychains_common.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/agent_scripts/qwen.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/agents.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/agents_modules/__init__.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/agents/aider.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/agents/claude.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/agents/cline.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/agents/codex.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/agents/codex_logrotate.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/agents/forgecode.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/agents/opencode.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/agents/proxychains.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/agents/qwen.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/bundles/docker.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/bundles/golang.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/bundles/nodejs.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/bundles/nvm.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/bundles/pyenv.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/bundles/python.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/bundles/rust.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/callback_plugins/agsekit_progress.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/callback_plugins/agsekit_rich.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/connection_plugins/agsekit_multipass.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/vm_packages.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible/vm_ssh.yml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible_runners.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/ansible_utils.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/backup.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/cli.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/cli_entry.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/codex_rust_toolchain_version.txt +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/__init__.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/addmount.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/backup_clean.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/backup_once.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/backup_repeated.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/config_example.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/config_gen.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/create_vm.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/destroy_vm.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/doctor.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/down.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/list_bundles.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/mounts.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/pip_upgrade.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/portforward.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/prepare.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/removemount.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/restart_vm.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/run.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/shell.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/ssh.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/start_vm.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/status.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/stop.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/systemd.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/up.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/commands/version.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/config.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/daemon_backends.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/debug.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/host_tools.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/i18n.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/interactive.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/mounts.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/prebuilt.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/prepare_strategies.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/progress.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/provision_handlers.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/run_agent.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/run_with_http_proxy.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/run_with_proxychains.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/systemd/agsekit-portforward.service +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/systemd_backend.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/tui_prompts.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/versioning.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm_bundle_definitions.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm_bundles.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm_installers/docker.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm_installers/golang.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm_installers/nodejs.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm_installers/nvm.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm_installers/pyenv.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm_installers/python.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm_installers/rust.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm_local_control_node.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm_prepare.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/agsekit_cli/vm_ssh_bootstrap.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/config-example.yaml +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/prebuilt-agents/codex-glibc/.dockerignore +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/prebuilt-agents/codex-glibc/Dockerfile +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/prebuilt-agents/codex-glibc/build-codex-glibc.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/prebuilt-agents/codex-glibc/build.sh +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/setup.cfg +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_addmount_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_agents.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_agents_modules.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_ansible_multipass_connection.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_ansible_utils.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_ansible_vm_bundles.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_backup_clean_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_backup_lock.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_backup_progress.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_backup_repeated.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_backup_repeated_commands.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_build_backfill_github_releases.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_build_check_pypi_version.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_build_extract_changelog.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_check_new_version_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_codex_logrotate_installers.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_config_agents.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_config_gen_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_config_main.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_config_mounts.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_config_vms.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_create_vm_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_debug.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_doctor_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_down_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_host_tools.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_i18n.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_install_script.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_interactive_mode.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_launchd_backend.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_make_single_backup.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_mount_commands.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_mounts.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_pip_upgrade_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_portforward.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_portforward_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_prebuilt.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_prepare_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_progress.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_provision_handlers.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_removemount_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_restart_vm_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_run_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_shell_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_ssh_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_start_vm_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_status_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_stop_vm_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_systemd_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_up_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_version_command.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_vm_compare.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_vm_launch.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_vm_local_control_node.py +0 -0
- {agsekit-1.7.3 → agsekit-1.7.4}/tests/test_vm_proxychains.py +0 -0
|
@@ -10,6 +10,7 @@ agsekit.egg-info/entry_points.txt
|
|
|
10
10
|
agsekit.egg-info/requires.txt
|
|
11
11
|
agsekit.egg-info/top_level.txt
|
|
12
12
|
agsekit_cli/__init__.py
|
|
13
|
+
agsekit_cli/agent_version_sources.py
|
|
13
14
|
agsekit_cli/agents.py
|
|
14
15
|
agsekit_cli/ansible_runners.py
|
|
15
16
|
agsekit_cli/ansible_utils.py
|
|
@@ -65,6 +66,7 @@ agsekit_cli/ansible/agents/cline.yml
|
|
|
65
66
|
agsekit_cli/ansible/agents/codex-glibc-prebuilt.yml
|
|
66
67
|
agsekit_cli/ansible/agents/codex-glibc.yml
|
|
67
68
|
agsekit_cli/ansible/agents/codex.yml
|
|
69
|
+
agsekit_cli/ansible/agents/codex_code_mode_host.yml
|
|
68
70
|
agsekit_cli/ansible/agents/codex_logrotate.yml
|
|
69
71
|
agsekit_cli/ansible/agents/forgecode.yml
|
|
70
72
|
agsekit_cli/ansible/agents/opencode.yml
|
|
@@ -125,6 +127,7 @@ prebuilt-agents/codex-glibc/Dockerfile
|
|
|
125
127
|
prebuilt-agents/codex-glibc/build-codex-glibc.sh
|
|
126
128
|
prebuilt-agents/codex-glibc/build.sh
|
|
127
129
|
tests/test_addmount_command.py
|
|
130
|
+
tests/test_agent_latest_versions.py
|
|
128
131
|
tests/test_agents.py
|
|
129
132
|
tests/test_agents_modules.py
|
|
130
133
|
tests/test_ansible_agent_proxychains.py
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from typing import Any, Dict, Iterable, Optional
|
|
6
|
+
from urllib.error import HTTPError, URLError
|
|
7
|
+
from urllib.parse import quote
|
|
8
|
+
from urllib.request import Request, urlopen
|
|
9
|
+
|
|
10
|
+
from packaging.version import InvalidVersion, Version
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AgentVersionCheckError(RuntimeError):
|
|
14
|
+
"""Raised when an upstream agent version cannot be resolved."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _headers() -> Dict[str, str]:
|
|
18
|
+
headers = {"Accept": "application/json", "User-Agent": "agsekit-agent-version-check"}
|
|
19
|
+
token = os.getenv("AGSEKIT_GITHUB_TOKEN") or os.getenv("GITHUB_TOKEN")
|
|
20
|
+
if token:
|
|
21
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
22
|
+
return headers
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def fetch_json(url: str, *, timeout: float = 30.0) -> Any:
|
|
26
|
+
request = Request(url, headers=_headers())
|
|
27
|
+
try:
|
|
28
|
+
with urlopen(request, timeout=timeout) as response:
|
|
29
|
+
return json.load(response)
|
|
30
|
+
except HTTPError as exc:
|
|
31
|
+
raise AgentVersionCheckError(f"Version request failed for {url}: HTTP {exc.code}") from exc
|
|
32
|
+
except URLError as exc:
|
|
33
|
+
raise AgentVersionCheckError(f"Version request failed for {url}: {exc.reason}") from exc
|
|
34
|
+
except (OSError, ValueError) as exc:
|
|
35
|
+
raise AgentVersionCheckError(f"Invalid version response from {url}: {exc}") from exc
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def latest_npm_version(package_name: str, *, timeout: float = 30.0) -> str:
|
|
39
|
+
payload = fetch_json(
|
|
40
|
+
f"https://registry.npmjs.org/{quote(package_name, safe='')}/latest",
|
|
41
|
+
timeout=timeout,
|
|
42
|
+
)
|
|
43
|
+
version = payload.get("version") if isinstance(payload, dict) else None
|
|
44
|
+
if not isinstance(version, str) or not version.strip():
|
|
45
|
+
raise AgentVersionCheckError(f"npm did not report a latest version for {package_name}")
|
|
46
|
+
return version.strip()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def latest_pypi_version(package_name: str, *, timeout: float = 30.0) -> str:
|
|
50
|
+
payload = fetch_json(f"https://pypi.org/pypi/{quote(package_name, safe='')}/json", timeout=timeout)
|
|
51
|
+
info = payload.get("info") if isinstance(payload, dict) else None
|
|
52
|
+
version = info.get("version") if isinstance(info, dict) else None
|
|
53
|
+
if not isinstance(version, str) or not version.strip():
|
|
54
|
+
raise AgentVersionCheckError(f"PyPI did not report a latest version for {package_name}")
|
|
55
|
+
return version.strip()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _stable_versions(values: Iterable[str]) -> Iterable[Version]:
|
|
59
|
+
for value in values:
|
|
60
|
+
try:
|
|
61
|
+
parsed = Version(value)
|
|
62
|
+
except InvalidVersion:
|
|
63
|
+
continue
|
|
64
|
+
if not parsed.is_prerelease and not parsed.is_devrelease:
|
|
65
|
+
yield parsed
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def latest_github_release_version(
|
|
69
|
+
repo: str,
|
|
70
|
+
*,
|
|
71
|
+
tag_prefix: str,
|
|
72
|
+
timeout: float = 30.0,
|
|
73
|
+
) -> str:
|
|
74
|
+
payload = fetch_json(f"https://api.github.com/repos/{repo}/releases?per_page=100", timeout=timeout)
|
|
75
|
+
if not isinstance(payload, list):
|
|
76
|
+
raise AgentVersionCheckError(f"GitHub did not report releases for {repo}")
|
|
77
|
+
|
|
78
|
+
candidates = []
|
|
79
|
+
for release in payload:
|
|
80
|
+
if not isinstance(release, dict) or release.get("draft") or release.get("prerelease"):
|
|
81
|
+
continue
|
|
82
|
+
tag = release.get("tag_name")
|
|
83
|
+
if not isinstance(tag, str) or not tag.startswith(tag_prefix):
|
|
84
|
+
continue
|
|
85
|
+
candidates.append(tag[len(tag_prefix):])
|
|
86
|
+
|
|
87
|
+
versions = list(_stable_versions(candidates))
|
|
88
|
+
if not versions:
|
|
89
|
+
raise AgentVersionCheckError(f"GitHub did not report a stable {tag_prefix} release for {repo}")
|
|
90
|
+
return str(max(versions))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .base import BaseAgent
|
|
2
|
+
from ..agent_version_sources import latest_pypi_version
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AiderAgent(BaseAgent):
|
|
6
|
+
type_name = "aider"
|
|
7
|
+
runtime_binary = "aider"
|
|
8
|
+
default_version = "0.86.2"
|
|
9
|
+
default_env = {"AIDER_CHECK_UPDATE": "false"}
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def check_latest_version(cls, *, architecture=None, timeout=30.0):
|
|
13
|
+
del architecture
|
|
14
|
+
return cls.normalize_version(latest_pypi_version("aider-chat", timeout=timeout))
|
|
@@ -33,6 +33,12 @@ class BaseAgent:
|
|
|
33
33
|
def needs_nvm(cls) -> bool:
|
|
34
34
|
return bool(cls._needs_nvm)
|
|
35
35
|
|
|
36
|
+
@classmethod
|
|
37
|
+
def check_latest_version(cls, *, architecture: Optional[str] = None, timeout: float = 30.0) -> str:
|
|
38
|
+
del architecture
|
|
39
|
+
del timeout
|
|
40
|
+
raise NotImplementedError(f"Latest-version lookup is not implemented for {cls.type_name}")
|
|
41
|
+
|
|
36
42
|
@classmethod
|
|
37
43
|
def playbook_name(cls) -> str:
|
|
38
44
|
if cls.installer_playbook:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .base import BaseAgent
|
|
2
|
+
from ..agent_version_sources import latest_npm_version
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ClaudeAgent(BaseAgent):
|
|
6
|
+
type_name = "claude"
|
|
7
|
+
runtime_binary = "claude"
|
|
8
|
+
default_version = "2.1.141"
|
|
9
|
+
default_env = {"DISABLE_AUTOUPDATER": "1"}
|
|
10
|
+
_needs_nvm = True
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def check_latest_version(cls, *, architecture=None, timeout=30.0):
|
|
14
|
+
del architecture
|
|
15
|
+
return cls.normalize_version(latest_npm_version("@anthropic-ai/claude-code", timeout=timeout))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .base import BaseAgent
|
|
2
|
+
from ..agent_version_sources import latest_npm_version
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ClineAgent(BaseAgent):
|
|
6
|
+
type_name = "cline"
|
|
7
|
+
runtime_binary = "cline"
|
|
8
|
+
default_version = "2.17.0"
|
|
9
|
+
default_env = {"CLINE_NO_AUTO_UPDATE": "1"}
|
|
10
|
+
_needs_nvm = True
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def check_latest_version(cls, *, architecture=None, timeout=30.0):
|
|
14
|
+
del architecture
|
|
15
|
+
return cls.normalize_version(latest_npm_version("cline", timeout=timeout))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .base import BaseAgent
|
|
2
|
+
from ..agent_version_sources import latest_npm_version
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CodexAgent(BaseAgent):
|
|
6
|
+
type_name = "codex"
|
|
7
|
+
runtime_binary = "codex"
|
|
8
|
+
default_version = "0.130.0"
|
|
9
|
+
_needs_nvm = True
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def check_latest_version(cls, *, architecture=None, timeout=30.0):
|
|
13
|
+
del architecture
|
|
14
|
+
return cls.normalize_version(latest_npm_version("@openai/codex", timeout=timeout))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .base import BaseAgent
|
|
2
|
+
from ..agent_version_sources import latest_github_release_version
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CodexGlibcAgent(BaseAgent):
|
|
6
|
+
type_name = "codex-glibc"
|
|
7
|
+
runtime_binary = "codex-glibc"
|
|
8
|
+
default_version = "0.130.0"
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def check_latest_version(cls, *, architecture=None, timeout=30.0):
|
|
12
|
+
del architecture
|
|
13
|
+
return cls.normalize_version(
|
|
14
|
+
latest_github_release_version("openai/codex", tag_prefix="rust-v", timeout=timeout)
|
|
15
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .base import BaseAgent
|
|
2
|
+
from ..agent_version_sources import AgentVersionCheckError
|
|
3
|
+
from ..prebuilt import PrebuiltReleaseError, resolve_codex_glibc_prebuilt_release
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CodexGlibcPrebuiltAgent(BaseAgent):
|
|
7
|
+
type_name = "codex-glibc-prebuilt"
|
|
8
|
+
runtime_binary = "codex-glibc-prebuilt"
|
|
9
|
+
default_version = "0.130.0"
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def check_latest_version(cls, *, architecture=None, timeout=30.0):
|
|
13
|
+
del timeout
|
|
14
|
+
if not architecture:
|
|
15
|
+
raise AgentVersionCheckError("Current architecture is required for codex-glibc-prebuilt")
|
|
16
|
+
try:
|
|
17
|
+
release = resolve_codex_glibc_prebuilt_release(arch=architecture)
|
|
18
|
+
except PrebuiltReleaseError as exc:
|
|
19
|
+
raise AgentVersionCheckError(str(exc)) from exc
|
|
20
|
+
prefix = "codex-glibc-rust-v"
|
|
21
|
+
if not release.tag.startswith(prefix):
|
|
22
|
+
raise AgentVersionCheckError(f"Unexpected codex-glibc prebuilt tag: {release.tag}")
|
|
23
|
+
return cls.normalize_version(release.tag[len(prefix):])
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .base import BaseAgent
|
|
2
|
+
from ..agent_version_sources import latest_github_release_version
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ForgecodeAgent(BaseAgent):
|
|
6
|
+
type_name = "forgecode"
|
|
7
|
+
runtime_binary = "forge"
|
|
8
|
+
default_version = "2.12.14"
|
|
9
|
+
default_env = {"FORGE_TRACKER": "false"}
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def check_latest_version(cls, *, architecture=None, timeout=30.0):
|
|
13
|
+
del architecture
|
|
14
|
+
return cls.normalize_version(
|
|
15
|
+
latest_github_release_version("tailcallhq/forgecode", tag_prefix="v", timeout=timeout)
|
|
16
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .base import BaseAgent
|
|
2
|
+
from ..agent_version_sources import latest_npm_version
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class OpencodeAgent(BaseAgent):
|
|
6
|
+
type_name = "opencode"
|
|
7
|
+
runtime_binary = "opencode"
|
|
8
|
+
default_version = "1.14.50"
|
|
9
|
+
default_env = {"OPENCODE_DISABLE_AUTOUPDATE": "true"}
|
|
10
|
+
_needs_nvm = True
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def check_latest_version(cls, *, architecture=None, timeout=30.0):
|
|
14
|
+
del architecture
|
|
15
|
+
return cls.normalize_version(latest_npm_version("opencode-ai", timeout=timeout))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .base import BaseAgent
|
|
2
|
+
from ..agent_version_sources import latest_npm_version
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class QwenAgent(BaseAgent):
|
|
6
|
+
type_name = "qwen"
|
|
7
|
+
runtime_binary = "qwen"
|
|
8
|
+
default_version = "0.15.11"
|
|
9
|
+
_needs_nvm = True
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def check_latest_version(cls, *, architecture=None, timeout=30.0):
|
|
13
|
+
del architecture
|
|
14
|
+
return cls.normalize_version(latest_npm_version("@qwen-code/qwen-code", timeout=timeout))
|
|
@@ -52,8 +52,7 @@
|
|
|
52
52
|
(ansible_playbook_python | quote)
|
|
53
53
|
~ ' -m agsekit_cli.prebuilt resolve-codex-glibc-prebuilt --arch '
|
|
54
54
|
~ (codex_prebuilt_arch | quote)
|
|
55
|
-
~ ' --tag '
|
|
56
|
-
~ (('--tag ' ~ ('codex-glibc-rust-v' ~ requested_agent_version)) if requested_agent_version else '')
|
|
55
|
+
~ ((' --tag ' ~ ('codex-glibc-rust-v' ~ requested_agent_version)) if requested_agent_version else '')
|
|
57
56
|
) | from_json
|
|
58
57
|
}}
|
|
59
58
|
|
|
@@ -149,3 +148,6 @@
|
|
|
149
148
|
ansible.builtin.command: "{{ codex_install_path }} --version"
|
|
150
149
|
register: codex_version_check
|
|
151
150
|
changed_when: false
|
|
151
|
+
|
|
152
|
+
- name: Install version-matched Codex Code Mode host
|
|
153
|
+
ansible.builtin.include_tasks: "{{ playbook_dir }}/codex_code_mode_host.yml"
|
|
@@ -263,6 +263,9 @@
|
|
|
263
263
|
register: codex_version_check
|
|
264
264
|
changed_when: false
|
|
265
265
|
|
|
266
|
+
- name: Install version-matched Codex Code Mode host
|
|
267
|
+
ansible.builtin.include_tasks: "{{ playbook_dir }}/codex_code_mode_host.yml"
|
|
268
|
+
|
|
266
269
|
- name: Cleanup rust and cargo toolchains used for build
|
|
267
270
|
ansible.builtin.shell: |
|
|
268
271
|
set -euo pipefail
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Extract installed Codex version for Code Mode host
|
|
3
|
+
ansible.builtin.set_fact:
|
|
4
|
+
codex_code_mode_host_version: >-
|
|
5
|
+
{{ codex_version_check.stdout | regex_search('[0-9]+\.[0-9]+\.[0-9]+(?:[-+][0-9A-Za-z.-]+)?') }}
|
|
6
|
+
|
|
7
|
+
- name: Fail when installed Codex version cannot be determined
|
|
8
|
+
ansible.builtin.fail:
|
|
9
|
+
msg: >-
|
|
10
|
+
Could not determine the installed Codex version from: {{ codex_version_check.stdout }}
|
|
11
|
+
when: not codex_code_mode_host_version
|
|
12
|
+
|
|
13
|
+
- name: Determine Codex Code Mode host release target
|
|
14
|
+
ansible.builtin.set_fact:
|
|
15
|
+
codex_code_mode_host_target: >-
|
|
16
|
+
{{ 'aarch64-unknown-linux-musl' if ansible_architecture in ['aarch64', 'arm64'] else 'x86_64-unknown-linux-musl' }}
|
|
17
|
+
codex_code_mode_host_path: "{{ codex_install_path | dirname }}/codex-code-mode-host"
|
|
18
|
+
|
|
19
|
+
- name: Remove previous Codex Code Mode host download
|
|
20
|
+
ansible.builtin.file:
|
|
21
|
+
path: /tmp/codex-code-mode-host.tar.gz
|
|
22
|
+
state: absent
|
|
23
|
+
|
|
24
|
+
- name: Download version-matched Codex Code Mode host
|
|
25
|
+
ansible.builtin.command: >-
|
|
26
|
+
{{ proxychains_prefix }}curl -fsSL --write-out %{http_code}
|
|
27
|
+
"https://github.com/openai/codex/releases/download/rust-v{{ codex_code_mode_host_version }}/codex-code-mode-host-{{ codex_code_mode_host_target }}.tar.gz"
|
|
28
|
+
-o /tmp/codex-code-mode-host.tar.gz
|
|
29
|
+
register: codex_code_mode_host_download
|
|
30
|
+
changed_when: codex_code_mode_host_download.rc == 0
|
|
31
|
+
failed_when:
|
|
32
|
+
- codex_code_mode_host_download.rc != 0
|
|
33
|
+
- codex_code_mode_host_download.stdout | trim != "404"
|
|
34
|
+
|
|
35
|
+
- name: Install Codex Code Mode host beside Codex binary
|
|
36
|
+
ansible.builtin.shell: |
|
|
37
|
+
set -euo pipefail
|
|
38
|
+
rm -rf /tmp/codex-code-mode-host-extract
|
|
39
|
+
mkdir -p /tmp/codex-code-mode-host-extract
|
|
40
|
+
tar -xzf /tmp/codex-code-mode-host.tar.gz -C /tmp/codex-code-mode-host-extract
|
|
41
|
+
helper="/tmp/codex-code-mode-host-extract/codex-code-mode-host-{{ codex_code_mode_host_target }}"
|
|
42
|
+
test -f "$helper"
|
|
43
|
+
{{ 'sudo ' if codex_install_mode == 'system' else '' }}install -m 0755 "$helper" "{{ codex_code_mode_host_path }}"
|
|
44
|
+
rm -rf /tmp/codex-code-mode-host-extract /tmp/codex-code-mode-host.tar.gz
|
|
45
|
+
args:
|
|
46
|
+
executable: /bin/bash
|
|
47
|
+
when: codex_code_mode_host_download.rc == 0
|
|
48
|
+
|
|
49
|
+
- name: Verify Codex Code Mode host works
|
|
50
|
+
ansible.builtin.command: "{{ codex_code_mode_host_path }} --help"
|
|
51
|
+
changed_when: false
|
|
52
|
+
when: codex_code_mode_host_download.rc == 0
|
|
@@ -5,7 +5,7 @@ from typing import Optional
|
|
|
5
5
|
|
|
6
6
|
import click
|
|
7
7
|
|
|
8
|
-
from ..config import resolve_config_path
|
|
8
|
+
from ..config import ConfigError, resolve_config_path
|
|
9
9
|
from ..i18n import tr
|
|
10
10
|
from ..state import CHANGELOG_URL, get_state_manager, initialize_state, run_check_new_version
|
|
11
11
|
from ..versioning import is_newer_version
|
|
@@ -24,7 +24,7 @@ from . import debug_option, non_interactive_option
|
|
|
24
24
|
help=tr("config.option_path"),
|
|
25
25
|
)
|
|
26
26
|
def check_new_version_command(config_path: Optional[str], non_interactive: bool, debug: bool) -> None:
|
|
27
|
-
"""
|
|
27
|
+
"""Refresh cached agsekit and configured-agent versions in state.yaml."""
|
|
28
28
|
del non_interactive
|
|
29
29
|
del debug
|
|
30
30
|
|
|
@@ -32,8 +32,8 @@ def check_new_version_command(config_path: Optional[str], non_interactive: bool,
|
|
|
32
32
|
initialize_state(resolved_path)
|
|
33
33
|
manager = get_state_manager()
|
|
34
34
|
try:
|
|
35
|
-
latest = run_check_new_version()
|
|
36
|
-
except RuntimeError as exc:
|
|
35
|
+
latest = run_check_new_version(resolved_path)
|
|
36
|
+
except (ConfigError, RuntimeError) as exc:
|
|
37
37
|
raise click.ClickException(tr("check_new_version.failed", error=str(exc)))
|
|
38
38
|
|
|
39
39
|
if is_newer_version(latest, manager.current_version):
|
|
@@ -77,7 +77,7 @@ def run_daemon_status() -> None:
|
|
|
77
77
|
|
|
78
78
|
@click.group(name="daemon", help=tr("daemon.group_help"))
|
|
79
79
|
def daemon_group() -> None:
|
|
80
|
-
"""Commands for managing the
|
|
80
|
+
"""Commands for managing the agsekit background daemon."""
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
@daemon_group.command(name="install", help=tr("daemon.install_help"))
|
|
@@ -34,6 +34,7 @@ from ..interactive import is_interactive_terminal
|
|
|
34
34
|
from ..i18n import tr
|
|
35
35
|
from ..progress import ProgressManager
|
|
36
36
|
from ..provision_handlers import PreparedVmSsh, choose_provision_handler
|
|
37
|
+
from ..state import initialize_state, refresh_agent_versions
|
|
37
38
|
from ..vm import MultipassError, ensure_multipass_available, resolve_proxychains
|
|
38
39
|
from ..vm_prepare import _ensure_vm_ssh_access, _fetch_vm_ips, ensure_host_ssh_keypair, vm_ssh_ansible_vars
|
|
39
40
|
from . import debug_option, non_interactive_option
|
|
@@ -246,6 +247,8 @@ def run_install_agents(
|
|
|
246
247
|
debug: bool,
|
|
247
248
|
interactive: bool,
|
|
248
249
|
progress: Optional[ProgressManager] = None,
|
|
250
|
+
upgrade: bool = False,
|
|
251
|
+
force_check_versions: bool = False,
|
|
249
252
|
) -> None:
|
|
250
253
|
with debug_scope(debug):
|
|
251
254
|
if not progress:
|
|
@@ -253,6 +256,8 @@ def run_install_agents(
|
|
|
253
256
|
|
|
254
257
|
if all_agents and agent_name:
|
|
255
258
|
raise click.ClickException(tr("install_agents.agent_conflict"))
|
|
259
|
+
if force_check_versions and not upgrade:
|
|
260
|
+
raise click.ClickException(tr("install_agents.force_check_requires_upgrade"))
|
|
256
261
|
|
|
257
262
|
resolved_path = resolve_config_path(Path(config_path) if config_path else None)
|
|
258
263
|
try:
|
|
@@ -307,6 +312,31 @@ def run_install_agents(
|
|
|
307
312
|
raise click.ClickException(tr("install_agents.vm_missing", vm_name=target_vm_name))
|
|
308
313
|
targets.append((agent.name, vms_config[target_vm_name]))
|
|
309
314
|
|
|
315
|
+
upgrade_versions: Dict[str, str] = {}
|
|
316
|
+
if upgrade:
|
|
317
|
+
state_manager = initialize_state(resolved_path)
|
|
318
|
+
selected_agent_types = {
|
|
319
|
+
find_agent(agents_config, target_agent_name).type
|
|
320
|
+
for target_agent_name, _target_vm in targets
|
|
321
|
+
}
|
|
322
|
+
if force_check_versions:
|
|
323
|
+
errors = refresh_agent_versions(selected_agent_types, force=True)
|
|
324
|
+
if errors:
|
|
325
|
+
details = "; ".join(
|
|
326
|
+
f"{agent_type}: {message}" for agent_type, message in sorted(errors.items())
|
|
327
|
+
)
|
|
328
|
+
raise click.ClickException(
|
|
329
|
+
tr("install_agents.force_check_failed", errors=details)
|
|
330
|
+
)
|
|
331
|
+
for target_agent_name in dict.fromkeys(name for name, _vm in targets):
|
|
332
|
+
target_agent = find_agent(agents_config, target_agent_name)
|
|
333
|
+
cached = state_manager.get_agent_version(target_agent.type)
|
|
334
|
+
if cached is None:
|
|
335
|
+
raise click.ClickException(
|
|
336
|
+
tr("install_agents.upgrade_version_missing", agent_type=target_agent.type)
|
|
337
|
+
)
|
|
338
|
+
upgrade_versions[target_agent.type] = cached.latest_version
|
|
339
|
+
|
|
310
340
|
if progress is None:
|
|
311
341
|
with ProgressManager(debug=debug) as owned_progress:
|
|
312
342
|
_run_install_targets(
|
|
@@ -317,6 +347,7 @@ def run_install_agents(
|
|
|
317
347
|
debug=debug,
|
|
318
348
|
progress=owned_progress,
|
|
319
349
|
show_overall_task=True,
|
|
350
|
+
upgrade_versions=upgrade_versions,
|
|
320
351
|
)
|
|
321
352
|
_emit_install_success(targets)
|
|
322
353
|
return
|
|
@@ -329,6 +360,7 @@ def run_install_agents(
|
|
|
329
360
|
debug=debug,
|
|
330
361
|
progress=progress,
|
|
331
362
|
show_overall_task=False,
|
|
363
|
+
upgrade_versions=upgrade_versions,
|
|
332
364
|
)
|
|
333
365
|
|
|
334
366
|
|
|
@@ -341,6 +373,7 @@ def _run_install_targets(
|
|
|
341
373
|
debug: bool,
|
|
342
374
|
progress: ProgressManager,
|
|
343
375
|
show_overall_task: bool,
|
|
376
|
+
upgrade_versions: Optional[Dict[str, str]] = None,
|
|
344
377
|
) -> None:
|
|
345
378
|
overall_task = progress.add_task(tr("progress.install_agents_title"), total=len(targets)) if show_overall_task else None
|
|
346
379
|
node_ready_vms: set[str] = set()
|
|
@@ -349,8 +382,9 @@ def _run_install_targets(
|
|
|
349
382
|
agent = find_agent(agents_config, target_agent_name)
|
|
350
383
|
agent_cls = get_agent_class(agent.type)
|
|
351
384
|
playbook_path = _playbook_for(agent)
|
|
385
|
+
requested_version = upgrade_versions.get(agent.type) if upgrade_versions else agent.version
|
|
352
386
|
installed_version = _installed_agent_version(agent, target_vm, debug=debug)
|
|
353
|
-
if
|
|
387
|
+
if requested_version is None and installed_version is not None:
|
|
354
388
|
click.echo(
|
|
355
389
|
tr(
|
|
356
390
|
"install_agents.version_unpinned",
|
|
@@ -363,14 +397,14 @@ def _run_install_targets(
|
|
|
363
397
|
if overall_task is not None:
|
|
364
398
|
progress.advance(overall_task)
|
|
365
399
|
continue
|
|
366
|
-
if
|
|
400
|
+
if requested_version is not None and installed_version == requested_version:
|
|
367
401
|
click.echo(
|
|
368
402
|
tr(
|
|
369
403
|
"install_agents.version_ok",
|
|
370
404
|
agent_name=agent.name,
|
|
371
405
|
agent_type=agent.type,
|
|
372
406
|
vm_name=target_vm.name,
|
|
373
|
-
version=
|
|
407
|
+
version=requested_version or "latest",
|
|
374
408
|
)
|
|
375
409
|
)
|
|
376
410
|
if overall_task is not None:
|
|
@@ -384,7 +418,7 @@ def _run_install_targets(
|
|
|
384
418
|
agent_name=agent.name,
|
|
385
419
|
agent_type=agent.type,
|
|
386
420
|
vm_name=target_vm.name,
|
|
387
|
-
version=
|
|
421
|
+
version=requested_version or "latest",
|
|
388
422
|
)
|
|
389
423
|
)
|
|
390
424
|
else:
|
|
@@ -395,7 +429,7 @@ def _run_install_targets(
|
|
|
395
429
|
agent_type=agent.type,
|
|
396
430
|
vm_name=target_vm.name,
|
|
397
431
|
current_version=installed_version,
|
|
398
|
-
required_version=
|
|
432
|
+
required_version=requested_version or "latest",
|
|
399
433
|
)
|
|
400
434
|
)
|
|
401
435
|
if debug:
|
|
@@ -405,7 +439,7 @@ def _run_install_targets(
|
|
|
405
439
|
agent_name=agent.name,
|
|
406
440
|
agent_type=agent.type,
|
|
407
441
|
vm_name=target_vm.name,
|
|
408
|
-
version=
|
|
442
|
+
version=requested_version or "latest",
|
|
409
443
|
script=playbook_path.name,
|
|
410
444
|
)
|
|
411
445
|
)
|
|
@@ -422,7 +456,7 @@ def _run_install_targets(
|
|
|
422
456
|
if overall_task is not None:
|
|
423
457
|
progress.update(overall_task, description=install_label)
|
|
424
458
|
extra_vars_overrides: Dict[str, object] = {
|
|
425
|
-
"agent_version":
|
|
459
|
+
"agent_version": requested_version,
|
|
426
460
|
"force_reinstall": force_reinstall,
|
|
427
461
|
}
|
|
428
462
|
if agent_cls.needs_nvm() and target_vm.name in node_ready_vms:
|
|
@@ -467,6 +501,12 @@ def _run_install_targets(
|
|
|
467
501
|
@click.argument("vm", required=False)
|
|
468
502
|
@click.option("--all-vms", is_flag=True, help=tr("install_agents.option_all_vms"))
|
|
469
503
|
@click.option("--all-agents", is_flag=True, help=tr("install_agents.option_all_agents"))
|
|
504
|
+
@click.option("--upgrade", is_flag=True, help=tr("install_agents.option_upgrade"))
|
|
505
|
+
@click.option(
|
|
506
|
+
"--force-check-versions",
|
|
507
|
+
is_flag=True,
|
|
508
|
+
help=tr("install_agents.option_force_check_versions"),
|
|
509
|
+
)
|
|
470
510
|
@click.option(
|
|
471
511
|
"config_path",
|
|
472
512
|
"--config",
|
|
@@ -487,6 +527,8 @@ def install_agents_command(
|
|
|
487
527
|
vm: Optional[str],
|
|
488
528
|
all_vms: bool,
|
|
489
529
|
all_agents: bool,
|
|
530
|
+
upgrade: bool,
|
|
531
|
+
force_check_versions: bool,
|
|
490
532
|
config_path: Optional[str],
|
|
491
533
|
proxychains: Optional[str],
|
|
492
534
|
debug: bool,
|
|
@@ -503,4 +545,6 @@ def install_agents_command(
|
|
|
503
545
|
proxychains=proxychains,
|
|
504
546
|
debug=debug,
|
|
505
547
|
interactive=interactive,
|
|
548
|
+
upgrade=upgrade,
|
|
549
|
+
force_check_versions=force_check_versions,
|
|
506
550
|
)
|