Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ The open [Agent Skills standard](https://agentskills.io/clients) is supported by

On macOS, a Keychain prompt may appear on first use to authorize access to your browser's cookie encryption key. Click **Always Allow** to skip future prompts.

On Linux, Chrome cookie decryption needs the OS keyring (gnome-keyring / libsecret, or KWallet on KDE) unlocked so the library can read **Chrome Safe Storage**. Prefer that native path for interactive use; do not inject `user_session` / `GH_SESSION_TOKEN` into unattended agent environments.

> [!NOTE]
> **When browser cookies aren't available:** Chrome 127+ on Windows isn't yet supported by the underlying cookie library ([workarounds](https://github.com/drogers0/gh-image/issues/4)), and Android (Termux) has no browser cookie store at all. In either case, supply the token explicitly via `GH_SESSION_TOKEN` (see [Session token override](#session-token-override) below); on Windows you can also just use another browser.

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ require (
golang.org/x/text v0.34.0 // indirect
gopkg.in/ini.v1 v1.67.1 // indirect
)

replace github.com/browserutils/kooky => github.com/MabezDev/kooky v0.2.11-0.20260728125024-3e00ef3ba597
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
github.com/MabezDev/kooky v0.2.11-0.20260728125024-3e00ef3ba597 h1:kZe2jEJNFHCK/kQLKatra4AuwriyfMiqfnJQo6J7dC8=
github.com/MabezDev/kooky v0.2.11-0.20260728125024-3e00ef3ba597/go.mod h1:ndMF+xzEi4voUsZ4dX0BL45oompbf5wqBaKR+J3lUNk=
github.com/browserutils/ese v0.0.0-20260314233042-37b6a03a93ce h1:xb/LXUukZgVLMRnTUyEiCfMNH7KUCFOS4aOZnc/N+H8=
github.com/browserutils/ese v0.0.0-20260314233042-37b6a03a93ce/go.mod h1:Rj9TJxm7cExxJmdec83sr8cjvyF3raBsszTFviSo/6U=
github.com/browserutils/kooky v0.2.10 h1:hEgbFJHf9lkMlSr0YdVEGtEo1yvqaTcGXNx0meNBgBA=
github.com/browserutils/kooky v0.2.10/go.mod h1:ndMF+xzEi4voUsZ4dX0BL45oompbf5wqBaKR+J3lUNk=
github.com/browserutils/sqlite3 v0.0.2 h1:RDSivmwoS5DauKYxh06G4Y+m6IX3da7Cq9Jh5x+oIUA=
github.com/browserutils/sqlite3 v0.0.2/go.mod h1:3i7CY1ba3/D+qovGRJyCgfDnu/lGc8sg8ieM6jIUO50=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
17 changes: 17 additions & 0 deletions internal/cookies/cookies.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ var browserReadHints = []struct{ match, hint string }{
"(F12 > Application > Cookies > github.com > user_session) and set " +
"GH_SESSION_TOKEN to it.",
},
{
// Surface keyring failures (libsecret / KWallet) with actionable recovery.
// Match is substring-based: kooky wraps these as
// "keyring password retrieval failed: …" (browserutils/kooky).
match: "keyring password retrieval failed",
hint: "Could not read the browser's cookie encryption key from the OS " +
"keyring. On Linux, ensure you are logged into GitHub in Chrome and " +
"that gnome-keyring (or KWallet on KDE) is unlocked. Confirm with: " +
"secret-tool lookup xdg:schema chrome_libsecret_os_crypt_password_v2. " +
"As a temporary override only, set GH_SESSION_TOKEN from DevTools.",
},
{
match: "secret not found in keyring",
hint: "The Chrome Safe Storage secret was not found in the OS keyring. " +
"Log into Chrome so it creates \"Chrome Safe Storage\", unlock the " +
"keyring, and retry. Temporary override: GH_SESSION_TOKEN from DevTools.",
},
}

// annotateReadError wraps a browser-read error as "reading browser cookies" and
Expand Down
18 changes: 18 additions & 0 deletions internal/cookies/cookies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,24 @@ func TestAnnotateReadError(t *testing.T) {
t.Errorf("expected bare wrap, got %q", got)
}
})

t.Run("keyring retrieval failure gets hint", func(t *testing.T) {
err := annotateReadError(fmt.Errorf("decrypting cookie: keyring password retrieval failed: kwallet: password not found"))
for _, want := range []string{"reading browser cookies", "keyring password retrieval failed", "hint:", "secret-tool", "GH_SESSION_TOKEN"} {
if !strings.Contains(err.Error(), want) {
t.Errorf("missing %q in %v", want, err)
}
}
})

t.Run("secret not found in keyring gets hint", func(t *testing.T) {
err := annotateReadError(fmt.Errorf("keyring password retrieval failed: secret not found in keyring for application \"chrome\""))
for _, want := range []string{"hint:", "Chrome Safe Storage", "GH_SESSION_TOKEN"} {
if !strings.Contains(err.Error(), want) {
t.Errorf("missing %q in %v", want, err)
}
}
})
}

func TestChooseSession(t *testing.T) {
Expand Down