Hello world
Release / Build and publish (push) Successful in 23s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kebandClaude Opus 5.5 committed 2026-10-12 14:21:32 +02:00
commit 09143eb29b
134 files changed
+27238

No files matched your search

+222
View File
@@ -0,0 +1,222 @@
// Package instance lets a lazysecrets:// link go to a lazysecrets that is
// already running, rather than a new window.
//
// Each running lazysecrets listens on a Unix socket of its own in a
// directory only the user can read (Unix sockets work the same on macOS,
// Linux and Windows 10 and later). The link handler asks each one when it
// was last used and whether it can be brought into view, and hands the
// link to the best one, which shows it and comes to the front.
package instance
import (
"bufio"
"context"
"encoding/json"
"errors"
"fmt"
"net"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
)
// Dir holds the sockets: $XDG_RUNTIME_DIR/lazysecrets if set, else
// run/ under the state directory.
func Dir(stateDir string) string {
if d := os.Getenv("XDG_RUNTIME_DIR"); d != "" {
return filepath.Join(d, "lazysecrets")
}
return filepath.Join(stateDir, "run")
}
// Status is what an instance tells the link handler about itself.
type Status struct {
PID int `json:"pid"`
LastActive time.Time `json:"lastActive"` // last key press or focus
// Reach is how readily it can be shown: 2 in view (outside tmux, or
// in a tmux session a client shows), 1 in a tmux session that a tmux
// client can be switched to, 0 not at all.
Reach int `json:"reach"`
// App is the terminal app it would show in (macOS), preferring the
// app the link was clicked in.
App string `json:"app,omitempty"`
}
type request struct {
Op string `json:"op"` // "status" or "open"
Link string `json:"link,omitempty"`
Front string `json:"front,omitempty"` // app the link was clicked in
}
type response struct {
Status
Err string `json:"err,omitempty"`
}
// Server is the running lazysecrets' end.
type Server struct {
ln net.Listener
path string
open func(link, front string) error // shows the link and comes to the front
mu sync.Mutex
active time.Time
}
// Listen starts listening in dir. open is called, from another goroutine,
// with each link handed over and the app it was clicked in; it should
// return quickly, as the link handler waits for it, and come to the front
// (Raise) afterwards.
func Listen(dir string, open func(link, front string) error) (*Server, error) {
if err := os.MkdirAll(dir, 0o700); err != nil {
return nil, err
}
_ = os.Chmod(dir, 0o700) // a pre-existing directory too
path := filepath.Join(dir, strconv.Itoa(os.Getpid())+".sock")
_ = os.Remove(path) // a crashed process with this PID
ln, err := net.Listen("unix", path)
if err != nil {
return nil, err
}
s := &Server{ln: ln, path: path, open: open, active: time.Now()}
go s.serve()
return s, nil
}
// Touch records that the user just used this instance.
func (s *Server) Touch() {
s.mu.Lock()
s.active = time.Now()
s.mu.Unlock()
}
// Close stops listening and removes the socket.
func (s *Server) Close() error {
err := s.ln.Close()
_ = os.Remove(s.path)
return err
}
func (s *Server) serve() {
for {
c, err := s.ln.Accept()
if err != nil {
return
}
go s.handle(c)
}
}
func (s *Server) handle(c net.Conn) {
defer c.Close()
_ = c.SetDeadline(time.Now().Add(5 * time.Second))
var req request
if err := json.NewDecoder(bufio.NewReader(c)).Decode(&req); err != nil {
return
}
var resp response
switch req.Op {
case "status":
_, app, reach := place(req.Front)
s.mu.Lock()
resp.Status = Status{PID: os.Getpid(), LastActive: s.active, Reach: reach, App: app}
s.mu.Unlock()
case "open":
if err := s.open(req.Link, req.Front); err != nil {
resp.Err = err.Error()
}
default:
resp.Err = fmt.Sprintf("unknown request %q", req.Op)
}
_ = json.NewEncoder(c).Encode(resp)
}
// Found is a running instance.
type Found struct {
Status
path string
}
// Running asks every instance in dir for its status for a link clicked in
// front, removing sockets no one answers on (left by a crash).
func Running(ctx context.Context, dir, front string) []Found {
entries, _ := os.ReadDir(dir)
var (
mu sync.Mutex
out []Found
wg sync.WaitGroup
)
for _, e := range entries {
if !strings.HasSuffix(e.Name(), ".sock") {
continue
}
path := filepath.Join(dir, e.Name())
wg.Add(1)
go func() {
defer wg.Done()
var resp response
err := call(ctx, path, request{Op: "status", Front: front}, &resp)
if errors.Is(err, errGone) {
_ = os.Remove(path)
return
}
if err != nil || resp.Err != "" {
return
}
mu.Lock()
out = append(out, Found{resp.Status, path})
mu.Unlock()
}()
}
wg.Wait()
return out
}
// Best is the instance to hand a link clicked in front to: the most
// recently used of those most readily shown. A link clicked in a terminal
// goes only to one showing in that terminal. ok is false if there's none.
func Best(found []Found, front string, terminal bool) (Found, bool) {
var best Found
for _, f := range found {
if terminal && f.App != front {
continue
}
if f.Reach > best.Reach || (f.Reach == best.Reach && f.LastActive.After(best.LastActive)) {
best = f
}
}
return best, best.Reach > 0
}
// Open hands link, clicked in front, to f.
func Open(ctx context.Context, f Found, link, front string) error {
var resp response
if err := call(ctx, f.path, request{Op: "open", Link: link, Front: front}, &resp); err != nil {
return err
}
if resp.Err != "" {
return errors.New(resp.Err)
}
return nil
}
var errGone = errors.New("no instance listening")
func call(ctx context.Context, path string, req request, resp *response) error {
var d net.Dialer
c, err := d.DialContext(ctx, "unix", path)
if err != nil {
return fmt.Errorf("%w: %v", errGone, err)
}
defer c.Close()
if dl, ok := ctx.Deadline(); ok {
_ = c.SetDeadline(dl)
}
if err := json.NewEncoder(c).Encode(req); err != nil {
return err
}
return json.NewDecoder(c).Decode(resp)
}
+99
View File
@@ -0,0 +1,99 @@
package instance
import (
"context"
"net"
"os"
"path/filepath"
"testing"
"time"
)
// shortDir is a temporary directory with a path short enough for a
// socket (macOS allows 104 bytes).
func shortDir(t *testing.T) string {
d, err := os.MkdirTemp("", "ls")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.RemoveAll(d) })
return d
}
func TestHandOver(t *testing.T) {
t.Setenv("TMUX", "")
dir := shortDir(t)
got := make(chan string, 1)
s, err := Listen(dir, func(link, front string) error { got <- link; return nil })
if err != nil {
t.Fatal(err)
}
defer s.Close()
if fi, _ := os.Stat(dir); fi.Mode().Perm() != 0o700 {
t.Errorf("dir mode %v", fi.Mode().Perm())
}
// A socket left by a crashed instance.
stale := filepath.Join(dir, "999999.sock")
ln, err := net.Listen("unix", stale)
if err != nil {
t.Fatal(err)
}
ln.(*net.UnixListener).SetUnlinkOnClose(false)
ln.Close()
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
found := Running(ctx, dir, "")
if len(found) != 1 || found[0].PID != os.Getpid() || found[0].Reach != 2 {
t.Fatalf("found %+v", found)
}
if _, err := os.Stat(stale); !os.IsNotExist(err) {
t.Error("stale socket left behind")
}
best, ok := Best(found, "", false)
if !ok {
t.Fatal("no instance to hand over to")
}
if err := Open(ctx, best, "lazysecrets://azure/kv.vault.azure.net", ""); err != nil {
t.Fatal(err)
}
if l := <-got; l != "lazysecrets://azure/kv.vault.azure.net" {
t.Errorf("got %q", l)
}
}
func TestBest(t *testing.T) {
now := time.Now()
f := func(reach int, ago time.Duration) Found {
return Found{Status: Status{Reach: reach, LastActive: now.Add(-ago), PID: int(ago)}}
}
// In view beats more recently used but needing a tmux switch.
if b, _ := Best([]Found{f(1, time.Second), f(2, time.Hour), f(2, time.Minute)}, "", false); b.PID != int(time.Minute) {
t.Errorf("best = %+v", b)
}
if _, ok := Best([]Found{f(0, 0)}, "", false); ok {
t.Error("handed over to an instance that can't be shown")
}
if _, ok := Best(nil, "", false); ok {
t.Error("best of none")
}
// Clicked in a terminal: only an instance showing there will do,
// however recently another was used.
kitty, dropdown := "/Applications/kitty.app", "/Applications/kitty.app/Contents/kitty-quick-access.app"
inDropdown := f(2, time.Second)
inDropdown.App = dropdown
inKitty := f(1, time.Hour)
inKitty.App = kitty
if b, ok := Best([]Found{inDropdown, inKitty}, kitty, true); !ok || b.App != kitty {
t.Errorf("clicked in kitty: %+v %v", b, ok)
}
if _, ok := Best([]Found{inDropdown}, kitty, true); ok {
t.Error("clicked in kitty, went to the dropdown")
}
// Clicked in Slack: the one used last, wherever.
if b, _ := Best([]Found{inDropdown, inKitty}, "/Applications/Slack.app", false); b.App != dropdown {
t.Errorf("clicked in Slack: %+v", b)
}
}
+123
View File
@@ -0,0 +1,123 @@
package instance
import (
"os"
"os/exec"
"slices"
"strconv"
"strings"
"git.kebcloud.no/keb/lazysecrets/internal/linkhandler"
)
// Where an instance shows depends on its terminal app (on macOS): for an
// instance in tmux, the app of a tmux client showing its session, or one
// that could be switched to it. front is the app the link was clicked in;
// clients in it are preferred, so a link clicked in a terminal opens
// there.
// tmux runs tmux on the server this instance runs in.
func tmux(args ...string) (string, error) {
out, err := exec.Command("tmux", args...).Output()
return strings.TrimSpace(string(out)), err
}
func inTmux() bool { return os.Getenv("TMUX") != "" && os.Getenv("TMUX_PANE") != "" }
type client struct {
name, session, activity string // activity: Unix seconds
pid int
app string
}
// clients lists the tmux server's clients, the one last typed in first.
func clients() []client {
out, err := tmux("list-clients", "-F", "#{client_activity} #{client_pid} #{client_name} #{session_name}")
if err != nil {
return nil
}
var cs []client
for _, l := range strings.Split(out, "\n") {
f := strings.SplitN(l, " ", 4)
if len(f) < 4 {
continue
}
pid, err := strconv.Atoi(f[1])
if err != nil {
continue
}
cs = append(cs, client{name: f[2], session: f[3], activity: f[0], pid: pid, app: linkhandler.HostApp(pid)})
}
// Activity has the same width for centuries: strings compare like
// numbers.
slices.SortStableFunc(cs, func(a, b client) int { return strings.Compare(b.activity, a.activity) })
return cs
}
// place is where this instance would show: the tmux client (zero outside
// tmux), the app, and reach: 2 already on screen there (outside tmux, or
// a client on its session), 1 after switching a client, 0 nowhere.
func place(front string) (client, string, int) {
if !inTmux() {
return client{}, linkhandler.HostApp(os.Getpid()), 2
}
session, err := tmux("display-message", "-p", "-t", os.Getenv("TMUX_PANE"), "#{session_name}")
if err != nil {
return client{}, "", 0
}
cs := clients()
// Best first: in the front app on its session, in the front app,
// on its session, any.
rank := func(c client) int {
r := 0
if front != "" && c.app == front {
r += 2
}
if c.session == session {
r++
}
return r
}
var best client
found := false
for _, c := range cs {
if !found || rank(c) > rank(best) {
best, found = c, true
}
}
if !found {
return client{}, "", 0
}
if best.session == session {
return best, best.app, 2
}
return best, best.app, 1
}
// Raise brings this instance into view for a link clicked in front: in
// tmux its window and pane, switching a client to its session if needed;
// then its app to the front, showing a hidden dropdown terminal.
func Raise(front string) {
c, app, reach := place(front)
if reach == 0 {
return
}
if inTmux() {
pane := os.Getenv("TMUX_PANE")
_, _ = tmux("select-window", "-t", pane)
_, _ = tmux("select-pane", "-t", pane)
if reach == 1 {
_, _ = tmux("switch-client", "-c", c.name, "-t", pane)
}
}
switch {
case app == "" || app == front:
// already in front (or unknown)
case linkhandler.IsDropdown(app):
linkhandler.ShowDropdown(app) // it isn't front, so hidden: this shows it
default:
// The bundle, not the name: "kitty" may pick an instance without
// windows, which then opens a new one.
linkhandler.Activate(app)
}
}