Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

herdr-exe

A herdr plugin for exe.dev VMs.

exe.dev VMs are persistent computers, not disposable sandboxes: many run 24/7. This plugin treats them that way. It links a VM to a herdr workspace, gets you a shell on it in one keystroke, shows its state in the sidebar, lends it your Claude subscription while you work there, and never stops or deletes a VM on its own.

  • Link a workspace to a VM, new or existing, and exe: shell splits a pane into it. The sidebar shows $exe / $exe_status for linked workspaces.
  • Ctrl+click an *.exe.xyz URL in any pane: shell, browser, web terminal, Shelley, share link. Other people's exe.xyz links still open in the browser.
  • A VM per git worktree (opt-in): herdr worktree create makes or reuses a VM, ships the branch to it as a git bundle, and opens a tab on it.
  • Claude on the VM without leaving a credential there, via imp, or by pushing a token that can't be refreshed (see Claude on the VM).
  • VMs as herdr machines: pick VMs and herdr machine add them, so agents running on them show up in your herdr sidebar.

Install

Needs herdr 0.9.0+, bash, jq, fzf, python3 (for the bundled imp; on macOS, the Command Line Tools), and an SSH key registered with exe.dev. ssh exe.dev whoami should work without a prompt, so ssh-add a key with a passphrase first: plugin hooks run in the background.

herdr plugin install metcalfc/herdr-exe
# or, from a checkout:
herdr plugin link ~/src/github.com/metcalfc/herdr-exe

cp config.env.example "$(herdr plugin config-dir exedev)/.env"   # optional

Add keys and the sidebar tokens to ~/.config/herdr/config.toml:

[[keys.command]]
key = "prefix+shift+e"
type = "plugin_action"
command = "exedev.vms"
description = "exe.dev VMs"

[[keys.command]]
key = "prefix+alt+e"
type = "plugin_action"
command = "exedev.ssh"
description = "shell into this workspace's VM"

[ui.sidebar.spaces]
rows = [
  ["state_icon", "workspace"],
  ["branch", "git_status"],
  ["$exe", { token = "$exe_status", rules = [
    { equals = "running", fg = "#a6e3a1" },
    { equals = "creating", fg = "#f9e2af" },
    { equals = "failed", fg = "#f38ba8" },
  ] }],
]

Actions

Action What it does
exedev.vms Picker over your VMs with a live stat preview. enter: shell · ctrl-a: link to this workspace · ctrl-u: unlink · ctrl-o: browser · ctrl-l: share link · ctrl-x: delete · ctrl-r: refresh
exedev.ssh Split a pane and ssh into this workspace's VM (opens the picker if none is linked). The pane is the session: leaving the VM closes it
exedev.new Create a VM for this workspace: name, optional Shelley prompt, then a shell
exedev.machines Add VMs as herdr machines. herdr installs itself on a VM if needed and asks first
exedev.auth Claude login for VMs: store a setup token, push, status, revoke
exedev.refresh Re-read ssh exe.dev ls and repaint the sidebar (also runs at startup)

A workspace is linked to a VM by its git checkout root, so every workspace or pane in that repo finds the same VM.

Claude on the VM

EXE_CLAUDE_AUTH decides how claude on a VM gets your subscription.

Mode On the VM Revoke
imp A loopback URL and an inert capability. The credential stays in your keychain; imp-proxy runs in an imp:<vm> tab and injects it per request Ctrl-C the imp:<vm> tab, or close the last exe:<vm> pane
setup-token A long-lived claude setup-token token, no refresh token exe: Claude login → revoke
rolling This machine's current access token, no refresh token. Lasts hours; topped up on attach, on focusing an exe: pane, and at startup exe: Claude login → revoke
none Nothing

auto (the default) is imp whenever python3 is available, else setup-token if you stored one, else none.

imp is bundled: vendor/imp/imp-proxy is a pinned copy of metcalfc/imp (MIT), so there is nothing else to install. scripts/vendor-imp [ref|checkout] refreshes it, and EXE_IMP_PROXY points the plugin at another copy, such as an imp checkout you are working on. The bundled copy wins over an imp-proxy on your PATH, since it is the version the plugin is tested against.

The plugin never copies a refresh token to a VM. Anthropic refresh tokens are single-use, so two machines holding the same chain log each other out. Tokens only travel over ssh stdin, never in a command line, and a VM where you ran claude login yourself is left alone. rolling reads Claude Code's keychain item through /usr/bin/security; macOS may ask once.

A setup token has fewer scopes than a full login. If a model is gated on your plan, use imp or rolling.

Codex: don't copy ~/.codex/auth.json (a reused ChatGPT refresh token is permanently fatal). Connect your ChatGPT subscription as an exe.dev LLM integration instead, attach it (EXE_INTEGRATIONS), and set EXE_CODEX_EXE_LLM=1 to point Codex on the VM at it.

A VM per worktree

With EXE_VM_PER_WORKTREE=1 (and ideally EXE_WORKTREE_REPOS), creating a herdr worktree:

  1. creates <repo>-<branch>, or reuses it if this plugin made it before;
  2. clones the branch into ~/work/<repo> from a git bundle, with origin pointing back at your remote;
  3. opens an exe:<vm> tab there, logged in to Claude, optionally running EXE_WORKTREE_COMMAND (e.g. claude).

Removing the worktree only sends a notification. The VMs picker then shows the link as (gone).

What it will never do

  • Stop, restart, or delete a VM from a hook, a refresh, or on exit. Tests pin this down.
  • Delete a VM it didn't create. ctrl-x works only on VMs recorded at creation or marked with the herdr tag or a herdr: comment, and asks you to type the name. A tag you share with other VMs (say dev) doesn't make them deletable.
  • Take over one of your VMs that happens to share a generated name.

Notes

  • Tag-scoped keys (ssh-key add --tag=X) can only create VMs tagged X. The plugin notices exe.dev's refusal, retries with X, and remembers it. Set EXE_TAGS to choose tags yourself.
  • herdr machines: herdr installs to ~/.local/bin on the VM, which isn't on a non-login ssh PATH on exeuntu. herdr warns and works anyway.
  • Scripted invocations: if you plugin.action.invoke with a partial context, herdr fills the missing fields from the active workspace, so .worktree can belong to a different workspace than .workspace_id. The plugin resolves the workspace through herdr's API rather than trusting those fields.
  • Toasts: herdr's ui.toast.delivery is off by default; set it to herdr to see the plugin's notifications.

Tests

tests/run.sh            # stubbed herdr/ssh/fzf/keychain; real git and jq
tests/run.sh /bin/bash  # again with macOS's bash 3.2
shellcheck bin/exe-herdr tests/run.sh tests/stubs/* scripts/vendor-imp

The stubs simulate exe.dev (including its error shapes, tag-scoped keys, and its habit of exiting 0 on failure), a herdr server whose pane labels stay consistent, a keychain, and a "VM" that runs remote scripts in a throwaway $HOME, so credential files, permissions, and clones are checked for real.

License

MIT. The bundled vendor/imp/imp-proxy is imp, also MIT; see vendor/imp/LICENSE.

About

herdr plugin for exe.dev VMs: link workspaces to VMs, lend them Claude via imp, VM per worktree

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages