remo-cli 2.0.0rc1__tar.gz → 2.0.0rc2__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 (207) hide show
  1. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/PKG-INFO +1 -1
  2. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/pyproject.toml +1 -1
  3. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/cli/shell.py +40 -13
  4. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/core/ssh.py +33 -11
  5. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/cli/test_shell.py +58 -5
  6. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/core/test_ssh.py +44 -18
  7. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/uv.lock +1 -1
  8. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.claude/commands/speckit.analyze.md +0 -0
  9. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.claude/commands/speckit.checklist.md +0 -0
  10. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.claude/commands/speckit.clarify.md +0 -0
  11. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.claude/commands/speckit.constitution.md +0 -0
  12. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.claude/commands/speckit.implement.md +0 -0
  13. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.claude/commands/speckit.plan.md +0 -0
  14. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.claude/commands/speckit.specify.md +0 -0
  15. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.claude/commands/speckit.tasks.md +0 -0
  16. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.claude/commands/speckit.taskstoissues.md +0 -0
  17. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.devcontainer/devcontainer.json +0 -0
  18. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.dockerignore +0 -0
  19. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.github/dependabot.yml +0 -0
  20. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.github/workflows/ci.yml +0 -0
  21. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.github/workflows/provision.yml +0 -0
  22. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.github/workflows/release.yml +0 -0
  23. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.github/workflows/smoke-test-cleanup.yml +0 -0
  24. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.github/workflows/smoke-test.yml +0 -0
  25. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.github/workflows/teardown.yml +0 -0
  26. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.gitignore +0 -0
  27. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.maverick/checkpoints/feature/implementation_complete.json +0 -0
  28. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.maverick/checkpoints/feature/phase_${{ index }}_complete.json +0 -0
  29. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.maverick/checkpoints/feature/validation_complete.json +0 -0
  30. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.specify/memory/constitution.md +0 -0
  31. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.specify/scripts/bash/check-prerequisites.sh +0 -0
  32. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.specify/scripts/bash/common.sh +0 -0
  33. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.specify/scripts/bash/create-new-feature.sh +0 -0
  34. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.specify/scripts/bash/setup-plan.sh +0 -0
  35. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.specify/scripts/bash/update-agent-context.sh +0 -0
  36. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.specify/templates/agent-file-template.md +0 -0
  37. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.specify/templates/checklist-template.md +0 -0
  38. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.specify/templates/plan-template.md +0 -0
  39. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.specify/templates/spec-template.md +0 -0
  40. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/.specify/templates/tasks-template.md +0 -0
  41. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/CLAUDE.md +0 -0
  42. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/CONTRIBUTING.md +0 -0
  43. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/Dockerfile +0 -0
  44. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/LICENSE +0 -0
  45. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/README.md +0 -0
  46. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/.ansible-lint +0 -0
  47. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/README.md +0 -0
  48. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/ansible.cfg +0 -0
  49. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/aws_configure.yml +0 -0
  50. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/aws_resize.yml +0 -0
  51. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/aws_site.yml +0 -0
  52. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/aws_teardown.yml +0 -0
  53. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/group_vars/all.yml +0 -0
  54. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/group_vars/incus_containers.yml +0 -0
  55. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/group_vars/proxmox_containers.yml +0 -0
  56. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/hetzner_configure.yml +0 -0
  57. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/hetzner_provision.yml +0 -0
  58. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/hetzner_resize.yml +0 -0
  59. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/hetzner_site.yml +0 -0
  60. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/hetzner_teardown.yml +0 -0
  61. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/incus_bootstrap.yml +0 -0
  62. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/incus_configure.yml +0 -0
  63. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/incus_provision.yml +0 -0
  64. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/incus_resize.yml +0 -0
  65. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/incus_site.yml +0 -0
  66. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/incus_teardown.yml +0 -0
  67. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/inventory/hosts.yml +0 -0
  68. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/inventory/incus_containers.yml +0 -0
  69. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/proxmox_bootstrap.yml +0 -0
  70. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/proxmox_configure.yml +0 -0
  71. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/proxmox_provision.yml +0 -0
  72. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/proxmox_resize.yml +0 -0
  73. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/proxmox_site.yml +0 -0
  74. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/proxmox_teardown.yml +0 -0
  75. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/requirements.yml +0 -0
  76. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/aws_server/defaults/main.yml +0 -0
  77. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/aws_server/tasks/ebs.yml +0 -0
  78. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/aws_server/tasks/ec2.yml +0 -0
  79. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/aws_server/tasks/main.yml +0 -0
  80. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/aws_server/tasks/patch_manager.yml +0 -0
  81. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/aws_server/tasks/preflight.yml +0 -0
  82. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/aws_server/tasks/security_group.yml +0 -0
  83. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/devcontainers/tasks/main.yml +0 -0
  84. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/docker/handlers/main.yml +0 -0
  85. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/docker/tasks/main.yml +0 -0
  86. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/fzf/tasks/main.yml +0 -0
  87. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/github_cli/tasks/main.yml +0 -0
  88. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/hetzner_server/defaults/main.yml +0 -0
  89. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/hetzner_server/tasks/main.yml +0 -0
  90. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/incus_bootstrap/defaults/main.yml +0 -0
  91. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/incus_bootstrap/handlers/main.yml +0 -0
  92. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/incus_bootstrap/tasks/main.yml +0 -0
  93. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/incus_container/defaults/main.yml +0 -0
  94. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/incus_container/handlers/main.yml +0 -0
  95. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/incus_container/meta/main.yml +0 -0
  96. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/incus_container/tasks/main.yml +0 -0
  97. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/incus_container/tasks/preflight.yml +0 -0
  98. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/incus_container_teardown/defaults/main.yml +0 -0
  99. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/incus_container_teardown/meta/main.yml +0 -0
  100. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/incus_container_teardown/tasks/main.yml +0 -0
  101. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/nodejs/defaults/main.yml +0 -0
  102. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/nodejs/tasks/main.yml +0 -0
  103. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/proxmox_bootstrap/defaults/main.yml +0 -0
  104. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/proxmox_bootstrap/meta/main.yml +0 -0
  105. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/proxmox_bootstrap/tasks/main.yml +0 -0
  106. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/proxmox_container/defaults/main.yml +0 -0
  107. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/proxmox_container/handlers/main.yml +0 -0
  108. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/proxmox_container/meta/main.yml +0 -0
  109. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/proxmox_container/tasks/main.yml +0 -0
  110. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/proxmox_container/tasks/preflight.yml +0 -0
  111. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/proxmox_container_teardown/defaults/main.yml +0 -0
  112. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/proxmox_container_teardown/meta/main.yml +0 -0
  113. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/proxmox_container_teardown/tasks/main.yml +0 -0
  114. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/user_setup/defaults/main.yml +0 -0
  115. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/user_setup/tasks/main.yml +0 -0
  116. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/user_setup/templates/devshell.sh.j2 +0 -0
  117. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/user_setup/templates/project-menu.sh.j2 +0 -0
  118. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/zellij/defaults/main.yml +0 -0
  119. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/zellij/tasks/main.yml +0 -0
  120. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/zellij/templates/config.kdl.j2 +0 -0
  121. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/roles/zellij/templates/layouts/minimal.kdl.j2 +0 -0
  122. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/ansible/tasks/configure_dev_tools.yml +0 -0
  123. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/docs/aws.md +0 -0
  124. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/docs/hetzner.md +0 -0
  125. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/docs/incus.md +0 -0
  126. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/docs/install.sh +0 -0
  127. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/docs/proxmox.md +0 -0
  128. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/install.sh +0 -0
  129. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/maverick.yaml +0 -0
  130. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/project-menu.sh +0 -0
  131. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/remo +0 -0
  132. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/run.sh +0 -0
  133. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/scripts/setup-aws-oidc.sh +0 -0
  134. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/001-bootstrap-incus-host/checklists/requirements.md +0 -0
  135. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/001-bootstrap-incus-host/checklists/validation.md +0 -0
  136. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/001-bootstrap-incus-host/contracts/playbook-interface.yaml +0 -0
  137. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/001-bootstrap-incus-host/contracts/role-interface.yaml +0 -0
  138. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/001-bootstrap-incus-host/data-model.md +0 -0
  139. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/001-bootstrap-incus-host/plan.md +0 -0
  140. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/001-bootstrap-incus-host/quickstart.md +0 -0
  141. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/001-bootstrap-incus-host/research.md +0 -0
  142. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/001-bootstrap-incus-host/spec.md +0 -0
  143. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/001-bootstrap-incus-host/tasks.md +0 -0
  144. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/002-incus-container-support/checklists/requirements.md +0 -0
  145. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/002-incus-container-support/contracts/playbook-interface.yaml +0 -0
  146. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/002-incus-container-support/contracts/role-interface.yaml +0 -0
  147. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/002-incus-container-support/data-model.md +0 -0
  148. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/002-incus-container-support/plan.md +0 -0
  149. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/002-incus-container-support/quickstart.md +0 -0
  150. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/002-incus-container-support/research.md +0 -0
  151. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/002-incus-container-support/spec.md +0 -0
  152. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/002-incus-container-support/tasks.md +0 -0
  153. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/003-python-cli-rewrite/checklists/requirements.md +0 -0
  154. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/003-python-cli-rewrite/data-model.md +0 -0
  155. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/003-python-cli-rewrite/plan.md +0 -0
  156. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/003-python-cli-rewrite/quickstart.md +0 -0
  157. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/003-python-cli-rewrite/research.md +0 -0
  158. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/003-python-cli-rewrite/spec.md +0 -0
  159. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/003-python-cli-rewrite/tasks.md +0 -0
  160. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/specs/004-proxmox-container-support/spec.md +0 -0
  161. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/__init__.py +0 -0
  162. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/__main__.py +0 -0
  163. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/cli/__init__.py +0 -0
  164. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/cli/cp.py +0 -0
  165. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/cli/main.py +0 -0
  166. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/cli/providers/__init__.py +0 -0
  167. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/cli/providers/aws.py +0 -0
  168. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/cli/providers/hetzner.py +0 -0
  169. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/cli/providers/incus.py +0 -0
  170. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/cli/providers/proxmox.py +0 -0
  171. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/core/__init__.py +0 -0
  172. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/core/ansible_runner.py +0 -0
  173. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/core/completion.py +0 -0
  174. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/core/config.py +0 -0
  175. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/core/known_hosts.py +0 -0
  176. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/core/output.py +0 -0
  177. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/core/picker.py +0 -0
  178. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/core/rsync.py +0 -0
  179. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/core/validation.py +0 -0
  180. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/core/version.py +0 -0
  181. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/models/__init__.py +0 -0
  182. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/models/host.py +0 -0
  183. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/providers/__init__.py +0 -0
  184. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/providers/aws.py +0 -0
  185. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/providers/hetzner.py +0 -0
  186. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/providers/incus.py +0 -0
  187. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/src/remo_cli/providers/proxmox.py +0 -0
  188. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/__init__.py +0 -0
  189. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/conftest.py +0 -0
  190. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/integration/__init__.py +0 -0
  191. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/integration/orbstack-cloud-init.yml +0 -0
  192. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/integration/orbstack.sh +0 -0
  193. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/__init__.py +0 -0
  194. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/cli/__init__.py +0 -0
  195. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/cli/test_cp.py +0 -0
  196. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/cli/test_main.py +0 -0
  197. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/core/__init__.py +0 -0
  198. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/core/test_ansible_runner.py +0 -0
  199. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/core/test_config.py +0 -0
  200. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/core/test_known_hosts.py +0 -0
  201. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/core/test_output.py +0 -0
  202. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/core/test_picker.py +0 -0
  203. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/core/test_rsync.py +0 -0
  204. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/core/test_validation.py +0 -0
  205. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/core/test_version.py +0 -0
  206. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/providers/__init__.py +0 -0
  207. {remo_cli-2.0.0rc1 → remo_cli-2.0.0rc2}/tests/unit/test_host_model.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remo-cli
3
- Version: 2.0.0rc1
3
+ Version: 2.0.0rc2
4
4
  Summary: Remote development environment CLI
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "remo-cli"
7
- version = "2.0.0rc1"
7
+ version = "2.0.0rc2"
8
8
  description = "Remote development environment CLI"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -33,7 +33,7 @@ def shell(
33
33
  ) -> None:
34
34
  """Connect to a remo environment (auto-detects or picker)."""
35
35
  from remo_cli.core.ssh import check_remote_version, resolve_remo_host, shell_connect # noqa: PLC0415
36
- from remo_cli.core.output import confirm, print_warning # noqa: PLC0415
36
+ from remo_cli.core.output import confirm, print_error, print_warning # noqa: PLC0415
37
37
  from remo_cli.core.version import get_current_version, version_is_newer # noqa: PLC0415
38
38
  from remo_cli.providers.aws import auto_start_aws_if_stopped # noqa: PLC0415
39
39
 
@@ -46,10 +46,20 @@ def shell(
46
46
  if not no_update_check:
47
47
  local_version = get_current_version()
48
48
  if local_version != "unknown":
49
- remote_version = check_remote_version(host)
49
+ remote_version, remote_err = check_remote_version(host)
50
50
 
51
51
  should_update = False
52
- if remote_version is None:
52
+ if remote_err is not None:
53
+ # SSH itself failed — we can't tell whether the marker exists
54
+ # or what version is on the box. Don't prompt to update from
55
+ # an unknown baseline; surface the error so the user can fix
56
+ # it (DNS, host key, auth, ...) and re-run.
57
+ print_warning(
58
+ f"Could not check tools version on '{host.name}':\n"
59
+ f" {remote_err}\n"
60
+ f" Skipping update check; proceeding with connection."
61
+ )
62
+ elif remote_version is None:
53
63
  # No marker file on remote
54
64
  should_update = confirm(
55
65
  f"Instance '{host.name}' has no version info. Update tools?",
@@ -71,29 +81,45 @@ def shell(
71
81
  )
72
82
 
73
83
  if should_update:
74
- _run_provider_update(host)
84
+ rc = _run_provider_update(host)
85
+ if rc != 0:
86
+ # The playbook log has already been dumped, but the SSH
87
+ # connection (and any remote project picker) would scroll
88
+ # it offscreen immediately. Pause so the user can read it.
89
+ print_error(
90
+ f"Tools update for '{host.name}' failed "
91
+ f"(ansible-playbook exit code {rc})."
92
+ )
93
+ if not confirm(
94
+ "Connect anyway?",
95
+ default=False,
96
+ ):
97
+ raise SystemExit(rc)
75
98
 
76
99
  shell_connect(host, list(tunnels), no_open)
77
100
 
78
101
 
79
- def _run_provider_update(host) -> None: # noqa: ANN001
80
- """Run the appropriate provider update for the given host."""
102
+ def _run_provider_update(host) -> int: # noqa: ANN001
103
+ """Run the appropriate provider update for the given host.
104
+
105
+ Returns the provider update's exit code (0 on success).
106
+ """
81
107
  from remo_cli.core.output import print_info # noqa: PLC0415
82
108
 
83
109
  print_info(f"Updating instance '{host.name}'...")
84
110
 
85
111
  if host.type == "aws":
86
112
  from remo_cli.providers.aws import update as aws_update # noqa: PLC0415
87
- aws_update(name=host.name)
88
- elif host.type == "hetzner":
113
+ return aws_update(name=host.name)
114
+ if host.type == "hetzner":
89
115
  from remo_cli.providers.hetzner import update as hetzner_update # noqa: PLC0415
90
- hetzner_update(name=host.name)
91
- elif host.type == "incus":
116
+ return hetzner_update(name=host.name)
117
+ if host.type == "incus":
92
118
  from remo_cli.providers.incus import update as incus_update # noqa: PLC0415
93
119
  # Incus name in known_hosts is "host/container" — extract just the container name
94
120
  container_name = host.name.split("/", maxsplit=1)[-1] if "/" in host.name else host.name
95
- incus_update(name=container_name)
96
- elif host.type == "proxmox":
121
+ return incus_update(name=container_name)
122
+ if host.type == "proxmox":
97
123
  from remo_cli.providers.proxmox import update as proxmox_update # noqa: PLC0415
98
124
  # Proxmox name in known_hosts is "node/container".
99
125
  # The proxmox SSH user is stored in the region slot (see providers.proxmox.create).
@@ -101,8 +127,9 @@ def _run_provider_update(host) -> None: # noqa: ANN001
101
127
  if not container_name:
102
128
  container_name = host.name
103
129
  proxmox_host = ""
104
- proxmox_update(
130
+ return proxmox_update(
105
131
  name=container_name,
106
132
  host=proxmox_host,
107
133
  user=host.region or "",
108
134
  )
135
+ return 0
@@ -257,24 +257,46 @@ def detect_timezone() -> str:
257
257
  return ""
258
258
 
259
259
 
260
- def check_remote_version(host: KnownHost) -> str | None:
260
+ def check_remote_version(host: KnownHost) -> tuple[str | None, str | None]:
261
261
  """Read the remo version marker from the remote instance.
262
262
 
263
- Runs ``cat ~/.remo-version`` over SSH and returns the version string,
264
- or ``None`` if the file doesn't exist or SSH fails.
263
+ Runs ``cat ~/.remo-version`` over SSH. Returns a ``(version, error)``
264
+ tuple where exactly one element is non-None:
265
+
266
+ * ``(version, None)`` — marker present and readable.
267
+ * ``(None, None)`` — SSH succeeded but the marker is missing/empty.
268
+ * ``(None, error)`` — SSH itself failed (DNS, host key, network,
269
+ timeout, ...); *error* is the underlying message so the caller can
270
+ surface it to the user.
265
271
  """
266
272
  ssh_opts, ssh_target = build_ssh_opts(host)
267
- cmd = ["ssh"] + ssh_opts + ["-o", "ConnectTimeout=10", ssh_target, "cat ~/.remo-version 2>/dev/null"]
273
+ cmd = ["ssh"] + ssh_opts + [
274
+ "-o", "ConnectTimeout=10",
275
+ "-o", "BatchMode=yes",
276
+ ssh_target,
277
+ "cat ~/.remo-version 2>/dev/null",
278
+ ]
268
279
 
269
280
  try:
270
281
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
271
- version = result.stdout.strip()
272
- if result.returncode == 0 and version:
273
- return version
274
- except (subprocess.TimeoutExpired, OSError):
275
- pass
276
-
277
- return None
282
+ except subprocess.TimeoutExpired:
283
+ return None, "SSH timed out after 15s"
284
+ except OSError as e:
285
+ return None, f"SSH failed: {e}"
286
+
287
+ # ssh exits with 255 for its own failures (auth, host key, DNS,
288
+ # connection refused, ...). Any other non-zero exit code comes from
289
+ # the remote command — here, `cat` failing because the marker is
290
+ # absent — which we treat as "no marker" rather than an SSH error.
291
+ if result.returncode == 255:
292
+ stderr = result.stderr.strip()
293
+ return None, stderr or "SSH connection failed (exit code 255)"
294
+
295
+ version = result.stdout.strip()
296
+ if result.returncode == 0 and version:
297
+ return version, None
298
+
299
+ return None, None
278
300
 
279
301
 
280
302
  def shell_connect(host: KnownHost, tunnels: list[str], no_open: bool) -> None:
@@ -52,7 +52,7 @@ class TestShellVersionCheck:
52
52
  def test_equal_versions_proceeds_silently(self, runner, mocker):
53
53
  """When remote and local versions match, no prompt is shown."""
54
54
  mocker.patch("remo_cli.core.version.get_current_version", return_value="0.8.0")
55
- mocker.patch("remo_cli.core.ssh.check_remote_version", return_value="0.8.0")
55
+ mocker.patch("remo_cli.core.ssh.check_remote_version", return_value=("0.8.0", None))
56
56
  mock_confirm = mocker.patch("remo_cli.core.output.confirm")
57
57
 
58
58
  result = runner.invoke(shell, [])
@@ -64,7 +64,7 @@ class TestShellVersionCheck:
64
64
  def test_remote_behind_prompts_update(self, runner, mocker):
65
65
  """When remote is behind local, user is prompted to update."""
66
66
  mocker.patch("remo_cli.core.version.get_current_version", return_value="0.9.0")
67
- mocker.patch("remo_cli.core.ssh.check_remote_version", return_value="0.8.0")
67
+ mocker.patch("remo_cli.core.ssh.check_remote_version", return_value=("0.8.0", None))
68
68
  mock_confirm = mocker.patch("remo_cli.core.output.confirm", return_value=False)
69
69
 
70
70
  result = runner.invoke(shell, [])
@@ -78,7 +78,7 @@ class TestShellVersionCheck:
78
78
  def test_remote_behind_update_accepted(self, runner, mocker):
79
79
  """When user accepts update, provider update is called."""
80
80
  mocker.patch("remo_cli.core.version.get_current_version", return_value="0.9.0")
81
- mocker.patch("remo_cli.core.ssh.check_remote_version", return_value="0.8.0")
81
+ mocker.patch("remo_cli.core.ssh.check_remote_version", return_value=("0.8.0", None))
82
82
  mocker.patch("remo_cli.core.output.confirm", return_value=True)
83
83
  mock_update = mocker.patch("remo_cli.providers.hetzner.update", return_value=0)
84
84
 
@@ -87,11 +87,45 @@ class TestShellVersionCheck:
87
87
  assert result.exit_code == 0
88
88
  mock_update.assert_called_once_with(name="webserver")
89
89
 
90
+ @pytest.mark.usefixtures("_patch_shell_deps")
91
+ def test_update_failure_prompts_before_connect(self, runner, mocker):
92
+ """When tools update fails, user is prompted to confirm connect."""
93
+ mocker.patch("remo_cli.core.version.get_current_version", return_value="0.9.0")
94
+ mocker.patch("remo_cli.core.ssh.check_remote_version", return_value=("0.8.0", None))
95
+ # First confirm() = "Update?" → True; second = "Connect anyway?" → True
96
+ mock_confirm = mocker.patch(
97
+ "remo_cli.core.output.confirm", side_effect=[True, True]
98
+ )
99
+ mocker.patch("remo_cli.providers.hetzner.update", return_value=2)
100
+ mock_shell_connect = mocker.patch("remo_cli.core.ssh.shell_connect")
101
+
102
+ result = runner.invoke(shell, [])
103
+
104
+ assert result.exit_code == 0
105
+ assert mock_confirm.call_count == 2
106
+ assert "Connect anyway?" in mock_confirm.call_args_list[1][0][0]
107
+ mock_shell_connect.assert_called_once()
108
+
109
+ @pytest.mark.usefixtures("_patch_shell_deps")
110
+ def test_update_failure_decline_aborts(self, runner, mocker):
111
+ """When user declines after failed update, shell_connect is not called."""
112
+ mocker.patch("remo_cli.core.version.get_current_version", return_value="0.9.0")
113
+ mocker.patch("remo_cli.core.ssh.check_remote_version", return_value=("0.8.0", None))
114
+ # First confirm() = "Update?" → True; second = "Connect anyway?" → False
115
+ mocker.patch("remo_cli.core.output.confirm", side_effect=[True, False])
116
+ mocker.patch("remo_cli.providers.hetzner.update", return_value=2)
117
+ mock_shell_connect = mocker.patch("remo_cli.core.ssh.shell_connect")
118
+
119
+ result = runner.invoke(shell, [])
120
+
121
+ assert result.exit_code == 2
122
+ mock_shell_connect.assert_not_called()
123
+
90
124
  @pytest.mark.usefixtures("_patch_shell_deps")
91
125
  def test_remote_ahead_shows_warning(self, runner, mocker):
92
126
  """When remote is ahead of local, a warning is shown."""
93
127
  mocker.patch("remo_cli.core.version.get_current_version", return_value="0.8.0")
94
- mocker.patch("remo_cli.core.ssh.check_remote_version", return_value="0.9.0")
128
+ mocker.patch("remo_cli.core.ssh.check_remote_version", return_value=("0.9.0", None))
95
129
  mock_confirm = mocker.patch("remo_cli.core.output.confirm")
96
130
 
97
131
  result = runner.invoke(shell, [])
@@ -105,7 +139,7 @@ class TestShellVersionCheck:
105
139
  def test_no_marker_prompts_update(self, runner, mocker):
106
140
  """When remote has no version marker, user is prompted to update."""
107
141
  mocker.patch("remo_cli.core.version.get_current_version", return_value="0.8.0")
108
- mocker.patch("remo_cli.core.ssh.check_remote_version", return_value=None)
142
+ mocker.patch("remo_cli.core.ssh.check_remote_version", return_value=(None, None))
109
143
  mock_confirm = mocker.patch("remo_cli.core.output.confirm", return_value=False)
110
144
 
111
145
  result = runner.invoke(shell, [])
@@ -114,6 +148,25 @@ class TestShellVersionCheck:
114
148
  mock_confirm.assert_called_once()
115
149
  assert "no version info" in mock_confirm.call_args[0][0]
116
150
 
151
+ @pytest.mark.usefixtures("_patch_shell_deps")
152
+ def test_ssh_error_skips_update_prompt(self, runner, mocker):
153
+ """When SSH itself fails, the user is warned and not prompted to update."""
154
+ mocker.patch("remo_cli.core.version.get_current_version", return_value="0.8.0")
155
+ mocker.patch(
156
+ "remo_cli.core.ssh.check_remote_version",
157
+ return_value=(None, "Host key verification failed."),
158
+ )
159
+ mock_confirm = mocker.patch("remo_cli.core.output.confirm")
160
+ mock_shell_connect = mocker.patch("remo_cli.core.ssh.shell_connect")
161
+
162
+ result = runner.invoke(shell, [])
163
+
164
+ assert result.exit_code == 0
165
+ mock_confirm.assert_not_called()
166
+ assert "Could not check tools version" in result.output
167
+ assert "Host key verification failed." in result.output
168
+ mock_shell_connect.assert_called_once()
169
+
117
170
  @pytest.mark.usefixtures("_patch_shell_deps")
118
171
  def test_unknown_local_version_skips_check(self, runner, mocker):
119
172
  """When local version is unknown, skip the version check."""
@@ -395,51 +395,71 @@ class TestCheckRemoteVersion:
395
395
 
396
396
  @pytest.mark.usefixtures("_suppress_tz")
397
397
  def test_returns_version_on_success(self, hetzner_host, mocker):
398
- """Returns the version string when SSH succeeds."""
398
+ """Returns (version, None) when SSH succeeds and marker is present."""
399
399
  mocker.patch("remo_cli.core.ssh.get_aws_region", return_value="us-west-2")
400
400
  mock_run = mocker.patch(
401
401
  "subprocess.run",
402
- return_value=MagicMock(returncode=0, stdout="0.8.0rc1\n"),
402
+ return_value=MagicMock(returncode=0, stdout="0.8.0rc1\n", stderr=""),
403
403
  )
404
404
 
405
405
  from remo_cli.core.ssh import check_remote_version
406
406
 
407
- result = check_remote_version(hetzner_host)
408
- assert result == "0.8.0rc1"
407
+ assert check_remote_version(hetzner_host) == ("0.8.0rc1", None)
409
408
 
410
409
  # Verify SSH command includes cat ~/.remo-version
411
410
  call_args = mock_run.call_args[0][0]
412
411
  assert "cat ~/.remo-version 2>/dev/null" in call_args
412
+ # And uses BatchMode so it can't hang on host-key/auth prompts
413
+ assert "BatchMode=yes" in call_args
413
414
 
414
415
  @pytest.mark.usefixtures("_suppress_tz")
415
- def test_returns_none_on_missing_file(self, hetzner_host, mocker):
416
- """Returns None when the remote file doesn't exist (rc != 0)."""
416
+ def test_returns_no_marker_on_missing_file(self, hetzner_host, mocker):
417
+ """Returns (None, None) when the remote command failed (cat marker missing)."""
417
418
  mocker.patch("remo_cli.core.ssh.get_aws_region", return_value="us-west-2")
418
419
  mocker.patch(
419
420
  "subprocess.run",
420
- return_value=MagicMock(returncode=1, stdout=""),
421
+ return_value=MagicMock(returncode=1, stdout="", stderr=""),
421
422
  )
422
423
 
423
424
  from remo_cli.core.ssh import check_remote_version
424
425
 
425
- assert check_remote_version(hetzner_host) is None
426
+ assert check_remote_version(hetzner_host) == (None, None)
426
427
 
427
428
  @pytest.mark.usefixtures("_suppress_tz")
428
- def test_returns_none_on_empty_output(self, hetzner_host, mocker):
429
- """Returns None when SSH succeeds but output is empty."""
429
+ def test_returns_no_marker_on_empty_output(self, hetzner_host, mocker):
430
+ """Returns (None, None) when SSH succeeds but the marker is empty."""
430
431
  mocker.patch("remo_cli.core.ssh.get_aws_region", return_value="us-west-2")
431
432
  mocker.patch(
432
433
  "subprocess.run",
433
- return_value=MagicMock(returncode=0, stdout=""),
434
+ return_value=MagicMock(returncode=0, stdout="", stderr=""),
434
435
  )
435
436
 
436
437
  from remo_cli.core.ssh import check_remote_version
437
438
 
438
- assert check_remote_version(hetzner_host) is None
439
+ assert check_remote_version(hetzner_host) == (None, None)
439
440
 
440
441
  @pytest.mark.usefixtures("_suppress_tz")
441
- def test_returns_none_on_timeout(self, hetzner_host, mocker):
442
- """Returns None when SSH times out."""
442
+ def test_returns_error_on_ssh_failure(self, hetzner_host, mocker):
443
+ """Returns (None, stderr) when SSH itself fails (rc 255)."""
444
+ mocker.patch("remo_cli.core.ssh.get_aws_region", return_value="us-west-2")
445
+ mocker.patch(
446
+ "subprocess.run",
447
+ return_value=MagicMock(
448
+ returncode=255,
449
+ stdout="",
450
+ stderr="Host key verification failed.\n",
451
+ ),
452
+ )
453
+
454
+ from remo_cli.core.ssh import check_remote_version
455
+
456
+ version, err = check_remote_version(hetzner_host)
457
+ assert version is None
458
+ assert err == "Host key verification failed."
459
+
460
+ @pytest.mark.usefixtures("_suppress_tz")
461
+ def test_returns_error_on_timeout(self, hetzner_host, mocker):
462
+ """Returns (None, error) when SSH times out."""
443
463
  mocker.patch("remo_cli.core.ssh.get_aws_region", return_value="us-west-2")
444
464
  mocker.patch(
445
465
  "subprocess.run",
@@ -448,14 +468,20 @@ class TestCheckRemoteVersion:
448
468
 
449
469
  from remo_cli.core.ssh import check_remote_version
450
470
 
451
- assert check_remote_version(hetzner_host) is None
471
+ version, err = check_remote_version(hetzner_host)
472
+ assert version is None
473
+ assert err is not None
474
+ assert "timed out" in err.lower()
452
475
 
453
476
  @pytest.mark.usefixtures("_suppress_tz")
454
- def test_returns_none_on_os_error(self, hetzner_host, mocker):
455
- """Returns None when SSH binary is not found."""
477
+ def test_returns_error_on_os_error(self, hetzner_host, mocker):
478
+ """Returns (None, error) when the ssh binary can't be invoked."""
456
479
  mocker.patch("remo_cli.core.ssh.get_aws_region", return_value="us-west-2")
457
480
  mocker.patch("subprocess.run", side_effect=OSError("No ssh"))
458
481
 
459
482
  from remo_cli.core.ssh import check_remote_version
460
483
 
461
- assert check_remote_version(hetzner_host) is None
484
+ version, err = check_remote_version(hetzner_host)
485
+ assert version is None
486
+ assert err is not None
487
+ assert "No ssh" in err
@@ -771,7 +771,7 @@ wheels = [
771
771
 
772
772
  [[package]]
773
773
  name = "remo-cli"
774
- version = "1.0.2"
774
+ version = "2.0.0rc2"
775
775
  source = { editable = "." }
776
776
  dependencies = [
777
777
  { name = "ansible-core" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes