remo-cli 4.2.0__tar.gz → 4.3.1__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 (647) hide show
  1. remo_cli-4.3.1/.release-please-manifest.json +3 -0
  2. {remo_cli-4.2.0 → remo_cli-4.3.1}/AGENTS.md +7 -3
  3. {remo_cli-4.2.0 → remo_cli-4.3.1}/CHANGELOG.md +19 -0
  4. {remo_cli-4.2.0 → remo_cli-4.3.1}/CLAUDE.md +7 -3
  5. {remo_cli-4.2.0 → remo_cli-4.3.1}/PKG-INFO +55 -1
  6. {remo_cli-4.2.0 → remo_cli-4.3.1}/README.md +54 -0
  7. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/user_setup/tasks/main.yml +14 -0
  8. remo_cli-4.3.1/ansible/ssh_configure.yml +240 -0
  9. remo_cli-4.3.1/docs/examples/orbstack-cloud-init.yaml +120 -0
  10. {remo_cli-4.2.0 → remo_cli-4.3.1}/docs/web-session-interface.md +9 -2
  11. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/package-lock.json +24 -24
  12. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/theme/terminalThemes.test.ts +38 -0
  13. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/theme/terminalThemes.ts +28 -5
  14. {remo_cli-4.2.0 → remo_cli-4.3.1}/pyproject.toml +1 -1
  15. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/014-register-ssh-host/spec.md +3 -0
  16. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/cli/added.py +71 -0
  17. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/cli/main.py +2 -1
  18. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/known_hosts.py +51 -0
  19. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/ssh.py +18 -1
  20. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/web_adopt.py +44 -9
  21. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/models/host.py +16 -0
  22. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/providers/added.py +135 -1
  23. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/check.py +4 -1
  24. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/config.py +25 -0
  25. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/discovery.py +36 -7
  26. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/terminal.py +4 -1
  27. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/ansible/test_apt_sources_repair.py +1 -0
  28. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/ansible/test_authorized_keys_bootstrap.py +6 -2
  29. remo_cli-4.3.1/tests/ansible/test_ssh_configure_connection.py +203 -0
  30. remo_cli-4.3.1/tests/ansible/test_user_setup_docker_group.py +88 -0
  31. remo_cli-4.3.1/tests/unit/cli/test_configure_cmd.py +115 -0
  32. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/test_main.py +1 -0
  33. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_ssh_identity_opts.py +66 -0
  34. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_web_adopt_trust.py +156 -1
  35. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_added_add.py +30 -0
  36. remo_cli-4.3.1/tests/unit/providers/test_added_configure.py +226 -0
  37. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_added_provider_guard.py +79 -1
  38. remo_cli-4.3.1/tests/unit/test_docs_examples.py +80 -0
  39. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/test_host_ssh_type.py +19 -0
  40. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_discovery.py +76 -1
  41. remo_cli-4.3.1/tests/unit/web/test_service_identity_selection.py +95 -0
  42. {remo_cli-4.2.0 → remo_cli-4.3.1}/uv.lock +51 -54
  43. remo_cli-4.2.0/.release-please-manifest.json +0 -3
  44. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/release/SKILL.md +0 -0
  45. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-analyze/SKILL.md +0 -0
  46. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-checklist/SKILL.md +0 -0
  47. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-clarify/SKILL.md +0 -0
  48. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-constitution/SKILL.md +0 -0
  49. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-converge/SKILL.md +0 -0
  50. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-git-commit/SKILL.md +0 -0
  51. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-git-feature/SKILL.md +0 -0
  52. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-git-initialize/SKILL.md +0 -0
  53. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-git-remote/SKILL.md +0 -0
  54. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-git-validate/SKILL.md +0 -0
  55. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-implement/SKILL.md +0 -0
  56. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-plan/SKILL.md +0 -0
  57. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-specify/SKILL.md +0 -0
  58. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-tasks/SKILL.md +0 -0
  59. {remo_cli-4.2.0 → remo_cli-4.3.1}/.claude/skills/speckit-taskstoissues/SKILL.md +0 -0
  60. {remo_cli-4.2.0 → remo_cli-4.3.1}/.devcontainer/devcontainer.json +0 -0
  61. {remo_cli-4.2.0 → remo_cli-4.3.1}/.dockerignore +0 -0
  62. {remo_cli-4.2.0 → remo_cli-4.3.1}/.github/CODEOWNERS +0 -0
  63. {remo_cli-4.2.0 → remo_cli-4.3.1}/.github/dependabot.yml +0 -0
  64. {remo_cli-4.2.0 → remo_cli-4.3.1}/.github/workflows/ci.yml +0 -0
  65. {remo_cli-4.2.0 → remo_cli-4.3.1}/.github/workflows/codeql.yml +0 -0
  66. {remo_cli-4.2.0 → remo_cli-4.3.1}/.github/workflows/dependency-review.yml +0 -0
  67. {remo_cli-4.2.0 → remo_cli-4.3.1}/.github/workflows/dev-build.yml +0 -0
  68. {remo_cli-4.2.0 → remo_cli-4.3.1}/.github/workflows/provision.yml +0 -0
  69. {remo_cli-4.2.0 → remo_cli-4.3.1}/.github/workflows/release-please.yml +0 -0
  70. {remo_cli-4.2.0 → remo_cli-4.3.1}/.github/workflows/release.yml +0 -0
  71. {remo_cli-4.2.0 → remo_cli-4.3.1}/.github/workflows/smoke-test-cleanup.yml +0 -0
  72. {remo_cli-4.2.0 → remo_cli-4.3.1}/.github/workflows/smoke-test.yml +0 -0
  73. {remo_cli-4.2.0 → remo_cli-4.3.1}/.github/workflows/teardown.yml +0 -0
  74. {remo_cli-4.2.0 → remo_cli-4.3.1}/.gitignore +0 -0
  75. {remo_cli-4.2.0 → remo_cli-4.3.1}/.maverick/checkpoints/feature/implementation_complete.json +0 -0
  76. {remo_cli-4.2.0 → remo_cli-4.3.1}/.maverick/checkpoints/feature/phase_${{ index }}_complete.json +0 -0
  77. {remo_cli-4.2.0 → remo_cli-4.3.1}/.maverick/checkpoints/feature/validation_complete.json +0 -0
  78. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/.registry +0 -0
  79. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/README.md +0 -0
  80. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/commands/speckit.git.commit.md +0 -0
  81. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/commands/speckit.git.feature.md +0 -0
  82. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/commands/speckit.git.initialize.md +0 -0
  83. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/commands/speckit.git.remote.md +0 -0
  84. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/commands/speckit.git.validate.md +0 -0
  85. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/config-template.yml +0 -0
  86. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/extension.yml +0 -0
  87. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/git-config.yml +0 -0
  88. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/scripts/bash/auto-commit.sh +0 -0
  89. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/scripts/bash/create-new-feature.sh +0 -0
  90. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/scripts/bash/git-common.sh +0 -0
  91. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/scripts/bash/initialize-repo.sh +0 -0
  92. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
  93. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/scripts/powershell/create-new-feature.ps1 +0 -0
  94. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/scripts/powershell/git-common.ps1 +0 -0
  95. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
  96. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/extensions.yml +0 -0
  97. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/feature.json +0 -0
  98. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/init-options.json +0 -0
  99. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/integration.json +0 -0
  100. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/integrations/claude.manifest.json +0 -0
  101. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/integrations/speckit.manifest.json +0 -0
  102. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/memory/constitution.md +0 -0
  103. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/scripts/bash/check-prerequisites.sh +0 -0
  104. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/scripts/bash/common.sh +0 -0
  105. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/scripts/bash/create-new-feature.sh +0 -0
  106. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/scripts/bash/setup-plan.sh +0 -0
  107. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/scripts/bash/setup-tasks.sh +0 -0
  108. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/scripts/bash/update-agent-context.sh +0 -0
  109. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/templates/agent-file-template.md +0 -0
  110. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/templates/checklist-template.md +0 -0
  111. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/templates/constitution-template.md +0 -0
  112. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/templates/plan-template.md +0 -0
  113. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/templates/spec-template.md +0 -0
  114. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/templates/tasks-template.md +0 -0
  115. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/workflows/speckit/workflow.yml +0 -0
  116. {remo_cli-4.2.0 → remo_cli-4.3.1}/.specify/workflows/workflow-registry.json +0 -0
  117. {remo_cli-4.2.0 → remo_cli-4.3.1}/CONTRIBUTING.md +0 -0
  118. {remo_cli-4.2.0 → remo_cli-4.3.1}/Dockerfile +0 -0
  119. {remo_cli-4.2.0 → remo_cli-4.3.1}/LICENSE +0 -0
  120. {remo_cli-4.2.0 → remo_cli-4.3.1}/SECURITY.md +0 -0
  121. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/.ansible-lint +0 -0
  122. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/README.md +0 -0
  123. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/ansible.cfg +0 -0
  124. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/aws_configure.yml +0 -0
  125. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/aws_resize.yml +0 -0
  126. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/aws_site.yml +0 -0
  127. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/aws_teardown.yml +0 -0
  128. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/group_vars/all.yml +0 -0
  129. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/group_vars/incus_containers.yml +0 -0
  130. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/group_vars/proxmox_containers.yml +0 -0
  131. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/hetzner_configure.yml +0 -0
  132. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/hetzner_provision.yml +0 -0
  133. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/hetzner_resize.yml +0 -0
  134. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/hetzner_site.yml +0 -0
  135. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/hetzner_teardown.yml +0 -0
  136. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/incus_bootstrap.yml +0 -0
  137. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/incus_configure.yml +0 -0
  138. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/incus_provision.yml +0 -0
  139. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/incus_resize.yml +0 -0
  140. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/incus_site.yml +0 -0
  141. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/incus_teardown.yml +0 -0
  142. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/inventory/hosts.yml +0 -0
  143. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/inventory/incus_containers.yml +0 -0
  144. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/proxmox_bootstrap.yml +0 -0
  145. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/proxmox_configure.yml +0 -0
  146. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/proxmox_provision.yml +0 -0
  147. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/proxmox_resize.yml +0 -0
  148. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/proxmox_site.yml +0 -0
  149. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/proxmox_teardown.yml +0 -0
  150. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/requirements.yml +0 -0
  151. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/aws_server/defaults/main.yml +0 -0
  152. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/aws_server/tasks/ebs.yml +0 -0
  153. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/aws_server/tasks/ec2.yml +0 -0
  154. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/aws_server/tasks/main.yml +0 -0
  155. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/aws_server/tasks/patch_manager.yml +0 -0
  156. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/aws_server/tasks/preflight.yml +0 -0
  157. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/aws_server/tasks/security_group.yml +0 -0
  158. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/deacon/defaults/main.yml +0 -0
  159. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/deacon/tasks/main.yml +0 -0
  160. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/devcontainers/tasks/main.yml +0 -0
  161. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/docker/handlers/main.yml +0 -0
  162. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/docker/tasks/main.yml +0 -0
  163. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/fzf/tasks/main.yml +0 -0
  164. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/github_cli/tasks/main.yml +0 -0
  165. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/hetzner_server/defaults/main.yml +0 -0
  166. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/hetzner_server/tasks/main.yml +0 -0
  167. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/incus_bootstrap/defaults/main.yml +0 -0
  168. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/incus_bootstrap/handlers/main.yml +0 -0
  169. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/incus_bootstrap/tasks/main.yml +0 -0
  170. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/incus_container/defaults/main.yml +0 -0
  171. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/incus_container/handlers/main.yml +0 -0
  172. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/incus_container/meta/main.yml +0 -0
  173. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/incus_container/tasks/main.yml +0 -0
  174. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/incus_container/tasks/preflight.yml +0 -0
  175. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/incus_container_teardown/defaults/main.yml +0 -0
  176. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/incus_container_teardown/meta/main.yml +0 -0
  177. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/incus_container_teardown/tasks/main.yml +0 -0
  178. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/nodejs/defaults/main.yml +0 -0
  179. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/nodejs/files/nodesource-repo.gpg.key +0 -0
  180. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/nodejs/tasks/main.yml +0 -0
  181. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/proxmox_bootstrap/defaults/main.yml +0 -0
  182. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/proxmox_bootstrap/meta/main.yml +0 -0
  183. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/proxmox_bootstrap/tasks/main.yml +0 -0
  184. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/proxmox_container/defaults/main.yml +0 -0
  185. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/proxmox_container/handlers/main.yml +0 -0
  186. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/proxmox_container/meta/main.yml +0 -0
  187. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/proxmox_container/tasks/main.yml +0 -0
  188. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/proxmox_container/tasks/preflight.yml +0 -0
  189. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/proxmox_container_teardown/defaults/main.yml +0 -0
  190. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/proxmox_container_teardown/meta/main.yml +0 -0
  191. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/proxmox_container_teardown/tasks/main.yml +0 -0
  192. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/user_setup/defaults/main.yml +0 -0
  193. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/user_setup/templates/devshell.sh.j2 +0 -0
  194. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/user_setup/templates/project-launch.sh.j2 +0 -0
  195. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/user_setup/templates/project-menu.sh.j2 +0 -0
  196. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/user_setup/templates/remo-host.sh.j2 +0 -0
  197. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/zellij/defaults/main.yml +0 -0
  198. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/zellij/tasks/main.yml +0 -0
  199. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/zellij/templates/config.kdl.j2 +0 -0
  200. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/roles/zellij/templates/layouts/minimal.kdl.j2 +0 -0
  201. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/tasks/configure_dev_tools.yml +0 -0
  202. {remo_cli-4.2.0 → remo_cli-4.3.1}/ansible/tasks/repair_apt_sources.yml +0 -0
  203. {remo_cli-4.2.0 → remo_cli-4.3.1}/docker/Dockerfile +0 -0
  204. {remo_cli-4.2.0 → remo_cli-4.3.1}/docker/compose.example.yml +0 -0
  205. {remo_cli-4.2.0 → remo_cli-4.3.1}/docker/entrypoint.sh +0 -0
  206. {remo_cli-4.2.0 → remo_cli-4.3.1}/docs/aws.md +0 -0
  207. {remo_cli-4.2.0 → remo_cli-4.3.1}/docs/feature-history.md +0 -0
  208. {remo_cli-4.2.0 → remo_cli-4.3.1}/docs/hetzner.md +0 -0
  209. {remo_cli-4.2.0 → remo_cli-4.3.1}/docs/incus.md +0 -0
  210. {remo_cli-4.2.0 → remo_cli-4.3.1}/docs/install.sh +0 -0
  211. {remo_cli-4.2.0 → remo_cli-4.3.1}/docs/maintaining-claude-md.md +0 -0
  212. {remo_cli-4.2.0 → remo_cli-4.3.1}/docs/maintaining-generated-types.md +0 -0
  213. {remo_cli-4.2.0 → remo_cli-4.3.1}/docs/providers.md +0 -0
  214. {remo_cli-4.2.0 → remo_cli-4.3.1}/docs/proxmox.md +0 -0
  215. {remo_cli-4.2.0 → remo_cli-4.3.1}/docs/remo-web-speckit-input.md +0 -0
  216. {remo_cli-4.2.0 → remo_cli-4.3.1}/docs/remo-web.html +0 -0
  217. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/index.html +0 -0
  218. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/package.json +0 -0
  219. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/scripts/check-types-fresh.mjs +0 -0
  220. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/scripts/generate-frame-types.mjs +0 -0
  221. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/scripts/generate-schema-types.mjs +0 -0
  222. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/api/client.test.ts +0 -0
  223. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/api/client.ts +0 -0
  224. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/api/generated/README.md +0 -0
  225. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/api/generated/openapi.json +0 -0
  226. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/api/generated/schema.d.ts +0 -0
  227. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/api/generated/terminal-frames.d.ts +0 -0
  228. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/api/generated/terminal-frames.json +0 -0
  229. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/api/generated.test.ts +0 -0
  230. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/AppRoot.tsx +0 -0
  231. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/AppShell.css +0 -0
  232. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/AppShell.tsx +0 -0
  233. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/AwaitingAdoption.css +0 -0
  234. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/AwaitingAdoption.tsx +0 -0
  235. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/OfflineOverlay.css +0 -0
  236. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/OfflineOverlay.tsx +0 -0
  237. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/PairToSync.css +0 -0
  238. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/PairToSync.tsx +0 -0
  239. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/SessionRail.css +0 -0
  240. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/SessionRail.tsx +0 -0
  241. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/SettingsPage.css +0 -0
  242. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/SettingsPage.tsx +0 -0
  243. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/ShortcutsModal.css +0 -0
  244. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/ShortcutsModal.tsx +0 -0
  245. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/TerminalCard.css +0 -0
  246. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/TerminalCard.test.tsx +0 -0
  247. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/TerminalCard.tsx +0 -0
  248. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/TopBar.css +0 -0
  249. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/TopBar.tsx +0 -0
  250. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/WorkspacePane.css +0 -0
  251. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/WorkspacePane.test.tsx +0 -0
  252. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/WorkspacePane.tsx +0 -0
  253. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/masterLayout.test.ts +0 -0
  254. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/masterLayout.ts +0 -0
  255. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/providerMeta.test.ts +0 -0
  256. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/providerMeta.ts +0 -0
  257. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/railModel.test.ts +0 -0
  258. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/components/railModel.ts +0 -0
  259. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/lib/clipboard.ts +0 -0
  260. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/lib/fullscreen.ts +0 -0
  261. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/main.tsx +0 -0
  262. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/state/discovery.test.ts +0 -0
  263. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/state/discovery.ts +0 -0
  264. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/state/fonts.ts +0 -0
  265. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/state/health.test.ts +0 -0
  266. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/state/health.ts +0 -0
  267. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/state/latency.test.ts +0 -0
  268. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/state/latency.ts +0 -0
  269. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/state/settings.test.ts +0 -0
  270. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/state/settings.ts +0 -0
  271. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/state/useConsoleKeyboard.ts +0 -0
  272. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/state/workspace.test.ts +0 -0
  273. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/state/workspace.ts +0 -0
  274. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/terminal/.gitkeep +0 -0
  275. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/terminal/RendererAdapter.ts +0 -0
  276. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/terminal/TerminalConnection.test.ts +0 -0
  277. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/terminal/TerminalConnection.ts +0 -0
  278. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/terminal/XtermRenderer.ts +0 -0
  279. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/terminal/defaultRenderer.ts +0 -0
  280. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/terminal/keymap.test.ts +0 -0
  281. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/terminal/keymap.ts +0 -0
  282. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/test/setup.ts +0 -0
  283. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/theme/fonts.ts +0 -0
  284. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/src/theme/tokens.css +0 -0
  285. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/tsconfig.json +0 -0
  286. {remo_cli-4.2.0 → remo_cli-4.3.1}/frontend/vite.config.ts +0 -0
  287. {remo_cli-4.2.0 → remo_cli-4.3.1}/install.sh +0 -0
  288. {remo_cli-4.2.0 → remo_cli-4.3.1}/maverick.yaml +0 -0
  289. {remo_cli-4.2.0 → remo_cli-4.3.1}/playwright.config.ts +0 -0
  290. {remo_cli-4.2.0 → remo_cli-4.3.1}/project-menu.sh +0 -0
  291. {remo_cli-4.2.0 → remo_cli-4.3.1}/release-please-config.json +0 -0
  292. {remo_cli-4.2.0 → remo_cli-4.3.1}/remo +0 -0
  293. {remo_cli-4.2.0 → remo_cli-4.3.1}/remo.svg +0 -0
  294. {remo_cli-4.2.0 → remo_cli-4.3.1}/run.sh +0 -0
  295. {remo_cli-4.2.0 → remo_cli-4.3.1}/scripts/export_openapi.py +0 -0
  296. {remo_cli-4.2.0 → remo_cli-4.3.1}/scripts/palette-check.sh +0 -0
  297. {remo_cli-4.2.0 → remo_cli-4.3.1}/scripts/setup-aws-oidc.sh +0 -0
  298. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/001-bootstrap-incus-host/checklists/requirements.md +0 -0
  299. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/001-bootstrap-incus-host/checklists/validation.md +0 -0
  300. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/001-bootstrap-incus-host/contracts/playbook-interface.yaml +0 -0
  301. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/001-bootstrap-incus-host/contracts/role-interface.yaml +0 -0
  302. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/001-bootstrap-incus-host/data-model.md +0 -0
  303. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/001-bootstrap-incus-host/plan.md +0 -0
  304. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/001-bootstrap-incus-host/quickstart.md +0 -0
  305. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/001-bootstrap-incus-host/research.md +0 -0
  306. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/001-bootstrap-incus-host/spec.md +0 -0
  307. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/001-bootstrap-incus-host/tasks.md +0 -0
  308. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/002-incus-container-support/checklists/requirements.md +0 -0
  309. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/002-incus-container-support/contracts/playbook-interface.yaml +0 -0
  310. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/002-incus-container-support/contracts/role-interface.yaml +0 -0
  311. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/002-incus-container-support/data-model.md +0 -0
  312. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/002-incus-container-support/plan.md +0 -0
  313. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/002-incus-container-support/quickstart.md +0 -0
  314. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/002-incus-container-support/research.md +0 -0
  315. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/002-incus-container-support/spec.md +0 -0
  316. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/002-incus-container-support/tasks.md +0 -0
  317. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/003-python-cli-rewrite/checklists/requirements.md +0 -0
  318. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/003-python-cli-rewrite/data-model.md +0 -0
  319. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/003-python-cli-rewrite/plan.md +0 -0
  320. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/003-python-cli-rewrite/quickstart.md +0 -0
  321. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/003-python-cli-rewrite/research.md +0 -0
  322. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/003-python-cli-rewrite/spec.md +0 -0
  323. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/003-python-cli-rewrite/tasks.md +0 -0
  324. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/004-proxmox-container-support/spec.md +0 -0
  325. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/005-provider-snapshots/checklists/requirements.md +0 -0
  326. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/005-provider-snapshots/contracts/cli-surface.md +0 -0
  327. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/005-provider-snapshots/data-model.md +0 -0
  328. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/005-provider-snapshots/plan.md +0 -0
  329. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/005-provider-snapshots/quickstart.md +0 -0
  330. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/005-provider-snapshots/research.md +0 -0
  331. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/005-provider-snapshots/spec.md +0 -0
  332. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/005-provider-snapshots/tasks.md +0 -0
  333. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/010-web-session-interface/checklists/requirements.md +0 -0
  334. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/010-web-session-interface/contracts/remo-host-protocol.md +0 -0
  335. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/010-web-session-interface/contracts/rest-api.md +0 -0
  336. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/010-web-session-interface/contracts/terminal-websocket.md +0 -0
  337. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/010-web-session-interface/data-model.md +0 -0
  338. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/010-web-session-interface/plan.md +0 -0
  339. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/010-web-session-interface/quickstart.md +0 -0
  340. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/010-web-session-interface/research.md +0 -0
  341. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/010-web-session-interface/spec.md +0 -0
  342. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/010-web-session-interface/tasks.md +0 -0
  343. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/011-web-adopt/checklists/requirements.md +0 -0
  344. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/011-web-adopt/contracts/cli-web-adopt.md +0 -0
  345. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/011-web-adopt/contracts/setup-api.md +0 -0
  346. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/011-web-adopt/data-model.md +0 -0
  347. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/011-web-adopt/plan.md +0 -0
  348. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/011-web-adopt/quickstart-results.md +0 -0
  349. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/011-web-adopt/quickstart.md +0 -0
  350. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/011-web-adopt/research.md +0 -0
  351. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/011-web-adopt/spec.md +0 -0
  352. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/011-web-adopt/tasks.md +0 -0
  353. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/012-web-adopt-pairing/contracts/cli-web-adopt.md +0 -0
  354. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/012-web-adopt-pairing/contracts/pairing-api.md +0 -0
  355. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/012-web-adopt-pairing/contracts/setup-api.md +0 -0
  356. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/012-web-adopt-pairing/data-model.md +0 -0
  357. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/012-web-adopt-pairing/plan.md +0 -0
  358. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/012-web-adopt-pairing/quickstart.md +0 -0
  359. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/012-web-adopt-pairing/research.md +0 -0
  360. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/012-web-adopt-pairing/spec.md +0 -0
  361. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/012-web-adopt-pairing/tasks.md +0 -0
  362. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/013-managed-instance-tags/contracts/cli-sync.md +0 -0
  363. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/013-managed-instance-tags/contracts/marker-commands.md +0 -0
  364. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/013-managed-instance-tags/data-model.md +0 -0
  365. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/013-managed-instance-tags/plan.md +0 -0
  366. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/013-managed-instance-tags/quickstart.md +0 -0
  367. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/013-managed-instance-tags/research.md +0 -0
  368. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/013-managed-instance-tags/spec.md +0 -0
  369. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/013-managed-instance-tags/tasks.md +0 -0
  370. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/014-register-ssh-host/contracts/add-command.md +0 -0
  371. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/014-register-ssh-host/contracts/remove-command.md +0 -0
  372. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/014-register-ssh-host/data-model.md +0 -0
  373. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/014-register-ssh-host/plan.md +0 -0
  374. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/014-register-ssh-host/quickstart.md +0 -0
  375. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/014-register-ssh-host/research.md +0 -0
  376. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/014-register-ssh-host/tasks.md +0 -0
  377. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/015-registry-v2/checklists/requirements.md +0 -0
  378. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/015-registry-v2/contracts/mirror-payload-v2.md +0 -0
  379. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/015-registry-v2/contracts/registry-accessor-api.md +0 -0
  380. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/015-registry-v2/contracts/registry-file-v2.md +0 -0
  381. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/015-registry-v2/data-model.md +0 -0
  382. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/015-registry-v2/plan.md +0 -0
  383. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/015-registry-v2/quickstart.md +0 -0
  384. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/015-registry-v2/research.md +0 -0
  385. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/015-registry-v2/spec.md +0 -0
  386. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/015-registry-v2/tasks.md +0 -0
  387. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/016-sync-reconcile/checklists/requirements.md +0 -0
  388. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/016-sync-reconcile/contracts/cli-sync.md +0 -0
  389. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/016-sync-reconcile/contracts/provider-probe.md +0 -0
  390. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/016-sync-reconcile/data-model.md +0 -0
  391. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/016-sync-reconcile/plan.md +0 -0
  392. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/016-sync-reconcile/quickstart.md +0 -0
  393. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/016-sync-reconcile/research.md +0 -0
  394. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/016-sync-reconcile/spec.md +0 -0
  395. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/016-sync-reconcile/tasks.md +0 -0
  396. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/017-web-adopt-simplify/checklists/requirements.md +0 -0
  397. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/017-web-adopt-simplify/contracts/cli-web-push.md +0 -0
  398. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/017-web-adopt-simplify/contracts/cli-web-status.md +0 -0
  399. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/017-web-adopt-simplify/contracts/revocation.md +0 -0
  400. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/017-web-adopt-simplify/contracts/setup-status-marker.md +0 -0
  401. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/017-web-adopt-simplify/data-model.md +0 -0
  402. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/017-web-adopt-simplify/plan.md +0 -0
  403. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/017-web-adopt-simplify/quickstart.md +0 -0
  404. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/017-web-adopt-simplify/research.md +0 -0
  405. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/017-web-adopt-simplify/spec.md +0 -0
  406. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/017-web-adopt-simplify/tasks.md +0 -0
  407. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/baseline-notes.md +0 -0
  408. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/checklists/requirements.md +0 -0
  409. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/contracts/cli-surface.md +0 -0
  410. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/contracts/errors.md +0 -0
  411. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/contracts/lifecycle-templates.md +0 -0
  412. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/contracts/provider-protocol.md +0 -0
  413. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/contracts/sync-merge.md +0 -0
  414. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/data-model.md +0 -0
  415. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/plan.md +0 -0
  416. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/quickstart.md +0 -0
  417. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/research.md +0 -0
  418. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/spec.md +0 -0
  419. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/018-provider-abstraction/tasks.md +0 -0
  420. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/019-hygiene-deps-docs/checklists/requirements.md +0 -0
  421. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/019-hygiene-deps-docs/contracts/cli-surface-delta.md +0 -0
  422. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/019-hygiene-deps-docs/contracts/docs-structure-check.md +0 -0
  423. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/019-hygiene-deps-docs/data-model.md +0 -0
  424. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/019-hygiene-deps-docs/plan.md +0 -0
  425. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/019-hygiene-deps-docs/quickstart.md +0 -0
  426. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/019-hygiene-deps-docs/research.md +0 -0
  427. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/019-hygiene-deps-docs/spec.md +0 -0
  428. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/019-hygiene-deps-docs/tasks.md +0 -0
  429. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/020-openapi-type-generation/checklists/requirements.md +0 -0
  430. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/020-openapi-type-generation/contracts/drift-checks.md +0 -0
  431. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/020-openapi-type-generation/contracts/terminal-frames-v1.md +0 -0
  432. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/020-openapi-type-generation/data-model.md +0 -0
  433. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/020-openapi-type-generation/plan.md +0 -0
  434. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/020-openapi-type-generation/quickstart.md +0 -0
  435. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/020-openapi-type-generation/research.md +0 -0
  436. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/020-openapi-type-generation/spec.md +0 -0
  437. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/020-openapi-type-generation/tasks.md +0 -0
  438. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/021-cli-plane-separation/contracts/cli-surface.md +0 -0
  439. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/021-cli-plane-separation/contracts/descriptor-schema.md +0 -0
  440. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/021-cli-plane-separation/data-model.md +0 -0
  441. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/021-cli-plane-separation/plan.md +0 -0
  442. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/021-cli-plane-separation/quickstart.md +0 -0
  443. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/021-cli-plane-separation/research.md +0 -0
  444. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/021-cli-plane-separation/spec.md +0 -0
  445. {remo_cli-4.2.0 → remo_cli-4.3.1}/specs/021-cli-plane-separation/tasks.md +0 -0
  446. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/__init__.py +0 -0
  447. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/__main__.py +0 -0
  448. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/cli/__init__.py +0 -0
  449. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/cli/cp.py +0 -0
  450. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/cli/providers/__init__.py +0 -0
  451. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/cli/providers/factory.py +0 -0
  452. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/cli/shell.py +0 -0
  453. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/cli/web.py +0 -0
  454. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/__init__.py +0 -0
  455. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/ansible_runner.py +0 -0
  456. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/completion.py +0 -0
  457. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/config.py +0 -0
  458. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/errors.py +0 -0
  459. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/lifecycle.py +0 -0
  460. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/output.py +0 -0
  461. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/picker.py +0 -0
  462. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/provider_protocol.py +0 -0
  463. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/provider_registry.py +0 -0
  464. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/reconcile.py +0 -0
  465. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/registry.py +0 -0
  466. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/remo_host_client.py +0 -0
  467. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/rsync.py +0 -0
  468. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/snapshot.py +0 -0
  469. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/validation.py +0 -0
  470. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/version.py +0 -0
  471. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/core/web_drift.py +0 -0
  472. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/models/__init__.py +0 -0
  473. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/models/capability.py +0 -0
  474. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/models/discovery.py +0 -0
  475. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/models/session_target.py +0 -0
  476. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/models/snapshot.py +0 -0
  477. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/providers/__init__.py +0 -0
  478. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/providers/aws.py +0 -0
  479. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/providers/aws_descriptor.py +0 -0
  480. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/providers/builtin.py +0 -0
  481. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/providers/hetzner.py +0 -0
  482. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/providers/hetzner_descriptor.py +0 -0
  483. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/providers/incus.py +0 -0
  484. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/providers/incus_descriptor.py +0 -0
  485. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/providers/proxmox.py +0 -0
  486. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/providers/proxmox_descriptor.py +0 -0
  487. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/__init__.py +0 -0
  488. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/api/__init__.py +0 -0
  489. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/api/hosts.py +0 -0
  490. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/api/pairing.py +0 -0
  491. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/api/setup.py +0 -0
  492. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/api/terminals.py +0 -0
  493. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/app.py +0 -0
  494. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/frames.py +0 -0
  495. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/health.py +0 -0
  496. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/logging_config.py +0 -0
  497. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/models.py +0 -0
  498. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/operator_auth.py +0 -0
  499. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/pairing.py +0 -0
  500. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/ssh_master.py +0 -0
  501. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/state.py +0 -0
  502. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/terminal_registry.py +0 -0
  503. {remo_cli-4.2.0 → remo_cli-4.3.1}/src/remo_cli/web/tokens.py +0 -0
  504. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/__init__.py +0 -0
  505. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/ansible/__init__.py +0 -0
  506. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/ansible/test_remo_host_idempotency.py +0 -0
  507. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/conftest.py +0 -0
  508. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/e2e/fixtures.ts +0 -0
  509. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/e2e/keyboard-routing.spec.ts +0 -0
  510. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/e2e/mobile.spec.ts +0 -0
  511. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/e2e/reconnect.spec.ts +0 -0
  512. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/e2e/terminal-shell-io.spec.ts +0 -0
  513. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/e2e/terminal-tui-compatibility.spec.ts +0 -0
  514. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/e2e/workspace-layout.spec.ts +0 -0
  515. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/image/__init__.py +0 -0
  516. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/image/test_docker_image.py +0 -0
  517. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/__init__.py +0 -0
  518. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/_nine_terminal_fixture.py +0 -0
  519. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/fish_completion.sh +0 -0
  520. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/orbstack-cloud-init.yml +0 -0
  521. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/orbstack.sh +0 -0
  522. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/test_nine_terminals.py +0 -0
  523. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/test_nine_terminals_soak.py +0 -0
  524. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/test_registry_concurrency.py +0 -0
  525. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/test_remo_host_e2e.py +0 -0
  526. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/test_security_rejections.py +0 -0
  527. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/test_setup_payload_versions.py +0 -0
  528. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/test_sync_reconcile.py +0 -0
  529. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/test_terminal_attach.py +0 -0
  530. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/test_web_adopt_e2e.py +0 -0
  531. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/integration/test_web_cli_parity.py +0 -0
  532. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/perf/__init__.py +0 -0
  533. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/perf/test_latency.py +0 -0
  534. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/perf/test_registry_perf.py +0 -0
  535. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/__init__.py +0 -0
  536. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/__init__.py +0 -0
  537. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/providers/test_aws_snapshot.py +0 -0
  538. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/providers/test_hetzner_snapshot.py +0 -0
  539. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/providers/test_incus_snapshot.py +0 -0
  540. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/providers/test_incus_sync_all.py +0 -0
  541. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/providers/test_proxmox_snapshot.py +0 -0
  542. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/providers/test_proxmox_sync_all.py +0 -0
  543. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/surface_baseline.py +0 -0
  544. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/test_add_cmd.py +0 -0
  545. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/test_cli_uniformity.py +0 -0
  546. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/test_cp.py +0 -0
  547. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/test_remove_cmd.py +0 -0
  548. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/test_shell.py +0 -0
  549. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/test_shell_added.py +0 -0
  550. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/test_startup_imports.py +0 -0
  551. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/test_surface_preservation.py +0 -0
  552. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/cli/test_web_adopt_cmd.py +0 -0
  553. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/__init__.py +0 -0
  554. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_ansible_runner.py +0 -0
  555. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_completion.py +0 -0
  556. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_config.py +0 -0
  557. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_errors.py +0 -0
  558. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_known_hosts.py +0 -0
  559. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_lifecycle.py +0 -0
  560. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_migration_tagging_notice.py +0 -0
  561. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_output.py +0 -0
  562. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_picker.py +0 -0
  563. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_provider_registry.py +0 -0
  564. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_reconcile.py +0 -0
  565. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_registry_format.py +0 -0
  566. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_registry_locking.py +0 -0
  567. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_registry_migration.py +0 -0
  568. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_remo_host_client.py +0 -0
  569. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_rsync.py +0 -0
  570. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_snapshot.py +0 -0
  571. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_ssh.py +0 -0
  572. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_ssh_added.py +0 -0
  573. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_ssh_controlpath.py +0 -0
  574. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_validation.py +0 -0
  575. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_version.py +0 -0
  576. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_web_adopt_authorize.py +0 -0
  577. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_web_adopt_code.py +0 -0
  578. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_web_adopt_payload.py +0 -0
  579. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_web_drift.py +0 -0
  580. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_web_push.py +0 -0
  581. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/core/test_web_push_self_heal.py +0 -0
  582. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/models/__init__.py +0 -0
  583. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/models/test_capability.py +0 -0
  584. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/models/test_discovery.py +0 -0
  585. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/models/test_session_target.py +0 -0
  586. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/__init__.py +0 -0
  587. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/fake_provider.py +0 -0
  588. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_added_update_remove.py +0 -0
  589. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_added_verify.py +0 -0
  590. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_aws_provider_protocol.py +0 -0
  591. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_aws_snapshot.py +0 -0
  592. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_aws_sync.py +0 -0
  593. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_aws_upgrade_resize.py +0 -0
  594. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_hetzner_entry_adapters.py +0 -0
  595. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_hetzner_http.py +0 -0
  596. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_hetzner_label.py +0 -0
  597. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_hetzner_resize.py +0 -0
  598. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_hetzner_snapshot.py +0 -0
  599. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_hetzner_sync.py +0 -0
  600. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_host_user_consistency.py +0 -0
  601. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_incus_entry_adapters.py +0 -0
  602. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_incus_marker.py +0 -0
  603. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_incus_resize.py +0 -0
  604. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_incus_snapshot.py +0 -0
  605. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_incus_sync.py +0 -0
  606. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_missing_sdk.py +0 -0
  607. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_provider_conformance.py +0 -0
  608. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_provider_registry_entries.py +0 -0
  609. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_proxmox_devcontainer_runtime.py +0 -0
  610. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_proxmox_marker.py +0 -0
  611. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_proxmox_provider_protocol.py +0 -0
  612. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_proxmox_resize.py +0 -0
  613. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_proxmox_snapshot.py +0 -0
  614. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/providers/test_proxmox_sync.py +0 -0
  615. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/test_ansible_templates.py +0 -0
  616. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/test_architecture.py +0 -0
  617. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/test_docs_structure.py +0 -0
  618. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/test_host_model.py +0 -0
  619. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/test_lockfile_version.py +0 -0
  620. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/test_schema_drift.py +0 -0
  621. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/__init__.py +0 -0
  622. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/_pairing_support.py +0 -0
  623. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/conftest.py +0 -0
  624. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/fixtures/hosts_response.json +0 -0
  625. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/fixtures/sessions_response.json +0 -0
  626. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_backpressure.py +0 -0
  627. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_check.py +0 -0
  628. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_csp.py +0 -0
  629. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_discovery_refresh_force.py +0 -0
  630. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_frames.py +0 -0
  631. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_health_states.py +0 -0
  632. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_hosts_mapping.py +0 -0
  633. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_lazy_import.py +0 -0
  634. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_log_redaction.py +0 -0
  635. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_mint_gating.py +0 -0
  636. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_operator_auth.py +0 -0
  637. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_pairing.py +0 -0
  638. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_pairing_origin.py +0 -0
  639. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_payload_equivalence.py +0 -0
  640. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_registry_readonly.py +0 -0
  641. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_setup_api.py +0 -0
  642. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_setup_auth.py +0 -0
  643. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_setup_dormancy.py +0 -0
  644. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_state.py +0 -0
  645. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_terminal_resize.py +0 -0
  646. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_terminals_api.py +0 -0
  647. {remo_cli-4.2.0 → remo_cli-4.3.1}/tests/unit/web/test_tokens.py +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "4.3.1"
3
+ }
@@ -31,6 +31,7 @@ Principles I, IV, and VIII are machine-enforced — see [Quality Gates](#quality
31
31
  - `core/registry.py`: stdlib `json` (format), `fcntl` (advisory locking via a `registry.lock` sidecar), `os.replace` (atomic writes). No new runtime deps. Setup API mirror payload moved to v2 (`contracts/mirror-payload-v2.md`) with a `payload_versions` capability handshake; an upgraded service still accepts v1 payloads. (015-registry-v2)
32
32
  - `core/reconcile.py`: provider-agnostic sync-reconcile engine (`SyncScope`, `DiscoveredHost`, `ProbeResult`, `build_plan` (pure), `render_plan`, the consent gate, `apply_plan` via the existing `mutate_registry()`, and the `run_sync` driver). No new runtime deps — stdlib only, built on `core/registry.py`/`core/output.py` as-is. (016-sync-reconcile)
33
33
  - `core/web_adopt.py` is now the single unified push engine (`run_push`; `run_adopt` is a thin deprecated alias) — first push adopts, later pushes re-sync, plus best-effort `remo-web@` revocation on removal, `--force` full re-authorization, and multi-workstation flap detection against the service's mirror-generation marker. `core/web_drift.py`: new stdlib-only offline registry-vs-push-cache diff (`diff_registry_against_cache`, `select_deployment`, `render_drift`) powering `remo web status` and the shared `out_of_date_notice()`/`emit_out_of_date_notice()` post-mutation nudge (importable without the `web` extra). Push cache bumped to `cache_version: 3` (`~/.config/remo/web-service.json`: per-deployment `{mirror_generation, instances}`, each instance retaining a non-secret connection tuple for revocation). Service side: `web/api/setup.py` writes/serves a `web-identity/mirror-meta.json` marker (generation + last-push descriptor) additively on `/setup/{status,registry}`; `web/state.py` mode detection fixed so a personal `~/.ssh/id_*` no longer forces `mount_configured` (non-writable `REMO_HOME` is the authoritative signal) with a new `REMO_WEB_MODE` override in `web/config.py`. No new runtime deps; no registry schema change. (017-web-adopt-simplify)
34
+ - No new runtime deps and no registry schema change. `ansible/ssh_configure.yml` is the first playbook to plumb an SSH **port** at all (`ansible_port` appeared nowhere in the repo before) and the first to take an identity from the registry rather than hardcoding `~/.ssh/id_rsa`; both arrive as namespaced `remo_ssh_*` extra-vars so no `ansible_*` name is ever emitted at extra-var precedence. `core/known_hosts.py` gains `guard_added_ssh_host_only`; `core/web_adopt.py` gains `known_hosts_lookup_key` and a `port=` parameter on `scan_and_verify_host_key`. Host key checking is inherited from `ansible.cfg` (disabled repo-wide) — a play-level override is impossible, since `ssh_common_args` is appended after `ssh_args` and ssh honours the first occurrence. (022-configure-added-hosts)
34
35
  - No new runtime deps or registry schema change. `pyproject.toml`'s existing `hcloud`/`boto3`/`httpx2` entries gained consumer-attribution comments; `tests/unit/test_docs_structure.py` is a new stdlib-only pytest module (parses the structure diagram below, no new dependency). (019-hygiene-deps-docs)
35
36
  - No new runtime deps; no registry schema change (the `host_user`/`node_user` JSON keys already matched the new flag spellings). Superseded for Proxmox by the `--node-user` → `--host-user` rename: both host-scoped providers now spell the node/host login `--host-user`/`host_user` on the CLI, in the provider kwargs and in `registry.json`; `core/registry.py` still reads a legacy `proxmox.node_user` key so pre-rename registries load unchanged and migrate on the next write. `core/provider_registry.py` gains `ArgumentSpec` (positional-argument metadata) and `CommandSpec.target`; `ProviderDescriptor.update_options` is replaced by `upgrade_options`/`resize_dimensions`/`resize_options`/`tag_options`/`host_commands`. `cli/providers/factory.py` gains `_build_upgrade`/`_build_resize`/`_build_tag`/`_build_host_group` (dropping `_build_update` and flat-mounted `bootstrap`). (021-cli-plane-separation)
36
37
 
@@ -48,7 +49,7 @@ src/remo_cli/ # Python CLI package (src layout, hatchling build)
48
49
  │ ├── main.py # Root CLI group; mounts one group per remo_cli.core.provider_registry.all_descriptors()
49
50
  │ ├── shell.py # remo shell — registry-dispatched update_entry() (delegates to upgrade()); prompt names the exact `remo <type> upgrade <name>` it will run; unknown type/ssh handled explicitly (no silent no-op)
50
51
  │ ├── cp.py # remo cp
51
- │ ├── added.py # remo add / remo remove — provider-neutral SSH host registration (feature 014)
52
+ │ ├── added.py # remo add / remo remove / remo configure — provider-neutral SSH host registration (014) and configure (022)
52
53
  │ ├── web.py # remo web {serve,check,push,status,adopt} — serve/check lazy-import remo_cli.web.* (NFR-008); push/status/adopt use core/web_adopt + core/web_drift only (adopt is a deprecated alias for push)
53
54
  │ └── providers/
54
55
  │ └── factory.py # build_provider_group(descriptor) generates create/destroy/upgrade/resize/list/info/sync/snapshot for every provider, plus tag (iff supports_managed_marker) and a host subgroup (iff host_commands non-empty) — the four hand-written per-provider CLI modules are gone
@@ -61,7 +62,7 @@ src/remo_cli/ # Python CLI package (src layout, hatchling build)
61
62
  │ ├── hetzner_descriptor.py # metadata-only ProviderDescriptor declaration, no SDK imports
62
63
  │ ├── aws_descriptor.py # metadata-only ProviderDescriptor declaration, no SDK imports
63
64
  │ ├── proxmox_descriptor.py # metadata-only ProviderDescriptor declaration, no SDK imports
64
- │ ├── added.py # Business logic for remo add / remo remove — provider-neutral SSH host registration (feature 014)
65
+ │ ├── added.py # Business logic for remo add / remo remove / remo configure — registration (014) + generic configure play (022)
65
66
  │ └── builtin.py # registers the four built-in descriptors; lazily auto-imported by provider_registry on first lookup
66
67
  ├── core/ # Shared utilities (no provider knowledge)
67
68
  │ ├── config.py # REMO_HOME, paths, read-only registry accessor
@@ -133,6 +134,9 @@ ansible/ # Ansible playbooks (invoked by Python via subprocess
133
134
  │ └── templates/
134
135
  │ └── remo-host.sh.j2 # Versioned `remo-host` command (capabilities/sessions/attach)
135
136
  ├── incus_bootstrap.yml
137
+ ├── ssh_configure.yml # Generic configure play for `remo add` hosts (022): maps remo_ssh_*
138
+ │ # -> ansible_port/ansible_ssh_private_key_file, binds remo_user to the
139
+ │ # registered account, reuses tasks/configure_dev_tools.yml
136
140
  └── requirements.yml
137
141
 
138
142
  scripts/ # Repo-root utility scripts (not part of the installed package)
@@ -315,9 +319,9 @@ checking nothing.
315
319
  - Ansible 2.14+ / YAML: Follow standard conventions plus Constitution principles
316
320
 
317
321
  ## Recent Changes
322
+ - 022-configure-added-hosts: Made manually-added SSH hosts first-class by giving them the one thing `remo add` (014) never did — a configure path. New provider-neutral `remo configure NAME` runs a new generic `ansible/ssh_configure.yml` against a registry `type="ssh"` entry, so an added host finally gets `remo-host`/zellij/`project-launch` and stops sitting in the `remo web` rail permanently badged `no_remo_host` with zero session targets. **No refactor of the four provider plays**: the role list was already shared via `tasks/configure_dev_tools.yml`, so a fifth playbook inherits the whole toolchain — the change is purely additive. The new play is two plays: a `localhost` play that asserts (never defaults) `remo_ssh_host`/`remo_ssh_user`/`remo_ssh_port` and `add_host`s them onto `ansible_host`/`ansible_user`/`ansible_port`/`ansible_ssh_private_key_file`, then a play targeting that named group (NOT `hosts: all`, which would pick up `inventory/hosts.yml`'s `hetzner_server` and whose zero-host case exits rc 0 — a silent false success). Four decisions the tests pin, each defending a *silent* failure: the identity is `| default(omit)`, never `'~/.ssh/id_rsa'` (aws_configure.yml's anti-pattern — a path default offers the WRONG key); it is paired with an explicit `-o IdentitiesOnly=yes`, which ansible-core does not add for `private_key_file` (without it a busy agent hits MaxAuthTries and dies "Too many authentication failures" while the passed key is never tried); both `raw` pre-flight probes (python3, `sudo -n true`) set `become: false`, since `ansible.cfg` sets `become = True` globally and a sudo probe run THROUGH sudo proves nothing; and `remo_user` is bound to `remo_ssh_user` rather than `group_vars/all.yml`'s `"remo"` — everything `user_setup` installs lands in `/home/{{ remo_user }}/.local/bin` and discovery logs in as the REGISTERED user, so the default would install `remo-host` into a second account and leave the console still reporting `no_remo_host` after a "successful" configure (it also skips the UID-1000 displacement, which only fires when 1000's holder is someone else). Root is refused (UID-1000 pin), the prompt defaults to no, and remo never reboots a host it did not provision — it reports via the task NAME, since `_filter_line` suppresses `debug` bodies. `core/known_hosts.py` gains `guard_added_ssh_host_only`, the mirror of `guard_not_added_ssh_host`: a provider host is refused naming `remo <type> upgrade NAME`, because the five configure plays are not interchangeable and a mis-target would succeed while configuring wrongly. **Bug fix in the same change** (`core/web_adopt.py`): `remo web push` hardcoded port 22 in `scan_and_verify_host_key`'s `ssh-keyscan` and `_lookup_trusted_keys`' `ssh-keygen -F`, so an added host on another port was reported `skipped/unreachable` — `authorize_service_key` never ran — with a remediation blaming a host that was reachable, or, worse, a different machine answering on 22 had its keys pushed. New `known_hosts_lookup_key()` follows OpenSSH's record form (bare for 22, `[host]:port` otherwise; verified against the real binary — neither direction is forgiving), and `-p` is omitted for 22 so every provider host's argv is byte-identical. `providers/added.py`'s `_reject_unsafe_field` now also rejects Jinja delimiters (registry values become extra-vars, which Ansible templates on the CONTROL NODE), re-checked on the configure read path so pre-existing entries are covered. `web/discovery.py`'s `no_remo_host` remediation now names the real command per host type instead of "re-run configure", which for an added host pointed at nothing. No registry schema change; no new runtime deps.
318
323
  - 021-cli-plane-separation: Split the three-intent `remo <provider> update` verb into three single-intent verbs — `upgrade` (in-instance configure play only, zero provider-side writes, SC-001), `resize` (resource change only — the factory rejects a dimensionless invocation with a `PreconditionError` listing that provider's dimension flags, before dispatching to the provider), and `tag` (managed-marker write only, generated iff `supports_managed_marker`; read-before-write no-op when already tagged, `OperationFailedError` — not `create`'s warn-and-continue — on write failure, SC-002) — and moved `bootstrap` off the flat instance plane into a new descriptor-driven `host` subgroup (`remo {incus,proxmox} host bootstrap`, generated iff `host_commands` is non-empty; `CommandSpec` gained a `target: ArgumentSpec | None` field so host commands take the host positionally — incus optional/defaults to localhost, proxmox required). `core/provider_registry.py`: `ProviderDescriptor.update_options` removed, replaced by `upgrade_options`/`resize_dimensions`/`resize_options`/`tag_options`/`host_commands`; new `ArgumentSpec` dataclass. `cli/providers/factory.py`: `_build_update` replaced by `_build_upgrade`/`_build_resize`/`_build_tag`/`_build_host_group`; `_instance_argument` grew a `param` kwarg so the new verbs take `NAME` positionally (matching the provider impl's `name` kwarg) while `create`/`destroy`/`info` keep `--name`; `_resolve_entry_for_destroy`'s user-hint lookup is now descriptor-driven (reads `kwargs` under the `registry_fields` JSON key ending in `_user`) instead of a `"user"` literal. Every provider module split `update()` into `upgrade()`/`resize()`/`tag()` (marker-supporting providers only) reusing existing private helpers unchanged; `update_entry()` (the `remo shell` tools-update path) now delegates to `upgrade()`. Renamed `--user` → `--host-user` (incus) / `--node-user` (proxmox) everywhere it appears (`remo add --user`, a different flag, is unchanged). Made every printed remedy truthful (SC-003): the registry-migration tagging notice and `sync`'s "Mark permanently:" line now recommend `tag` (previously wrongly recommended `sync`/`update`, which never wrote the marker); `remo shell`'s version-mismatch prompt now names the exact `remo <type> upgrade <name>` command it will run. Breaking change, no shim, no registry schema change: `remo <type> update`, flat `bootstrap`, and the old `--user` spelling are all Click "unknown command"/"no such option" errors post-change. FakeProvider's descriptor gained `upgrade_options`/`resize_dimensions`/`tag_options`/one `host_commands` entry, proving the full new surface is descriptor-only (SC-005).
319
324
  - 020-openapi-type-generation: Made the FastAPI service the machine-checked source of truth for every shape the browser console consumes, and made drift a build failure with an actionable message — three moves, in dependency order. **(1) Contract completeness, declaration-only** (not one serialized byte moves): `web/api/hosts.py` annotates `InstanceOut.status`/`SessionTargetOut.zellij_state`/`devcontainer_running` with the existing closed domain enums (dropping the `.value` unwrapping — a `str`-Enum serializes identically) and adds `KnownProviderType(str, Enum)` (`incus`/`hetzner`/`aws`/`proxmox`, fixed to the built-in set per FR-004a — never derived from the live `provider_registry`, so a third-party provider install can't perturb the artifact) typed `KnownProviderType | str` (`anyOf[$ref, string]`, keeping the wire field open); new `ErrorEnvelope`/`HealthResponse`/`ReadinessResponse`/`MintPairingResponse`/`DetailResponse` response models declare what four previously-untyped routes (`/health`, `/ready`, `/pairing/mint`, `POST /terminals`) already return, with the `{"error": {...}}` envelope declared only on routes that actually return it (never on `pairing.py`, whose 403 is `{"detail": ...}`). **(2) Generate and consume**: new stdlib `scripts/export_openapi.py` exports `frontend/src/api/generated/openapi.json` (`create_app().openapi()`, `sort_keys=True` + trailing newline for byte-reproducibility) and `terminal-frames.json` (a new `web/frames.py`: six Pydantic control-frame models + `InboundFrame`/`OutboundFrame` discriminated unions, wrapped via `TypeAdapter(...).json_schema()` in a `{protocol: "remo-terminal.v1", frame_version: 1, ...}` envelope that versions independently of the REST contract); `openapi-typescript` v7 (exact-pinned frontend devDependency) generates `schema.d.ts`, and a small synthetic-OpenAPI wrapper (`frontend/scripts/generate-frame-types.mjs`) generates `terminal-frames.d.ts` (openapi-typescript itself only accepts genuine OpenAPI documents). `web/api/terminals.py`'s five ad-hoc `{"v": 1, ...}` WS control-frame dict literals are gone, replaced by typed `frames.py` model construction/validation — `_handle_control`'s silent-drop behavior for malformed JSON, non-object payloads, and unknown frame types is preserved exactly (the one place this refactor could have changed runtime behavior; dedicated tests cover it). `frontend/src/api/client.ts` replaced ~12 hand-declared service-shaped interfaces with `components["schemas"][...]` aliases (console-owned shapes — `ApiError`, `ServiceStatus`, the forward-auth re-auth path — stay hand-written); `frontend/src/components/providerMeta.ts` now keys a `Record<InstanceStatus | KnownProviderType, ...>` off the generated unions, so a new enum member is a compile error at the presentation mapping while an off-union runtime value still renders via a preserved fallback (FR-013a — deleting that fallback to "achieve exhaustiveness" is explicitly wrong). **(3) Gate it**: three drift checks modeled on `tests/unit/test_docs_structure.py`'s established shape (name the artifact, group findings by kind, close with the exact regeneration command and a dependency-bump note) — `tests/unit/test_schema_drift.py` (REST + frame freshness against the live Python app, `test` CI job) and `frontend/scripts/check-types-fresh.mjs` (regenerated `.d.ts` vs. checked-in, `frontend` CI job) — fail, never skip, and never write a tracked file. New `docs/maintaining-generated-types.md` documents the four generated artifacts (never hand-edited; internal build input, no external compatibility promise, FR-029) and how to read a failure. No registry schema change; no new service runtime dependency.
320
- - 019-hygiene-deps-docs: Repository hygiene pass across dependencies, dead code, and documentation — no behavior change except one deliberate CLI break. Annotated every dependency whose necessity isn't visible from `src/remo_cli/` imports alone (`hcloud`/`boto3` in `pyproject.toml` now name their real consumers — the Ansible `hetzner.hcloud`/`amazon.aws`/`community.aws` collections — and stay unconditional pending issue #94; the `httpx2` dev-extra comment clarifies it's a real pydantic package, not a typo) and annotated two currently-unreachable missing-boto3 code paths in `providers/aws.py` rather than deleting them. Removed the phantom `remo init` command from every doc surface (README, docs/aws.md, install.sh, CONTRIBUTING.md) — Ansible collections install automatically on first provider command via `core/ansible_runner.py::_ensure_collections`. Corrected this file's structure diagram, Commands, and Active Technologies sections to match the real tree (removed two phantom entries, documented 13 previously-undocumented modules, dropped two nonexistent `--extra` install commands) and rewrote `AGENTS.md`, which had described an unrelated project. Added `tests/unit/test_docs_structure.py`, a new CI-gating pytest module (stdlib-only, no new dependency) that parses both files' structure diagrams and fails the build on drift, naming the offending paths; `docs/maintaining-claude-md.md` documents the fix procedure. Deleted the dead `providers/proxmox.py::_parse_pct_json` (zero call sites) and its orphaned `import json`. Consolidated all four of `providers/hetzner.py`'s hand-rolled `urllib.request.Request` call sites onto the module's existing `_hetzner_api()` helper, preserving each site's distinct raise-vs-swallow/timeout/message-text contract exactly (`tests/unit/providers/test_hetzner_http.py` characterizes and pins the behavior). **Breaking change**: removed the `--yes`/`-y` flag from `remo <provider> create` on all four providers — it never had any effect (creation has no confirmation prompt); `--yes` is unchanged on `destroy`, `sync`, `snapshot restore`, `snapshot delete`, and `remo remove`.
321
325
  Only the newest three entries live here — `update-agent-context.sh` discards the rest on every run. The complete history is archived in [`docs/feature-history.md`](docs/feature-history.md); move displaced entries there rather than letting the generator drop them.
322
326
 
323
327
 
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.3.1](https://github.com/get2knowio/remo/compare/v4.3.0...v4.3.1) (2026-08-05)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * three defects found while building `remo configure` ([#144](https://github.com/get2knowio/remo/issues/144)) ([608a914](https://github.com/get2knowio/remo/commit/608a9146560b9e1bf0e45811ce76a0c517dafa45))
9
+
10
+ ## [4.3.0](https://github.com/get2knowio/remo/compare/v4.2.0...v4.3.0) (2026-08-05)
11
+
12
+
13
+ ### Features
14
+
15
+ * **cli:** configure manually-added SSH hosts with `remo configure` ([#143](https://github.com/get2knowio/remo/issues/143)) ([4a34f70](https://github.com/get2knowio/remo/commit/4a34f70674f9afd85f92529ac119cc97a015f3a0))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **web:** stop three light themes printing invisible text ([#141](https://github.com/get2knowio/remo/issues/141)) ([a1c2bea](https://github.com/get2knowio/remo/commit/a1c2bea06c13503fe6c623f2f18483c862c20b46))
21
+
3
22
  ## [4.2.0](https://github.com/get2knowio/remo/compare/v4.1.0...v4.2.0) (2026-08-05)
4
23
 
5
24
 
@@ -31,6 +31,7 @@ Principles I, IV, and VIII are machine-enforced — see [Quality Gates](#quality
31
31
  - `core/registry.py`: stdlib `json` (format), `fcntl` (advisory locking via a `registry.lock` sidecar), `os.replace` (atomic writes). No new runtime deps. Setup API mirror payload moved to v2 (`contracts/mirror-payload-v2.md`) with a `payload_versions` capability handshake; an upgraded service still accepts v1 payloads. (015-registry-v2)
32
32
  - `core/reconcile.py`: provider-agnostic sync-reconcile engine (`SyncScope`, `DiscoveredHost`, `ProbeResult`, `build_plan` (pure), `render_plan`, the consent gate, `apply_plan` via the existing `mutate_registry()`, and the `run_sync` driver). No new runtime deps — stdlib only, built on `core/registry.py`/`core/output.py` as-is. (016-sync-reconcile)
33
33
  - `core/web_adopt.py` is now the single unified push engine (`run_push`; `run_adopt` is a thin deprecated alias) — first push adopts, later pushes re-sync, plus best-effort `remo-web@` revocation on removal, `--force` full re-authorization, and multi-workstation flap detection against the service's mirror-generation marker. `core/web_drift.py`: new stdlib-only offline registry-vs-push-cache diff (`diff_registry_against_cache`, `select_deployment`, `render_drift`) powering `remo web status` and the shared `out_of_date_notice()`/`emit_out_of_date_notice()` post-mutation nudge (importable without the `web` extra). Push cache bumped to `cache_version: 3` (`~/.config/remo/web-service.json`: per-deployment `{mirror_generation, instances}`, each instance retaining a non-secret connection tuple for revocation). Service side: `web/api/setup.py` writes/serves a `web-identity/mirror-meta.json` marker (generation + last-push descriptor) additively on `/setup/{status,registry}`; `web/state.py` mode detection fixed so a personal `~/.ssh/id_*` no longer forces `mount_configured` (non-writable `REMO_HOME` is the authoritative signal) with a new `REMO_WEB_MODE` override in `web/config.py`. No new runtime deps; no registry schema change. (017-web-adopt-simplify)
34
+ - No new runtime deps and no registry schema change. `ansible/ssh_configure.yml` is the first playbook to plumb an SSH **port** at all (`ansible_port` appeared nowhere in the repo before) and the first to take an identity from the registry rather than hardcoding `~/.ssh/id_rsa`; both arrive as namespaced `remo_ssh_*` extra-vars so no `ansible_*` name is ever emitted at extra-var precedence. `core/known_hosts.py` gains `guard_added_ssh_host_only`; `core/web_adopt.py` gains `known_hosts_lookup_key` and a `port=` parameter on `scan_and_verify_host_key`. Host key checking is inherited from `ansible.cfg` (disabled repo-wide) — a play-level override is impossible, since `ssh_common_args` is appended after `ssh_args` and ssh honours the first occurrence. (022-configure-added-hosts)
34
35
  - No new runtime deps or registry schema change. `pyproject.toml`'s existing `hcloud`/`boto3`/`httpx2` entries gained consumer-attribution comments; `tests/unit/test_docs_structure.py` is a new stdlib-only pytest module (parses the structure diagram below, no new dependency). (019-hygiene-deps-docs)
35
36
  - No new runtime deps; no registry schema change (the `host_user`/`node_user` JSON keys already matched the new flag spellings). Superseded for Proxmox by the `--node-user` → `--host-user` rename: both host-scoped providers now spell the node/host login `--host-user`/`host_user` on the CLI, in the provider kwargs and in `registry.json`; `core/registry.py` still reads a legacy `proxmox.node_user` key so pre-rename registries load unchanged and migrate on the next write. `core/provider_registry.py` gains `ArgumentSpec` (positional-argument metadata) and `CommandSpec.target`; `ProviderDescriptor.update_options` is replaced by `upgrade_options`/`resize_dimensions`/`resize_options`/`tag_options`/`host_commands`. `cli/providers/factory.py` gains `_build_upgrade`/`_build_resize`/`_build_tag`/`_build_host_group` (dropping `_build_update` and flat-mounted `bootstrap`). (021-cli-plane-separation)
36
37
 
@@ -48,7 +49,7 @@ src/remo_cli/ # Python CLI package (src layout, hatchling build)
48
49
  │ ├── main.py # Root CLI group; mounts one group per remo_cli.core.provider_registry.all_descriptors()
49
50
  │ ├── shell.py # remo shell — registry-dispatched update_entry() (delegates to upgrade()); prompt names the exact `remo <type> upgrade <name>` it will run; unknown type/ssh handled explicitly (no silent no-op)
50
51
  │ ├── cp.py # remo cp
51
- │ ├── added.py # remo add / remo remove — provider-neutral SSH host registration (feature 014)
52
+ │ ├── added.py # remo add / remo remove / remo configure — provider-neutral SSH host registration (014) and configure (022)
52
53
  │ ├── web.py # remo web {serve,check,push,status,adopt} — serve/check lazy-import remo_cli.web.* (NFR-008); push/status/adopt use core/web_adopt + core/web_drift only (adopt is a deprecated alias for push)
53
54
  │ └── providers/
54
55
  │ └── factory.py # build_provider_group(descriptor) generates create/destroy/upgrade/resize/list/info/sync/snapshot for every provider, plus tag (iff supports_managed_marker) and a host subgroup (iff host_commands non-empty) — the four hand-written per-provider CLI modules are gone
@@ -61,7 +62,7 @@ src/remo_cli/ # Python CLI package (src layout, hatchling build)
61
62
  │ ├── hetzner_descriptor.py # metadata-only ProviderDescriptor declaration, no SDK imports
62
63
  │ ├── aws_descriptor.py # metadata-only ProviderDescriptor declaration, no SDK imports
63
64
  │ ├── proxmox_descriptor.py # metadata-only ProviderDescriptor declaration, no SDK imports
64
- │ ├── added.py # Business logic for remo add / remo remove — provider-neutral SSH host registration (feature 014)
65
+ │ ├── added.py # Business logic for remo add / remo remove / remo configure — registration (014) + generic configure play (022)
65
66
  │ └── builtin.py # registers the four built-in descriptors; lazily auto-imported by provider_registry on first lookup
66
67
  ├── core/ # Shared utilities (no provider knowledge)
67
68
  │ ├── config.py # REMO_HOME, paths, read-only registry accessor
@@ -134,6 +135,9 @@ ansible/ # Ansible playbooks (invoked by Python via subprocess
134
135
  │ └── templates/
135
136
  │ └── remo-host.sh.j2 # Versioned `remo-host` command (capabilities/sessions/attach)
136
137
  ├── incus_bootstrap.yml
138
+ ├── ssh_configure.yml # Generic configure play for `remo add` hosts (022): maps remo_ssh_*
139
+ │ # -> ansible_port/ansible_ssh_private_key_file, binds remo_user to the
140
+ │ # registered account, reuses tasks/configure_dev_tools.yml
137
141
  └── requirements.yml
138
142
 
139
143
  scripts/ # Repo-root utility scripts (not part of the installed package)
@@ -316,9 +320,9 @@ checking nothing.
316
320
  - Ansible 2.14+ / YAML: Follow standard conventions plus Constitution principles
317
321
 
318
322
  ## Recent Changes
323
+ - 022-configure-added-hosts: Made manually-added SSH hosts first-class by giving them the one thing `remo add` (014) never did — a configure path. New provider-neutral `remo configure NAME` runs a new generic `ansible/ssh_configure.yml` against a registry `type="ssh"` entry, so an added host finally gets `remo-host`/zellij/`project-launch` and stops sitting in the `remo web` rail permanently badged `no_remo_host` with zero session targets. **No refactor of the four provider plays**: the role list was already shared via `tasks/configure_dev_tools.yml`, so a fifth playbook inherits the whole toolchain — the change is purely additive. The new play is two plays: a `localhost` play that asserts (never defaults) `remo_ssh_host`/`remo_ssh_user`/`remo_ssh_port` and `add_host`s them onto `ansible_host`/`ansible_user`/`ansible_port`/`ansible_ssh_private_key_file`, then a play targeting that named group (NOT `hosts: all`, which would pick up `inventory/hosts.yml`'s `hetzner_server` and whose zero-host case exits rc 0 — a silent false success). Four decisions the tests pin, each defending a *silent* failure: the identity is `| default(omit)`, never `'~/.ssh/id_rsa'` (aws_configure.yml's anti-pattern — a path default offers the WRONG key); it is paired with an explicit `-o IdentitiesOnly=yes`, which ansible-core does not add for `private_key_file` (without it a busy agent hits MaxAuthTries and dies "Too many authentication failures" while the passed key is never tried); both `raw` pre-flight probes (python3, `sudo -n true`) set `become: false`, since `ansible.cfg` sets `become = True` globally and a sudo probe run THROUGH sudo proves nothing; and `remo_user` is bound to `remo_ssh_user` rather than `group_vars/all.yml`'s `"remo"` — everything `user_setup` installs lands in `/home/{{ remo_user }}/.local/bin` and discovery logs in as the REGISTERED user, so the default would install `remo-host` into a second account and leave the console still reporting `no_remo_host` after a "successful" configure (it also skips the UID-1000 displacement, which only fires when 1000's holder is someone else). Root is refused (UID-1000 pin), the prompt defaults to no, and remo never reboots a host it did not provision — it reports via the task NAME, since `_filter_line` suppresses `debug` bodies. `core/known_hosts.py` gains `guard_added_ssh_host_only`, the mirror of `guard_not_added_ssh_host`: a provider host is refused naming `remo <type> upgrade NAME`, because the five configure plays are not interchangeable and a mis-target would succeed while configuring wrongly. **Bug fix in the same change** (`core/web_adopt.py`): `remo web push` hardcoded port 22 in `scan_and_verify_host_key`'s `ssh-keyscan` and `_lookup_trusted_keys`' `ssh-keygen -F`, so an added host on another port was reported `skipped/unreachable` — `authorize_service_key` never ran — with a remediation blaming a host that was reachable, or, worse, a different machine answering on 22 had its keys pushed. New `known_hosts_lookup_key()` follows OpenSSH's record form (bare for 22, `[host]:port` otherwise; verified against the real binary — neither direction is forgiving), and `-p` is omitted for 22 so every provider host's argv is byte-identical. `providers/added.py`'s `_reject_unsafe_field` now also rejects Jinja delimiters (registry values become extra-vars, which Ansible templates on the CONTROL NODE), re-checked on the configure read path so pre-existing entries are covered. `web/discovery.py`'s `no_remo_host` remediation now names the real command per host type instead of "re-run configure", which for an added host pointed at nothing. No registry schema change; no new runtime deps.
319
324
  - 021-cli-plane-separation: Split the three-intent `remo <provider> update` verb into three single-intent verbs — `upgrade` (in-instance configure play only, zero provider-side writes, SC-001), `resize` (resource change only — the factory rejects a dimensionless invocation with a `PreconditionError` listing that provider's dimension flags, before dispatching to the provider), and `tag` (managed-marker write only, generated iff `supports_managed_marker`; read-before-write no-op when already tagged, `OperationFailedError` — not `create`'s warn-and-continue — on write failure, SC-002) — and moved `bootstrap` off the flat instance plane into a new descriptor-driven `host` subgroup (`remo {incus,proxmox} host bootstrap`, generated iff `host_commands` is non-empty; `CommandSpec` gained a `target: ArgumentSpec | None` field so host commands take the host positionally — incus optional/defaults to localhost, proxmox required). `core/provider_registry.py`: `ProviderDescriptor.update_options` removed, replaced by `upgrade_options`/`resize_dimensions`/`resize_options`/`tag_options`/`host_commands`; new `ArgumentSpec` dataclass. `cli/providers/factory.py`: `_build_update` replaced by `_build_upgrade`/`_build_resize`/`_build_tag`/`_build_host_group`; `_instance_argument` grew a `param` kwarg so the new verbs take `NAME` positionally (matching the provider impl's `name` kwarg) while `create`/`destroy`/`info` keep `--name`; `_resolve_entry_for_destroy`'s user-hint lookup is now descriptor-driven (reads `kwargs` under the `registry_fields` JSON key ending in `_user`) instead of a `"user"` literal. Every provider module split `update()` into `upgrade()`/`resize()`/`tag()` (marker-supporting providers only) reusing existing private helpers unchanged; `update_entry()` (the `remo shell` tools-update path) now delegates to `upgrade()`. Renamed `--user` → `--host-user` (incus) / `--node-user` (proxmox) everywhere it appears (`remo add --user`, a different flag, is unchanged). Made every printed remedy truthful (SC-003): the registry-migration tagging notice and `sync`'s "Mark permanently:" line now recommend `tag` (previously wrongly recommended `sync`/`update`, which never wrote the marker); `remo shell`'s version-mismatch prompt now names the exact `remo <type> upgrade <name>` command it will run. Breaking change, no shim, no registry schema change: `remo <type> update`, flat `bootstrap`, and the old `--user` spelling are all Click "unknown command"/"no such option" errors post-change. FakeProvider's descriptor gained `upgrade_options`/`resize_dimensions`/`tag_options`/one `host_commands` entry, proving the full new surface is descriptor-only (SC-005).
320
325
  - 020-openapi-type-generation: Made the FastAPI service the machine-checked source of truth for every shape the browser console consumes, and made drift a build failure with an actionable message — three moves, in dependency order. **(1) Contract completeness, declaration-only** (not one serialized byte moves): `web/api/hosts.py` annotates `InstanceOut.status`/`SessionTargetOut.zellij_state`/`devcontainer_running` with the existing closed domain enums (dropping the `.value` unwrapping — a `str`-Enum serializes identically) and adds `KnownProviderType(str, Enum)` (`incus`/`hetzner`/`aws`/`proxmox`, fixed to the built-in set per FR-004a — never derived from the live `provider_registry`, so a third-party provider install can't perturb the artifact) typed `KnownProviderType | str` (`anyOf[$ref, string]`, keeping the wire field open); new `ErrorEnvelope`/`HealthResponse`/`ReadinessResponse`/`MintPairingResponse`/`DetailResponse` response models declare what four previously-untyped routes (`/health`, `/ready`, `/pairing/mint`, `POST /terminals`) already return, with the `{"error": {...}}` envelope declared only on routes that actually return it (never on `pairing.py`, whose 403 is `{"detail": ...}`). **(2) Generate and consume**: new stdlib `scripts/export_openapi.py` exports `frontend/src/api/generated/openapi.json` (`create_app().openapi()`, `sort_keys=True` + trailing newline for byte-reproducibility) and `terminal-frames.json` (a new `web/frames.py`: six Pydantic control-frame models + `InboundFrame`/`OutboundFrame` discriminated unions, wrapped via `TypeAdapter(...).json_schema()` in a `{protocol: "remo-terminal.v1", frame_version: 1, ...}` envelope that versions independently of the REST contract); `openapi-typescript` v7 (exact-pinned frontend devDependency) generates `schema.d.ts`, and a small synthetic-OpenAPI wrapper (`frontend/scripts/generate-frame-types.mjs`) generates `terminal-frames.d.ts` (openapi-typescript itself only accepts genuine OpenAPI documents). `web/api/terminals.py`'s five ad-hoc `{"v": 1, ...}` WS control-frame dict literals are gone, replaced by typed `frames.py` model construction/validation — `_handle_control`'s silent-drop behavior for malformed JSON, non-object payloads, and unknown frame types is preserved exactly (the one place this refactor could have changed runtime behavior; dedicated tests cover it). `frontend/src/api/client.ts` replaced ~12 hand-declared service-shaped interfaces with `components["schemas"][...]` aliases (console-owned shapes — `ApiError`, `ServiceStatus`, the forward-auth re-auth path — stay hand-written); `frontend/src/components/providerMeta.ts` now keys a `Record<InstanceStatus | KnownProviderType, ...>` off the generated unions, so a new enum member is a compile error at the presentation mapping while an off-union runtime value still renders via a preserved fallback (FR-013a — deleting that fallback to "achieve exhaustiveness" is explicitly wrong). **(3) Gate it**: three drift checks modeled on `tests/unit/test_docs_structure.py`'s established shape (name the artifact, group findings by kind, close with the exact regeneration command and a dependency-bump note) — `tests/unit/test_schema_drift.py` (REST + frame freshness against the live Python app, `test` CI job) and `frontend/scripts/check-types-fresh.mjs` (regenerated `.d.ts` vs. checked-in, `frontend` CI job) — fail, never skip, and never write a tracked file. New `docs/maintaining-generated-types.md` documents the four generated artifacts (never hand-edited; internal build input, no external compatibility promise, FR-029) and how to read a failure. No registry schema change; no new service runtime dependency.
321
- - 019-hygiene-deps-docs: Repository hygiene pass across dependencies, dead code, and documentation — no behavior change except one deliberate CLI break. Annotated every dependency whose necessity isn't visible from `src/remo_cli/` imports alone (`hcloud`/`boto3` in `pyproject.toml` now name their real consumers — the Ansible `hetzner.hcloud`/`amazon.aws`/`community.aws` collections — and stay unconditional pending issue #94; the `httpx2` dev-extra comment clarifies it's a real pydantic package, not a typo) and annotated two currently-unreachable missing-boto3 code paths in `providers/aws.py` rather than deleting them. Removed the phantom `remo init` command from every doc surface (README, docs/aws.md, install.sh, CONTRIBUTING.md) — Ansible collections install automatically on first provider command via `core/ansible_runner.py::_ensure_collections`. Corrected this file's structure diagram, Commands, and Active Technologies sections to match the real tree (removed two phantom entries, documented 13 previously-undocumented modules, dropped two nonexistent `--extra` install commands) and rewrote `AGENTS.md`, which had described an unrelated project. Added `tests/unit/test_docs_structure.py`, a new CI-gating pytest module (stdlib-only, no new dependency) that parses both files' structure diagrams and fails the build on drift, naming the offending paths; `docs/maintaining-claude-md.md` documents the fix procedure. Deleted the dead `providers/proxmox.py::_parse_pct_json` (zero call sites) and its orphaned `import json`. Consolidated all four of `providers/hetzner.py`'s hand-rolled `urllib.request.Request` call sites onto the module's existing `_hetzner_api()` helper, preserving each site's distinct raise-vs-swallow/timeout/message-text contract exactly (`tests/unit/providers/test_hetzner_http.py` characterizes and pins the behavior). **Breaking change**: removed the `--yes`/`-y` flag from `remo <provider> create` on all four providers — it never had any effect (creation has no confirmation prompt); `--yes` is unchanged on `destroy`, `sync`, `snapshot restore`, `snapshot delete`, and `remo remove`.
322
326
  Only the newest three entries live here — `update-agent-context.sh` discards the rest on every run. The complete history is archived in [`docs/feature-history.md`](docs/feature-history.md); move displaced entries there rather than letting the generator drop them.
323
327
 
324
328
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remo-cli
3
- Version: 4.2.0
3
+ Version: 4.3.1
4
4
  Summary: Remote development environment CLI
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -286,11 +286,63 @@ as any other host.
286
286
 
287
287
  ```bash
288
288
  remo add mybox 10.0.0.5 --port 2222 --identity ~/.ssh/mybox_ed25519 --verify
289
+ remo configure mybox # install the dev tools (see below)
289
290
  remo shell mybox # open a shell over direct SSH
290
291
  remo cp ./deploy.sh mybox:/tmp/ # copy a file up
291
292
  remo remove mybox # deregister when you're done
292
293
  ```
293
294
 
295
+ ### Install the dev tools: `remo configure`
296
+
297
+ `add` only *registers* a host — it installs nothing. Until you configure it, an
298
+ added host has no `remo-host`, no zellij and no project launcher, so `remo web`
299
+ lists it with an **"update req."** badge and no sessions you can open.
300
+
301
+ ```bash
302
+ remo configure NAME [--only TOOL] [--skip TOOL] [--yes] [-v]
303
+ ```
304
+
305
+ This runs the same Ansible play the providers use, so the host ends up with
306
+ Docker, Node.js, zellij, fzf, the GitHub CLI, the devcontainers CLI and
307
+ `remo-host`. It is idempotent — re-run it any time to refresh the tools, which
308
+ is also how you update a host after upgrading `remo` itself.
309
+
310
+ Two things worth knowing before the first run:
311
+
312
+ - It configures **the account you registered** and grants that account
313
+ passwordless sudo. It also apt-upgrades the system. You are asked to confirm
314
+ (defaulting to *no*); `--yes` skips the prompt.
315
+ - The host must be Debian/Ubuntu with `python3` and sudo available. Registering
316
+ as `root` is refused — the workspace account is pinned to UID 1000, which
317
+ would break the host.
318
+
319
+ Unlike the provider `create` path, remo **never reboots** a host it did not
320
+ provision; if package updates need one, it tells you and leaves the decision to
321
+ you. For a provider-managed host use `remo <provider> upgrade NAME` instead —
322
+ `remo configure` refuses those and names the right command.
323
+
324
+ ```bash
325
+ remo configure mybox # first-time setup, or refresh the tools
326
+ remo configure mybox --skip docker # everything except Docker
327
+ remo web push # authorize the web service on it
328
+ ```
329
+
330
+ #### Example: an OrbStack VM
331
+
332
+ [`docs/examples/orbstack-cloud-init.yaml`](docs/examples/orbstack-cloud-init.yaml)
333
+ prepares an [OrbStack](https://orbstack.dev) VM on a Mac so `remo add` and
334
+ `remo configure` work first time:
335
+
336
+ ```bash
337
+ orb create ubuntu remo-mbp -c docs/examples/orbstack-cloud-init.yaml
338
+ ```
339
+
340
+ It installs only what Ansible needs to connect and hand over — `remo configure`
341
+ does the rest. It deliberately creates no user: OrbStack already makes one named
342
+ after your macOS account holding UID 1000, and registering *that* user is what
343
+ puts `remo-host` in the home directory the web service logs into, while skipping
344
+ `user_setup`'s UID-1000 reassignment entirely.
345
+
294
346
  `remo remove NAME [--yes]` deregisters an added host by deleting **only** the
295
347
  local registry entry — it makes no connection to and no change on the remote
296
348
  environment (unlike a provider `destroy`, which tears down infrastructure). It
@@ -323,6 +375,8 @@ remo add NAME [user@]host[:port] # Register a single SSH host
323
375
  remo add NAME host --port 2222 --identity ~/.ssh/key # Custom port + key
324
376
  remo add NAME host --verify # Fail-closed SSH reachability check first
325
377
  remo add NAME host --user alice # Override default SSH user (default: remo)
378
+ remo configure NAME # Install/refresh the dev tools on an added host
379
+ remo configure NAME --skip docker # Skip a tool (repeatable; --only is the inverse)
326
380
  remo remove NAME [--yes] # Deregister an added host (local-only)
327
381
 
328
382
  # Hetzner Cloud
@@ -260,11 +260,63 @@ as any other host.
260
260
 
261
261
  ```bash
262
262
  remo add mybox 10.0.0.5 --port 2222 --identity ~/.ssh/mybox_ed25519 --verify
263
+ remo configure mybox # install the dev tools (see below)
263
264
  remo shell mybox # open a shell over direct SSH
264
265
  remo cp ./deploy.sh mybox:/tmp/ # copy a file up
265
266
  remo remove mybox # deregister when you're done
266
267
  ```
267
268
 
269
+ ### Install the dev tools: `remo configure`
270
+
271
+ `add` only *registers* a host — it installs nothing. Until you configure it, an
272
+ added host has no `remo-host`, no zellij and no project launcher, so `remo web`
273
+ lists it with an **"update req."** badge and no sessions you can open.
274
+
275
+ ```bash
276
+ remo configure NAME [--only TOOL] [--skip TOOL] [--yes] [-v]
277
+ ```
278
+
279
+ This runs the same Ansible play the providers use, so the host ends up with
280
+ Docker, Node.js, zellij, fzf, the GitHub CLI, the devcontainers CLI and
281
+ `remo-host`. It is idempotent — re-run it any time to refresh the tools, which
282
+ is also how you update a host after upgrading `remo` itself.
283
+
284
+ Two things worth knowing before the first run:
285
+
286
+ - It configures **the account you registered** and grants that account
287
+ passwordless sudo. It also apt-upgrades the system. You are asked to confirm
288
+ (defaulting to *no*); `--yes` skips the prompt.
289
+ - The host must be Debian/Ubuntu with `python3` and sudo available. Registering
290
+ as `root` is refused — the workspace account is pinned to UID 1000, which
291
+ would break the host.
292
+
293
+ Unlike the provider `create` path, remo **never reboots** a host it did not
294
+ provision; if package updates need one, it tells you and leaves the decision to
295
+ you. For a provider-managed host use `remo <provider> upgrade NAME` instead —
296
+ `remo configure` refuses those and names the right command.
297
+
298
+ ```bash
299
+ remo configure mybox # first-time setup, or refresh the tools
300
+ remo configure mybox --skip docker # everything except Docker
301
+ remo web push # authorize the web service on it
302
+ ```
303
+
304
+ #### Example: an OrbStack VM
305
+
306
+ [`docs/examples/orbstack-cloud-init.yaml`](docs/examples/orbstack-cloud-init.yaml)
307
+ prepares an [OrbStack](https://orbstack.dev) VM on a Mac so `remo add` and
308
+ `remo configure` work first time:
309
+
310
+ ```bash
311
+ orb create ubuntu remo-mbp -c docs/examples/orbstack-cloud-init.yaml
312
+ ```
313
+
314
+ It installs only what Ansible needs to connect and hand over — `remo configure`
315
+ does the rest. It deliberately creates no user: OrbStack already makes one named
316
+ after your macOS account holding UID 1000, and registering *that* user is what
317
+ puts `remo-host` in the home directory the web service logs into, while skipping
318
+ `user_setup`'s UID-1000 reassignment entirely.
319
+
268
320
  `remo remove NAME [--yes]` deregisters an added host by deleting **only** the
269
321
  local registry entry — it makes no connection to and no change on the remote
270
322
  environment (unlike a provider `destroy`, which tears down infrastructure). It
@@ -297,6 +349,8 @@ remo add NAME [user@]host[:port] # Register a single SSH host
297
349
  remo add NAME host --port 2222 --identity ~/.ssh/key # Custom port + key
298
350
  remo add NAME host --verify # Fail-closed SSH reachability check first
299
351
  remo add NAME host --user alice # Override default SSH user (default: remo)
352
+ remo configure NAME # Install/refresh the dev tools on an added host
353
+ remo configure NAME --skip docker # Skip a tool (repeatable; --only is the inverse)
300
354
  remo remove NAME [--yes] # Deregister an added host (local-only)
301
355
 
302
356
  # Hetzner Cloud
@@ -243,11 +243,25 @@
243
243
  - user_setup_cloud_keys.content | default('') | length > 0
244
244
  # --- end cloud-injected key bootstrap --------------------------------------
245
245
 
246
+ # The docker group only exists if the `docker` role ran. Skipping that role
247
+ # (`--skip docker`, or `configure_docker=false`) previously made the next task
248
+ # fail outright with "Group docker does not exist", taking the whole configure
249
+ # down after it had already changed the host — so `--skip docker` was, in
250
+ # effect, not a supported option on any provider. Gate on the group rather than
251
+ # on `configure_docker` so a host where Docker was installed by other means
252
+ # (or by an earlier run) still gets the membership.
253
+ - name: Check whether a docker group exists
254
+ ansible.builtin.command: getent group docker
255
+ register: user_setup_docker_group
256
+ changed_when: false
257
+ failed_when: false
258
+
246
259
  - name: Add remo user to docker group
247
260
  ansible.builtin.user:
248
261
  name: "{{ remo_user }}"
249
262
  groups: docker
250
263
  append: true
264
+ when: user_setup_docker_group.rc | default(1) == 0
251
265
 
252
266
  - name: Configure passwordless sudo for remo user
253
267
  ansible.builtin.lineinfile:
@@ -0,0 +1,240 @@
1
+ ---
2
+ # Generic SSH Configure Playbook — install/refresh remo's dev tools on a host
3
+ # that NO provider manages (registry type="ssh", registered via `remo add`).
4
+ #
5
+ # Caller: src/remo_cli/providers/added.py::configure (`remo configure NAME`)
6
+ #
7
+ # Usage:
8
+ # ./run.sh ssh_configure.yml \
9
+ # -e remo_ssh_host=10.0.0.5 -e remo_ssh_user=remo -e remo_ssh_port=2222 \
10
+ # -e remo_ssh_identity=~/.ssh/k
11
+ #
12
+ # Required variables (the CLI ALWAYS passes all three; none is defaulted here on
13
+ # purpose — a missing port must fail loudly, never quietly fall back to 22):
14
+ # remo_ssh_host: address of the registered host
15
+ # remo_ssh_user: SSH user, which is ALSO the account this play configures
16
+ # remo_ssh_port: SSH port
17
+ #
18
+ # Optional variables:
19
+ # remo_ssh_identity: private key path. `~` is expanded by the ssh connection
20
+ # plugin (os.path.expanduser), so no client-side expansion is needed.
21
+ # configure_docker / configure_user_setup / configure_nodejs /
22
+ # configure_devcontainers / configure_github_cli / configure_fzf /
23
+ # configure_zellij: per-tool toggles — see tasks/configure_dev_tools.yml
24
+ #
25
+ # Host key checking is INHERITED from ansible.cfg, which disables it repo-wide
26
+ # (host_key_checking=False AND StrictHostKeyChecking=no in ssh_args). That is a
27
+ # deliberate inheritance, not an oversight: ssh_common_args is appended AFTER
28
+ # ssh_args and ssh honours the first occurrence of an option, so a play-level
29
+ # override here would be silently ignored. Tightening it is a repo-wide change
30
+ # to ansible.cfg across every playbook, not a per-play special case.
31
+
32
+ - name: Build the inventory for the registered SSH host
33
+ hosts: localhost
34
+ connection: local
35
+ gather_facts: false
36
+ become: false
37
+
38
+ tasks:
39
+ - name: Validate the connection variables supplied by the CLI
40
+ ansible.builtin.assert:
41
+ that:
42
+ - remo_ssh_host is defined and (remo_ssh_host | string | length) > 0
43
+ - remo_ssh_user is defined and (remo_ssh_user | string | length) > 0
44
+ - remo_ssh_port is defined
45
+ - (remo_ssh_port | int) > 0
46
+ - (remo_ssh_port | int) < 65536
47
+ fail_msg: >-
48
+ ssh_configure.yml needs remo_ssh_host, remo_ssh_user and remo_ssh_port.
49
+ Run it through 'remo configure <name>' rather than directly.
50
+ quiet: true
51
+
52
+ - name: Refuse to reconfigure the root account
53
+ ansible.builtin.assert:
54
+ that:
55
+ - remo_ssh_user != 'root'
56
+ fail_msg: >-
57
+ remo configures the SSH user you registered as the workspace account,
58
+ and user_setup pins that account to UID 1000 — doing that to root
59
+ would break the host. Re-register with a normal user:
60
+ remo add <name> {{ remo_ssh_host | default('<host>') }} --user <user>
61
+ quiet: true
62
+
63
+ # Fixed, colon-free name: add_host runs its `name` through parse_address(),
64
+ # so an IP-with-port name would set ansible_ssh_port behind our back.
65
+ - name: Add the registered SSH host to the in-memory inventory
66
+ ansible.builtin.add_host:
67
+ name: remo_ssh_target
68
+ groups:
69
+ - remo_ssh_hosts
70
+ ansible_host: "{{ remo_ssh_host }}"
71
+ ansible_user: "{{ remo_ssh_user }}"
72
+ ansible_port: "{{ remo_ssh_port | int }}"
73
+ ansible_python_interpreter: /usr/bin/python3
74
+ # `omit`, NOT a '~/.ssh/id_rsa' fallback. "No identity stored" means
75
+ # "let ssh/agent resolve the key"; defaulting to a path here is exactly
76
+ # how aws_configure.yml can silently offer the WRONG key.
77
+ ansible_ssh_private_key_file: "{{ remo_ssh_identity | default(omit) }}"
78
+ # ansible-core emits `-o IdentityFile=` for private_key_file but NOT
79
+ # `-o IdentitiesOnly=yes` (that pairing exists only on the `private_key`
80
+ # branch of connection/ssh.py). Without this, ssh offers every agent key
81
+ # first and a busy agent hits MaxAuthTries — "Too many authentication
82
+ # failures" — while the key you passed is never tried. core/ssh.py pairs
83
+ # them for this exact reason; match it.
84
+ ansible_ssh_common_args: >-
85
+ {{ '-o IdentitiesOnly=yes'
86
+ if (remo_ssh_identity | default('') | length) > 0
87
+ else omit }}
88
+
89
+ - name: Configure a manually-registered SSH host with development tools
90
+ hosts: remo_ssh_hosts
91
+ become: true
92
+ # Facts are gathered explicitly in pre_tasks, AFTER the interpreter probe: the
93
+ # implicit gather runs a Python module, so on a host with no python3 it fails
94
+ # first, with a message that never names the actual problem.
95
+ gather_facts: false
96
+
97
+ vars:
98
+ # The account remo configures IS the account you registered. Everything
99
+ # user_setup installs lands in /home/{{ remo_user }}/.local/bin — including
100
+ # remo-host, which `remo web` discovery invokes over SSH as the REGISTERED
101
+ # user. Inventing a second 'remo' user here would leave discovery reporting
102
+ # no_remo_host after a "successful" configure. For the default `remo add`
103
+ # user this is a no-op: remo_user is 'remo' either way. It also skips the
104
+ # UID-1000 displacement in user_setup, which only fires when the holder of
105
+ # UID 1000 is someone other than remo_user.
106
+ remo_user: "{{ remo_ssh_user }}"
107
+ # No cloud provider injected a key on this host, so there is nothing to
108
+ # bootstrap FROM — and root's authorized_keys on a machine remo does not own
109
+ # belongs to its owner, not to a workspace account (#121). With remo_user
110
+ # set above, the connecting user's own authorized_keys is already non-empty,
111
+ # so the first-contact gate would decline anyway; false makes it structural.
112
+ user_setup_bootstrap_cloud_keys: false
113
+
114
+ pre_tasks:
115
+ # `raw` needs no interpreter on the target, so it can diagnose the absence
116
+ # of one. `become: false` is mandatory on both probes: ansible.cfg sets
117
+ # become=True globally, so without it they run THROUGH sudo and prove
118
+ # nothing — the sudo probe would report success on a host where the user
119
+ # cannot in fact sudo, because the connection itself would have failed.
120
+ - name: Probe for a Python 3 interpreter
121
+ ansible.builtin.raw: command -v python3
122
+ become: false
123
+ register: remo_python_probe
124
+ changed_when: false
125
+ failed_when: false
126
+
127
+ - name: Fail with actionable guidance when the host has no Python 3
128
+ ansible.builtin.fail:
129
+ msg: >-
130
+ No python3 on {{ ansible_host }}:{{ ansible_port }} — Ansible needs a
131
+ Python 3 interpreter on the target. Install it and retry:
132
+ ssh -p {{ ansible_port }} {{ ansible_user }}@{{ ansible_host }}
133
+ 'sudo apt-get update && sudo apt-get install -y python3'
134
+ when: (remo_python_probe.rc | default(1)) != 0
135
+
136
+ - name: Probe for passwordless sudo
137
+ ansible.builtin.raw: sudo -n true
138
+ become: false
139
+ register: remo_sudo_probe
140
+ changed_when: false
141
+ failed_when: false
142
+
143
+ - name: Fail with actionable guidance when the SSH user cannot sudo
144
+ ansible.builtin.fail:
145
+ msg: >-
146
+ '{{ ansible_user }}' cannot sudo without a password on
147
+ {{ ansible_host }}. remo installs packages and system services, so it
148
+ needs passwordless sudo. Grant it once, then retry:
149
+ echo '{{ ansible_user }} ALL=(ALL) NOPASSWD:ALL' | sudo tee
150
+ /etc/sudoers.d/{{ ansible_user }} && sudo chmod 440
151
+ /etc/sudoers.d/{{ ansible_user }}
152
+ when: (remo_sudo_probe.rc | default(1)) != 0
153
+
154
+ - name: Gather facts
155
+ ansible.builtin.setup:
156
+
157
+ - name: Fail on a non-apt host before any role starts changing it
158
+ ansible.builtin.assert:
159
+ that:
160
+ - ansible_os_family | default('') == 'Debian'
161
+ fail_msg: >-
162
+ {{ ansible_host }} runs {{ ansible_distribution | default('an unknown
163
+ distribution') }} {{ ansible_distribution_version | default('') }};
164
+ every remo role is apt-based (Ubuntu and Debian are supported).
165
+ Nothing was changed.
166
+ quiet: true
167
+
168
+ - name: Wait for apt to be ready
169
+ ansible.builtin.shell: |
170
+ while fuser /var/lib/dpkg/lock-frontend /var/lib/apt/lists/lock /var/cache/apt/archives/lock >/dev/null 2>&1; do
171
+ sleep 5
172
+ done
173
+ args:
174
+ executable: /bin/bash
175
+ register: apt_lock_result
176
+ changed_when: false
177
+ retries: 30
178
+ delay: 10
179
+ until: apt_lock_result.rc | default(1) == 0
180
+
181
+ # Unwedge apt before the first apt call: a sources line naming a keyring
182
+ # that no longer exists makes apt refuse to read ANY source list, which
183
+ # would fail the cache update below and leave the host unrepairable (#120).
184
+ - name: Repair apt sources referencing a missing keyring
185
+ ansible.builtin.include_tasks: tasks/repair_apt_sources.yml
186
+
187
+ - name: Update apt cache
188
+ ansible.builtin.apt:
189
+ update_cache: true
190
+ cache_valid_time: 3600
191
+
192
+ - name: Upgrade all packages
193
+ ansible.builtin.apt:
194
+ upgrade: dist
195
+ autoremove: true
196
+
197
+ - name: Check if reboot is required
198
+ ansible.builtin.stat:
199
+ path: /var/run/reboot-required
200
+ register: reboot_required_file
201
+
202
+ tasks:
203
+ - name: Configure dev tools
204
+ ansible.builtin.include_tasks: tasks/configure_dev_tools.yml
205
+
206
+ post_tasks:
207
+ # Deliberately NOT ansible.builtin.reboot: hetzner/aws reboot because remo
208
+ # created and solely owns those VMs. This host is the user's own machine and
209
+ # may be running their other work; remo has no API to bring it back, and no
210
+ # way to notice if it doesn't. Report and let them choose.
211
+ #
212
+ # The message lives in the task NAME, not the debug body: the non-verbose
213
+ # output filter (core/ansible_runner.py::_filter_line) suppresses debug msg
214
+ # bodies entirely but does print task names — and clears them on `skipping:`,
215
+ # so this line appears exactly when a reboot really is pending.
216
+ - name: "Reboot pending — remo did NOT reboot this host (it is yours, not remo-provisioned); run 'sudo reboot' when convenient"
217
+ ansible.builtin.debug:
218
+ msg: >-
219
+ Package updates need a reboot:
220
+ ssh -p {{ ansible_port }} {{ ansible_user }}@{{ ansible_host }} 'sudo reboot'
221
+ when: reboot_required_file.stat.exists | default(false)
222
+
223
+ - name: Display completion message
224
+ ansible.builtin.debug:
225
+ msg: |
226
+ Host configuration complete!
227
+
228
+ Configured account: {{ remo_user }}
229
+ Workspace: {{ dev_workspace_dir }}
230
+
231
+ Installed:
232
+ - Docker (with docker-compose plugin)
233
+ - User '{{ remo_user }}' with sudo NOPASSWD access and docker group
234
+ - Node.js LTS (v{{ nodejs_version }})
235
+ - fzf, @devcontainers/cli, GitHub CLI (gh), Zellij
236
+ - remo-host, which is what makes this host appear in 'remo web'
237
+
238
+ Connect with:
239
+ remo shell
240
+ ssh -p {{ ansible_port }} {{ ansible_user }}@{{ ansible_host }}