{"id":51226504,"url":"https://github.com/dupe-com/agent-mac-ops","last_synced_at":"2026-06-28T12:02:15.994Z","repository":{"id":364150231,"uuid":"1266636180","full_name":"dupe-com/agent-mac-ops","owner":"dupe-com","description":"Operate your always-on Mac with an AI agent + connect as native iTerm2 or Ghostty windows. Includes remote OAuth/localhost forwarding \u0026 browser handoff.","archived":false,"fork":false,"pushed_at":"2026-06-25T13:54:14.000Z","size":1706,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-25T15:36:14.463Z","etag":null,"topics":["ai-agent","claude","ghostty","iterm2","macos","remote-development","ssh","tailscale","tmux"],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dupe-com.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-11T20:09:32.000Z","updated_at":"2026-06-25T13:54:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dupe-com/agent-mac-ops","commit_stats":null,"previous_names":["dupe-com/agent-mac-ops"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dupe-com/agent-mac-ops","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dupe-com%2Fagent-mac-ops","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dupe-com%2Fagent-mac-ops/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dupe-com%2Fagent-mac-ops/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dupe-com%2Fagent-mac-ops/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dupe-com","download_url":"https://codeload.github.com/dupe-com/agent-mac-ops/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dupe-com%2Fagent-mac-ops/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34887328,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agent","claude","ghostty","iterm2","macos","remote-development","ssh","tailscale","tmux"],"created_at":"2026-06-28T12:02:15.226Z","updated_at":"2026-06-28T12:02:15.986Z","avatar_url":"https://github.com/dupe-com.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/agent-mac-ops.png\" alt=\"agent-mac-ops\" width=\"200\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eagent-mac-ops\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eOperate your always-on Mac by talking to an AI agent —\u003cbr\u003eand work on it remotely like you're sitting right in front of it.\u003c/b\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e •\n  \u003ca href=\"#the-problem\"\u003eThe problem\u003c/a\u003e •\n  \u003ca href=\"#requirements\"\u003eRequirements\u003c/a\u003e •\n  \u003ca href=\"#quickstart\"\u003eQuickstart\u003c/a\u003e •\n  \u003ca href=\"#how-it-works\"\u003eHow it works\u003c/a\u003e •\n  \u003ca href=\"#what-this-is-not\"\u003eWhat this is not\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Features\n\n- 🤖 **Drive it with an AI agent.** Point Claude or Codex at `control/ops/` and say *\"check on the box\"* —\n  it SSHes in, reports uptime / disk / load / dev-session health in plain language, and revives a wedged\n  session. No SSH commands typed by you.\n- 🖥️ **Native remote terminal sessions.** One command opens the remote as native, resizable panes — real\n  `Cmd+D` / `Cmd+T` splits connected to the remote, normal keys, auto-colored so you never confuse it\n  with localhost. Works in **iTerm2** (`tmux -CC`) and **Ghostty** (native splits), auto-detected.\n  ([how it's done →](SETUP.md))\n- ⚡ **Instant-feeling typing on slow links.** `box-mosh` connects over [mosh](https://mosh.org) for\n  predictive local echo, so characters appear immediately even over high latency.\n- 🔐 **Log in without Screen Sharing.** Remote auth pages open on *your* Mac, and OAuth `localhost`\n  callbacks reach the right machine — ports are forwarded automatically and remote browser opens are\n  handed back to your laptop. ([details →](docs/REMOTE-AUTH.md))\n- 📋 **Clipboard bridge (optional).** Enable `install-clip-watch.sh` and screenshots you copy locally\n  auto-mirror to the remote's clipboard — `Ctrl+V` them straight into a tool running on the remote.\n  Prefer no background watcher? Push on demand with `box-clip`.\n- 🧑‍💻 **`code .` for the remote.** Run `code-box` in the remote session and your laptop's Cursor (or\n  VS Code) opens in Remote-SSH mode at that folder — so the editor runs locally, on the remote's files.\n  Or `box-code` from a cold laptop shell, no session needed. Reuses the handoff listener. An optional\n  one-block shell snippet makes literal `code .` *smart* — it hands off to your laptop when you're\n  driving the box over SSH, and opens locally when you're sitting at it.\n  ([details →](docs/REMOTE-AUTH.md#make-plain-code--smart-optional))\n- 📦 **Painless file transfer \u0026 forwarding.** `box-push` / `box-pull` move files without typing the host;\n  `box-fwd` forwards ad-hoc ports (including random OAuth callbacks).\n- 🔪 **Reclaim a busy port in one keypress.** Port management grew up and moved into its own project:\n  [**ports-cli**](https://github.com/dupe-com/ports-cli) — an interactive TUI of every local TCP\n  listener, grouped by category and aware of the ssh forwards this repo creates (your forwarded dev\n  server shows as `DEV (SSH)`, not mystery noise). One keypress kills the holder; it also manages\n  auto-reconnecting `kubectl port-forward`s. `brew install dupe-com/tap/ports-cli`. A minimal fzf\n  fallback ships in `control/bin/ports.sh` — the shell snippet only aliases it when ports-cli isn't\n  installed.\n- 📊 **Optional daily health digest** to Slack / Discord / any webhook.\n- 👥 **Team provisioning.** One command adds a new developer account with their own named host\n  (`alice.studio`, `bob.studio` …) and loopback IP — everyone's dev servers run on standard ports\n  (`:3000`, `:8080`) without conflicts. A LaunchDaemon keeps the network alias alive across reboots.\n  ([details →](SETUP.md#7-team-provisioning))\n\n## The problem\n\nYou've got a Mac that never sleeps — a Studio under the desk, an old MacBook on a shelf — and you want\nto actually *use* it from your laptop. In practice that means a pile of `ssh` one-liners, Screen Sharing\nthat feels like molasses, remote shells that don't behave like local ones, and logins that break because\nthe OAuth callback points at the wrong machine. When the dev session wedges, you're the one SSHing in to\ndebug it.\n\nagent-mac-ops turns that always-on Mac into something you operate by *talking to an agent* and connect to\nas a native-feeling session — as close to \"sitting in front of it\" as a remote box gets.\n\n## Requirements\n\n- **macOS + iTerm2 _or_ Ghostty** on the control machine — for the native-window magic. iTerm2 uses\n  `tmux -CC` (Terminal.app won't do it, WezTerm only partially); Ghostty (≥ 1.2.0) uses native splits\n  that each auto-ssh in. `box` auto-detects which you're using. *(The agent-ops half —\n  status/logs/revive/digest — is plain SSH + bash and works against any host.)*\n- **tmux** on the remote (`brew install tmux`).\n- **mosh** (optional, for `box-mosh`) on both ends — `./setup.sh remote` installs it on the remote;\n  `brew install mosh` locally. Needs UDP 60000–61000 reachable (Tailscale carries it).\n- **SSH reachability** to the remote. [Tailscale](https://tailscale.com) is the recommended way (no\n  public exposure, works from anywhere) but anything your `ssh` can reach is fine — just put a Host\n  alias in `~/.ssh/config` and use its name.\n- `python3` and `curl` on the control machine (both ship with macOS) for the optional webhook digest.\n\n## Quickstart\n\n```bash\ngit clone https://github.com/dupe-com/agent-mac-ops \u0026\u0026 cd agent-mac-ops\n./setup.sh            # prompts for host, hostname, dirs, alias, webhook → writes config.env\n./setup.sh remote     # pushes the dev-session launcher to the remote\n\n# add to ~/.zshrc, BEFORE your iTerm2 shell-integration line:\nsource /path/to/agent-mac-ops/control/shell-snippet.sh\n\n# iTerm2: follow SETUP.md for the profile + Automatic Profile Switching (the coloring)\n# Ghostty: nothing else to do — see SETUP.md §4b\n```\n\nNow:\n\n- **`box`** (or whatever alias you chose) → native, colored window into the remote (iTerm2 `-CC`\n  panes, or Ghostty native splits — auto-detected). Ghostty adds **`box-tmux`** for a persistent\n  session that survives disconnect.\n- **`box-mosh`** (either terminal) → connect over mosh for snappy, predictive typing when the link is\n  laggy. Single session + tmux for persistence; the `-L` forwards ride the SSH master, the handoff\n  reverse tunnel rides its own self-healing launchd agent.\n- **Point your agent at `control/ops/`** and say *\"check on the box.\"*\n- **Browser/editor handoff:** `control/bin/install-open-listener.sh` (listener on your Mac) +\n  `control/bin/install-handoff-tunnel.sh` (an always-on, self-healing reverse tunnel) so remote auth\n  pages — and `code-box` — open on your Mac. The tunnel re-dials on its own after network drops and\n  reboots (and within ~a minute of a laptop sleep/wake), with or without a session connected.\n- **Clipboard auto-mirror:** `control/bin/install-clip-watch.sh` so screenshots you copy here land on\n  the remote's clipboard automatically — `Ctrl+V` them in a tool on the remote, no command in between.\n- **Open the remote in your local editor:** inside the session, `code-box` (current dir) opens Cursor/\n  VS Code on your Mac in Remote-SSH mode; from a local shell, `box-code [path]`. Needs the handoff\n  listener (`install-open-listener.sh`), the self-healing tunnel (`install-handoff-tunnel.sh`), and\n  the host in your `~/.ssh/config`.\n- **`ports`** → see every local TCP listener and kill the holder in one keypress. Install\n  [ports-cli](https://github.com/dupe-com/ports-cli) (`brew install dupe-com/tap/ports-cli`) for the\n  full TUI — it classifies the ssh forwards this repo creates as `DEV (SSH)` instead of system noise.\n  Without it, the shell snippet falls back to the bundled fzf picker (`control/bin/ports.sh`).\n- **Optional:** `control/ops/bin/install-launchd.sh` for a daily health digest.\n\n## How it works\n\n```\ncontrol machine (your laptop)                     always-on Mac (the remote)\n─────────────────────────────                     ──────────────────────────\nshell-snippet.sh  → `box` ───────── ssh -t ───▶   ~/dev-session.sh\n   (iTerm2)                                          └─ tmux -CC attach  ──▶ native iTerm2 windows\n   (Ghostty) ghostty-connect.sh ─── ssh -t ───▶      └─ login shell / tmux ─▶ native Ghostty splits\n   (mosh)    `box-mosh` ───────── mosh/UDP ────▶      └─ tmux attach ───────▶ predictive-echo session\ncontrol/ops/AGENTS.md  ← your agent reads this\ncontrol/ops/bin/remote-run.sh ── ssh + stdin ──▶   status.sh / logs.sh / revive.sh (run, then gone)\ncontrol/ops/bin/daily-check.sh ── launchd ──▶      webhook digest (Slack/Discord/…)\nopen-listener.py  ◀── reverse tunnel (-R) ─────    ~/bin/open shim (opens auth URLs on your Mac)\nyour localhost:3000  ◀── forward (-L) ─────────    remote dev server / OAuth callback\n```\n\n- **Config lives in one file.** `config.env` (gitignored, written by `setup.sh`) holds the host,\n  hostname, work dir, alias, webhook, etc. Every script sources it; the two `*.tmpl` files are\n  rendered from it.\n- **The remote stays stateless.** Only `~/dev-session.sh` lives there. The health scripts are\n  *shipped over SSH on stdin* with config prepended, so there's nothing to install or keep in sync.\n- **No secrets in git.** The only optional secret is the notify webhook, and it lives in the\n  gitignored `config.env`. Use a webhook URL, never a bare token.\n\n## What this is *not*\n\nDeliberately small. It does **not** sync dotfiles, manage packages, or sync editor settings — use\n[`chezmoi`](https://github.com/twpayne/chezmoi) / [`yadm`](https://yadm.io) / GNU `stow` for that.\nThis repo is just the above: agent-operable + a native-feeling remote session.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdupe-com%2Fagent-mac-ops","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdupe-com%2Fagent-mac-ops","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdupe-com%2Fagent-mac-ops/lists"}