remo-cli 4.0.1__tar.gz → 4.1.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 (641) hide show
  1. remo_cli-4.1.0/.release-please-manifest.json +3 -0
  2. {remo_cli-4.0.1 → remo_cli-4.1.0}/AGENTS.md +3 -1
  3. {remo_cli-4.0.1 → remo_cli-4.1.0}/CHANGELOG.md +7 -0
  4. {remo_cli-4.0.1 → remo_cli-4.1.0}/CLAUDE.md +3 -1
  5. {remo_cli-4.0.1 → remo_cli-4.1.0}/PKG-INFO +1 -1
  6. {remo_cli-4.0.1 → remo_cli-4.1.0}/docs/web-session-interface.md +10 -1
  7. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/TerminalCard.tsx +45 -0
  8. remo_cli-4.1.0/frontend/src/components/WorkspacePane.css +139 -0
  9. remo_cli-4.1.0/frontend/src/components/WorkspacePane.test.tsx +175 -0
  10. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/WorkspacePane.tsx +120 -35
  11. remo_cli-4.1.0/frontend/src/components/masterLayout.test.ts +201 -0
  12. remo_cli-4.1.0/frontend/src/components/masterLayout.ts +190 -0
  13. remo_cli-4.1.0/frontend/src/state/workspace.test.ts +296 -0
  14. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/state/workspace.ts +156 -8
  15. {remo_cli-4.0.1 → remo_cli-4.1.0}/pyproject.toml +1 -1
  16. remo_cli-4.1.0/scripts/palette-check.sh +67 -0
  17. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_discovery.py +57 -18
  18. {remo_cli-4.0.1 → remo_cli-4.1.0}/uv.lock +1 -1
  19. remo_cli-4.0.1/.release-please-manifest.json +0 -3
  20. remo_cli-4.0.1/frontend/src/components/WorkspacePane.css +0 -70
  21. remo_cli-4.0.1/frontend/src/state/workspace.test.ts +0 -98
  22. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/release/SKILL.md +0 -0
  23. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-analyze/SKILL.md +0 -0
  24. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-checklist/SKILL.md +0 -0
  25. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-clarify/SKILL.md +0 -0
  26. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-constitution/SKILL.md +0 -0
  27. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-converge/SKILL.md +0 -0
  28. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-git-commit/SKILL.md +0 -0
  29. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-git-feature/SKILL.md +0 -0
  30. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-git-initialize/SKILL.md +0 -0
  31. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-git-remote/SKILL.md +0 -0
  32. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-git-validate/SKILL.md +0 -0
  33. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-implement/SKILL.md +0 -0
  34. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-plan/SKILL.md +0 -0
  35. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-specify/SKILL.md +0 -0
  36. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-tasks/SKILL.md +0 -0
  37. {remo_cli-4.0.1 → remo_cli-4.1.0}/.claude/skills/speckit-taskstoissues/SKILL.md +0 -0
  38. {remo_cli-4.0.1 → remo_cli-4.1.0}/.devcontainer/devcontainer.json +0 -0
  39. {remo_cli-4.0.1 → remo_cli-4.1.0}/.dockerignore +0 -0
  40. {remo_cli-4.0.1 → remo_cli-4.1.0}/.github/CODEOWNERS +0 -0
  41. {remo_cli-4.0.1 → remo_cli-4.1.0}/.github/dependabot.yml +0 -0
  42. {remo_cli-4.0.1 → remo_cli-4.1.0}/.github/workflows/ci.yml +0 -0
  43. {remo_cli-4.0.1 → remo_cli-4.1.0}/.github/workflows/codeql.yml +0 -0
  44. {remo_cli-4.0.1 → remo_cli-4.1.0}/.github/workflows/dependency-review.yml +0 -0
  45. {remo_cli-4.0.1 → remo_cli-4.1.0}/.github/workflows/dev-build.yml +0 -0
  46. {remo_cli-4.0.1 → remo_cli-4.1.0}/.github/workflows/provision.yml +0 -0
  47. {remo_cli-4.0.1 → remo_cli-4.1.0}/.github/workflows/release-please.yml +0 -0
  48. {remo_cli-4.0.1 → remo_cli-4.1.0}/.github/workflows/release.yml +0 -0
  49. {remo_cli-4.0.1 → remo_cli-4.1.0}/.github/workflows/smoke-test-cleanup.yml +0 -0
  50. {remo_cli-4.0.1 → remo_cli-4.1.0}/.github/workflows/smoke-test.yml +0 -0
  51. {remo_cli-4.0.1 → remo_cli-4.1.0}/.github/workflows/teardown.yml +0 -0
  52. {remo_cli-4.0.1 → remo_cli-4.1.0}/.gitignore +0 -0
  53. {remo_cli-4.0.1 → remo_cli-4.1.0}/.maverick/checkpoints/feature/implementation_complete.json +0 -0
  54. {remo_cli-4.0.1 → remo_cli-4.1.0}/.maverick/checkpoints/feature/phase_${{ index }}_complete.json +0 -0
  55. {remo_cli-4.0.1 → remo_cli-4.1.0}/.maverick/checkpoints/feature/validation_complete.json +0 -0
  56. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/.registry +0 -0
  57. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/README.md +0 -0
  58. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/commands/speckit.git.commit.md +0 -0
  59. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/commands/speckit.git.feature.md +0 -0
  60. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/commands/speckit.git.initialize.md +0 -0
  61. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/commands/speckit.git.remote.md +0 -0
  62. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/commands/speckit.git.validate.md +0 -0
  63. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/config-template.yml +0 -0
  64. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/extension.yml +0 -0
  65. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/git-config.yml +0 -0
  66. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/scripts/bash/auto-commit.sh +0 -0
  67. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/scripts/bash/create-new-feature.sh +0 -0
  68. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/scripts/bash/git-common.sh +0 -0
  69. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/scripts/bash/initialize-repo.sh +0 -0
  70. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
  71. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/scripts/powershell/create-new-feature.ps1 +0 -0
  72. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/scripts/powershell/git-common.ps1 +0 -0
  73. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
  74. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/extensions.yml +0 -0
  75. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/feature.json +0 -0
  76. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/init-options.json +0 -0
  77. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/integration.json +0 -0
  78. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/integrations/claude.manifest.json +0 -0
  79. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/integrations/speckit.manifest.json +0 -0
  80. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/memory/constitution.md +0 -0
  81. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/scripts/bash/check-prerequisites.sh +0 -0
  82. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/scripts/bash/common.sh +0 -0
  83. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/scripts/bash/create-new-feature.sh +0 -0
  84. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/scripts/bash/setup-plan.sh +0 -0
  85. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/scripts/bash/setup-tasks.sh +0 -0
  86. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/scripts/bash/update-agent-context.sh +0 -0
  87. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/templates/agent-file-template.md +0 -0
  88. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/templates/checklist-template.md +0 -0
  89. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/templates/constitution-template.md +0 -0
  90. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/templates/plan-template.md +0 -0
  91. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/templates/spec-template.md +0 -0
  92. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/templates/tasks-template.md +0 -0
  93. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/workflows/speckit/workflow.yml +0 -0
  94. {remo_cli-4.0.1 → remo_cli-4.1.0}/.specify/workflows/workflow-registry.json +0 -0
  95. {remo_cli-4.0.1 → remo_cli-4.1.0}/CONTRIBUTING.md +0 -0
  96. {remo_cli-4.0.1 → remo_cli-4.1.0}/Dockerfile +0 -0
  97. {remo_cli-4.0.1 → remo_cli-4.1.0}/LICENSE +0 -0
  98. {remo_cli-4.0.1 → remo_cli-4.1.0}/README.md +0 -0
  99. {remo_cli-4.0.1 → remo_cli-4.1.0}/SECURITY.md +0 -0
  100. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/.ansible-lint +0 -0
  101. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/README.md +0 -0
  102. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/ansible.cfg +0 -0
  103. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/aws_configure.yml +0 -0
  104. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/aws_resize.yml +0 -0
  105. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/aws_site.yml +0 -0
  106. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/aws_teardown.yml +0 -0
  107. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/group_vars/all.yml +0 -0
  108. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/group_vars/incus_containers.yml +0 -0
  109. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/group_vars/proxmox_containers.yml +0 -0
  110. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/hetzner_configure.yml +0 -0
  111. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/hetzner_provision.yml +0 -0
  112. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/hetzner_resize.yml +0 -0
  113. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/hetzner_site.yml +0 -0
  114. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/hetzner_teardown.yml +0 -0
  115. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/incus_bootstrap.yml +0 -0
  116. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/incus_configure.yml +0 -0
  117. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/incus_provision.yml +0 -0
  118. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/incus_resize.yml +0 -0
  119. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/incus_site.yml +0 -0
  120. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/incus_teardown.yml +0 -0
  121. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/inventory/hosts.yml +0 -0
  122. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/inventory/incus_containers.yml +0 -0
  123. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/proxmox_bootstrap.yml +0 -0
  124. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/proxmox_configure.yml +0 -0
  125. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/proxmox_provision.yml +0 -0
  126. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/proxmox_resize.yml +0 -0
  127. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/proxmox_site.yml +0 -0
  128. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/proxmox_teardown.yml +0 -0
  129. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/requirements.yml +0 -0
  130. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/aws_server/defaults/main.yml +0 -0
  131. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/aws_server/tasks/ebs.yml +0 -0
  132. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/aws_server/tasks/ec2.yml +0 -0
  133. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/aws_server/tasks/main.yml +0 -0
  134. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/aws_server/tasks/patch_manager.yml +0 -0
  135. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/aws_server/tasks/preflight.yml +0 -0
  136. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/aws_server/tasks/security_group.yml +0 -0
  137. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/deacon/defaults/main.yml +0 -0
  138. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/deacon/tasks/main.yml +0 -0
  139. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/devcontainers/tasks/main.yml +0 -0
  140. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/docker/handlers/main.yml +0 -0
  141. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/docker/tasks/main.yml +0 -0
  142. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/fzf/tasks/main.yml +0 -0
  143. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/github_cli/tasks/main.yml +0 -0
  144. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/hetzner_server/defaults/main.yml +0 -0
  145. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/hetzner_server/tasks/main.yml +0 -0
  146. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/incus_bootstrap/defaults/main.yml +0 -0
  147. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/incus_bootstrap/handlers/main.yml +0 -0
  148. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/incus_bootstrap/tasks/main.yml +0 -0
  149. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/incus_container/defaults/main.yml +0 -0
  150. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/incus_container/handlers/main.yml +0 -0
  151. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/incus_container/meta/main.yml +0 -0
  152. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/incus_container/tasks/main.yml +0 -0
  153. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/incus_container/tasks/preflight.yml +0 -0
  154. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/incus_container_teardown/defaults/main.yml +0 -0
  155. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/incus_container_teardown/meta/main.yml +0 -0
  156. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/incus_container_teardown/tasks/main.yml +0 -0
  157. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/nodejs/defaults/main.yml +0 -0
  158. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/nodejs/files/nodesource-repo.gpg.key +0 -0
  159. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/nodejs/tasks/main.yml +0 -0
  160. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/proxmox_bootstrap/defaults/main.yml +0 -0
  161. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/proxmox_bootstrap/meta/main.yml +0 -0
  162. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/proxmox_bootstrap/tasks/main.yml +0 -0
  163. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/proxmox_container/defaults/main.yml +0 -0
  164. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/proxmox_container/handlers/main.yml +0 -0
  165. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/proxmox_container/meta/main.yml +0 -0
  166. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/proxmox_container/tasks/main.yml +0 -0
  167. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/proxmox_container/tasks/preflight.yml +0 -0
  168. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/proxmox_container_teardown/defaults/main.yml +0 -0
  169. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/proxmox_container_teardown/meta/main.yml +0 -0
  170. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/proxmox_container_teardown/tasks/main.yml +0 -0
  171. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/user_setup/defaults/main.yml +0 -0
  172. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/user_setup/tasks/main.yml +0 -0
  173. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/user_setup/templates/devshell.sh.j2 +0 -0
  174. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/user_setup/templates/project-launch.sh.j2 +0 -0
  175. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/user_setup/templates/project-menu.sh.j2 +0 -0
  176. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/user_setup/templates/remo-host.sh.j2 +0 -0
  177. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/zellij/defaults/main.yml +0 -0
  178. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/zellij/tasks/main.yml +0 -0
  179. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/zellij/templates/config.kdl.j2 +0 -0
  180. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/roles/zellij/templates/layouts/minimal.kdl.j2 +0 -0
  181. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/tasks/configure_dev_tools.yml +0 -0
  182. {remo_cli-4.0.1 → remo_cli-4.1.0}/ansible/tasks/repair_apt_sources.yml +0 -0
  183. {remo_cli-4.0.1 → remo_cli-4.1.0}/docker/Dockerfile +0 -0
  184. {remo_cli-4.0.1 → remo_cli-4.1.0}/docker/compose.example.yml +0 -0
  185. {remo_cli-4.0.1 → remo_cli-4.1.0}/docker/entrypoint.sh +0 -0
  186. {remo_cli-4.0.1 → remo_cli-4.1.0}/docs/aws.md +0 -0
  187. {remo_cli-4.0.1 → remo_cli-4.1.0}/docs/feature-history.md +0 -0
  188. {remo_cli-4.0.1 → remo_cli-4.1.0}/docs/hetzner.md +0 -0
  189. {remo_cli-4.0.1 → remo_cli-4.1.0}/docs/incus.md +0 -0
  190. {remo_cli-4.0.1 → remo_cli-4.1.0}/docs/install.sh +0 -0
  191. {remo_cli-4.0.1 → remo_cli-4.1.0}/docs/maintaining-claude-md.md +0 -0
  192. {remo_cli-4.0.1 → remo_cli-4.1.0}/docs/maintaining-generated-types.md +0 -0
  193. {remo_cli-4.0.1 → remo_cli-4.1.0}/docs/providers.md +0 -0
  194. {remo_cli-4.0.1 → remo_cli-4.1.0}/docs/proxmox.md +0 -0
  195. {remo_cli-4.0.1 → remo_cli-4.1.0}/docs/remo-web-speckit-input.md +0 -0
  196. {remo_cli-4.0.1 → remo_cli-4.1.0}/docs/remo-web.html +0 -0
  197. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/index.html +0 -0
  198. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/package-lock.json +0 -0
  199. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/package.json +0 -0
  200. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/scripts/check-types-fresh.mjs +0 -0
  201. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/scripts/generate-frame-types.mjs +0 -0
  202. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/scripts/generate-schema-types.mjs +0 -0
  203. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/api/client.test.ts +0 -0
  204. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/api/client.ts +0 -0
  205. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/api/generated/README.md +0 -0
  206. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/api/generated/openapi.json +0 -0
  207. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/api/generated/schema.d.ts +0 -0
  208. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/api/generated/terminal-frames.d.ts +0 -0
  209. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/api/generated/terminal-frames.json +0 -0
  210. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/api/generated.test.ts +0 -0
  211. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/AppRoot.tsx +0 -0
  212. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/AppShell.css +0 -0
  213. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/AppShell.tsx +0 -0
  214. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/AwaitingAdoption.css +0 -0
  215. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/AwaitingAdoption.tsx +0 -0
  216. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/OfflineOverlay.css +0 -0
  217. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/OfflineOverlay.tsx +0 -0
  218. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/PairToSync.css +0 -0
  219. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/PairToSync.tsx +0 -0
  220. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/SessionRail.css +0 -0
  221. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/SessionRail.tsx +0 -0
  222. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/SettingsPage.css +0 -0
  223. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/SettingsPage.tsx +0 -0
  224. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/ShortcutsModal.css +0 -0
  225. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/ShortcutsModal.tsx +0 -0
  226. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/TerminalCard.css +0 -0
  227. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/TerminalCard.test.tsx +0 -0
  228. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/TopBar.css +0 -0
  229. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/TopBar.tsx +0 -0
  230. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/providerMeta.test.ts +0 -0
  231. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/providerMeta.ts +0 -0
  232. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/railModel.test.ts +0 -0
  233. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/components/railModel.ts +0 -0
  234. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/lib/clipboard.ts +0 -0
  235. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/lib/fullscreen.ts +0 -0
  236. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/main.tsx +0 -0
  237. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/state/discovery.test.ts +0 -0
  238. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/state/discovery.ts +0 -0
  239. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/state/fonts.ts +0 -0
  240. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/state/health.test.ts +0 -0
  241. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/state/health.ts +0 -0
  242. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/state/latency.test.ts +0 -0
  243. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/state/latency.ts +0 -0
  244. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/state/settings.test.ts +0 -0
  245. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/state/settings.ts +0 -0
  246. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/state/useConsoleKeyboard.ts +0 -0
  247. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/terminal/.gitkeep +0 -0
  248. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/terminal/RendererAdapter.ts +0 -0
  249. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/terminal/TerminalConnection.test.ts +0 -0
  250. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/terminal/TerminalConnection.ts +0 -0
  251. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/terminal/XtermRenderer.ts +0 -0
  252. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/terminal/defaultRenderer.ts +0 -0
  253. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/terminal/keymap.test.ts +0 -0
  254. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/terminal/keymap.ts +0 -0
  255. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/test/setup.ts +0 -0
  256. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/theme/fonts.ts +0 -0
  257. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/theme/terminalThemes.test.ts +0 -0
  258. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/theme/terminalThemes.ts +0 -0
  259. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/src/theme/tokens.css +0 -0
  260. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/tsconfig.json +0 -0
  261. {remo_cli-4.0.1 → remo_cli-4.1.0}/frontend/vite.config.ts +0 -0
  262. {remo_cli-4.0.1 → remo_cli-4.1.0}/install.sh +0 -0
  263. {remo_cli-4.0.1 → remo_cli-4.1.0}/maverick.yaml +0 -0
  264. {remo_cli-4.0.1 → remo_cli-4.1.0}/playwright.config.ts +0 -0
  265. {remo_cli-4.0.1 → remo_cli-4.1.0}/project-menu.sh +0 -0
  266. {remo_cli-4.0.1 → remo_cli-4.1.0}/release-please-config.json +0 -0
  267. {remo_cli-4.0.1 → remo_cli-4.1.0}/remo +0 -0
  268. {remo_cli-4.0.1 → remo_cli-4.1.0}/remo.svg +0 -0
  269. {remo_cli-4.0.1 → remo_cli-4.1.0}/run.sh +0 -0
  270. {remo_cli-4.0.1 → remo_cli-4.1.0}/scripts/export_openapi.py +0 -0
  271. {remo_cli-4.0.1 → remo_cli-4.1.0}/scripts/setup-aws-oidc.sh +0 -0
  272. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/checklists/requirements.md +0 -0
  273. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/checklists/validation.md +0 -0
  274. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/contracts/playbook-interface.yaml +0 -0
  275. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/contracts/role-interface.yaml +0 -0
  276. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/data-model.md +0 -0
  277. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/plan.md +0 -0
  278. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/quickstart.md +0 -0
  279. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/research.md +0 -0
  280. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/spec.md +0 -0
  281. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/tasks.md +0 -0
  282. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/002-incus-container-support/checklists/requirements.md +0 -0
  283. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/002-incus-container-support/contracts/playbook-interface.yaml +0 -0
  284. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/002-incus-container-support/contracts/role-interface.yaml +0 -0
  285. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/002-incus-container-support/data-model.md +0 -0
  286. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/002-incus-container-support/plan.md +0 -0
  287. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/002-incus-container-support/quickstart.md +0 -0
  288. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/002-incus-container-support/research.md +0 -0
  289. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/002-incus-container-support/spec.md +0 -0
  290. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/002-incus-container-support/tasks.md +0 -0
  291. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/checklists/requirements.md +0 -0
  292. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/data-model.md +0 -0
  293. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/plan.md +0 -0
  294. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/quickstart.md +0 -0
  295. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/research.md +0 -0
  296. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/spec.md +0 -0
  297. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/tasks.md +0 -0
  298. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/004-proxmox-container-support/spec.md +0 -0
  299. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/005-provider-snapshots/checklists/requirements.md +0 -0
  300. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/005-provider-snapshots/contracts/cli-surface.md +0 -0
  301. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/005-provider-snapshots/data-model.md +0 -0
  302. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/005-provider-snapshots/plan.md +0 -0
  303. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/005-provider-snapshots/quickstart.md +0 -0
  304. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/005-provider-snapshots/research.md +0 -0
  305. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/005-provider-snapshots/spec.md +0 -0
  306. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/005-provider-snapshots/tasks.md +0 -0
  307. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/010-web-session-interface/checklists/requirements.md +0 -0
  308. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/010-web-session-interface/contracts/remo-host-protocol.md +0 -0
  309. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/010-web-session-interface/contracts/rest-api.md +0 -0
  310. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/010-web-session-interface/contracts/terminal-websocket.md +0 -0
  311. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/010-web-session-interface/data-model.md +0 -0
  312. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/010-web-session-interface/plan.md +0 -0
  313. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/010-web-session-interface/quickstart.md +0 -0
  314. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/010-web-session-interface/research.md +0 -0
  315. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/010-web-session-interface/spec.md +0 -0
  316. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/010-web-session-interface/tasks.md +0 -0
  317. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/011-web-adopt/checklists/requirements.md +0 -0
  318. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/011-web-adopt/contracts/cli-web-adopt.md +0 -0
  319. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/011-web-adopt/contracts/setup-api.md +0 -0
  320. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/011-web-adopt/data-model.md +0 -0
  321. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/011-web-adopt/plan.md +0 -0
  322. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/011-web-adopt/quickstart-results.md +0 -0
  323. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/011-web-adopt/quickstart.md +0 -0
  324. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/011-web-adopt/research.md +0 -0
  325. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/011-web-adopt/spec.md +0 -0
  326. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/011-web-adopt/tasks.md +0 -0
  327. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/contracts/cli-web-adopt.md +0 -0
  328. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/contracts/pairing-api.md +0 -0
  329. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/contracts/setup-api.md +0 -0
  330. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/data-model.md +0 -0
  331. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/plan.md +0 -0
  332. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/quickstart.md +0 -0
  333. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/research.md +0 -0
  334. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/spec.md +0 -0
  335. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/tasks.md +0 -0
  336. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/013-managed-instance-tags/contracts/cli-sync.md +0 -0
  337. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/013-managed-instance-tags/contracts/marker-commands.md +0 -0
  338. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/013-managed-instance-tags/data-model.md +0 -0
  339. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/013-managed-instance-tags/plan.md +0 -0
  340. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/013-managed-instance-tags/quickstart.md +0 -0
  341. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/013-managed-instance-tags/research.md +0 -0
  342. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/013-managed-instance-tags/spec.md +0 -0
  343. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/013-managed-instance-tags/tasks.md +0 -0
  344. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/014-register-ssh-host/contracts/add-command.md +0 -0
  345. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/014-register-ssh-host/contracts/remove-command.md +0 -0
  346. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/014-register-ssh-host/data-model.md +0 -0
  347. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/014-register-ssh-host/plan.md +0 -0
  348. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/014-register-ssh-host/quickstart.md +0 -0
  349. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/014-register-ssh-host/research.md +0 -0
  350. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/014-register-ssh-host/spec.md +0 -0
  351. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/014-register-ssh-host/tasks.md +0 -0
  352. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/015-registry-v2/checklists/requirements.md +0 -0
  353. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/015-registry-v2/contracts/mirror-payload-v2.md +0 -0
  354. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/015-registry-v2/contracts/registry-accessor-api.md +0 -0
  355. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/015-registry-v2/contracts/registry-file-v2.md +0 -0
  356. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/015-registry-v2/data-model.md +0 -0
  357. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/015-registry-v2/plan.md +0 -0
  358. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/015-registry-v2/quickstart.md +0 -0
  359. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/015-registry-v2/research.md +0 -0
  360. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/015-registry-v2/spec.md +0 -0
  361. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/015-registry-v2/tasks.md +0 -0
  362. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/016-sync-reconcile/checklists/requirements.md +0 -0
  363. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/016-sync-reconcile/contracts/cli-sync.md +0 -0
  364. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/016-sync-reconcile/contracts/provider-probe.md +0 -0
  365. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/016-sync-reconcile/data-model.md +0 -0
  366. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/016-sync-reconcile/plan.md +0 -0
  367. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/016-sync-reconcile/quickstart.md +0 -0
  368. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/016-sync-reconcile/research.md +0 -0
  369. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/016-sync-reconcile/spec.md +0 -0
  370. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/016-sync-reconcile/tasks.md +0 -0
  371. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/checklists/requirements.md +0 -0
  372. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/contracts/cli-web-push.md +0 -0
  373. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/contracts/cli-web-status.md +0 -0
  374. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/contracts/revocation.md +0 -0
  375. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/contracts/setup-status-marker.md +0 -0
  376. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/data-model.md +0 -0
  377. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/plan.md +0 -0
  378. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/quickstart.md +0 -0
  379. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/research.md +0 -0
  380. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/spec.md +0 -0
  381. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/tasks.md +0 -0
  382. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/baseline-notes.md +0 -0
  383. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/checklists/requirements.md +0 -0
  384. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/contracts/cli-surface.md +0 -0
  385. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/contracts/errors.md +0 -0
  386. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/contracts/lifecycle-templates.md +0 -0
  387. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/contracts/provider-protocol.md +0 -0
  388. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/contracts/sync-merge.md +0 -0
  389. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/data-model.md +0 -0
  390. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/plan.md +0 -0
  391. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/quickstart.md +0 -0
  392. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/research.md +0 -0
  393. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/spec.md +0 -0
  394. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/018-provider-abstraction/tasks.md +0 -0
  395. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/checklists/requirements.md +0 -0
  396. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/contracts/cli-surface-delta.md +0 -0
  397. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/contracts/docs-structure-check.md +0 -0
  398. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/data-model.md +0 -0
  399. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/plan.md +0 -0
  400. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/quickstart.md +0 -0
  401. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/research.md +0 -0
  402. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/spec.md +0 -0
  403. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/tasks.md +0 -0
  404. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/020-openapi-type-generation/checklists/requirements.md +0 -0
  405. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/020-openapi-type-generation/contracts/drift-checks.md +0 -0
  406. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/020-openapi-type-generation/contracts/terminal-frames-v1.md +0 -0
  407. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/020-openapi-type-generation/data-model.md +0 -0
  408. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/020-openapi-type-generation/plan.md +0 -0
  409. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/020-openapi-type-generation/quickstart.md +0 -0
  410. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/020-openapi-type-generation/research.md +0 -0
  411. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/020-openapi-type-generation/spec.md +0 -0
  412. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/020-openapi-type-generation/tasks.md +0 -0
  413. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/021-cli-plane-separation/contracts/cli-surface.md +0 -0
  414. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/021-cli-plane-separation/contracts/descriptor-schema.md +0 -0
  415. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/021-cli-plane-separation/data-model.md +0 -0
  416. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/021-cli-plane-separation/plan.md +0 -0
  417. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/021-cli-plane-separation/quickstart.md +0 -0
  418. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/021-cli-plane-separation/research.md +0 -0
  419. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/021-cli-plane-separation/spec.md +0 -0
  420. {remo_cli-4.0.1 → remo_cli-4.1.0}/specs/021-cli-plane-separation/tasks.md +0 -0
  421. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/__init__.py +0 -0
  422. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/__main__.py +0 -0
  423. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/cli/__init__.py +0 -0
  424. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/cli/added.py +0 -0
  425. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/cli/cp.py +0 -0
  426. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/cli/main.py +0 -0
  427. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/cli/providers/__init__.py +0 -0
  428. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/cli/providers/factory.py +0 -0
  429. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/cli/shell.py +0 -0
  430. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/cli/web.py +0 -0
  431. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/__init__.py +0 -0
  432. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/ansible_runner.py +0 -0
  433. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/completion.py +0 -0
  434. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/config.py +0 -0
  435. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/errors.py +0 -0
  436. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/known_hosts.py +0 -0
  437. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/lifecycle.py +0 -0
  438. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/output.py +0 -0
  439. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/picker.py +0 -0
  440. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/provider_protocol.py +0 -0
  441. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/provider_registry.py +0 -0
  442. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/reconcile.py +0 -0
  443. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/registry.py +0 -0
  444. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/remo_host_client.py +0 -0
  445. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/rsync.py +0 -0
  446. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/snapshot.py +0 -0
  447. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/ssh.py +0 -0
  448. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/validation.py +0 -0
  449. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/version.py +0 -0
  450. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/web_adopt.py +0 -0
  451. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/core/web_drift.py +0 -0
  452. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/models/__init__.py +0 -0
  453. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/models/capability.py +0 -0
  454. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/models/discovery.py +0 -0
  455. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/models/host.py +0 -0
  456. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/models/session_target.py +0 -0
  457. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/models/snapshot.py +0 -0
  458. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/providers/__init__.py +0 -0
  459. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/providers/added.py +0 -0
  460. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/providers/aws.py +0 -0
  461. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/providers/aws_descriptor.py +0 -0
  462. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/providers/builtin.py +0 -0
  463. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/providers/hetzner.py +0 -0
  464. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/providers/hetzner_descriptor.py +0 -0
  465. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/providers/incus.py +0 -0
  466. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/providers/incus_descriptor.py +0 -0
  467. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/providers/proxmox.py +0 -0
  468. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/providers/proxmox_descriptor.py +0 -0
  469. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/__init__.py +0 -0
  470. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/api/__init__.py +0 -0
  471. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/api/hosts.py +0 -0
  472. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/api/pairing.py +0 -0
  473. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/api/setup.py +0 -0
  474. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/api/terminals.py +0 -0
  475. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/app.py +0 -0
  476. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/check.py +0 -0
  477. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/config.py +0 -0
  478. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/discovery.py +0 -0
  479. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/frames.py +0 -0
  480. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/health.py +0 -0
  481. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/logging_config.py +0 -0
  482. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/models.py +0 -0
  483. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/operator_auth.py +0 -0
  484. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/pairing.py +0 -0
  485. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/ssh_master.py +0 -0
  486. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/state.py +0 -0
  487. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/terminal.py +0 -0
  488. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/terminal_registry.py +0 -0
  489. {remo_cli-4.0.1 → remo_cli-4.1.0}/src/remo_cli/web/tokens.py +0 -0
  490. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/__init__.py +0 -0
  491. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/ansible/__init__.py +0 -0
  492. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/ansible/test_apt_sources_repair.py +0 -0
  493. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/ansible/test_authorized_keys_bootstrap.py +0 -0
  494. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/ansible/test_remo_host_idempotency.py +0 -0
  495. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/conftest.py +0 -0
  496. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/e2e/fixtures.ts +0 -0
  497. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/e2e/keyboard-routing.spec.ts +0 -0
  498. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/e2e/mobile.spec.ts +0 -0
  499. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/e2e/reconnect.spec.ts +0 -0
  500. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/e2e/terminal-shell-io.spec.ts +0 -0
  501. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/e2e/terminal-tui-compatibility.spec.ts +0 -0
  502. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/e2e/workspace-layout.spec.ts +0 -0
  503. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/image/__init__.py +0 -0
  504. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/image/test_docker_image.py +0 -0
  505. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/__init__.py +0 -0
  506. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/_nine_terminal_fixture.py +0 -0
  507. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/fish_completion.sh +0 -0
  508. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/orbstack-cloud-init.yml +0 -0
  509. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/orbstack.sh +0 -0
  510. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/test_nine_terminals.py +0 -0
  511. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/test_nine_terminals_soak.py +0 -0
  512. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/test_registry_concurrency.py +0 -0
  513. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/test_remo_host_e2e.py +0 -0
  514. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/test_security_rejections.py +0 -0
  515. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/test_setup_payload_versions.py +0 -0
  516. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/test_sync_reconcile.py +0 -0
  517. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/test_terminal_attach.py +0 -0
  518. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/test_web_adopt_e2e.py +0 -0
  519. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/integration/test_web_cli_parity.py +0 -0
  520. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/perf/__init__.py +0 -0
  521. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/perf/test_latency.py +0 -0
  522. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/perf/test_registry_perf.py +0 -0
  523. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/__init__.py +0 -0
  524. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/__init__.py +0 -0
  525. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/providers/test_aws_snapshot.py +0 -0
  526. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/providers/test_hetzner_snapshot.py +0 -0
  527. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/providers/test_incus_snapshot.py +0 -0
  528. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/providers/test_incus_sync_all.py +0 -0
  529. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/providers/test_proxmox_snapshot.py +0 -0
  530. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/providers/test_proxmox_sync_all.py +0 -0
  531. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/surface_baseline.py +0 -0
  532. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/test_add_cmd.py +0 -0
  533. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/test_cli_uniformity.py +0 -0
  534. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/test_cp.py +0 -0
  535. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/test_main.py +0 -0
  536. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/test_remove_cmd.py +0 -0
  537. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/test_shell.py +0 -0
  538. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/test_shell_added.py +0 -0
  539. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/test_startup_imports.py +0 -0
  540. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/test_surface_preservation.py +0 -0
  541. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/cli/test_web_adopt_cmd.py +0 -0
  542. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/__init__.py +0 -0
  543. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_ansible_runner.py +0 -0
  544. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_completion.py +0 -0
  545. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_config.py +0 -0
  546. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_errors.py +0 -0
  547. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_known_hosts.py +0 -0
  548. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_lifecycle.py +0 -0
  549. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_migration_tagging_notice.py +0 -0
  550. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_output.py +0 -0
  551. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_picker.py +0 -0
  552. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_provider_registry.py +0 -0
  553. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_reconcile.py +0 -0
  554. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_registry_format.py +0 -0
  555. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_registry_locking.py +0 -0
  556. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_registry_migration.py +0 -0
  557. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_remo_host_client.py +0 -0
  558. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_rsync.py +0 -0
  559. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_snapshot.py +0 -0
  560. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_ssh.py +0 -0
  561. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_ssh_added.py +0 -0
  562. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_ssh_controlpath.py +0 -0
  563. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_ssh_identity_opts.py +0 -0
  564. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_validation.py +0 -0
  565. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_version.py +0 -0
  566. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_web_adopt_authorize.py +0 -0
  567. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_web_adopt_code.py +0 -0
  568. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_web_adopt_payload.py +0 -0
  569. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_web_adopt_trust.py +0 -0
  570. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_web_drift.py +0 -0
  571. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_web_push.py +0 -0
  572. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/core/test_web_push_self_heal.py +0 -0
  573. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/models/__init__.py +0 -0
  574. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/models/test_capability.py +0 -0
  575. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/models/test_discovery.py +0 -0
  576. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/models/test_session_target.py +0 -0
  577. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/__init__.py +0 -0
  578. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/fake_provider.py +0 -0
  579. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_added_add.py +0 -0
  580. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_added_provider_guard.py +0 -0
  581. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_added_update_remove.py +0 -0
  582. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_added_verify.py +0 -0
  583. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_aws_provider_protocol.py +0 -0
  584. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_aws_snapshot.py +0 -0
  585. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_aws_sync.py +0 -0
  586. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_aws_upgrade_resize.py +0 -0
  587. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_hetzner_entry_adapters.py +0 -0
  588. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_hetzner_http.py +0 -0
  589. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_hetzner_label.py +0 -0
  590. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_hetzner_resize.py +0 -0
  591. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_hetzner_snapshot.py +0 -0
  592. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_hetzner_sync.py +0 -0
  593. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_host_user_consistency.py +0 -0
  594. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_incus_entry_adapters.py +0 -0
  595. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_incus_marker.py +0 -0
  596. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_incus_resize.py +0 -0
  597. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_incus_snapshot.py +0 -0
  598. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_incus_sync.py +0 -0
  599. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_missing_sdk.py +0 -0
  600. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_provider_conformance.py +0 -0
  601. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_provider_registry_entries.py +0 -0
  602. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_proxmox_devcontainer_runtime.py +0 -0
  603. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_proxmox_marker.py +0 -0
  604. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_proxmox_provider_protocol.py +0 -0
  605. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_proxmox_resize.py +0 -0
  606. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_proxmox_snapshot.py +0 -0
  607. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/providers/test_proxmox_sync.py +0 -0
  608. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/test_ansible_templates.py +0 -0
  609. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/test_architecture.py +0 -0
  610. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/test_docs_structure.py +0 -0
  611. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/test_host_model.py +0 -0
  612. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/test_host_ssh_type.py +0 -0
  613. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/test_lockfile_version.py +0 -0
  614. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/test_schema_drift.py +0 -0
  615. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/__init__.py +0 -0
  616. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/_pairing_support.py +0 -0
  617. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/conftest.py +0 -0
  618. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/fixtures/hosts_response.json +0 -0
  619. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/fixtures/sessions_response.json +0 -0
  620. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_backpressure.py +0 -0
  621. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_check.py +0 -0
  622. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_csp.py +0 -0
  623. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_discovery_refresh_force.py +0 -0
  624. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_frames.py +0 -0
  625. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_health_states.py +0 -0
  626. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_hosts_mapping.py +0 -0
  627. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_lazy_import.py +0 -0
  628. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_log_redaction.py +0 -0
  629. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_mint_gating.py +0 -0
  630. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_operator_auth.py +0 -0
  631. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_pairing.py +0 -0
  632. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_pairing_origin.py +0 -0
  633. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_payload_equivalence.py +0 -0
  634. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_registry_readonly.py +0 -0
  635. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_setup_api.py +0 -0
  636. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_setup_auth.py +0 -0
  637. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_setup_dormancy.py +0 -0
  638. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_state.py +0 -0
  639. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_terminal_resize.py +0 -0
  640. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_terminals_api.py +0 -0
  641. {remo_cli-4.0.1 → remo_cli-4.1.0}/tests/unit/web/test_tokens.py +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "4.1.0"
3
+ }
@@ -117,6 +117,7 @@ frontend/ # remo-web browser SPA (Vite + React + TypeScript)
117
117
  ├── src/
118
118
  │ ├── api/client.ts # REST + WS terminal client (remo-terminal.v1 subprotocol)
119
119
  │ ├── components/ # Dashboard, InstanceGroup, TargetCard, GridView, TabView, TerminalCard
120
+ │ │ # + masterLayout.ts (pure pane geometry: uniform grid + master/stack tiling)
120
121
  │ ├── state/ # discovery.ts, workspace.ts (layout persisted to localStorage)
121
122
  │ └── terminal/ # RendererAdapter (seam), XtermRenderer (the one engine), TerminalConnection, keymap
122
123
 
@@ -135,7 +136,8 @@ ansible/ # Ansible playbooks (invoked by Python via subprocess
135
136
  └── requirements.yml
136
137
 
137
138
  scripts/ # Repo-root utility scripts (not part of the installed package)
138
- └── export_openapi.py # Exports frontend/src/api/generated/{openapi.json,terminal-frames.json} (feature 020)
139
+ ├── export_openapi.py # Exports frontend/src/api/generated/{openapi.json,terminal-frames.json} (feature 020)
140
+ └── palette-check.sh # Prints every ANSI colour x normal/bold/dim/bright in a live terminal — audits a theme's legibility where contrast maths can't
139
141
 
140
142
  pyproject.toml # Build config, dependencies (incl. `web` extra), console_scripts entry point
141
143
  ```
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.1.0](https://github.com/get2knowio/remo/compare/v4.0.1...v4.1.0) (2026-08-04)
4
+
5
+
6
+ ### Features
7
+
8
+ * **web:** tile a terminal to a pane edge as a master area ([#137](https://github.com/get2knowio/remo/issues/137)) ([41802fb](https://github.com/get2knowio/remo/commit/41802fb17ae5741918a31ee9d662aa90808ebb9a))
9
+
3
10
  ## [4.0.1](https://github.com/get2knowio/remo/compare/v4.0.0...v4.0.1) (2026-08-03)
4
11
 
5
12
 
@@ -117,6 +117,7 @@ frontend/ # remo-web browser SPA (Vite + React + TypeScript)
117
117
  ├── src/
118
118
  │ ├── api/client.ts # REST + WS terminal client (remo-terminal.v1 subprotocol)
119
119
  │ ├── components/ # Dashboard, InstanceGroup, TargetCard, GridView, TabView, TerminalCard
120
+ │ │ # + masterLayout.ts (pure pane geometry: uniform grid + master/stack tiling)
120
121
  │ ├── state/ # discovery.ts, workspace.ts (layout persisted to localStorage), settings.ts (display prefs: site light/dark mode, accent, fonts, terminal theme + per-target overrides)
121
122
  │ ├── terminal/ # RendererAdapter (seam), XtermRenderer (the one engine), TerminalConnection, keymap
122
123
  │ └── theme/ # tokens.css (light-dark() palette, one pair per token), fonts.ts, terminalThemes.ts (8 terminal color schemes: a token-derived Remo Dark/Light pair the default 'auto' selection tracks, plus 6 curated third-party)
@@ -136,7 +137,8 @@ ansible/ # Ansible playbooks (invoked by Python via subprocess
136
137
  └── requirements.yml
137
138
 
138
139
  scripts/ # Repo-root utility scripts (not part of the installed package)
139
- └── export_openapi.py # Exports frontend/src/api/generated/{openapi.json,terminal-frames.json} (feature 020)
140
+ ├── export_openapi.py # Exports frontend/src/api/generated/{openapi.json,terminal-frames.json} (feature 020)
141
+ └── palette-check.sh # Prints every ANSI colour x normal/bold/dim/bright in a live terminal — audits a theme's legibility where contrast maths can't
140
142
 
141
143
  pyproject.toml # Build config, dependencies (incl. `web` extra), console_scripts entry point
142
144
  ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remo-cli
3
- Version: 4.0.1
3
+ Version: 4.1.0
4
4
  Summary: Remote development environment CLI
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -108,7 +108,16 @@ The SPA is a two-pane **web console**:
108
108
  row (or its `+` button) **adds** it to a responsive grid (1/2/3 columns by count). In a grid, **drag
109
109
  a tile's header onto another to swap their positions** — a window outline follows the cursor and the
110
110
  swap target shows a dashed outline (mouse or touch press-and-hold; keyboard-accessible via the
111
- handle); the arrangement persists until the grid is rebuilt. In a grid, **resting the pointer on a
111
+ handle); the arrangement persists until the grid is rebuilt. **Drag a tile to a pane edge instead of
112
+ onto another tile** and it takes that whole side as a *master area* — full height on the left/right,
113
+ full width on the top/bottom — with the remaining tiles tiling the leftover strip (the dwm/xmonad
114
+ master+stack model). A translucent band marks each edge while dragging and fills in when the drop
115
+ would land there. The **▦** control in a tile's header cycles the same thing without a drag
116
+ (▌ 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
119
+ what lets three terminals fill the pane without the empty fourth cell a 2x2 grid would leave. In a
120
+ grid, **resting the pointer on a
112
121
  tile focuses it** (focus-follows-mouse, with a short dwell so passing through tiles doesn't steal
113
122
  focus) — keystrokes go where the pointer is, no click needed. The **◻** control solos a tile; **Esc**
114
123
  collapses the grid back to the focused terminal;
@@ -30,6 +30,7 @@ import {
30
30
  type TerminalFontOptions,
31
31
  } from "../state/settings";
32
32
  import { TERMINAL_THEMES, type TerminalThemeColors } from "../theme/terminalThemes";
33
+ import type { MasterSide } from "../state/workspace";
33
34
  import { removeLatency, reportLatency } from "../state/latency";
34
35
  import type { RendererAdapter } from "../terminal/RendererAdapter";
35
36
  import { createDefaultRenderer } from "../terminal/defaultRenderer";
@@ -41,6 +42,16 @@ const DEFAULT_ROWS = 24;
41
42
  /** How much to shrink the terminal font in a grid tile when "scale to fit". */
42
43
  const GRID_FIT_SCALE = 0.8;
43
44
 
45
+ /** Glyph + label per tiling state. The glyph is a half block drawing where the
46
+ * master area sits, so the button reads as a picture of the layout. */
47
+ const MASTER_GLYPH: Record<MasterSide | "grid", { glyph: string; title: string }> = {
48
+ grid: { glyph: "▦", title: "Tile this terminal to the left" },
49
+ left: { glyph: "▌", title: "Mastering the left — tile to the top" },
50
+ top: { glyph: "▀", title: "Mastering the top — tile to the right" },
51
+ right: { glyph: "▐", title: "Mastering the right — tile to the bottom" },
52
+ bottom: { glyph: "▄", title: "Mastering the bottom — back to the even grid" },
53
+ };
54
+
44
55
  const STATE_LABELS: Record<TerminalConnectionState, string> = {
45
56
  connecting: "Connecting…",
46
57
  ready: "Connected",
@@ -70,6 +81,16 @@ interface TerminalCardProps {
70
81
  /** When true, the header acts as a dnd-kit drag handle and the tile is a drop
71
82
  * target (grid reorder). The DndContext + swap live in WorkspacePane. */
72
83
  reorderEnabled?: boolean;
84
+ /** This tile's slot in a master/stack tiling, as a CSS `grid-area`. Undefined
85
+ * in the uniform grid, where tiles auto-place exactly as they always have.
86
+ * Applied to the card's OWN root element — never a wrapper, see WorkspacePane. */
87
+ gridArea?: string;
88
+ /** Which side this tile masters, or null when it is a plain stack tile.
89
+ * Undefined hides the tiling control entirely (single view, fullscreen). */
90
+ masterSide?: MasterSide | null;
91
+ /** Advance this tile through the tiling cycle (left -> top -> right -> bottom
92
+ * -> uniform grid). */
93
+ onCycleTile?: () => void;
73
94
  /** Window-control "Normal": solo this terminal into the single view. */
74
95
  onNormal: () => void;
75
96
  /** Window-control "Grid": show the grid — omitted when none is available. */
@@ -173,6 +194,9 @@ export function TerminalCard({
173
194
  viewState,
174
195
  onClose,
175
196
  reorderEnabled,
197
+ gridArea,
198
+ masterSide,
199
+ onCycleTile,
176
200
  onNormal,
177
201
  onGrid,
178
202
  onToggleFullscreen,
@@ -544,6 +568,7 @@ export function TerminalCard({
544
568
  style={
545
569
  {
546
570
  display: isVisible ? undefined : "none",
571
+ gridArea,
547
572
  "--bg-term": theme.colors.background,
548
573
  } as CSSProperties
549
574
  }
@@ -672,6 +697,26 @@ export function TerminalCard({
672
697
  </div>
673
698
  )}
674
699
  </div>
700
+ {/* Tiling control. Cycles rather than opening a menu: it is one tap on
701
+ * a touch device, where dragging to a 64px edge band with a thumb is
702
+ * the awkward path. The half-block glyphs draw where the master area
703
+ * lands, so the current state is legible without the tooltip. */}
704
+ {onCycleTile && (
705
+ <button
706
+ type="button"
707
+ className={`tc-btn tc-btn--icon${masterSide ? " tc-btn--active" : ""}`}
708
+ data-testid={`terminal-tile-${target.id}`}
709
+ title={MASTER_GLYPH[masterSide ?? "grid"].title}
710
+ aria-label={MASTER_GLYPH[masterSide ?? "grid"].title}
711
+ aria-pressed={Boolean(masterSide)}
712
+ onClick={(e) => {
713
+ e.stopPropagation();
714
+ onCycleTile();
715
+ }}
716
+ >
717
+ {MASTER_GLYPH[masterSide ?? "grid"].glyph}
718
+ </button>
719
+ )}
675
720
  {/* Window-control cluster: mutually-exclusive display modes ordered
676
721
  * by how much space they take — Grid (smaller/tiled) → Normal (fills
677
722
  * the app's main pane) → Fullscreen (whole window) — plus close. The
@@ -0,0 +1,139 @@
1
+ .workspace {
2
+ flex: 1;
3
+ min-width: 0;
4
+ min-height: 0;
5
+ display: flex;
6
+ flex-direction: column;
7
+ background: var(--bg-pane);
8
+ }
9
+
10
+ .workspace-body {
11
+ flex: 1;
12
+ min-height: 0;
13
+ padding: 12px;
14
+ gap: 10px;
15
+ }
16
+
17
+ .workspace-body--single {
18
+ display: flex;
19
+ flex-direction: column;
20
+ }
21
+
22
+ .workspace-body--grid {
23
+ display: grid;
24
+ }
25
+
26
+ /* Containing block for the snap zones below, and a stop for touch scroll
27
+ * chaining while dragging a tile around. */
28
+ .workspace-body {
29
+ position: relative;
30
+ overscroll-behavior: contain;
31
+ }
32
+
33
+ /* --- edge drop targets (master/stack tiling) ---
34
+ *
35
+ * `pointer-events: none` is load-bearing, not a tidy-up: dnd-kit resolves drops
36
+ * from measured rects rather than hit-testing, so these can blanket the pane
37
+ * edges without intercepting anything — including the rail's resize handle,
38
+ * which overhangs 3px into this pane's left edge.
39
+ *
40
+ * z-index 20 sits in the free band between the rail divider (8) and the terminal
41
+ * theme popup (60). */
42
+ .workspace-snap {
43
+ position: absolute;
44
+ z-index: 20;
45
+ pointer-events: none;
46
+ opacity: 0;
47
+ border-radius: var(--radius-lg);
48
+ border: 2px dashed transparent;
49
+ background: color-mix(in oklch, var(--accent) 14%, transparent);
50
+ }
51
+ @media (prefers-reduced-motion: no-preference) {
52
+ .workspace-snap {
53
+ transition: opacity 120ms ease;
54
+ }
55
+ }
56
+ /* Visible but quiet while a drag is in flight, solid once it would land here. */
57
+ .workspace-snap--armed {
58
+ opacity: 0.4;
59
+ }
60
+ .workspace-snap--over {
61
+ opacity: 1;
62
+ border-color: var(--accent-strong);
63
+ background: color-mix(in oklch, var(--accent) 28%, transparent);
64
+ }
65
+
66
+ .workspace-snap--left,
67
+ .workspace-snap--right {
68
+ top: 0;
69
+ bottom: 0;
70
+ width: min(64px, 18%);
71
+ }
72
+ .workspace-snap--left {
73
+ left: 0;
74
+ }
75
+ .workspace-snap--right {
76
+ right: 0;
77
+ }
78
+
79
+ /* Inset horizontally by the same band, so the CORNERS belong unambiguously to
80
+ * left/right — the vertical split is what this feature is for, and an overlap
81
+ * would resolve a corner by centre distance, i.e. arbitrarily. */
82
+ .workspace-snap--top,
83
+ .workspace-snap--bottom {
84
+ left: min(64px, 18%);
85
+ right: min(64px, 18%);
86
+ height: min(64px, 18%);
87
+ }
88
+ .workspace-snap--top {
89
+ top: 0;
90
+ }
91
+ .workspace-snap--bottom {
92
+ bottom: 0;
93
+ }
94
+
95
+ /* Empty state */
96
+ .workspace-empty {
97
+ flex: 1;
98
+ display: flex;
99
+ flex-direction: column;
100
+ align-items: center;
101
+ justify-content: center;
102
+ text-align: center;
103
+ padding: 24px;
104
+ color: var(--text-dim);
105
+ }
106
+
107
+ .workspace-empty-art {
108
+ font-family: var(--font-mono);
109
+ font-size: 13px;
110
+ line-height: 1.25;
111
+ color: color-mix(in oklch, var(--accent) 55%, var(--text-faint));
112
+ margin: 0 0 18px;
113
+ }
114
+
115
+ .workspace-empty-title {
116
+ font-size: 15px;
117
+ font-weight: 600;
118
+ color: var(--text-1);
119
+ margin-bottom: 7px;
120
+ }
121
+
122
+ .workspace-empty-text {
123
+ font-size: 12.5px;
124
+ max-width: 340px;
125
+ line-height: 1.6;
126
+ margin: 0 0 4px;
127
+ }
128
+
129
+ .workspace-empty-text kbd,
130
+ .workspace-empty-text span {
131
+ color: var(--text-1);
132
+ font-family: var(--font-mono);
133
+ }
134
+
135
+ .workspace-empty-hint {
136
+ font-size: 11.5px;
137
+ color: var(--text-faint);
138
+ font-family: var(--font-mono);
139
+ }
@@ -0,0 +1,175 @@
1
+ // WorkspacePane's wiring: does the computed layout actually reach the DOM, and
2
+ // — the one that matters — can a layout change ever remount a TerminalCard?
3
+ //
4
+ // A remount re-runs the card's [target.id] effect, whose cleanup calls
5
+ // connection.close() and adapter.dispose(): the SSH connection drops and the
6
+ // scrollback goes with it. Master/stack changes which slot a tile occupies on
7
+ // every mastership change, so this is the feature's central structural risk.
8
+ // The guarantee is "one flat keyed child list; slots are grid-area styles on
9
+ // each card's own root, never wrapper elements", and the test below is what
10
+ // proves it rather than merely documenting it.
11
+
12
+ import { act, render, screen } from "@testing-library/react";
13
+ import { beforeEach, describe, expect, it, vi } from "vitest";
14
+ import type { SessionTarget } from "../api/client";
15
+
16
+ const adapters: { dispose: ReturnType<typeof vi.fn> }[] = [];
17
+
18
+ vi.mock("../terminal/defaultRenderer", () => ({
19
+ createDefaultRenderer: vi.fn(() => {
20
+ const adapter = {
21
+ open: vi.fn(),
22
+ write: vi.fn(),
23
+ onData: vi.fn().mockReturnValue(() => {}),
24
+ fit: vi.fn().mockReturnValue({ cols: 80, rows: 24 }),
25
+ resize: vi.fn(),
26
+ applyFont: vi.fn(),
27
+ applyTheme: vi.fn(),
28
+ focus: vi.fn(),
29
+ onTitleChange: vi.fn().mockReturnValue(() => {}),
30
+ onSelectionChange: vi.fn().mockReturnValue(() => {}),
31
+ getSelection: vi.fn().mockReturnValue(null),
32
+ copySelection: vi.fn().mockResolvedValue(false),
33
+ dispose: vi.fn(),
34
+ };
35
+ adapters.push(adapter);
36
+ return adapter;
37
+ }),
38
+ }));
39
+
40
+ vi.mock("../terminal/TerminalConnection", () => ({
41
+ TerminalConnection: class {
42
+ needsManualReconnect = false;
43
+ connect = vi.fn().mockResolvedValue(undefined);
44
+ close = vi.fn().mockResolvedValue(undefined);
45
+ sendInput = vi.fn();
46
+ sendResize = vi.fn();
47
+ },
48
+ }));
49
+
50
+ // jsdom has neither; useDroppable and TerminalCard both observe their nodes.
51
+ class NoopResizeObserver {
52
+ observe(): void {}
53
+ unobserve(): void {}
54
+ disconnect(): void {}
55
+ }
56
+ globalThis.ResizeObserver = NoopResizeObserver as unknown as typeof ResizeObserver;
57
+
58
+ const target = (id: string): SessionTarget =>
59
+ ({ id, project: id, instance_type: "incus", instance_name: "box" }) as unknown as SessionTarget;
60
+
61
+ async function mount(ids: string[]) {
62
+ vi.resetModules();
63
+ window.localStorage.clear();
64
+ adapters.length = 0;
65
+ const workspaceMod = await import("../state/workspace");
66
+ const { WorkspacePane } = await import("./WorkspacePane");
67
+ const targetsById = new Map(ids.map((id) => [id, target(id)]));
68
+
69
+ const view = render(
70
+ <WorkspacePane
71
+ targetsById={targetsById}
72
+ regionByKey={new Map()}
73
+ onTerminalEnded={() => {}}
74
+ onTerminalStarted={() => {}}
75
+ narrow={false}
76
+ />,
77
+ );
78
+ // Drive the store directly: the rail isn't mounted here.
79
+ const store = renderStore(workspaceMod);
80
+ act(() => store.openMany(ids.map(target)));
81
+ return { view, store };
82
+ }
83
+
84
+ /** The store's actions, outside React. `useWorkspace` returns them, but this
85
+ * file never renders a consumer of its own. */
86
+ function renderStore(mod: typeof import("../state/workspace")) {
87
+ let api!: ReturnType<typeof mod.useWorkspace>;
88
+ function Probe(): null {
89
+ api = mod.useWorkspace();
90
+ return null;
91
+ }
92
+ render(<Probe />);
93
+ return new Proxy({} as ReturnType<typeof mod.useWorkspace>, {
94
+ get: (_t, key) => api[key as keyof typeof api],
95
+ });
96
+ }
97
+
98
+ const body = (): HTMLElement => screen.getByTestId("workspace").querySelector(".workspace-body")!;
99
+
100
+ beforeEach(() => {
101
+ window.localStorage.clear();
102
+ });
103
+
104
+ describe("WorkspacePane layout wiring", () => {
105
+ it("renders the uniform grid exactly as before when nothing is tiled", async () => {
106
+ await mount(["a", "b", "c"]);
107
+ expect(body().style.gridTemplateColumns).toBe("repeat(2, minmax(0, 1fr))");
108
+ expect(body().style.gridTemplateRows).toBe("repeat(2, minmax(0, 1fr))");
109
+ // No explicit placement: tiles auto-place, as they always have.
110
+ expect(screen.getByTestId("terminal-card-a").style.gridArea).toBe("");
111
+ });
112
+
113
+ it("pushes the master template and every tile's slot into the DOM", async () => {
114
+ const { store } = await mount(["a", "b", "c"]);
115
+ act(() => store.setMaster("c", "right"));
116
+
117
+ expect(body().style.gridTemplateColumns).toBe("repeat(1, minmax(0, 40fr)) minmax(0, 60fr)");
118
+ expect(screen.getByTestId("terminal-card-c").style.gridArea).toBe("1 / 2 / 3 / 3");
119
+ expect(screen.getByTestId("terminal-card-a").style.gridArea).toBe("1 / 1 / 2 / 2");
120
+ expect(screen.getByTestId("terminal-card-b").style.gridArea).toBe("2 / 1 / 3 / 2");
121
+ });
122
+
123
+ it("offers the four edge zones only while a reorder is possible", async () => {
124
+ const { store } = await mount(["a", "b", "c"]);
125
+ expect(screen.getAllByTestId(/^snap-zone-/)).toHaveLength(4);
126
+
127
+ // Single view: nothing to reorder, nothing to snap onto.
128
+ act(() => store.selectOnly(target("a")));
129
+ expect(screen.queryAllByTestId(/^snap-zone-/)).toHaveLength(0);
130
+ });
131
+
132
+ it("hides the edge zones while a terminal is fullscreen", async () => {
133
+ const { store } = await mount(["a", "b"]);
134
+ act(() => store.maximize("a"));
135
+ expect(screen.queryAllByTestId(/^snap-zone-/)).toHaveLength(0);
136
+ });
137
+
138
+ // THE structural guarantee. Mutation: wrap the master (or the stack) in a
139
+ // slot <div> in WorkspacePane and this must fail — a card changing parent
140
+ // remounts, dropping its SSH connection and scrollback.
141
+ it("never remounts a terminal across layout changes", async () => {
142
+ const { store } = await mount(["a", "b", "c"]);
143
+ const surfaceBefore = screen.getByTestId("terminal-surface-a");
144
+ const buildsBefore = adapters.length;
145
+
146
+ act(() => store.setMaster("a", "left"));
147
+ act(() => store.setMaster("a", "top"));
148
+ act(() => store.setMaster("b", "right"));
149
+ act(() => store.clearMaster());
150
+
151
+ expect(Object.is(screen.getByTestId("terminal-surface-a"), surfaceBefore)).toBe(true);
152
+ expect(adapters).toHaveLength(buildsBefore);
153
+ for (const adapter of adapters) {
154
+ expect(adapter.dispose).not.toHaveBeenCalled();
155
+ }
156
+ });
157
+
158
+ it("cycles a tile through the sides from its header control", async () => {
159
+ const { store } = await mount(["a", "b"]);
160
+ const button = (): HTMLElement => screen.getByTestId("terminal-tile-a");
161
+
162
+ act(() => button().click());
163
+ expect(store.layout).toMatchObject({ kind: "master", id: "a", side: "left" });
164
+ expect(button().getAttribute("aria-pressed")).toBe("true");
165
+
166
+ act(() => button().click());
167
+ expect(store.layout).toMatchObject({ side: "top" });
168
+
169
+ act(() => button().click());
170
+ act(() => button().click());
171
+ act(() => button().click());
172
+ expect(store.layout).toEqual({ kind: "grid" });
173
+ expect(button().getAttribute("aria-pressed")).toBe("false");
174
+ });
175
+ });