remo-cli 4.1.0__tar.gz → 4.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (643) hide show
  1. remo_cli-4.3.0/.release-please-manifest.json +3 -0
  2. {remo_cli-4.1.0 → remo_cli-4.3.0}/AGENTS.md +7 -3
  3. {remo_cli-4.1.0 → remo_cli-4.3.0}/CHANGELOG.md +21 -0
  4. {remo_cli-4.1.0 → remo_cli-4.3.0}/CLAUDE.md +7 -3
  5. {remo_cli-4.1.0 → remo_cli-4.3.0}/PKG-INFO +39 -1
  6. {remo_cli-4.1.0 → remo_cli-4.3.0}/README.md +38 -0
  7. remo_cli-4.3.0/ansible/ssh_configure.yml +240 -0
  8. {remo_cli-4.1.0 → remo_cli-4.3.0}/docs/web-session-interface.md +27 -6
  9. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/AppShell.tsx +3 -4
  10. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/SettingsPage.tsx +30 -0
  11. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/TerminalCard.tsx +328 -240
  12. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/TopBar.css +8 -0
  13. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/TopBar.tsx +18 -28
  14. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/WorkspacePane.test.tsx +96 -0
  15. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/WorkspacePane.tsx +38 -4
  16. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/masterLayout.test.ts +14 -13
  17. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/masterLayout.ts +7 -1
  18. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/state/settings.test.ts +50 -12
  19. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/state/settings.ts +56 -6
  20. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/state/workspace.test.ts +10 -16
  21. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/state/workspace.ts +15 -20
  22. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/theme/terminalThemes.test.ts +38 -0
  23. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/theme/terminalThemes.ts +28 -5
  24. {remo_cli-4.1.0 → remo_cli-4.3.0}/pyproject.toml +1 -1
  25. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/014-register-ssh-host/spec.md +3 -0
  26. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/cli/added.py +71 -0
  27. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/cli/main.py +2 -1
  28. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/known_hosts.py +51 -0
  29. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/web_adopt.py +44 -9
  30. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/providers/added.py +135 -1
  31. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/discovery.py +25 -2
  32. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/ansible/test_apt_sources_repair.py +1 -0
  33. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/ansible/test_authorized_keys_bootstrap.py +6 -2
  34. remo_cli-4.3.0/tests/ansible/test_ssh_configure_connection.py +203 -0
  35. remo_cli-4.3.0/tests/unit/cli/test_configure_cmd.py +115 -0
  36. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/test_main.py +1 -0
  37. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_web_adopt_trust.py +156 -1
  38. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_added_add.py +30 -0
  39. remo_cli-4.3.0/tests/unit/providers/test_added_configure.py +226 -0
  40. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_added_provider_guard.py +79 -1
  41. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_discovery.py +44 -1
  42. {remo_cli-4.1.0 → remo_cli-4.3.0}/uv.lock +1 -1
  43. remo_cli-4.1.0/.release-please-manifest.json +0 -3
  44. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/release/SKILL.md +0 -0
  45. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-analyze/SKILL.md +0 -0
  46. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-checklist/SKILL.md +0 -0
  47. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-clarify/SKILL.md +0 -0
  48. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-constitution/SKILL.md +0 -0
  49. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-converge/SKILL.md +0 -0
  50. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-git-commit/SKILL.md +0 -0
  51. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-git-feature/SKILL.md +0 -0
  52. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-git-initialize/SKILL.md +0 -0
  53. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-git-remote/SKILL.md +0 -0
  54. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-git-validate/SKILL.md +0 -0
  55. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-implement/SKILL.md +0 -0
  56. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-plan/SKILL.md +0 -0
  57. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-specify/SKILL.md +0 -0
  58. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-tasks/SKILL.md +0 -0
  59. {remo_cli-4.1.0 → remo_cli-4.3.0}/.claude/skills/speckit-taskstoissues/SKILL.md +0 -0
  60. {remo_cli-4.1.0 → remo_cli-4.3.0}/.devcontainer/devcontainer.json +0 -0
  61. {remo_cli-4.1.0 → remo_cli-4.3.0}/.dockerignore +0 -0
  62. {remo_cli-4.1.0 → remo_cli-4.3.0}/.github/CODEOWNERS +0 -0
  63. {remo_cli-4.1.0 → remo_cli-4.3.0}/.github/dependabot.yml +0 -0
  64. {remo_cli-4.1.0 → remo_cli-4.3.0}/.github/workflows/ci.yml +0 -0
  65. {remo_cli-4.1.0 → remo_cli-4.3.0}/.github/workflows/codeql.yml +0 -0
  66. {remo_cli-4.1.0 → remo_cli-4.3.0}/.github/workflows/dependency-review.yml +0 -0
  67. {remo_cli-4.1.0 → remo_cli-4.3.0}/.github/workflows/dev-build.yml +0 -0
  68. {remo_cli-4.1.0 → remo_cli-4.3.0}/.github/workflows/provision.yml +0 -0
  69. {remo_cli-4.1.0 → remo_cli-4.3.0}/.github/workflows/release-please.yml +0 -0
  70. {remo_cli-4.1.0 → remo_cli-4.3.0}/.github/workflows/release.yml +0 -0
  71. {remo_cli-4.1.0 → remo_cli-4.3.0}/.github/workflows/smoke-test-cleanup.yml +0 -0
  72. {remo_cli-4.1.0 → remo_cli-4.3.0}/.github/workflows/smoke-test.yml +0 -0
  73. {remo_cli-4.1.0 → remo_cli-4.3.0}/.github/workflows/teardown.yml +0 -0
  74. {remo_cli-4.1.0 → remo_cli-4.3.0}/.gitignore +0 -0
  75. {remo_cli-4.1.0 → remo_cli-4.3.0}/.maverick/checkpoints/feature/implementation_complete.json +0 -0
  76. {remo_cli-4.1.0 → remo_cli-4.3.0}/.maverick/checkpoints/feature/phase_${{ index }}_complete.json +0 -0
  77. {remo_cli-4.1.0 → remo_cli-4.3.0}/.maverick/checkpoints/feature/validation_complete.json +0 -0
  78. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/.registry +0 -0
  79. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/README.md +0 -0
  80. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/commands/speckit.git.commit.md +0 -0
  81. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/commands/speckit.git.feature.md +0 -0
  82. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/commands/speckit.git.initialize.md +0 -0
  83. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/commands/speckit.git.remote.md +0 -0
  84. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/commands/speckit.git.validate.md +0 -0
  85. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/config-template.yml +0 -0
  86. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/extension.yml +0 -0
  87. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/git-config.yml +0 -0
  88. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/scripts/bash/auto-commit.sh +0 -0
  89. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/scripts/bash/create-new-feature.sh +0 -0
  90. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/scripts/bash/git-common.sh +0 -0
  91. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/scripts/bash/initialize-repo.sh +0 -0
  92. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
  93. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/scripts/powershell/create-new-feature.ps1 +0 -0
  94. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/scripts/powershell/git-common.ps1 +0 -0
  95. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
  96. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/extensions.yml +0 -0
  97. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/feature.json +0 -0
  98. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/init-options.json +0 -0
  99. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/integration.json +0 -0
  100. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/integrations/claude.manifest.json +0 -0
  101. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/integrations/speckit.manifest.json +0 -0
  102. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/memory/constitution.md +0 -0
  103. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/scripts/bash/check-prerequisites.sh +0 -0
  104. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/scripts/bash/common.sh +0 -0
  105. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/scripts/bash/create-new-feature.sh +0 -0
  106. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/scripts/bash/setup-plan.sh +0 -0
  107. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/scripts/bash/setup-tasks.sh +0 -0
  108. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/scripts/bash/update-agent-context.sh +0 -0
  109. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/templates/agent-file-template.md +0 -0
  110. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/templates/checklist-template.md +0 -0
  111. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/templates/constitution-template.md +0 -0
  112. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/templates/plan-template.md +0 -0
  113. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/templates/spec-template.md +0 -0
  114. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/templates/tasks-template.md +0 -0
  115. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/workflows/speckit/workflow.yml +0 -0
  116. {remo_cli-4.1.0 → remo_cli-4.3.0}/.specify/workflows/workflow-registry.json +0 -0
  117. {remo_cli-4.1.0 → remo_cli-4.3.0}/CONTRIBUTING.md +0 -0
  118. {remo_cli-4.1.0 → remo_cli-4.3.0}/Dockerfile +0 -0
  119. {remo_cli-4.1.0 → remo_cli-4.3.0}/LICENSE +0 -0
  120. {remo_cli-4.1.0 → remo_cli-4.3.0}/SECURITY.md +0 -0
  121. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/.ansible-lint +0 -0
  122. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/README.md +0 -0
  123. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/ansible.cfg +0 -0
  124. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/aws_configure.yml +0 -0
  125. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/aws_resize.yml +0 -0
  126. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/aws_site.yml +0 -0
  127. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/aws_teardown.yml +0 -0
  128. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/group_vars/all.yml +0 -0
  129. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/group_vars/incus_containers.yml +0 -0
  130. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/group_vars/proxmox_containers.yml +0 -0
  131. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/hetzner_configure.yml +0 -0
  132. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/hetzner_provision.yml +0 -0
  133. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/hetzner_resize.yml +0 -0
  134. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/hetzner_site.yml +0 -0
  135. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/hetzner_teardown.yml +0 -0
  136. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/incus_bootstrap.yml +0 -0
  137. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/incus_configure.yml +0 -0
  138. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/incus_provision.yml +0 -0
  139. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/incus_resize.yml +0 -0
  140. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/incus_site.yml +0 -0
  141. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/incus_teardown.yml +0 -0
  142. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/inventory/hosts.yml +0 -0
  143. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/inventory/incus_containers.yml +0 -0
  144. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/proxmox_bootstrap.yml +0 -0
  145. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/proxmox_configure.yml +0 -0
  146. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/proxmox_provision.yml +0 -0
  147. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/proxmox_resize.yml +0 -0
  148. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/proxmox_site.yml +0 -0
  149. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/proxmox_teardown.yml +0 -0
  150. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/requirements.yml +0 -0
  151. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/aws_server/defaults/main.yml +0 -0
  152. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/aws_server/tasks/ebs.yml +0 -0
  153. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/aws_server/tasks/ec2.yml +0 -0
  154. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/aws_server/tasks/main.yml +0 -0
  155. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/aws_server/tasks/patch_manager.yml +0 -0
  156. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/aws_server/tasks/preflight.yml +0 -0
  157. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/aws_server/tasks/security_group.yml +0 -0
  158. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/deacon/defaults/main.yml +0 -0
  159. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/deacon/tasks/main.yml +0 -0
  160. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/devcontainers/tasks/main.yml +0 -0
  161. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/docker/handlers/main.yml +0 -0
  162. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/docker/tasks/main.yml +0 -0
  163. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/fzf/tasks/main.yml +0 -0
  164. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/github_cli/tasks/main.yml +0 -0
  165. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/hetzner_server/defaults/main.yml +0 -0
  166. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/hetzner_server/tasks/main.yml +0 -0
  167. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/incus_bootstrap/defaults/main.yml +0 -0
  168. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/incus_bootstrap/handlers/main.yml +0 -0
  169. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/incus_bootstrap/tasks/main.yml +0 -0
  170. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/incus_container/defaults/main.yml +0 -0
  171. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/incus_container/handlers/main.yml +0 -0
  172. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/incus_container/meta/main.yml +0 -0
  173. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/incus_container/tasks/main.yml +0 -0
  174. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/incus_container/tasks/preflight.yml +0 -0
  175. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/incus_container_teardown/defaults/main.yml +0 -0
  176. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/incus_container_teardown/meta/main.yml +0 -0
  177. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/incus_container_teardown/tasks/main.yml +0 -0
  178. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/nodejs/defaults/main.yml +0 -0
  179. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/nodejs/files/nodesource-repo.gpg.key +0 -0
  180. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/nodejs/tasks/main.yml +0 -0
  181. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/proxmox_bootstrap/defaults/main.yml +0 -0
  182. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/proxmox_bootstrap/meta/main.yml +0 -0
  183. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/proxmox_bootstrap/tasks/main.yml +0 -0
  184. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/proxmox_container/defaults/main.yml +0 -0
  185. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/proxmox_container/handlers/main.yml +0 -0
  186. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/proxmox_container/meta/main.yml +0 -0
  187. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/proxmox_container/tasks/main.yml +0 -0
  188. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/proxmox_container/tasks/preflight.yml +0 -0
  189. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/proxmox_container_teardown/defaults/main.yml +0 -0
  190. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/proxmox_container_teardown/meta/main.yml +0 -0
  191. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/proxmox_container_teardown/tasks/main.yml +0 -0
  192. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/user_setup/defaults/main.yml +0 -0
  193. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/user_setup/tasks/main.yml +0 -0
  194. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/user_setup/templates/devshell.sh.j2 +0 -0
  195. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/user_setup/templates/project-launch.sh.j2 +0 -0
  196. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/user_setup/templates/project-menu.sh.j2 +0 -0
  197. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/user_setup/templates/remo-host.sh.j2 +0 -0
  198. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/zellij/defaults/main.yml +0 -0
  199. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/zellij/tasks/main.yml +0 -0
  200. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/zellij/templates/config.kdl.j2 +0 -0
  201. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/roles/zellij/templates/layouts/minimal.kdl.j2 +0 -0
  202. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/tasks/configure_dev_tools.yml +0 -0
  203. {remo_cli-4.1.0 → remo_cli-4.3.0}/ansible/tasks/repair_apt_sources.yml +0 -0
  204. {remo_cli-4.1.0 → remo_cli-4.3.0}/docker/Dockerfile +0 -0
  205. {remo_cli-4.1.0 → remo_cli-4.3.0}/docker/compose.example.yml +0 -0
  206. {remo_cli-4.1.0 → remo_cli-4.3.0}/docker/entrypoint.sh +0 -0
  207. {remo_cli-4.1.0 → remo_cli-4.3.0}/docs/aws.md +0 -0
  208. {remo_cli-4.1.0 → remo_cli-4.3.0}/docs/feature-history.md +0 -0
  209. {remo_cli-4.1.0 → remo_cli-4.3.0}/docs/hetzner.md +0 -0
  210. {remo_cli-4.1.0 → remo_cli-4.3.0}/docs/incus.md +0 -0
  211. {remo_cli-4.1.0 → remo_cli-4.3.0}/docs/install.sh +0 -0
  212. {remo_cli-4.1.0 → remo_cli-4.3.0}/docs/maintaining-claude-md.md +0 -0
  213. {remo_cli-4.1.0 → remo_cli-4.3.0}/docs/maintaining-generated-types.md +0 -0
  214. {remo_cli-4.1.0 → remo_cli-4.3.0}/docs/providers.md +0 -0
  215. {remo_cli-4.1.0 → remo_cli-4.3.0}/docs/proxmox.md +0 -0
  216. {remo_cli-4.1.0 → remo_cli-4.3.0}/docs/remo-web-speckit-input.md +0 -0
  217. {remo_cli-4.1.0 → remo_cli-4.3.0}/docs/remo-web.html +0 -0
  218. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/index.html +0 -0
  219. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/package-lock.json +0 -0
  220. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/package.json +0 -0
  221. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/scripts/check-types-fresh.mjs +0 -0
  222. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/scripts/generate-frame-types.mjs +0 -0
  223. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/scripts/generate-schema-types.mjs +0 -0
  224. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/api/client.test.ts +0 -0
  225. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/api/client.ts +0 -0
  226. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/api/generated/README.md +0 -0
  227. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/api/generated/openapi.json +0 -0
  228. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/api/generated/schema.d.ts +0 -0
  229. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/api/generated/terminal-frames.d.ts +0 -0
  230. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/api/generated/terminal-frames.json +0 -0
  231. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/api/generated.test.ts +0 -0
  232. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/AppRoot.tsx +0 -0
  233. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/AppShell.css +0 -0
  234. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/AwaitingAdoption.css +0 -0
  235. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/AwaitingAdoption.tsx +0 -0
  236. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/OfflineOverlay.css +0 -0
  237. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/OfflineOverlay.tsx +0 -0
  238. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/PairToSync.css +0 -0
  239. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/PairToSync.tsx +0 -0
  240. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/SessionRail.css +0 -0
  241. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/SessionRail.tsx +0 -0
  242. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/SettingsPage.css +0 -0
  243. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/ShortcutsModal.css +0 -0
  244. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/ShortcutsModal.tsx +0 -0
  245. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/TerminalCard.css +0 -0
  246. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/TerminalCard.test.tsx +0 -0
  247. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/WorkspacePane.css +0 -0
  248. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/providerMeta.test.ts +0 -0
  249. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/providerMeta.ts +0 -0
  250. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/railModel.test.ts +0 -0
  251. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/components/railModel.ts +0 -0
  252. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/lib/clipboard.ts +0 -0
  253. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/lib/fullscreen.ts +0 -0
  254. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/main.tsx +0 -0
  255. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/state/discovery.test.ts +0 -0
  256. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/state/discovery.ts +0 -0
  257. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/state/fonts.ts +0 -0
  258. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/state/health.test.ts +0 -0
  259. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/state/health.ts +0 -0
  260. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/state/latency.test.ts +0 -0
  261. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/state/latency.ts +0 -0
  262. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/state/useConsoleKeyboard.ts +0 -0
  263. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/terminal/.gitkeep +0 -0
  264. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/terminal/RendererAdapter.ts +0 -0
  265. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/terminal/TerminalConnection.test.ts +0 -0
  266. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/terminal/TerminalConnection.ts +0 -0
  267. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/terminal/XtermRenderer.ts +0 -0
  268. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/terminal/defaultRenderer.ts +0 -0
  269. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/terminal/keymap.test.ts +0 -0
  270. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/terminal/keymap.ts +0 -0
  271. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/test/setup.ts +0 -0
  272. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/theme/fonts.ts +0 -0
  273. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/src/theme/tokens.css +0 -0
  274. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/tsconfig.json +0 -0
  275. {remo_cli-4.1.0 → remo_cli-4.3.0}/frontend/vite.config.ts +0 -0
  276. {remo_cli-4.1.0 → remo_cli-4.3.0}/install.sh +0 -0
  277. {remo_cli-4.1.0 → remo_cli-4.3.0}/maverick.yaml +0 -0
  278. {remo_cli-4.1.0 → remo_cli-4.3.0}/playwright.config.ts +0 -0
  279. {remo_cli-4.1.0 → remo_cli-4.3.0}/project-menu.sh +0 -0
  280. {remo_cli-4.1.0 → remo_cli-4.3.0}/release-please-config.json +0 -0
  281. {remo_cli-4.1.0 → remo_cli-4.3.0}/remo +0 -0
  282. {remo_cli-4.1.0 → remo_cli-4.3.0}/remo.svg +0 -0
  283. {remo_cli-4.1.0 → remo_cli-4.3.0}/run.sh +0 -0
  284. {remo_cli-4.1.0 → remo_cli-4.3.0}/scripts/export_openapi.py +0 -0
  285. {remo_cli-4.1.0 → remo_cli-4.3.0}/scripts/palette-check.sh +0 -0
  286. {remo_cli-4.1.0 → remo_cli-4.3.0}/scripts/setup-aws-oidc.sh +0 -0
  287. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/001-bootstrap-incus-host/checklists/requirements.md +0 -0
  288. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/001-bootstrap-incus-host/checklists/validation.md +0 -0
  289. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/001-bootstrap-incus-host/contracts/playbook-interface.yaml +0 -0
  290. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/001-bootstrap-incus-host/contracts/role-interface.yaml +0 -0
  291. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/001-bootstrap-incus-host/data-model.md +0 -0
  292. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/001-bootstrap-incus-host/plan.md +0 -0
  293. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/001-bootstrap-incus-host/quickstart.md +0 -0
  294. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/001-bootstrap-incus-host/research.md +0 -0
  295. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/001-bootstrap-incus-host/spec.md +0 -0
  296. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/001-bootstrap-incus-host/tasks.md +0 -0
  297. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/002-incus-container-support/checklists/requirements.md +0 -0
  298. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/002-incus-container-support/contracts/playbook-interface.yaml +0 -0
  299. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/002-incus-container-support/contracts/role-interface.yaml +0 -0
  300. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/002-incus-container-support/data-model.md +0 -0
  301. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/002-incus-container-support/plan.md +0 -0
  302. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/002-incus-container-support/quickstart.md +0 -0
  303. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/002-incus-container-support/research.md +0 -0
  304. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/002-incus-container-support/spec.md +0 -0
  305. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/002-incus-container-support/tasks.md +0 -0
  306. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/003-python-cli-rewrite/checklists/requirements.md +0 -0
  307. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/003-python-cli-rewrite/data-model.md +0 -0
  308. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/003-python-cli-rewrite/plan.md +0 -0
  309. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/003-python-cli-rewrite/quickstart.md +0 -0
  310. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/003-python-cli-rewrite/research.md +0 -0
  311. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/003-python-cli-rewrite/spec.md +0 -0
  312. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/003-python-cli-rewrite/tasks.md +0 -0
  313. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/004-proxmox-container-support/spec.md +0 -0
  314. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/005-provider-snapshots/checklists/requirements.md +0 -0
  315. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/005-provider-snapshots/contracts/cli-surface.md +0 -0
  316. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/005-provider-snapshots/data-model.md +0 -0
  317. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/005-provider-snapshots/plan.md +0 -0
  318. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/005-provider-snapshots/quickstart.md +0 -0
  319. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/005-provider-snapshots/research.md +0 -0
  320. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/005-provider-snapshots/spec.md +0 -0
  321. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/005-provider-snapshots/tasks.md +0 -0
  322. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/010-web-session-interface/checklists/requirements.md +0 -0
  323. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/010-web-session-interface/contracts/remo-host-protocol.md +0 -0
  324. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/010-web-session-interface/contracts/rest-api.md +0 -0
  325. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/010-web-session-interface/contracts/terminal-websocket.md +0 -0
  326. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/010-web-session-interface/data-model.md +0 -0
  327. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/010-web-session-interface/plan.md +0 -0
  328. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/010-web-session-interface/quickstart.md +0 -0
  329. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/010-web-session-interface/research.md +0 -0
  330. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/010-web-session-interface/spec.md +0 -0
  331. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/010-web-session-interface/tasks.md +0 -0
  332. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/011-web-adopt/checklists/requirements.md +0 -0
  333. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/011-web-adopt/contracts/cli-web-adopt.md +0 -0
  334. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/011-web-adopt/contracts/setup-api.md +0 -0
  335. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/011-web-adopt/data-model.md +0 -0
  336. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/011-web-adopt/plan.md +0 -0
  337. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/011-web-adopt/quickstart-results.md +0 -0
  338. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/011-web-adopt/quickstart.md +0 -0
  339. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/011-web-adopt/research.md +0 -0
  340. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/011-web-adopt/spec.md +0 -0
  341. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/011-web-adopt/tasks.md +0 -0
  342. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/012-web-adopt-pairing/contracts/cli-web-adopt.md +0 -0
  343. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/012-web-adopt-pairing/contracts/pairing-api.md +0 -0
  344. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/012-web-adopt-pairing/contracts/setup-api.md +0 -0
  345. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/012-web-adopt-pairing/data-model.md +0 -0
  346. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/012-web-adopt-pairing/plan.md +0 -0
  347. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/012-web-adopt-pairing/quickstart.md +0 -0
  348. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/012-web-adopt-pairing/research.md +0 -0
  349. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/012-web-adopt-pairing/spec.md +0 -0
  350. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/012-web-adopt-pairing/tasks.md +0 -0
  351. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/013-managed-instance-tags/contracts/cli-sync.md +0 -0
  352. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/013-managed-instance-tags/contracts/marker-commands.md +0 -0
  353. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/013-managed-instance-tags/data-model.md +0 -0
  354. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/013-managed-instance-tags/plan.md +0 -0
  355. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/013-managed-instance-tags/quickstart.md +0 -0
  356. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/013-managed-instance-tags/research.md +0 -0
  357. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/013-managed-instance-tags/spec.md +0 -0
  358. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/013-managed-instance-tags/tasks.md +0 -0
  359. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/014-register-ssh-host/contracts/add-command.md +0 -0
  360. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/014-register-ssh-host/contracts/remove-command.md +0 -0
  361. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/014-register-ssh-host/data-model.md +0 -0
  362. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/014-register-ssh-host/plan.md +0 -0
  363. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/014-register-ssh-host/quickstart.md +0 -0
  364. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/014-register-ssh-host/research.md +0 -0
  365. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/014-register-ssh-host/tasks.md +0 -0
  366. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/015-registry-v2/checklists/requirements.md +0 -0
  367. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/015-registry-v2/contracts/mirror-payload-v2.md +0 -0
  368. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/015-registry-v2/contracts/registry-accessor-api.md +0 -0
  369. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/015-registry-v2/contracts/registry-file-v2.md +0 -0
  370. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/015-registry-v2/data-model.md +0 -0
  371. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/015-registry-v2/plan.md +0 -0
  372. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/015-registry-v2/quickstart.md +0 -0
  373. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/015-registry-v2/research.md +0 -0
  374. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/015-registry-v2/spec.md +0 -0
  375. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/015-registry-v2/tasks.md +0 -0
  376. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/016-sync-reconcile/checklists/requirements.md +0 -0
  377. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/016-sync-reconcile/contracts/cli-sync.md +0 -0
  378. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/016-sync-reconcile/contracts/provider-probe.md +0 -0
  379. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/016-sync-reconcile/data-model.md +0 -0
  380. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/016-sync-reconcile/plan.md +0 -0
  381. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/016-sync-reconcile/quickstart.md +0 -0
  382. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/016-sync-reconcile/research.md +0 -0
  383. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/016-sync-reconcile/spec.md +0 -0
  384. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/016-sync-reconcile/tasks.md +0 -0
  385. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/017-web-adopt-simplify/checklists/requirements.md +0 -0
  386. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/017-web-adopt-simplify/contracts/cli-web-push.md +0 -0
  387. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/017-web-adopt-simplify/contracts/cli-web-status.md +0 -0
  388. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/017-web-adopt-simplify/contracts/revocation.md +0 -0
  389. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/017-web-adopt-simplify/contracts/setup-status-marker.md +0 -0
  390. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/017-web-adopt-simplify/data-model.md +0 -0
  391. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/017-web-adopt-simplify/plan.md +0 -0
  392. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/017-web-adopt-simplify/quickstart.md +0 -0
  393. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/017-web-adopt-simplify/research.md +0 -0
  394. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/017-web-adopt-simplify/spec.md +0 -0
  395. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/017-web-adopt-simplify/tasks.md +0 -0
  396. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/baseline-notes.md +0 -0
  397. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/checklists/requirements.md +0 -0
  398. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/contracts/cli-surface.md +0 -0
  399. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/contracts/errors.md +0 -0
  400. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/contracts/lifecycle-templates.md +0 -0
  401. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/contracts/provider-protocol.md +0 -0
  402. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/contracts/sync-merge.md +0 -0
  403. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/data-model.md +0 -0
  404. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/plan.md +0 -0
  405. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/quickstart.md +0 -0
  406. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/research.md +0 -0
  407. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/spec.md +0 -0
  408. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/018-provider-abstraction/tasks.md +0 -0
  409. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/019-hygiene-deps-docs/checklists/requirements.md +0 -0
  410. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/019-hygiene-deps-docs/contracts/cli-surface-delta.md +0 -0
  411. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/019-hygiene-deps-docs/contracts/docs-structure-check.md +0 -0
  412. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/019-hygiene-deps-docs/data-model.md +0 -0
  413. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/019-hygiene-deps-docs/plan.md +0 -0
  414. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/019-hygiene-deps-docs/quickstart.md +0 -0
  415. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/019-hygiene-deps-docs/research.md +0 -0
  416. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/019-hygiene-deps-docs/spec.md +0 -0
  417. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/019-hygiene-deps-docs/tasks.md +0 -0
  418. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/020-openapi-type-generation/checklists/requirements.md +0 -0
  419. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/020-openapi-type-generation/contracts/drift-checks.md +0 -0
  420. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/020-openapi-type-generation/contracts/terminal-frames-v1.md +0 -0
  421. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/020-openapi-type-generation/data-model.md +0 -0
  422. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/020-openapi-type-generation/plan.md +0 -0
  423. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/020-openapi-type-generation/quickstart.md +0 -0
  424. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/020-openapi-type-generation/research.md +0 -0
  425. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/020-openapi-type-generation/spec.md +0 -0
  426. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/020-openapi-type-generation/tasks.md +0 -0
  427. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/021-cli-plane-separation/contracts/cli-surface.md +0 -0
  428. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/021-cli-plane-separation/contracts/descriptor-schema.md +0 -0
  429. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/021-cli-plane-separation/data-model.md +0 -0
  430. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/021-cli-plane-separation/plan.md +0 -0
  431. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/021-cli-plane-separation/quickstart.md +0 -0
  432. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/021-cli-plane-separation/research.md +0 -0
  433. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/021-cli-plane-separation/spec.md +0 -0
  434. {remo_cli-4.1.0 → remo_cli-4.3.0}/specs/021-cli-plane-separation/tasks.md +0 -0
  435. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/__init__.py +0 -0
  436. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/__main__.py +0 -0
  437. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/cli/__init__.py +0 -0
  438. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/cli/cp.py +0 -0
  439. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/cli/providers/__init__.py +0 -0
  440. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/cli/providers/factory.py +0 -0
  441. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/cli/shell.py +0 -0
  442. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/cli/web.py +0 -0
  443. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/__init__.py +0 -0
  444. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/ansible_runner.py +0 -0
  445. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/completion.py +0 -0
  446. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/config.py +0 -0
  447. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/errors.py +0 -0
  448. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/lifecycle.py +0 -0
  449. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/output.py +0 -0
  450. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/picker.py +0 -0
  451. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/provider_protocol.py +0 -0
  452. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/provider_registry.py +0 -0
  453. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/reconcile.py +0 -0
  454. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/registry.py +0 -0
  455. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/remo_host_client.py +0 -0
  456. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/rsync.py +0 -0
  457. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/snapshot.py +0 -0
  458. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/ssh.py +0 -0
  459. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/validation.py +0 -0
  460. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/version.py +0 -0
  461. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/core/web_drift.py +0 -0
  462. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/models/__init__.py +0 -0
  463. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/models/capability.py +0 -0
  464. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/models/discovery.py +0 -0
  465. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/models/host.py +0 -0
  466. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/models/session_target.py +0 -0
  467. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/models/snapshot.py +0 -0
  468. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/providers/__init__.py +0 -0
  469. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/providers/aws.py +0 -0
  470. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/providers/aws_descriptor.py +0 -0
  471. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/providers/builtin.py +0 -0
  472. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/providers/hetzner.py +0 -0
  473. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/providers/hetzner_descriptor.py +0 -0
  474. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/providers/incus.py +0 -0
  475. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/providers/incus_descriptor.py +0 -0
  476. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/providers/proxmox.py +0 -0
  477. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/providers/proxmox_descriptor.py +0 -0
  478. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/__init__.py +0 -0
  479. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/api/__init__.py +0 -0
  480. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/api/hosts.py +0 -0
  481. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/api/pairing.py +0 -0
  482. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/api/setup.py +0 -0
  483. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/api/terminals.py +0 -0
  484. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/app.py +0 -0
  485. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/check.py +0 -0
  486. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/config.py +0 -0
  487. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/frames.py +0 -0
  488. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/health.py +0 -0
  489. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/logging_config.py +0 -0
  490. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/models.py +0 -0
  491. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/operator_auth.py +0 -0
  492. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/pairing.py +0 -0
  493. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/ssh_master.py +0 -0
  494. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/state.py +0 -0
  495. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/terminal.py +0 -0
  496. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/terminal_registry.py +0 -0
  497. {remo_cli-4.1.0 → remo_cli-4.3.0}/src/remo_cli/web/tokens.py +0 -0
  498. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/__init__.py +0 -0
  499. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/ansible/__init__.py +0 -0
  500. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/ansible/test_remo_host_idempotency.py +0 -0
  501. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/conftest.py +0 -0
  502. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/e2e/fixtures.ts +0 -0
  503. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/e2e/keyboard-routing.spec.ts +0 -0
  504. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/e2e/mobile.spec.ts +0 -0
  505. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/e2e/reconnect.spec.ts +0 -0
  506. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/e2e/terminal-shell-io.spec.ts +0 -0
  507. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/e2e/terminal-tui-compatibility.spec.ts +0 -0
  508. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/e2e/workspace-layout.spec.ts +0 -0
  509. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/image/__init__.py +0 -0
  510. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/image/test_docker_image.py +0 -0
  511. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/__init__.py +0 -0
  512. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/_nine_terminal_fixture.py +0 -0
  513. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/fish_completion.sh +0 -0
  514. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/orbstack-cloud-init.yml +0 -0
  515. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/orbstack.sh +0 -0
  516. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/test_nine_terminals.py +0 -0
  517. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/test_nine_terminals_soak.py +0 -0
  518. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/test_registry_concurrency.py +0 -0
  519. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/test_remo_host_e2e.py +0 -0
  520. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/test_security_rejections.py +0 -0
  521. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/test_setup_payload_versions.py +0 -0
  522. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/test_sync_reconcile.py +0 -0
  523. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/test_terminal_attach.py +0 -0
  524. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/test_web_adopt_e2e.py +0 -0
  525. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/integration/test_web_cli_parity.py +0 -0
  526. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/perf/__init__.py +0 -0
  527. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/perf/test_latency.py +0 -0
  528. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/perf/test_registry_perf.py +0 -0
  529. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/__init__.py +0 -0
  530. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/__init__.py +0 -0
  531. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/providers/test_aws_snapshot.py +0 -0
  532. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/providers/test_hetzner_snapshot.py +0 -0
  533. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/providers/test_incus_snapshot.py +0 -0
  534. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/providers/test_incus_sync_all.py +0 -0
  535. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/providers/test_proxmox_snapshot.py +0 -0
  536. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/providers/test_proxmox_sync_all.py +0 -0
  537. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/surface_baseline.py +0 -0
  538. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/test_add_cmd.py +0 -0
  539. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/test_cli_uniformity.py +0 -0
  540. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/test_cp.py +0 -0
  541. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/test_remove_cmd.py +0 -0
  542. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/test_shell.py +0 -0
  543. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/test_shell_added.py +0 -0
  544. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/test_startup_imports.py +0 -0
  545. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/test_surface_preservation.py +0 -0
  546. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/cli/test_web_adopt_cmd.py +0 -0
  547. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/__init__.py +0 -0
  548. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_ansible_runner.py +0 -0
  549. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_completion.py +0 -0
  550. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_config.py +0 -0
  551. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_errors.py +0 -0
  552. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_known_hosts.py +0 -0
  553. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_lifecycle.py +0 -0
  554. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_migration_tagging_notice.py +0 -0
  555. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_output.py +0 -0
  556. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_picker.py +0 -0
  557. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_provider_registry.py +0 -0
  558. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_reconcile.py +0 -0
  559. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_registry_format.py +0 -0
  560. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_registry_locking.py +0 -0
  561. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_registry_migration.py +0 -0
  562. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_remo_host_client.py +0 -0
  563. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_rsync.py +0 -0
  564. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_snapshot.py +0 -0
  565. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_ssh.py +0 -0
  566. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_ssh_added.py +0 -0
  567. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_ssh_controlpath.py +0 -0
  568. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_ssh_identity_opts.py +0 -0
  569. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_validation.py +0 -0
  570. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_version.py +0 -0
  571. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_web_adopt_authorize.py +0 -0
  572. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_web_adopt_code.py +0 -0
  573. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_web_adopt_payload.py +0 -0
  574. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_web_drift.py +0 -0
  575. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_web_push.py +0 -0
  576. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/core/test_web_push_self_heal.py +0 -0
  577. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/models/__init__.py +0 -0
  578. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/models/test_capability.py +0 -0
  579. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/models/test_discovery.py +0 -0
  580. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/models/test_session_target.py +0 -0
  581. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/__init__.py +0 -0
  582. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/fake_provider.py +0 -0
  583. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_added_update_remove.py +0 -0
  584. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_added_verify.py +0 -0
  585. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_aws_provider_protocol.py +0 -0
  586. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_aws_snapshot.py +0 -0
  587. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_aws_sync.py +0 -0
  588. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_aws_upgrade_resize.py +0 -0
  589. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_hetzner_entry_adapters.py +0 -0
  590. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_hetzner_http.py +0 -0
  591. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_hetzner_label.py +0 -0
  592. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_hetzner_resize.py +0 -0
  593. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_hetzner_snapshot.py +0 -0
  594. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_hetzner_sync.py +0 -0
  595. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_host_user_consistency.py +0 -0
  596. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_incus_entry_adapters.py +0 -0
  597. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_incus_marker.py +0 -0
  598. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_incus_resize.py +0 -0
  599. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_incus_snapshot.py +0 -0
  600. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_incus_sync.py +0 -0
  601. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_missing_sdk.py +0 -0
  602. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_provider_conformance.py +0 -0
  603. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_provider_registry_entries.py +0 -0
  604. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_proxmox_devcontainer_runtime.py +0 -0
  605. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_proxmox_marker.py +0 -0
  606. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_proxmox_provider_protocol.py +0 -0
  607. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_proxmox_resize.py +0 -0
  608. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_proxmox_snapshot.py +0 -0
  609. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/providers/test_proxmox_sync.py +0 -0
  610. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/test_ansible_templates.py +0 -0
  611. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/test_architecture.py +0 -0
  612. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/test_docs_structure.py +0 -0
  613. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/test_host_model.py +0 -0
  614. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/test_host_ssh_type.py +0 -0
  615. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/test_lockfile_version.py +0 -0
  616. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/test_schema_drift.py +0 -0
  617. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/__init__.py +0 -0
  618. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/_pairing_support.py +0 -0
  619. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/conftest.py +0 -0
  620. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/fixtures/hosts_response.json +0 -0
  621. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/fixtures/sessions_response.json +0 -0
  622. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_backpressure.py +0 -0
  623. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_check.py +0 -0
  624. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_csp.py +0 -0
  625. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_discovery_refresh_force.py +0 -0
  626. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_frames.py +0 -0
  627. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_health_states.py +0 -0
  628. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_hosts_mapping.py +0 -0
  629. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_lazy_import.py +0 -0
  630. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_log_redaction.py +0 -0
  631. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_mint_gating.py +0 -0
  632. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_operator_auth.py +0 -0
  633. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_pairing.py +0 -0
  634. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_pairing_origin.py +0 -0
  635. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_payload_equivalence.py +0 -0
  636. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_registry_readonly.py +0 -0
  637. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_setup_api.py +0 -0
  638. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_setup_auth.py +0 -0
  639. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_setup_dormancy.py +0 -0
  640. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_state.py +0 -0
  641. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_terminal_resize.py +0 -0
  642. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_terminals_api.py +0 -0
  643. {remo_cli-4.1.0 → remo_cli-4.3.0}/tests/unit/web/test_tokens.py +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "4.3.0"
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,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.3.0](https://github.com/get2knowio/remo/compare/v4.2.0...v4.3.0) (2026-08-05)
4
+
5
+
6
+ ### Features
7
+
8
+ * **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))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **web:** stop three light themes printing invisible text ([#141](https://github.com/get2knowio/remo/issues/141)) ([a1c2bea](https://github.com/get2knowio/remo/commit/a1c2bea06c13503fe6c623f2f18483c862c20b46))
14
+
15
+ ## [4.2.0](https://github.com/get2knowio/remo/compare/v4.1.0...v4.2.0) (2026-08-05)
16
+
17
+
18
+ ### Features
19
+
20
+ * **web:** flatten a tiling from ⊞, and hide terminal headers wholesale ([#139](https://github.com/get2knowio/remo/issues/139)) ([e5890f0](https://github.com/get2knowio/remo/commit/e5890f031c214b835ec8eab9ff85e8e343c12832))
21
+ * **web:** choose the tiling split (40/60, 45/55 or 50/50) from Settings — applied live to a tiling you already have ([#139](https://github.com/get2knowio/remo/issues/139)) ([89f6ee7](https://github.com/get2knowio/remo/commit/89f6ee7))
22
+ * **web:** the tiling control now appears in every mode; from a single view it rebuilds the grid with that terminal as the master ([#139](https://github.com/get2knowio/remo/issues/139)) ([977a1d5](https://github.com/get2knowio/remo/commit/977a1d5))
23
+
3
24
  ## [4.1.0](https://github.com/get2knowio/remo/compare/v4.0.1...v4.1.0) (2026-08-04)
4
25
 
5
26
 
@@ -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.1.0
3
+ Version: 4.3.0
4
4
  Summary: Remote development environment CLI
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -286,11 +286,47 @@ 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
+
294
330
  `remo remove NAME [--yes]` deregisters an added host by deleting **only** the
295
331
  local registry entry — it makes no connection to and no change on the remote
296
332
  environment (unlike a provider `destroy`, which tears down infrastructure). It
@@ -323,6 +359,8 @@ remo add NAME [user@]host[:port] # Register a single SSH host
323
359
  remo add NAME host --port 2222 --identity ~/.ssh/key # Custom port + key
324
360
  remo add NAME host --verify # Fail-closed SSH reachability check first
325
361
  remo add NAME host --user alice # Override default SSH user (default: remo)
362
+ remo configure NAME # Install/refresh the dev tools on an added host
363
+ remo configure NAME --skip docker # Skip a tool (repeatable; --only is the inverse)
326
364
  remo remove NAME [--yes] # Deregister an added host (local-only)
327
365
 
328
366
  # Hetzner Cloud
@@ -260,11 +260,47 @@ 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
+
268
304
  `remo remove NAME [--yes]` deregisters an added host by deleting **only** the
269
305
  local registry entry — it makes no connection to and no change on the remote
270
306
  environment (unlike a provider `destroy`, which tears down infrastructure). It
@@ -297,6 +333,8 @@ remo add NAME [user@]host[:port] # Register a single SSH host
297
333
  remo add NAME host --port 2222 --identity ~/.ssh/key # Custom port + key
298
334
  remo add NAME host --verify # Fail-closed SSH reachability check first
299
335
  remo add NAME host --user alice # Override default SSH user (default: remo)
336
+ remo configure NAME # Install/refresh the dev tools on an added host
337
+ remo configure NAME --skip docker # Skip a tool (repeatable; --only is the inverse)
300
338
  remo remove NAME [--yes] # Deregister an added host (local-only)
301
339
 
302
340
  # Hetzner Cloud
@@ -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 }}
@@ -104,6 +104,13 @@ The SPA is a two-pane **web console**:
104
104
  row showing its name, git glyphs, and a Zellij-active bolt. A search box, provider-color filter
105
105
  chips, and an "⚡ Active only" toggle narrow the list; "⊞ Open all · N" opens every available
106
106
  target as a grid.
107
+ - **Terminal headers** can be hidden wholesale from the **▤/▭** toggle in the app header, which buys
108
+ back ~37px of terminal per tile — a 16% gain on a stacked tile at tablet size, and the reason it
109
+ exists. The trade is real and deliberate: the header carries the per-tile controls AND is the drag
110
+ handle, so with it hidden a tile can't be dragged, tiled, themed or closed from the card itself (use
111
+ the rail, or toggle the headers back — the toggle is in the app header, which never hides). A
112
+ fullscreen terminal keeps its header regardless: there is only one card on screen, so the space saved
113
+ is negligible and its ✕/⤡ controls are the way out.
107
114
  - **Terminal pane** (right). Clicking a row opens that target **solo** (single view); ⌘/Ctrl-click a
108
115
  row (or its `+` button) **adds** it to a responsive grid (1/2/3 columns by count). In a grid, **drag
109
116
  a tile's header onto another to swap their positions** — a window outline follows the cursor and the
@@ -114,8 +121,13 @@ The SPA is a two-pane **web console**:
114
121
  master+stack model). A translucent band marks each edge while dragging and fills in when the drop
115
122
  would land there. The **▦** control in a tile's header cycles the same thing without a drag
116
123
  (▌ left → ▀ top → ▐ right → ▄ bottom → back to the even grid), which is the practical path on a touch
117
- device. The master takes 60% by default, closing it promotes the head of the stack rather than
118
- un-tiling everything, and the arrangement persists across reload alongside the tile order. This is
124
+ device — and it is in the cluster in every mode, not just a grid: from a single view it rebuilds the
125
+ grid with the terminal you were looking at as the master (inert only when there is no grid to build).
126
+ The master's share is a setting (**Settings → Tiling split**: 40/60, 45/55 or 50/50, applied
127
+ live to a tiling you already have), closing it promotes the head of the stack rather than
128
+ un-tiling everything, and the arrangement persists across reload alongside the tile order. **⊞ flattens
129
+ a tiling back to even tiles** — in a plain grid that control is inert, so it doubles as the way out
130
+ rather than making you cycle ▦ forward until it wraps. This is
119
131
  what lets three terminals fill the pane without the empty fourth cell a 2x2 grid would leave. In a
120
132
  grid, **resting the pointer on a
121
133
  tile focuses it** (focus-follows-mouse, with a short dwell so passing through tiles doesn't steal
@@ -152,13 +164,15 @@ they can be stale until something else fetches. Git glyphs only appear on instan
152
164
  `remo-host` new enough to report git status; see [Upgrade compatibility](#upgrade-compatibility).
153
165
 
154
166
  **Settings** (⚙, top bar; stored in this browser only, FR-034): **appearance** (site light/dark mode),
155
- accent color, terminal font, **terminal theme**, font size, program ligatures, grid display mode
167
+ accent color, terminal font, **terminal theme**, **tiling split**, font size, program ligatures,
168
+ grid display mode
156
169
  (actual-size vs scale-to-fit), **focus dwell** (how long the pointer rests before focus-follows-mouse
157
170
  fires), a **Nerd Font uploader**, and **Pair CLI to sync** (mint a re-sync pairing
158
171
  code).
159
172
 
160
173
  **Appearance** is tri-state — `system` (the default, following the OS `prefers-color-scheme`),
161
- `light`, or `dark` — toggled from the ◐/☀/☾ button in the top bar as well as from Settings. The
174
+ `light`, or `dark` — set from Settings. (It briefly had a top-bar button too; that was removed once
175
+ the header ran short of room, since Settings already owned the choice.) The
162
176
  console's palette is a set of `light-dark()` CSS custom properties in `theme/tokens.css`; "system"
163
177
  therefore needs no JavaScript to render, and an explicit choice is applied as `data-theme` on `<html>`.
164
178
  **Terminal theme** defaults to **Follow site theme**, which tracks the site mode: a light console gets
@@ -168,7 +182,12 @@ background is `--bg-term`, foreground `--text`, ANSI 1–6 the `--danger`/`--ok`
168
182
  (a terminal palette must be literal `#rrggbb`; xterm.js's `ITheme` accepts neither `oklch()` nor
169
183
  `var()`), so editing a token does not update them. Six curated third-party schemes are also available —
170
184
  Catppuccin Mocha/Latte, Dracula, Gruvbox Dark/Light, Solarized Light — and any theme can be used under
171
- either site mode; picking one explicitly stops the terminal following the site. The Settings choice is
185
+ either site mode. Those ports are faithful with one deliberate exception: three of the light variants
186
+ assign a *background* tone to a neutral slot that applications print as **text** (Solarized Light's
187
+ white/brightWhite, Gruvbox Light's black, Catppuccin Latte's white/brightWhite), which made that text
188
+ invisible on the theme's own background. Those five values are replaced — each with another colour from
189
+ the same palette — and a contrast floor in `terminalThemes.test.ts` keeps every slot above 2.0:1. Every
190
+ chromatic slot is untouched; picking one explicitly stops the terminal following the site. The Settings choice is
172
191
  the default for every terminal; an individual terminal can override it from the color swatch in its
173
192
  header, and clearing that override back to "Default" makes it follow the global choice again. Theme
174
193
  changes apply live — the terminal is recolored in place, keeping the connection and scrollback. Because a browser can't read fonts installed on the
@@ -612,7 +631,9 @@ Your web deployment may now be out of date — run 'remo web status' to see what
612
631
  ```
613
632
 
614
633
  It fires after a successful `remo <provider> create`/`destroy`, an **applied** `remo <provider> sync`
615
- (the shared reconcile/`run_sync` path), `remo add`, and `remo remove`. It never fires when this
634
+ (the shared reconcile/`run_sync` path), `remo add`, `remo remove`, and `remo configure` (which does not
635
+ mutate the registry, but is normally the point at which a newly added host first becomes worth
636
+ pushing). It never fires when this
616
637
  workstation has never pushed (no cache to be out of date against), on a dry-run or no-op `sync`, or
617
638
  on `remo aws stop`/`start`/`reboot` (which don't mutate the registry). The nudge is gated on cache
618
639
  existence only — a rare false positive after a no-op mutation is acceptable because `remo web status`