Something went wrong. Try again.
tool for detachable ptys with optional tailscale discovery
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566package main
import ( "fmt" "os" "slices" "strings"
"tobi/internal/app" "tobi/internal/remote" "tobi/internal/session")
func main() { if err := dispatch(os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) }}
const usage = `tobi - persistent terminals across your devices
usage: tobi attach to a session with the fuzzy selector tobi <query> attach using fuzzy search (tobi hx, tobi host.id) tobi <target> -- cmd spawn cmd in a session on target
target is [user@][host.]id and host is optional, so tobi .id -- nu creates alocal session with that id. the daemon detaches when the terminal windowcloses and detached sessions are cleaned up after 24h. sessions arereachable from any device on the tailscale network by running tobi. tobidoesn't nest: running it inside a tobi session replaces it.
requires tssh (or ssh) and tailscale in PATH.`
func dispatch(args []string) error { switch op := os.Getenv("TOBI_RPC"); { case os.Getenv("TOBI_COMPLETE") == "1": return app.Complete(strings.Join(args, " ")) case os.Getenv("TOBI_OWNER") == "1": id := os.Getenv("TOBI_ID") for _, k := range []string{"TOBI_OWNER", "TOBI_ID", "TOBI_RPC", "TOBI_COMPLETE"} { os.Unsetenv(k) } return wrapErr("owner", session.RunOwner(id, args)) case op != "": return wrapErr("rpc "+op, remote.RunRPC(op, os.Getenv("TOBI_ID"))) } if len(args) > 0 && (args[0] == "--help" || args[0] == "-h") { fmt.Print(usage) return nil } if i := slices.Index(args, "--"); i != -1 { return app.Spawn(strings.Join(args[:i], " "), args[i+1:]) } return app.Interactive(strings.Join(args, " "))}
func wrapErr(prefix string, err error) error { if err != nil { return fmt.Errorf("%s: %w", prefix, err) } return nil}