On macOS, the Keychain can silently write 0-byte entries when it is
locked in a headless/server environment, even though Set returns no
error. The pre-flight check (EnsureKeychainAccess) does not catch
every case because the Keychain can appear unlocked yet still drop
data.
Add a read-back verification after SetToken writes the primary token.
If the read-back fails or returns empty data, return an actionable
error that suggests switching to the file-based keyring backend.
Fixes#206
Co-Authored-By: Claude <noreply@anthropic.com>
Why
- gogcli refreshed access tokens from a stored refresh token but never wrote back
a rotated refresh token returned by Google.
- In multi-process CLI usage this can leave keyring state stale across invocations
and eventually cause invalid_grant when the old token is retired.
What
- add persistingTokenSource wrapper in internal/googleapi/client.go
- wrap oauth2.Config TokenSource in tokenSourceForAccountScopes
- persist rotated refresh token back into secrets store when it changes
- keep persistence failures non-fatal (warn, return token)
- add unit tests for rotate/no-rotate/persist-failure paths
Tests
- go test ./internal/googleapi
Add new `calendar subscribe` command to add an existing calendar to the
user's calendar list. Supports optional flags for color, hidden state,
and selection state.
Aliases: sub, add-calendar
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The deleteOtherContact function called CopyOtherContactToMyContactsGroup
with an empty CopyOtherContactToMyContactsGroupRequest{}, but the Google
People API requires the CopyMask field to specify which fields to copy.
Omitting it causes a 400 badRequest error: 'copyMask is required'.
Fix by setting CopyMask to include all relevant contact fields.
Fixes#383
Co-authored-by: Rahul Bansal <rahul@hudle.in>
When a Keep-specific service account file (keep-sa-*.json) exists,
tokenSourceForServiceAccountScopes falls back to it for all API calls,
not just Keep. This causes 401 errors on Calendar, Gmail, Drive, and
other services that should use OAuth.
Only use keep-sa and legacy Keep SA files when serviceLabel is "keep",
allowing other services to fall through to OAuth authentication.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(drive): include shortcutDetails in drive get fields
Add shortcutDetails to the Drive Get API fields to enable resolving
shortcut target file IDs and MIME types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(googleapi): replace Client.Timeout with transport-level ResponseHeaderTimeout
The global http.Client.Timeout (30s) applied to the entire request
lifecycle, causing large Drive file downloads (videos, backups, etc.)
to time out. Replace it with http.Transport.ResponseHeaderTimeout
which only limits the time waiting for the server to begin responding.
Once response headers arrive and the body starts streaming, there is
no hard cap — large transfers complete naturally.
- Set ResponseHeaderTimeout=30s on the base transport
- Remove http.Client.Timeout from the API client
- Keep a dedicated tokenExchangeTimeout=30s for OAuth2 token refreshes
- Add tests verifying the new transport configuration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Ensure gmail watch serve re-applies the selected OAuth client to request-time contexts before creating Gmail services, so push handling does not fall back to client default in multi-client setups.
* fix(gmail): allow workspace native aliases for --from
* fix: land Workspace alias send fix and changelog (#407) (thanks @salmonumbrella)
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(gmail-watch): delay history fetch in watch serve
* fix: land gmail watch fetch delay and changelog (#397) (thanks @salmonumbrella)
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
- Import time/tzdata in cmd/gog/main.go to bundle the IANA tz database
into the binary (~450KB), fixing time.LoadLocation on Windows
- Add tzdata_test.go verifying LoadLocation works for representative zones
- Add windows-latest CI job running the full gate (fmt, lint, test, build)
* fix(contacts): correct parameter types in contacts apply helpers
Go's parameter grouping syntax caused `given` and `org` to be typed as
`bool` instead of `string` in contactsApplyPersonName and
contactsApplyPersonOrganization, resulting in a build failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(contacts): resolve lint errors from upstream refactor
- Rewrite if-else chain to switch statement (gocritic)
- Rename `clear` variable to `clearAll` to avoid shadowing predeclared
identifier (predeclared)
- Remove unused `primaryURL` function (unused)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add `gog sheets links` (alias: hyperlinks) command that retrieves
hyperlinks embedded in cells via spreadsheets.get with IncludeGridData.
Mirrors the existing `notes` command pattern using the CellData.Hyperlink field.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>