feat: add pause/resume/toggle to keep daemon alive without clipboard hijack#22
Merged
Conversation
…hijack
The 0.7.x model — daemon always rewrites the clipboard with the remote
path — assumed every screenshot was AI-bound. In practice ~80% of
screenshots go to Slack/Teams/WhatsApp/GitHub PRs/web, where the
hijack is actively destructive (you paste a path string instead of an
image). The only escape hatch was `sshshot stop` + `sshshot start` +
re-selecting the SSH target, which is heavier than `scp`-by-hand.
This adds a `paused: boolean` to config and three new commands:
sshshot pause — daemon stays alive, target stays selected,
clipboard stops being touched
sshshot resume — back to processing screenshots normally
sshshot toggle — flip between the two
Implementation:
- processNewImage() early-returns when config.paused is true. Both the
poll-driven and fs.watch-driven paths funnel through here, so one
guard covers both.
- A module-level `lastSeenPausedState` makes the daemon log the
transition once instead of repeating "skipping" on every paused
screenshot.
- `sshshot status` shows `[paused]` next to the running PID so users
see which mode they're in at a glance.
This is the short-term ergonomic fix. The longer-term work
(context-aware clipboard routing — only hijack when the foreground
app is a terminal) is queued as Phase 6b in the project memory.
…lake The 100-iteration uniqueness test in test/monitor-helpers.test.ts was asserting "no collisions across 100 same-millisecond calls" against a 4-char hex suffix (16⁴ = 65,536 buckets). Birthday-paradox math says that's a ~7% collision rate at 100 trials — unlucky but not improbable. Round H/I/J/K/L all happened to land lucky draws on every Node version. Round-M (PR #22) finally hit a Node-24-only bad RNG sequence and the test failed with "expected 99 to be 100". Same code, just statistics catching up. Bump random suffix to 4 bytes / 8 hex chars (4.3B buckets), making the collision probability for 100 same-ms calls ~10⁻⁶. The shape constraint (SAFE_REMOTE_FILENAME_RE) is updated to require the new length, and the dangerous-input test fixtures are refreshed to use 8-char suffixes so the "rejects 4-char as old shape" assertion still documents the historical break-point.
|
🎉 This PR is included in version 0.8.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Short-term ergonomic fix for the "stop/start every time I want a non-AI screenshot" UX problem. Daemon stays alive (target stays selected, no restart cost); a single command toggles whether it processes screenshots.
```
sshshot pause # daemon alive, clipboard untouched
sshshot resume # back to normal processing
sshshot toggle # flip
```
`sshshot status` shows `[paused]` next to the running PID. Daemon logs the pause/resume transition once (not on every paused poll).
Why
The 0.7.x always-on clipboard hijack assumed every screenshot was bound for Claude Code / Codex on the remote. In practice ~80% of screenshots go to Slack, GitHub PRs, iMessage, etc. — where replacing the clipboard image with a path string is actively destructive. The previous escape hatch (`sshshot stop` → take screenshot → `sshshot start` → re-select target) was heavier than `scp` by hand. This makes the toggle a single command with no state loss.
This is not the long-term fix. The roadmap target is context-aware clipboard routing — replace clipboard only when the foreground app is a terminal-ish thing (Terminal/iTerm2/Warp/Ghostty/Zed/etc.) where you're about to paste into an AI agent. Queued as Phase 6b in the project memory.
Test plan