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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kebandClaude Opus 5.5 committed 2026-10-10 23:58:25 +02:00
commit 6fab41703e
111 files changed
+22988

No files matched your search

+100
View File
@@ -0,0 +1,100 @@
package linkhandler
import (
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
)
// macOS apps: where a link was clicked (the frontmost app; the link
// handler runs in the background, so it doesn't change it), and which app
// a process's terminal belongs to. Elsewhere these know nothing.
// App is a running macOS app: its bundle path and process ID.
type App struct {
Bundle string
PID int
}
// FrontApp is the frontmost app.
func FrontApp() App {
if runtime.GOOS != "darwin" {
return App{}
}
asn, err := exec.Command("lsappinfo", "front").Output()
if err != nil {
return App{}
}
out, err := exec.Command("lsappinfo", "info", "-only", "pid", "-only", "bundlepath", strings.TrimSpace(string(asn))).Output()
if err != nil {
return App{}
}
var a App
for _, l := range strings.Split(string(out), "\n") {
k, v, _ := strings.Cut(strings.TrimSpace(l), "=")
switch strings.Trim(k, `"`) {
case "pid":
a.PID, _ = strconv.Atoi(v)
case "LSBundlePath":
a.Bundle = strings.Trim(v, `"`)
}
}
return a
}
// HostApp is the app bundle among pid's ancestors (the terminal it runs
// in), "" if none.
func HostApp(pid int) string {
if runtime.GOOS != "darwin" {
return ""
}
for range 32 {
out, err := exec.Command("ps", "-o", "ppid=,comm=", "-p", strconv.Itoa(pid)).Output()
if err != nil {
return ""
}
ppid, comm, _ := strings.Cut(strings.TrimSpace(string(out)), " ")
// The innermost bundle: kitty's quick access terminal is one
// inside kitty.app.
if i := strings.LastIndex(comm, ".app/Contents/MacOS/"); i >= 0 {
return comm[:i+len(".app")]
}
if pid, err = strconv.Atoi(ppid); err != nil || pid <= 1 {
return ""
}
}
return ""
}
// IsTerminal reports whether bundle is a terminal app.
func IsTerminal(bundle string) bool {
name := strings.ToLower(filepath.Base(bundle))
for _, t := range []string{"kitty", "ghostty", "wezterm", "iterm", "terminal.app", "alacritty", "warp"} {
if strings.Contains(name, t) {
return true
}
}
return false
}
// IsDropdown reports whether bundle is a terminal shown only by a hotkey
// (kitty's quick access terminal): activating it doesn't show it.
func IsDropdown(bundle string) bool { return strings.Contains(bundle, "quick-access") }
// ShowDropdown shows a hidden dropdown terminal (it toggles: call it only
// when the dropdown isn't frontmost).
func ShowDropdown(bundle string) {
if i := strings.Index(bundle, "kitty.app"); i >= 0 {
kitten := bundle[:i+len("kitty.app")] + "/Contents/MacOS/kitten"
_ = exec.Command(kitten, "quick-access-terminal").Start()
}
}
// Activate brings the app to the front.
func Activate(bundle string) {
if runtime.GOOS == "darwin" && bundle != "" {
_ = exec.Command("open", "-a", bundle).Run()
}
}
+141
View File
@@ -0,0 +1,141 @@
package linkhandler
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)
// On macOS a URL scheme belongs to an app bundle, and links arrive as an
// Apple Event rather than an argument. A small AppleScript applet,
// registered for the scheme, passes them on to "lazysecrets launch".
const bundleID = "no.kebcloud.lazysecrets.links"
// AppPath is where the applet is installed.
func AppPath() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, "Applications", "Lazysecrets Links.app"), nil
}
const lsregister = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister"
// Install registers launch (lazysecrets launch and its flags, the link
// appended) as the handler for lazysecrets:// links. env is set for it,
// since apps start with a minimal environment (no Homebrew on PATH, so no
// az).
func Install(launch []string, env []string) (string, error) {
app, err := AppPath()
if err != nil {
return "", err
}
if err := buildApp(app, launch, env); err != nil {
return "", err
}
if err := run(lsregister, "-f", app); err != nil {
return "", err
}
return app, nil
}
// buildApp writes the applet to app.
func buildApp(app string, launch []string, env []string) error {
cmd := shellJoin(append(append([]string{"/usr/bin/env"}, env...), launch...))
script := fmt.Sprintf(`on open location theURL
do shell script %s & " " & quoted form of theURL & " >/dev/null 2>&1 &"
end open location
on run
display dialog "Lazysecrets Links opens lazysecrets:// links in lazysecrets. It has nothing to show on its own." buttons {"OK"} default button 1 with title "Lazysecrets Links"
end run
`, appleString(cmd))
if err := os.MkdirAll(filepath.Dir(app), 0o755); err != nil {
return err
}
_ = os.RemoveAll(app)
f, err := os.CreateTemp("", "lazysecrets-links-*.applescript")
if err != nil {
return err
}
defer os.Remove(f.Name())
if _, err := f.WriteString(script); err != nil {
f.Close()
return err
}
if err := f.Close(); err != nil {
return err
}
src := f.Name()
plist := filepath.Join(app, "Contents", "Info.plist")
steps := [][]string{
{"osacompile", "-o", app, src},
{"plutil", "-replace", "CFBundleIdentifier", "-string", bundleID, plist},
{"plutil", "-replace", "LSUIElement", "-bool", "true", plist}, // no Dock icon
{"plutil", "-replace", exeKey, "-string", launch[0], plist}, // for Check
{"plutil", "-replace", "CFBundleURLTypes", "-json",
`[{"CFBundleURLName":"lazysecrets link","CFBundleURLSchemes":["lazysecrets"]}]`, plist},
// Editing Info.plist breaks osacompile's signature; sign again,
// ad hoc, so macOS runs it.
{"codesign", "--force", "--sign", "-", app},
}
for _, s := range steps {
if err := run(s...); err != nil {
return err
}
}
return nil
}
func run(argv ...string) error {
if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil {
return fmt.Errorf("%s: %v: %s", filepath.Base(argv[0]), err, strings.TrimSpace(string(out)))
}
return nil
}
// exeKey records, in the applet's Info.plist, the lazysecrets it runs.
const exeKey = "LazysecretsExecutable"
// Check reports why lazysecrets:// links don't open lazysecrets, or "" if
// they do (as far as can be told without opening one).
func Check() string {
app, err := AppPath()
if err != nil {
return ""
}
if _, err := os.Stat(app); err != nil {
return "lazysecrets:// links don't open lazysecrets yet"
}
out, err := exec.Command("plutil", "-extract", exeKey, "raw", filepath.Join(app, "Contents", "Info.plist")).Output()
if err != nil {
return "" // installed before the key was recorded: assume it works
}
if exe := strings.TrimSpace(string(out)); !exists(exe) {
return "lazysecrets:// links run " + exe + ", which no longer exists"
}
return ""
}
func exists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
// Uninstall removes the applet and its registration.
func Uninstall() (string, error) {
app, err := AppPath()
if err != nil {
return "", err
}
if _, err := os.Stat(app); os.IsNotExist(err) {
return "", fmt.Errorf("not installed (%s doesn't exist)", app)
}
_ = exec.Command(lsregister, "-u", app).Run()
return app, os.RemoveAll(app)
}
@@ -0,0 +1,53 @@
package linkhandler
import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
// The applet builds, claims the scheme and passes its signature check. It
// isn't registered, so the system's handler is untouched.
func TestBuildApp(t *testing.T) {
app := filepath.Join(t.TempDir(), "Lazysecrets Links.app")
if err := buildApp(app, []string{"/usr/local/bin/lazysecrets", "launch", "--terminal", "kitty"}, []string{"PATH=/opt/homebrew/bin:/usr/bin"}); err != nil {
t.Fatal(err)
}
plist := filepath.Join(app, "Contents", "Info.plist")
out, err := exec.Command("plutil", "-extract", "CFBundleURLTypes.0.CFBundleURLSchemes.0", "raw", plist).Output()
if err != nil || strings.TrimSpace(string(out)) != "lazysecrets" {
t.Errorf("scheme = %q, %v", out, err)
}
if out, err := exec.Command("codesign", "--verify", "--strict", app).CombinedOutput(); err != nil {
t.Errorf("signature: %v: %s", err, out)
}
src, err := exec.Command("osadecompile", filepath.Join(app, "Contents", "Resources", "Scripts", "main.scpt")).Output()
if err != nil || !strings.Contains(string(src), `'/usr/bin/env' 'PATH=/opt/homebrew/bin:/usr/bin' '/usr/local/bin/lazysecrets' 'launch'`) {
t.Errorf("script:\n%s %v", src, err)
}
}
// Check, against an applet in a temporary home (never registered).
func TestCheck(t *testing.T) {
t.Setenv("HOME", t.TempDir())
if r := Check(); !strings.Contains(r, "don't open lazysecrets yet") {
t.Errorf("nothing installed: %q", r)
}
app, _ := AppPath()
exe := filepath.Join(t.TempDir(), "lazysecrets")
if err := os.WriteFile(exe, nil, 0o755); err != nil {
t.Fatal(err)
}
if err := buildApp(app, []string{exe, "launch"}, nil); err != nil {
t.Fatal(err)
}
if r := Check(); r != "" {
t.Errorf("installed: %q", r)
}
os.Remove(exe)
if r := Check(); !strings.Contains(r, "no longer exists") {
t.Errorf("binary gone: %q", r)
}
}
+108
View File
@@ -0,0 +1,108 @@
package linkhandler
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)
// On Linux a desktop entry claims the x-scheme-handler/lazysecrets type,
// and xdg-open passes links to it as %u.
const desktopFile = "lazysecrets-links.desktop"
// AppPath is where the desktop entry is installed.
func AppPath() (string, error) {
dir := os.Getenv("XDG_DATA_HOME")
if dir == "" {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
dir = filepath.Join(home, ".local", "share")
}
return filepath.Join(dir, "applications", desktopFile), nil
}
// Install registers launch (lazysecrets launch and its flags, the link
// appended) as the handler for lazysecrets:// links, with env set.
func Install(launch []string, env []string) (string, error) {
path, err := AppPath()
if err != nil {
return "", err
}
argv := append(append([]string{"env"}, env...), launch...)
entry := fmt.Sprintf(`[Desktop Entry]
Type=Application
Name=Lazysecrets Links
Comment=Opens lazysecrets:// links in lazysecrets
Exec=%s %%u
MimeType=x-scheme-handler/lazysecrets;
NoDisplay=true
Terminal=false
%s=%s
`, desktopExec(argv), exeKey, launch[0])
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return "", err
}
if err := os.WriteFile(path, []byte(entry), 0o644); err != nil {
return "", err
}
if out, err := exec.Command("xdg-mime", "default", desktopFile, "x-scheme-handler/lazysecrets").CombinedOutput(); err != nil {
return "", fmt.Errorf("xdg-mime: %v: %s", err, strings.TrimSpace(string(out)))
}
_ = exec.Command("update-desktop-database", filepath.Dir(path)).Run() // optional
return path, nil
}
// desktopExec quotes args for a desktop entry's Exec key.
func desktopExec(args []string) string {
q := make([]string, len(args))
for i, a := range args {
a = strings.NewReplacer(`\`, `\\`, `"`, `\"`, "`", "\\`", "$", `\$`, "%", "%%").Replace(a)
q[i] = `"` + a + `"`
}
return strings.Join(q, " ")
}
// exeKey records, in the desktop entry, the lazysecrets it runs.
const exeKey = "X-Lazysecrets-Executable"
// Check reports why lazysecrets:// links don't open lazysecrets, or "" if
// they do (as far as can be told without opening one).
func Check() string {
path, err := AppPath()
if err != nil {
return ""
}
b, err := os.ReadFile(path)
if err != nil {
return "lazysecrets:// links don't open lazysecrets yet"
}
if out, err := exec.Command("xdg-mime", "query", "default", "x-scheme-handler/lazysecrets").Output(); err == nil &&
strings.TrimSpace(string(out)) != desktopFile {
return "lazysecrets:// links open " + strings.TrimSpace(string(out)) + ", not lazysecrets"
}
for _, l := range strings.Split(string(b), "\n") {
if exe, ok := strings.CutPrefix(l, exeKey+"="); ok {
if _, err := os.Stat(exe); err != nil {
return "lazysecrets:// links run " + exe + ", which no longer exists"
}
}
}
return ""
}
// Uninstall removes the desktop entry.
func Uninstall() (string, error) {
path, err := AppPath()
if err != nil {
return "", err
}
if _, err := os.Stat(path); os.IsNotExist(err) {
return "", fmt.Errorf("not installed (%s doesn't exist)", path)
}
return path, os.Remove(path)
}
+15
View File
@@ -0,0 +1,15 @@
//go:build !darwin && !linux
package linkhandler
import (
"errors"
"runtime"
)
var errUnsupported = errors.New("registering lazysecrets:// links isn't supported on " + runtime.GOOS + " yet")
func AppPath() (string, error) { return "", errUnsupported }
func Install(launch []string, env []string) (string, error) { return "", errUnsupported }
func Check() string { return "" } // nothing to set up
func Uninstall() (string, error) { return "", errUnsupported }
+184
View File
@@ -0,0 +1,184 @@
// Package linkhandler makes lazysecrets:// links open lazysecrets: it
// registers the scheme with the operating system, and launches a new
// terminal window running "lazysecrets open <link>" when a link is opened.
//
// The terminal is the one install ran in, detected from its environment,
// unless the config names another or gives a command of its own.
package linkhandler
import (
"fmt"
"os"
"os/exec"
"slices"
"strings"
)
// Terminals lists the terminals launch knows, by name.
var Terminals = []string{"kitty", "ghostty", "wezterm", "iterm2", "terminal", "alacritty", "gnome-terminal", "konsole", "foot"}
// Detect names the terminal from its environment (getenv), or "" if it
// isn't one launch knows. Terminal-specific variables come first: inside
// tmux, TERM_PROGRAM is "tmux" but the terminal's own variables are
// usually inherited.
func Detect(getenv func(string) string) string {
switch {
case getenv("KITTY_WINDOW_ID") != "" || getenv("TERM") == "xterm-kitty":
return "kitty"
case getenv("GHOSTTY_RESOURCES_DIR") != "" || getenv("TERM") == "xterm-ghostty":
return "ghostty"
case getenv("WEZTERM_EXECUTABLE") != "" || getenv("WEZTERM_PANE") != "":
return "wezterm"
case getenv("LC_TERMINAL") == "iTerm2" || getenv("ITERM_SESSION_ID") != "":
return "iterm2"
case getenv("ALACRITTY_WINDOW_ID") != "" || getenv("ALACRITTY_SOCKET") != "":
return "alacritty"
case getenv("KONSOLE_VERSION") != "":
return "konsole"
case getenv("GNOME_TERMINAL_SCREEN") != "":
return "gnome-terminal"
case getenv("TERM") == "foot":
return "foot"
}
switch getenv("TERM_PROGRAM") {
case "iTerm.app":
return "iterm2"
case "Apple_Terminal":
return "terminal"
case "WezTerm":
return "wezterm"
case "ghostty":
return "ghostty"
}
return ""
}
// Placeholder in a custom command stands for the command to run in it.
const Placeholder = "{cmd}"
// Command is the command that opens a new window of terminal running
// inner, for the operating system goos. custom, if set, is used instead,
// with Placeholder replaced by inner's arguments.
func Command(goos, terminal string, custom, inner []string) ([]string, error) {
if len(custom) > 0 {
if !slices.Contains(custom, Placeholder) {
return nil, fmt.Errorf("linkHandler.command must contain %s, where the lazysecrets command goes", Placeholder)
}
var out []string
for _, a := range custom {
if a == Placeholder {
out = append(out, inner...)
} else {
out = append(out, a)
}
}
return out, nil
}
if goos == "darwin" {
// Apps are started through open(1), so they needn't be on PATH.
// -n: a new instance, which opens a window even if one runs.
app := func(name string, args ...string) []string {
return append([]string{"open", "-na", name, "--args"}, append(args, inner...)...)
}
switch terminal {
case "kitty":
// Its own instance, so quit it with the window: kitty
// otherwise stays running without windows.
return app("kitty", "-o", "macos_quit_when_last_window_closed=yes"), nil
case "ghostty":
return app("Ghostty", "-e"), nil
case "wezterm":
return app("WezTerm", "start", "--"), nil
case "alacritty":
return app("Alacritty", "-e"), nil
case "iterm2":
return osascript(`tell application "iTerm"
activate
create window with default profile command %s
end tell`, inner), nil
case "terminal":
return osascript(`tell application "Terminal"
activate
do script %s
end tell`, inner), nil
}
} else {
switch terminal {
case "kitty", "foot":
return append([]string{terminal}, inner...), nil
case "ghostty", "alacritty", "konsole":
return append([]string{terminal, "-e"}, inner...), nil
case "wezterm":
return append([]string{"wezterm", "start", "--"}, inner...), nil
case "gnome-terminal":
return append([]string{"gnome-terminal", "--"}, inner...), nil
}
}
if terminal == "" {
return nil, fmt.Errorf("no terminal set: run lazysecrets handler install from your terminal, or set linkHandler.terminal or linkHandler.command in the config")
}
return nil, fmt.Errorf("unknown terminal %q (known: %s); linkHandler.command runs any other", terminal, strings.Join(Terminals, ", "))
}
// osascript runs an AppleScript with inner, shell-quoted, as a string
// literal in place of %s.
func osascript(script string, inner []string) []string {
return []string{"osascript", "-e", fmt.Sprintf(script, appleString(shellJoin(inner)))}
}
// shellJoin quotes args for a POSIX shell.
func shellJoin(args []string) string {
q := make([]string, len(args))
for i, a := range args {
q[i] = "'" + strings.ReplaceAll(a, "'", `'\''`) + "'"
}
return strings.Join(q, " ")
}
// appleString is s as an AppleScript string literal.
func appleString(s string) string {
return `"` + strings.NewReplacer(`\`, `\\`, `"`, `\"`).Replace(s) + `"`
}
// Launch opens a new terminal window running inner, without waiting for
// it.
func Launch(argv []string) error {
cmd := exec.Command(argv[0], argv[1:]...)
cmd.Stdin, cmd.Stdout, cmd.Stderr = nil, nil, nil
if err := cmd.Start(); err != nil {
return err
}
return cmd.Process.Release()
}
// Executable is the path of the running lazysecrets, to register. A
// binary from "go run" is deleted when it exits, so it's refused.
func Executable() (string, error) {
exe, err := os.Executable()
if err != nil {
return "", err
}
if strings.Contains(exe, "go-build") {
return "", fmt.Errorf("this is a temporary binary from go run: build or install lazysecrets first (go install ./cmd/lazysecrets), then run handler install with it")
}
return exe, nil
}
// passEnv lists the variables carried from the install's environment to
// lazysecrets in the new window: apps start with a minimal environment,
// without Homebrew's PATH (so no az) or the user's config locations.
var passEnv = []string{
"PATH", "LAZYSECRETS_CONFIG_FILE", "LAZYSECRETS_ICONS", "XDG_CONFIG_HOME", "XDG_STATE_HOME",
"AZURE_CONFIG_DIR", "DATABRICKS_CONFIG_FILE",
}
// Env returns NAME=value for each variable to carry that is set.
func Env(getenv func(string) string) []string {
var out []string
for _, k := range passEnv {
if v := getenv(k); v != "" {
out = append(out, k+"="+v)
}
}
return out
}
+123
View File
@@ -0,0 +1,123 @@
package linkhandler
import (
"os"
"os/exec"
"slices"
"strings"
"testing"
)
func env(m map[string]string) func(string) string { return func(k string) string { return m[k] } }
func TestDetect(t *testing.T) {
for _, c := range []struct {
env map[string]string
want string
}{
{map[string]string{"KITTY_WINDOW_ID": "3", "TERM_PROGRAM": "tmux"}, "kitty"}, // kitty, inside tmux
{map[string]string{"TERM_PROGRAM": "ghostty", "GHOSTTY_RESOURCES_DIR": "/x"}, "ghostty"},
{map[string]string{"TERM_PROGRAM": "WezTerm"}, "wezterm"},
{map[string]string{"TERM_PROGRAM": "tmux", "LC_TERMINAL": "iTerm2"}, "iterm2"},
{map[string]string{"TERM_PROGRAM": "Apple_Terminal"}, "terminal"},
{map[string]string{"ALACRITTY_WINDOW_ID": "1"}, "alacritty"},
{map[string]string{"TERM_PROGRAM": "vscode"}, ""},
} {
if got := Detect(env(c.env)); got != c.want {
t.Errorf("Detect(%v) = %q, want %q", c.env, got, c.want)
}
}
}
var inner = []string{"/usr/bin/env", "PATH=/opt/homebrew/bin:/usr/bin", "/usr/local/bin/lazysecrets", "open", "lazysecrets://azure/kv.vault.azure.net/secrets/it's"}
func TestCommandDarwin(t *testing.T) {
got, err := Command("darwin", "kitty", nil, inner)
if err != nil || !slices.Equal(got, append([]string{"open", "-na", "kitty", "--args", "-o", "macos_quit_when_last_window_closed=yes"}, inner...)) {
t.Errorf("kitty: %q %v", got, err)
}
got, _ = Command("darwin", "ghostty", nil, inner)
if !slices.Equal(got[:5], []string{"open", "-na", "Ghostty", "--args", "-e"}) {
t.Errorf("ghostty: %q", got)
}
got, _ = Command("darwin", "iterm2", nil, inner)
if got[0] != "osascript" || !strings.Contains(got[2], `create window with default profile command "'/usr/bin/env'`) {
t.Errorf("iterm2: %q", got)
}
}
// The AppleScript for Terminal.app (and iTerm2, if installed) compiles, with the quote in the link.
func TestAppleScriptCompiles(t *testing.T) {
if _, err := exec.LookPath("osacompile"); err != nil {
t.Skip("no osacompile")
}
terms := []string{"terminal"}
if _, err := os.Stat("/Applications/iTerm.app"); err == nil {
terms = append(terms, "iterm2") // its commands come from the app
}
for _, term := range terms {
argv, _ := Command("darwin", term, nil, inner)
out, err := exec.Command("osacompile", "-o", t.TempDir()+"/x.scpt", "-e", argv[2]).CombinedOutput()
if err != nil {
t.Errorf("%s: %v: %s", term, err, out)
}
}
}
func TestCommandLinux(t *testing.T) {
got, _ := Command("linux", "gnome-terminal", nil, inner)
if !slices.Equal(got, append([]string{"gnome-terminal", "--"}, inner...)) {
t.Errorf("gnome-terminal: %q", got)
}
}
func TestCommandCustom(t *testing.T) {
got, err := Command("linux", "kitty", []string{"foot", "--title", "secrets", Placeholder}, inner)
if err != nil || !slices.Equal(got, append([]string{"foot", "--title", "secrets"}, inner...)) {
t.Errorf("custom: %q %v", got, err)
}
if _, err := Command("linux", "", []string{"foot"}, inner); err == nil {
t.Error("custom command without {cmd} accepted")
}
}
func TestCommandErrors(t *testing.T) {
if _, err := Command("darwin", "", nil, inner); err == nil || !strings.Contains(err.Error(), "handler install") {
t.Errorf("no terminal: %v", err)
}
if _, err := Command("darwin", "xterm2000", nil, inner); err == nil || !strings.Contains(err.Error(), "unknown terminal") {
t.Errorf("unknown terminal: %v", err)
}
}
func TestShellJoinRoundTrip(t *testing.T) {
out, err := exec.Command("sh", "-c", `printf '%s\n' `+shellJoin(inner)).Output()
if err != nil {
t.Fatal(err)
}
if got := strings.Split(strings.TrimSuffix(string(out), "\n"), "\n"); !slices.Equal(got, inner) {
t.Errorf("round trip = %q", got)
}
}
func TestEnv(t *testing.T) {
got := Env(env(map[string]string{"PATH": "/bin", "HOME": "/h", "AZURE_CONFIG_DIR": "/az"}))
if !slices.Equal(got, []string{"PATH=/bin", "AZURE_CONFIG_DIR=/az"}) {
t.Errorf("Env = %q", got)
}
}
func TestIsTerminal(t *testing.T) {
for b, want := range map[string]bool{
"/Applications/kitty.app": true, "/Applications/kitty.app/Contents/kitty-quick-access.app": true,
"/Applications/Ghostty.app": true, "/System/Applications/Utilities/Terminal.app": true,
"/Applications/Slack.app": false, "/Applications/Safari.app": false, "": false,
} {
if IsTerminal(b) != want {
t.Errorf("IsTerminal(%q) = %v", b, !want)
}
}
if !IsDropdown("/Applications/kitty.app/Contents/kitty-quick-access.app") || IsDropdown("/Applications/kitty.app") {
t.Error("IsDropdown")
}
}