remo-cli 2.2.0rc10__tar.gz → 2.2.0rc12__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 (440) hide show
  1. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/PKG-INFO +1 -1
  2. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/docs/web-session-interface.md +11 -3
  3. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/package-lock.json +29 -0
  4. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/package.json +2 -0
  5. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/TerminalCard.tsx +59 -1
  6. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/terminal/GhosttyRenderer.ts +15 -1
  7. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/terminal/RendererAdapter.ts +6 -0
  8. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/terminal/XtermRenderer.ts +36 -1
  9. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/terminal/keymap.test.ts +18 -1
  10. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/terminal/keymap.ts +15 -0
  11. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/pyproject.toml +1 -1
  12. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/uv.lock +1 -1
  13. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-analyze/SKILL.md +0 -0
  14. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-checklist/SKILL.md +0 -0
  15. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-clarify/SKILL.md +0 -0
  16. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-constitution/SKILL.md +0 -0
  17. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-converge/SKILL.md +0 -0
  18. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-git-commit/SKILL.md +0 -0
  19. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-git-feature/SKILL.md +0 -0
  20. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-git-initialize/SKILL.md +0 -0
  21. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-git-remote/SKILL.md +0 -0
  22. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-git-validate/SKILL.md +0 -0
  23. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-implement/SKILL.md +0 -0
  24. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-plan/SKILL.md +0 -0
  25. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-specify/SKILL.md +0 -0
  26. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-tasks/SKILL.md +0 -0
  27. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.claude/skills/speckit-taskstoissues/SKILL.md +0 -0
  28. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.devcontainer/devcontainer.json +0 -0
  29. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.dockerignore +0 -0
  30. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.github/dependabot.yml +0 -0
  31. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.github/workflows/ci.yml +0 -0
  32. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.github/workflows/provision.yml +0 -0
  33. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.github/workflows/release.yml +0 -0
  34. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.github/workflows/smoke-test-cleanup.yml +0 -0
  35. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.github/workflows/smoke-test.yml +0 -0
  36. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.github/workflows/teardown.yml +0 -0
  37. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.gitignore +0 -0
  38. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.maverick/checkpoints/feature/implementation_complete.json +0 -0
  39. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.maverick/checkpoints/feature/phase_${{ index }}_complete.json +0 -0
  40. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.maverick/checkpoints/feature/validation_complete.json +0 -0
  41. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/.registry +0 -0
  42. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/README.md +0 -0
  43. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/commands/speckit.git.commit.md +0 -0
  44. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/commands/speckit.git.feature.md +0 -0
  45. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/commands/speckit.git.initialize.md +0 -0
  46. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/commands/speckit.git.remote.md +0 -0
  47. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/commands/speckit.git.validate.md +0 -0
  48. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/config-template.yml +0 -0
  49. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/extension.yml +0 -0
  50. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/git-config.yml +0 -0
  51. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/scripts/bash/auto-commit.sh +0 -0
  52. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/scripts/bash/create-new-feature.sh +0 -0
  53. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/scripts/bash/git-common.sh +0 -0
  54. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/scripts/bash/initialize-repo.sh +0 -0
  55. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
  56. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/scripts/powershell/create-new-feature.ps1 +0 -0
  57. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/scripts/powershell/git-common.ps1 +0 -0
  58. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
  59. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/extensions.yml +0 -0
  60. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/feature.json +0 -0
  61. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/init-options.json +0 -0
  62. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/integration.json +0 -0
  63. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/integrations/claude.manifest.json +0 -0
  64. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/integrations/speckit.manifest.json +0 -0
  65. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/memory/constitution.md +0 -0
  66. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/scripts/bash/check-prerequisites.sh +0 -0
  67. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/scripts/bash/common.sh +0 -0
  68. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/scripts/bash/create-new-feature.sh +0 -0
  69. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/scripts/bash/setup-plan.sh +0 -0
  70. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/scripts/bash/setup-tasks.sh +0 -0
  71. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/scripts/bash/update-agent-context.sh +0 -0
  72. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/templates/agent-file-template.md +0 -0
  73. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/templates/checklist-template.md +0 -0
  74. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/templates/constitution-template.md +0 -0
  75. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/templates/plan-template.md +0 -0
  76. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/templates/spec-template.md +0 -0
  77. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/templates/tasks-template.md +0 -0
  78. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/workflows/speckit/workflow.yml +0 -0
  79. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/.specify/workflows/workflow-registry.json +0 -0
  80. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/AGENTS.md +0 -0
  81. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/CLAUDE.md +0 -0
  82. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/CONTRIBUTING.md +0 -0
  83. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/Dockerfile +0 -0
  84. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/LICENSE +0 -0
  85. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/README.md +0 -0
  86. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/.ansible-lint +0 -0
  87. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/README.md +0 -0
  88. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/ansible.cfg +0 -0
  89. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/aws_configure.yml +0 -0
  90. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/aws_resize.yml +0 -0
  91. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/aws_site.yml +0 -0
  92. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/aws_teardown.yml +0 -0
  93. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/group_vars/all.yml +0 -0
  94. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/group_vars/incus_containers.yml +0 -0
  95. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/group_vars/proxmox_containers.yml +0 -0
  96. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/hetzner_configure.yml +0 -0
  97. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/hetzner_provision.yml +0 -0
  98. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/hetzner_resize.yml +0 -0
  99. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/hetzner_site.yml +0 -0
  100. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/hetzner_teardown.yml +0 -0
  101. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/incus_bootstrap.yml +0 -0
  102. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/incus_configure.yml +0 -0
  103. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/incus_provision.yml +0 -0
  104. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/incus_resize.yml +0 -0
  105. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/incus_site.yml +0 -0
  106. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/incus_teardown.yml +0 -0
  107. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/inventory/hosts.yml +0 -0
  108. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/inventory/incus_containers.yml +0 -0
  109. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/proxmox_bootstrap.yml +0 -0
  110. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/proxmox_configure.yml +0 -0
  111. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/proxmox_provision.yml +0 -0
  112. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/proxmox_resize.yml +0 -0
  113. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/proxmox_site.yml +0 -0
  114. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/proxmox_teardown.yml +0 -0
  115. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/requirements.yml +0 -0
  116. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/aws_server/defaults/main.yml +0 -0
  117. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/aws_server/tasks/ebs.yml +0 -0
  118. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/aws_server/tasks/ec2.yml +0 -0
  119. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/aws_server/tasks/main.yml +0 -0
  120. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/aws_server/tasks/patch_manager.yml +0 -0
  121. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/aws_server/tasks/preflight.yml +0 -0
  122. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/aws_server/tasks/security_group.yml +0 -0
  123. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/deacon/defaults/main.yml +0 -0
  124. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/deacon/tasks/main.yml +0 -0
  125. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/devcontainers/tasks/main.yml +0 -0
  126. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/docker/handlers/main.yml +0 -0
  127. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/docker/tasks/main.yml +0 -0
  128. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/fzf/tasks/main.yml +0 -0
  129. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/github_cli/tasks/main.yml +0 -0
  130. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/hetzner_server/defaults/main.yml +0 -0
  131. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/hetzner_server/tasks/main.yml +0 -0
  132. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/incus_bootstrap/defaults/main.yml +0 -0
  133. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/incus_bootstrap/handlers/main.yml +0 -0
  134. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/incus_bootstrap/tasks/main.yml +0 -0
  135. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/incus_container/defaults/main.yml +0 -0
  136. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/incus_container/handlers/main.yml +0 -0
  137. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/incus_container/meta/main.yml +0 -0
  138. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/incus_container/tasks/main.yml +0 -0
  139. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/incus_container/tasks/preflight.yml +0 -0
  140. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/incus_container_teardown/defaults/main.yml +0 -0
  141. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/incus_container_teardown/meta/main.yml +0 -0
  142. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/incus_container_teardown/tasks/main.yml +0 -0
  143. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/nodejs/defaults/main.yml +0 -0
  144. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/nodejs/tasks/main.yml +0 -0
  145. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/proxmox_bootstrap/defaults/main.yml +0 -0
  146. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/proxmox_bootstrap/meta/main.yml +0 -0
  147. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/proxmox_bootstrap/tasks/main.yml +0 -0
  148. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/proxmox_container/defaults/main.yml +0 -0
  149. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/proxmox_container/handlers/main.yml +0 -0
  150. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/proxmox_container/meta/main.yml +0 -0
  151. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/proxmox_container/tasks/main.yml +0 -0
  152. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/proxmox_container/tasks/preflight.yml +0 -0
  153. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/proxmox_container_teardown/defaults/main.yml +0 -0
  154. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/proxmox_container_teardown/meta/main.yml +0 -0
  155. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/proxmox_container_teardown/tasks/main.yml +0 -0
  156. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/user_setup/defaults/main.yml +0 -0
  157. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/user_setup/tasks/main.yml +0 -0
  158. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/user_setup/templates/devshell.sh.j2 +0 -0
  159. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/user_setup/templates/project-launch.sh.j2 +0 -0
  160. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/user_setup/templates/project-menu.sh.j2 +0 -0
  161. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/user_setup/templates/remo-host.sh.j2 +0 -0
  162. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/zellij/defaults/main.yml +0 -0
  163. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/zellij/tasks/main.yml +0 -0
  164. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/zellij/templates/config.kdl.j2 +0 -0
  165. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/roles/zellij/templates/layouts/minimal.kdl.j2 +0 -0
  166. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/ansible/tasks/configure_dev_tools.yml +0 -0
  167. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/docker/Dockerfile +0 -0
  168. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/docker/compose.example.yml +0 -0
  169. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/docker/entrypoint.sh +0 -0
  170. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/docs/aws.md +0 -0
  171. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/docs/hetzner.md +0 -0
  172. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/docs/incus.md +0 -0
  173. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/docs/install.sh +0 -0
  174. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/docs/proxmox.md +0 -0
  175. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/docs/remo-web-speckit-input.md +0 -0
  176. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/docs/remo-web.html +0 -0
  177. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/index.html +0 -0
  178. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/public/README.md +0 -0
  179. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/public/dist__ghostty-vt.wasm +0 -0
  180. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/public/ghostty-vt.wasm +0 -0
  181. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/scripts/copy-ghostty-wasm.mjs +0 -0
  182. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/api/client.ts +0 -0
  183. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/AppRoot.tsx +0 -0
  184. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/AppShell.css +0 -0
  185. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/AppShell.tsx +0 -0
  186. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/AwaitingAdoption.css +0 -0
  187. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/AwaitingAdoption.tsx +0 -0
  188. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/OfflineOverlay.css +0 -0
  189. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/OfflineOverlay.tsx +0 -0
  190. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/PairToSync.css +0 -0
  191. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/PairToSync.tsx +0 -0
  192. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/SessionRail.css +0 -0
  193. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/SessionRail.tsx +0 -0
  194. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/SettingsPage.css +0 -0
  195. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/SettingsPage.tsx +0 -0
  196. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/ShortcutsModal.css +0 -0
  197. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/ShortcutsModal.tsx +0 -0
  198. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/TerminalCard.css +0 -0
  199. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/TopBar.css +0 -0
  200. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/TopBar.tsx +0 -0
  201. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/WorkspacePane.css +0 -0
  202. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/WorkspacePane.tsx +0 -0
  203. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/providerMeta.ts +0 -0
  204. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/components/railModel.ts +0 -0
  205. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/lib/clipboard.ts +0 -0
  206. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/lib/fullscreen.ts +0 -0
  207. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/main.tsx +0 -0
  208. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/state/discovery.ts +0 -0
  209. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/state/fonts.ts +0 -0
  210. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/state/health.ts +0 -0
  211. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/state/settings.ts +0 -0
  212. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/state/useConsoleKeyboard.ts +0 -0
  213. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/state/workspace.test.ts +0 -0
  214. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/state/workspace.ts +0 -0
  215. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/terminal/.gitkeep +0 -0
  216. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/terminal/TerminalConnection.test.ts +0 -0
  217. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/terminal/TerminalConnection.ts +0 -0
  218. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/terminal/defaultRenderer.ts +0 -0
  219. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/test/setup.ts +0 -0
  220. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/theme/fonts.ts +0 -0
  221. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/src/theme/tokens.css +0 -0
  222. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/tsconfig.json +0 -0
  223. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/frontend/vite.config.ts +0 -0
  224. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/install.sh +0 -0
  225. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/maverick.yaml +0 -0
  226. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/playwright.config.ts +0 -0
  227. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/project-menu.sh +0 -0
  228. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/remo +0 -0
  229. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/run.sh +0 -0
  230. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/scripts/setup-aws-oidc.sh +0 -0
  231. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/001-bootstrap-incus-host/checklists/requirements.md +0 -0
  232. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/001-bootstrap-incus-host/checklists/validation.md +0 -0
  233. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/001-bootstrap-incus-host/contracts/playbook-interface.yaml +0 -0
  234. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/001-bootstrap-incus-host/contracts/role-interface.yaml +0 -0
  235. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/001-bootstrap-incus-host/data-model.md +0 -0
  236. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/001-bootstrap-incus-host/plan.md +0 -0
  237. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/001-bootstrap-incus-host/quickstart.md +0 -0
  238. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/001-bootstrap-incus-host/research.md +0 -0
  239. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/001-bootstrap-incus-host/spec.md +0 -0
  240. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/001-bootstrap-incus-host/tasks.md +0 -0
  241. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/002-incus-container-support/checklists/requirements.md +0 -0
  242. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/002-incus-container-support/contracts/playbook-interface.yaml +0 -0
  243. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/002-incus-container-support/contracts/role-interface.yaml +0 -0
  244. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/002-incus-container-support/data-model.md +0 -0
  245. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/002-incus-container-support/plan.md +0 -0
  246. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/002-incus-container-support/quickstart.md +0 -0
  247. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/002-incus-container-support/research.md +0 -0
  248. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/002-incus-container-support/spec.md +0 -0
  249. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/002-incus-container-support/tasks.md +0 -0
  250. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/003-python-cli-rewrite/checklists/requirements.md +0 -0
  251. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/003-python-cli-rewrite/data-model.md +0 -0
  252. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/003-python-cli-rewrite/plan.md +0 -0
  253. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/003-python-cli-rewrite/quickstart.md +0 -0
  254. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/003-python-cli-rewrite/research.md +0 -0
  255. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/003-python-cli-rewrite/spec.md +0 -0
  256. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/003-python-cli-rewrite/tasks.md +0 -0
  257. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/004-proxmox-container-support/spec.md +0 -0
  258. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/005-provider-snapshots/checklists/requirements.md +0 -0
  259. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/005-provider-snapshots/contracts/cli-surface.md +0 -0
  260. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/005-provider-snapshots/data-model.md +0 -0
  261. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/005-provider-snapshots/plan.md +0 -0
  262. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/005-provider-snapshots/quickstart.md +0 -0
  263. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/005-provider-snapshots/research.md +0 -0
  264. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/005-provider-snapshots/spec.md +0 -0
  265. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/005-provider-snapshots/tasks.md +0 -0
  266. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/010-web-session-interface/checklists/requirements.md +0 -0
  267. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/010-web-session-interface/contracts/remo-host-protocol.md +0 -0
  268. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/010-web-session-interface/contracts/rest-api.md +0 -0
  269. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/010-web-session-interface/contracts/terminal-websocket.md +0 -0
  270. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/010-web-session-interface/data-model.md +0 -0
  271. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/010-web-session-interface/plan.md +0 -0
  272. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/010-web-session-interface/quickstart.md +0 -0
  273. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/010-web-session-interface/research.md +0 -0
  274. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/010-web-session-interface/spec.md +0 -0
  275. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/010-web-session-interface/tasks.md +0 -0
  276. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/011-web-adopt/checklists/requirements.md +0 -0
  277. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/011-web-adopt/contracts/cli-web-adopt.md +0 -0
  278. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/011-web-adopt/contracts/setup-api.md +0 -0
  279. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/011-web-adopt/data-model.md +0 -0
  280. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/011-web-adopt/plan.md +0 -0
  281. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/011-web-adopt/quickstart-results.md +0 -0
  282. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/011-web-adopt/quickstart.md +0 -0
  283. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/011-web-adopt/research.md +0 -0
  284. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/011-web-adopt/spec.md +0 -0
  285. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/011-web-adopt/tasks.md +0 -0
  286. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/012-web-adopt-pairing/contracts/cli-web-adopt.md +0 -0
  287. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/012-web-adopt-pairing/contracts/pairing-api.md +0 -0
  288. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/012-web-adopt-pairing/contracts/setup-api.md +0 -0
  289. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/012-web-adopt-pairing/data-model.md +0 -0
  290. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/012-web-adopt-pairing/plan.md +0 -0
  291. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/012-web-adopt-pairing/quickstart.md +0 -0
  292. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/012-web-adopt-pairing/research.md +0 -0
  293. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/012-web-adopt-pairing/spec.md +0 -0
  294. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/specs/012-web-adopt-pairing/tasks.md +0 -0
  295. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/__init__.py +0 -0
  296. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/__main__.py +0 -0
  297. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/cli/__init__.py +0 -0
  298. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/cli/cp.py +0 -0
  299. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/cli/main.py +0 -0
  300. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/cli/providers/__init__.py +0 -0
  301. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/cli/providers/aws.py +0 -0
  302. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/cli/providers/hetzner.py +0 -0
  303. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/cli/providers/incus.py +0 -0
  304. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/cli/providers/proxmox.py +0 -0
  305. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/cli/shell.py +0 -0
  306. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/cli/web.py +0 -0
  307. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/__init__.py +0 -0
  308. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/ansible_runner.py +0 -0
  309. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/completion.py +0 -0
  310. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/config.py +0 -0
  311. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/known_hosts.py +0 -0
  312. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/output.py +0 -0
  313. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/picker.py +0 -0
  314. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/remo_host_client.py +0 -0
  315. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/rsync.py +0 -0
  316. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/snapshot.py +0 -0
  317. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/ssh.py +0 -0
  318. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/validation.py +0 -0
  319. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/version.py +0 -0
  320. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/core/web_adopt.py +0 -0
  321. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/models/__init__.py +0 -0
  322. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/models/capability.py +0 -0
  323. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/models/discovery.py +0 -0
  324. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/models/host.py +0 -0
  325. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/models/session_target.py +0 -0
  326. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/models/snapshot.py +0 -0
  327. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/providers/__init__.py +0 -0
  328. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/providers/aws.py +0 -0
  329. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/providers/hetzner.py +0 -0
  330. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/providers/incus.py +0 -0
  331. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/providers/proxmox.py +0 -0
  332. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/__init__.py +0 -0
  333. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/api/__init__.py +0 -0
  334. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/api/hosts.py +0 -0
  335. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/api/pairing.py +0 -0
  336. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/api/setup.py +0 -0
  337. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/api/terminals.py +0 -0
  338. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/app.py +0 -0
  339. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/check.py +0 -0
  340. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/config.py +0 -0
  341. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/discovery.py +0 -0
  342. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/health.py +0 -0
  343. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/logging_config.py +0 -0
  344. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/models.py +0 -0
  345. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/operator_auth.py +0 -0
  346. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/pairing.py +0 -0
  347. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/ssh_master.py +0 -0
  348. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/state.py +0 -0
  349. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/terminal.py +0 -0
  350. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/terminal_registry.py +0 -0
  351. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/src/remo_cli/web/tokens.py +0 -0
  352. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/__init__.py +0 -0
  353. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/ansible/__init__.py +0 -0
  354. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/ansible/test_remo_host_idempotency.py +0 -0
  355. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/conftest.py +0 -0
  356. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/e2e/fixtures.ts +0 -0
  357. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/e2e/ghostty-shell-io.spec.ts +0 -0
  358. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/e2e/ghostty-tui-compatibility.spec.ts +0 -0
  359. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/e2e/keyboard-routing.spec.ts +0 -0
  360. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/e2e/mobile.spec.ts +0 -0
  361. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/e2e/reconnect.spec.ts +0 -0
  362. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/e2e/workspace-layout.spec.ts +0 -0
  363. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/image/__init__.py +0 -0
  364. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/image/test_docker_image.py +0 -0
  365. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/integration/__init__.py +0 -0
  366. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/integration/_nine_terminal_fixture.py +0 -0
  367. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/integration/orbstack-cloud-init.yml +0 -0
  368. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/integration/orbstack.sh +0 -0
  369. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/integration/test_nine_terminals.py +0 -0
  370. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/integration/test_nine_terminals_soak.py +0 -0
  371. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/integration/test_remo_host_e2e.py +0 -0
  372. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/integration/test_security_rejections.py +0 -0
  373. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/integration/test_terminal_attach.py +0 -0
  374. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/integration/test_web_adopt_e2e.py +0 -0
  375. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/integration/test_web_cli_parity.py +0 -0
  376. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/perf/__init__.py +0 -0
  377. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/perf/test_latency.py +0 -0
  378. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/__init__.py +0 -0
  379. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/cli/__init__.py +0 -0
  380. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/cli/providers/test_aws_snapshot.py +0 -0
  381. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/cli/providers/test_hetzner_snapshot.py +0 -0
  382. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/cli/providers/test_incus_snapshot.py +0 -0
  383. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/cli/providers/test_proxmox_snapshot.py +0 -0
  384. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/cli/test_cp.py +0 -0
  385. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/cli/test_main.py +0 -0
  386. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/cli/test_shell.py +0 -0
  387. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/cli/test_web_adopt_cmd.py +0 -0
  388. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/__init__.py +0 -0
  389. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_ansible_runner.py +0 -0
  390. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_config.py +0 -0
  391. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_known_hosts.py +0 -0
  392. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_output.py +0 -0
  393. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_picker.py +0 -0
  394. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_remo_host_client.py +0 -0
  395. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_rsync.py +0 -0
  396. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_snapshot.py +0 -0
  397. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_ssh.py +0 -0
  398. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_ssh_controlpath.py +0 -0
  399. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_ssh_identity_opts.py +0 -0
  400. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_validation.py +0 -0
  401. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_version.py +0 -0
  402. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_web_adopt_authorize.py +0 -0
  403. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_web_adopt_code.py +0 -0
  404. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_web_adopt_payload.py +0 -0
  405. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_web_adopt_trust.py +0 -0
  406. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/core/test_web_push.py +0 -0
  407. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/models/__init__.py +0 -0
  408. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/models/test_capability.py +0 -0
  409. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/models/test_discovery.py +0 -0
  410. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/models/test_session_target.py +0 -0
  411. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/providers/__init__.py +0 -0
  412. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/providers/test_aws_snapshot.py +0 -0
  413. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/providers/test_hetzner_snapshot.py +0 -0
  414. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/providers/test_incus_snapshot.py +0 -0
  415. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/providers/test_proxmox_devcontainer_runtime.py +0 -0
  416. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/providers/test_proxmox_snapshot.py +0 -0
  417. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/test_ansible_templates.py +0 -0
  418. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/test_host_model.py +0 -0
  419. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/__init__.py +0 -0
  420. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/_pairing_support.py +0 -0
  421. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/conftest.py +0 -0
  422. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_backpressure.py +0 -0
  423. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_check.py +0 -0
  424. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_csp.py +0 -0
  425. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_discovery.py +0 -0
  426. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_health_states.py +0 -0
  427. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_hosts_mapping.py +0 -0
  428. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_lazy_import.py +0 -0
  429. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_log_redaction.py +0 -0
  430. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_mint_gating.py +0 -0
  431. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_operator_auth.py +0 -0
  432. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_pairing.py +0 -0
  433. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_pairing_origin.py +0 -0
  434. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_setup_api.py +0 -0
  435. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_setup_auth.py +0 -0
  436. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_setup_dormancy.py +0 -0
  437. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_state.py +0 -0
  438. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_terminal_resize.py +0 -0
  439. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_terminals_api.py +0 -0
  440. {remo_cli-2.2.0rc10 → remo_cli-2.2.0rc12}/tests/unit/web/test_tokens.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remo-cli
3
- Version: 2.2.0rc10
3
+ Version: 2.2.0rc12
4
4
  Summary: Remote development environment CLI
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -102,9 +102,10 @@ The SPA is a two-pane **web console**:
102
102
  row (or its `+` button) **adds** it to a responsive grid (1/2/3 columns by count). In a grid, **drag
103
103
  a tile's header onto another to swap their positions** — a window outline follows the cursor and the
104
104
  swap target shows a dashed outline (mouse or touch press-and-hold; keyboard-accessible via the
105
- handle); the arrangement persists until the grid is rebuilt. In a grid, **hovering a tile focuses it**
106
- (focus-follows-mouse) so keystrokes go where the pointer is no click needed. The **◻** control solos
107
- a tile; **Esc** collapses the grid back to the focused terminal;
105
+ handle); the arrangement persists until the grid is rebuilt. In a grid, **resting the pointer on a
106
+ tile focuses it** (focus-follows-mouse, with a short dwell so passing through tiles doesn't steal
107
+ focus) keystrokes go where the pointer is, no click needed. The **◻** control solos a tile; **Esc**
108
+ collapses the grid back to the focused terminal;
108
109
  number keys **1–9** jump to the numbered sessions (⌘ 1–9 add to the grid). Hidden terminals stay
109
110
  connected and keep their scrollback. Each terminal header shows `provider · instance · region`
110
111
  (doubling as the drag handle), connection state, and a
@@ -115,6 +116,13 @@ The SPA is a two-pane **web console**:
115
116
  toggle fullscreen on the focused terminal; **Esc** exits it. Fullscreen is a presentation overlay: it
116
117
  never disturbs the single/grid layout underneath, so exiting returns to exactly where you were.
117
118
 
119
+ **Clipboard & links.** Select text and press **⌘C** (macOS) / **Ctrl+Shift+C** (Linux/Windows), or click
120
+ the **⧉ Copy** button that appears on selection, to copy to the system clipboard; bare **Ctrl+C** stays
121
+ SIGINT. **Paste** with ⌘V / Ctrl+V. **http(s) URLs are clickable** and open in a new tab. Remote apps that
122
+ emit **OSC 52** (e.g. Claude Code's copy-on-select) can write to the browser clipboard — best-effort: it
123
+ must traverse Zellij and the browser must permit a gesture-less clipboard write. OSC 52 *reads* are denied
124
+ (a remote app can never read your clipboard). Clipboard access needs a secure context (HTTPS or localhost).
125
+
118
126
  **Session-row glyphs** (also shown in the rail legend):
119
127
 
120
128
  | Glyph | Meaning |
@@ -13,8 +13,10 @@
13
13
  "@fontsource/ibm-plex-mono": "^5.2.7",
14
14
  "@fontsource/jetbrains-mono": "^5.2.8",
15
15
  "@fontsource/source-code-pro": "^5.2.7",
16
+ "@xterm/addon-clipboard": "^0.1.0",
16
17
  "@xterm/addon-fit": "^0.10.0",
17
18
  "@xterm/addon-ligatures": "^0.9.0",
19
+ "@xterm/addon-web-links": "^0.11.0",
18
20
  "@xterm/addon-webgl": "^0.18.0",
19
21
  "@xterm/xterm": "^5.5.0",
20
22
  "ghostty-web": "0.4.0",
@@ -1851,6 +1853,18 @@
1851
1853
  "url": "https://opencollective.com/vitest"
1852
1854
  }
1853
1855
  },
1856
+ "node_modules/@xterm/addon-clipboard": {
1857
+ "version": "0.1.0",
1858
+ "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.1.0.tgz",
1859
+ "integrity": "sha512-zdoM7p53T5sv/HbRTyp4hY0kKmEQ3MZvAvEtiXqNIHc/JdpqwByCtsTaQF5DX2n4hYdXRPO4P/eOS0QEhX1nPw==",
1860
+ "license": "MIT",
1861
+ "dependencies": {
1862
+ "js-base64": "^3.7.5"
1863
+ },
1864
+ "peerDependencies": {
1865
+ "@xterm/xterm": "^5.4.0"
1866
+ }
1867
+ },
1854
1868
  "node_modules/@xterm/addon-fit": {
1855
1869
  "version": "0.10.0",
1856
1870
  "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz",
@@ -1876,6 +1890,15 @@
1876
1890
  "@xterm/xterm": "^5.0.0"
1877
1891
  }
1878
1892
  },
1893
+ "node_modules/@xterm/addon-web-links": {
1894
+ "version": "0.11.0",
1895
+ "resolved": "https://registry.npmjs.org/@xterm/addon-web-links/-/addon-web-links-0.11.0.tgz",
1896
+ "integrity": "sha512-nIHQ38pQI+a5kXnRaTgwqSHnX7KE6+4SVoceompgHL26unAxdfP6IPqUTSYPQgSwM56hsElfoNrrW5V7BUED/Q==",
1897
+ "license": "MIT",
1898
+ "peerDependencies": {
1899
+ "@xterm/xterm": "^5.0.0"
1900
+ }
1901
+ },
1879
1902
  "node_modules/@xterm/addon-webgl": {
1880
1903
  "version": "0.18.0",
1881
1904
  "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.18.0.tgz",
@@ -2611,6 +2634,12 @@
2611
2634
  "@pkgjs/parseargs": "^0.11.0"
2612
2635
  }
2613
2636
  },
2637
+ "node_modules/js-base64": {
2638
+ "version": "3.9.1",
2639
+ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.9.1.tgz",
2640
+ "integrity": "sha512-U73qptcvf/HIOauFOmqT3a0mDUp0MYlfd15oqoe9kqZt5XhiXVb+HG09sLvI9PQ9tZIBFS4nlErai8zbWazP0g==",
2641
+ "license": "BSD-3-Clause"
2642
+ },
2614
2643
  "node_modules/js-tokens": {
2615
2644
  "version": "4.0.0",
2616
2645
  "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -20,8 +20,10 @@
20
20
  "@fontsource/ibm-plex-mono": "^5.2.7",
21
21
  "@fontsource/jetbrains-mono": "^5.2.8",
22
22
  "@fontsource/source-code-pro": "^5.2.7",
23
+ "@xterm/addon-clipboard": "^0.1.0",
23
24
  "@xterm/addon-fit": "^0.10.0",
24
25
  "@xterm/addon-ligatures": "^0.9.0",
26
+ "@xterm/addon-web-links": "^0.11.0",
25
27
  "@xterm/addon-webgl": "^0.18.0",
26
28
  "@xterm/xterm": "^5.5.0",
27
29
  "ghostty-web": "0.4.0",
@@ -28,6 +28,9 @@ const DEFAULT_COLS = 80;
28
28
  const DEFAULT_ROWS = 24;
29
29
  /** How much to shrink the terminal font in a grid tile when "scale to fit". */
30
30
  const GRID_FIT_SCALE = 0.8;
31
+ /** Focus-follows-mouse dwell: the pointer must REST on a tile this long before
32
+ * it takes focus, so passing through tiles (or a small drift) doesn't steal it. */
33
+ const HOVER_FOCUS_DELAY_MS = 220;
31
34
 
32
35
  const STATE_LABELS: Record<TerminalConnectionState, string> = {
33
36
  connecting: "Connecting…",
@@ -119,10 +122,14 @@ export function TerminalCard({
119
122
  // (to skip redundant resize frames).
120
123
  const fitRafRef = useRef<number | null>(null);
121
124
  const lastSentDimsRef = useRef<{ cols: number; rows: number } | null>(null);
125
+ // Pending focus-follows-mouse dwell timer (cleared if the pointer leaves first).
126
+ const hoverTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
122
127
 
123
128
  const [connectionState, setConnectionState] = useState<TerminalConnectionState>("connecting");
124
129
  const [needsManualReconnect, setNeedsManualReconnect] = useState(false);
125
130
  const [error, setError] = useState<TypedError | null>(null);
131
+ const [hasSelection, setHasSelection] = useState(false);
132
+ const [copied, setCopied] = useState(false);
126
133
 
127
134
  // dnd-kit reorder: the tile is both a draggable (handle = header grip, below)
128
135
  // and a droppable (swap target). Disabled outside a reorderable grid. We use
@@ -247,6 +254,9 @@ export function TerminalCard({
247
254
  }
248
255
  });
249
256
 
257
+ // Show/hide the Copy affordance as the terminal selection changes.
258
+ const unsubscribeSelection = adapter.onSelectionChange((has) => setHasSelection(has));
259
+
250
260
  // Reflow on every container size change (window resize, rail drag, grid
251
261
  // <-> single, tile show/hide). scheduleFit coalesces bursts to one fit per
252
262
  // frame and skips the hidden-0x0 case.
@@ -263,6 +273,7 @@ export function TerminalCard({
263
273
  }
264
274
  lastSentDimsRef.current = null;
265
275
  unsubscribeInput();
276
+ unsubscribeSelection();
266
277
  resizeObserver.disconnect();
267
278
  void connection.close();
268
279
  adapter.dispose();
@@ -306,6 +317,38 @@ export function TerminalCard({
306
317
  onFocusRequest?.();
307
318
  }, [onFocusRequest]);
308
319
 
320
+ const handleCopy = useCallback(() => {
321
+ void adapterRef.current?.copySelection().then((ok) => {
322
+ if (ok) {
323
+ setCopied(true);
324
+ setTimeout(() => setCopied(false), 1200);
325
+ }
326
+ });
327
+ }, []);
328
+
329
+ const clearHoverTimer = useCallback(() => {
330
+ if (hoverTimerRef.current !== null) {
331
+ clearTimeout(hoverTimerRef.current);
332
+ hoverTimerRef.current = null;
333
+ }
334
+ }, []);
335
+
336
+ // Focus-follows-mouse with a dwell delay: arm a timer on enter, fire only if
337
+ // the pointer is still resting here after HOVER_FOCUS_DELAY_MS.
338
+ const handleMouseEnter = useCallback(() => {
339
+ if (!onHoverFocus) {
340
+ return;
341
+ }
342
+ clearHoverTimer();
343
+ hoverTimerRef.current = setTimeout(() => {
344
+ hoverTimerRef.current = null;
345
+ onHoverFocus();
346
+ }, HOVER_FOCUS_DELAY_MS);
347
+ }, [onHoverFocus, clearHoverTimer]);
348
+
349
+ // Cancel a pending dwell when the pointer leaves or the card unmounts.
350
+ useEffect(() => clearHoverTimer, [clearHoverTimer]);
351
+
309
352
  const prov = providerMeta(target.instance_type);
310
353
  const badge = [prov.label, target.instance_name, region].filter(Boolean).join(" · ");
311
354
 
@@ -322,7 +365,8 @@ export function TerminalCard({
322
365
  data-focused={isFocused}
323
366
  data-connection-state={connectionState}
324
367
  style={{ display: isVisible ? undefined : "none" }}
325
- onMouseEnter={onHoverFocus}
368
+ onMouseEnter={handleMouseEnter}
369
+ onMouseLeave={clearHoverTimer}
326
370
  >
327
371
  <header className="terminal-card-header">
328
372
  {/* The header (left of the controls) is the drag handle for reordering
@@ -350,6 +394,20 @@ export function TerminalCard({
350
394
  {STATE_LABELS[connectionState]}
351
395
  </span>
352
396
  <div className="terminal-card-controls">
397
+ {hasSelection && (
398
+ <button
399
+ type="button"
400
+ className="tc-btn"
401
+ data-testid={`terminal-copy-${target.id}`}
402
+ title="Copy selection (⌘C / Ctrl+Shift+C)"
403
+ onClick={(e) => {
404
+ e.stopPropagation();
405
+ handleCopy();
406
+ }}
407
+ >
408
+ {copied ? "✓ Copied" : "⧉ Copy"}
409
+ </button>
410
+ )}
353
411
  {needsManualReconnect && (
354
412
  <button
355
413
  type="button"
@@ -10,7 +10,8 @@
10
10
  // configured font/size so the cell grid stays correct after a font change.
11
11
 
12
12
  import { Terminal } from "ghostty-web";
13
- import { inputForKeyEvent } from "./keymap";
13
+ import { copyText } from "../lib/clipboard";
14
+ import { inputForKeyEvent, isCopyChord } from "./keymap";
14
15
  import type {
15
16
  RendererAdapter,
16
17
  TerminalDimensions,
@@ -63,6 +64,11 @@ export class GhosttyRenderer implements RendererAdapter {
63
64
  }
64
65
 
65
66
  private handleKeyEvent(e: KeyboardEvent): boolean {
67
+ if (isCopyChord(e) && this.getSelection()) {
68
+ e.preventDefault();
69
+ void this.copySelection();
70
+ return false;
71
+ }
66
72
  const seq = inputForKeyEvent(e);
67
73
  if (seq !== null) {
68
74
  e.preventDefault();
@@ -134,6 +140,14 @@ export class GhosttyRenderer implements RendererAdapter {
134
140
  return selection && selection.length > 0 ? selection : null;
135
141
  }
136
142
 
143
+ async copySelection(): Promise<boolean> {
144
+ const selection = this.getSelection();
145
+ if (!selection) {
146
+ return false;
147
+ }
148
+ return copyText(selection);
149
+ }
150
+
137
151
  dispose(): void {
138
152
  this.terminal.dispose();
139
153
  }
@@ -93,6 +93,12 @@ export interface RendererAdapter {
93
93
  /** Returns the currently selected text, or `null` if nothing is selected. */
94
94
  getSelection(): string | null;
95
95
 
96
+ /**
97
+ * Copies the current selection to the system clipboard (best-effort; needs a
98
+ * secure context). Returns true if there was a selection that was copied.
99
+ */
100
+ copySelection(): Promise<boolean>;
101
+
96
102
  /** Tears down the renderer and releases all resources/listeners. */
97
103
  dispose(): void;
98
104
  }
@@ -20,16 +20,30 @@ import { Terminal } from "@xterm/xterm";
20
20
  import { FitAddon } from "@xterm/addon-fit";
21
21
  import { LigaturesAddon } from "@xterm/addon-ligatures";
22
22
  import { WebglAddon } from "@xterm/addon-webgl";
23
+ import { WebLinksAddon } from "@xterm/addon-web-links";
24
+ import { ClipboardAddon, type IClipboardProvider } from "@xterm/addon-clipboard";
23
25
  // xterm ships its own stylesheet; without it the terminal renders with broken
24
26
  // cell sizing/positioning. Bundled here so it loads whenever this renderer is.
25
27
  import "@xterm/xterm/css/xterm.css";
26
- import { inputForKeyEvent } from "./keymap";
28
+ import { copyText } from "../lib/clipboard";
29
+ import { inputForKeyEvent, isCopyChord } from "./keymap";
27
30
  import type {
28
31
  RendererAdapter,
29
32
  TerminalDimensions,
30
33
  TerminalFontOptions,
31
34
  } from "./RendererAdapter";
32
35
 
36
+ // OSC 52 handling: WRITE lets a remote app (e.g. Claude Code's copy-on-select)
37
+ // push text to the browser clipboard; READ is denied so a remote app can never
38
+ // exfiltrate the operator's clipboard. Best-effort — an escape-sequence write
39
+ // has no user gesture, so some browsers may reject it.
40
+ const _osc52Provider: IClipboardProvider = {
41
+ readText: async () => "",
42
+ writeText: async (_selection, text) => {
43
+ await copyText(text);
44
+ },
45
+ };
46
+
33
47
  const DEFAULT_FONT: TerminalFontOptions = {
34
48
  fontFamily: "Menlo, Consolas, 'DejaVu Sans Mono', monospace",
35
49
  fontSize: 13,
@@ -80,6 +94,13 @@ export class XtermRenderer implements RendererAdapter {
80
94
  }
81
95
 
82
96
  private handleKeyEvent(e: KeyboardEvent): boolean {
97
+ // Copy the selection on ⌘C / Ctrl+Shift+C — but only when something is
98
+ // selected, so bare Ctrl+C still reaches the shell as SIGINT.
99
+ if (isCopyChord(e) && this.terminal.hasSelection()) {
100
+ e.preventDefault();
101
+ void this.copySelection();
102
+ return false;
103
+ }
83
104
  const seq = inputForKeyEvent(e);
84
105
  if (seq !== null) {
85
106
  // preventDefault stops the browser from also inserting a newline into
@@ -116,6 +137,12 @@ export class XtermRenderer implements RendererAdapter {
116
137
  this.enableWebgl();
117
138
  // Now that the terminal is attached, load the ligatures addon if wanted.
118
139
  this.applyLigatures(this.ligaturesEnabled);
140
+ // Clickable http(s) links — open in a new tab, severing opener access.
141
+ this.terminal.loadAddon(
142
+ new WebLinksAddon((_event, uri) => window.open(uri, "_blank", "noopener,noreferrer")),
143
+ );
144
+ // OSC 52 (remote-app clipboard writes, e.g. Claude Code copy-on-select).
145
+ this.terminal.loadAddon(new ClipboardAddon(_osc52Provider));
119
146
  }
120
147
 
121
148
  // GPU-accelerated rendering. Must run after open(). Best-effort: a failed
@@ -185,6 +212,14 @@ export class XtermRenderer implements RendererAdapter {
185
212
  return selection.length > 0 ? selection : null;
186
213
  }
187
214
 
215
+ async copySelection(): Promise<boolean> {
216
+ const selection = this.getSelection();
217
+ if (!selection) {
218
+ return false;
219
+ }
220
+ return copyText(selection);
221
+ }
222
+
188
223
  dispose(): void {
189
224
  // Dispose the WebGL addon before the terminal so its GPU context/canvas is
190
225
  // released cleanly (Terminal.dispose would also drop it, but ordering is
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { inputForKeyEvent } from "./keymap";
2
+ import { inputForKeyEvent, isCopyChord } from "./keymap";
3
3
 
4
4
  function key(init: KeyboardEventInit & { type?: string }): KeyboardEvent {
5
5
  const { type = "keydown", ...rest } = init;
@@ -30,3 +30,20 @@ describe("inputForKeyEvent", () => {
30
30
  expect(inputForKeyEvent(key({ key: "a", shiftKey: true }))).toBeNull();
31
31
  });
32
32
  });
33
+
34
+ describe("isCopyChord", () => {
35
+ it("recognizes ⌘C and Ctrl+Shift+C", () => {
36
+ expect(isCopyChord(key({ key: "c", metaKey: true }))).toBe(true);
37
+ expect(isCopyChord(key({ key: "C", ctrlKey: true, shiftKey: true }))).toBe(true);
38
+ });
39
+
40
+ it("leaves bare Ctrl+C alone (stays SIGINT)", () => {
41
+ expect(isCopyChord(key({ key: "c", ctrlKey: true }))).toBe(false);
42
+ });
43
+
44
+ it("ignores other keys, plain c, and non-keydown events", () => {
45
+ expect(isCopyChord(key({ key: "v", metaKey: true }))).toBe(false);
46
+ expect(isCopyChord(key({ key: "c" }))).toBe(false);
47
+ expect(isCopyChord(key({ key: "c", metaKey: true, type: "keyup" }))).toBe(false);
48
+ });
49
+ });
@@ -25,3 +25,18 @@ export function inputForKeyEvent(e: KeyboardEvent): string | null {
25
25
  }
26
26
  return null;
27
27
  }
28
+
29
+ /**
30
+ * Whether a key event is the "copy the selection" chord: **Cmd+C** (macOS) or
31
+ * **Ctrl+Shift+C** (Linux/Windows). Bare `Ctrl+C` is deliberately excluded so it
32
+ * stays SIGINT. The renderer only copies when there is actually a selection —
33
+ * otherwise the chord is left alone.
34
+ */
35
+ export function isCopyChord(e: KeyboardEvent): boolean {
36
+ if (e.type !== "keydown" || (e.key !== "c" && e.key !== "C")) {
37
+ return false;
38
+ }
39
+ const cmdC = e.metaKey && !e.ctrlKey && !e.altKey; // macOS ⌘C
40
+ const ctrlShiftC = e.ctrlKey && e.shiftKey && !e.metaKey && !e.altKey; // Linux/Win
41
+ return cmdC || ctrlShiftC;
42
+ }
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "remo-cli"
7
- version = "2.2.0rc10"
7
+ version = "2.2.0rc12"
8
8
  description = "Remote development environment CLI"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1038,7 +1038,7 @@ wheels = [
1038
1038
 
1039
1039
  [[package]]
1040
1040
  name = "remo-cli"
1041
- version = "2.2.0rc10"
1041
+ version = "2.2.0rc12"
1042
1042
  source = { editable = "." }
1043
1043
  dependencies = [
1044
1044
  { name = "ansible-core" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes