diff --git a/README.md b/README.md index 825eaff..b3859e5 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,14 @@ After creating the OAuth credentials in step 2 (or 3), run: gro init ``` +Before touching anything, init names the profile (and account) it is about to +(re)authenticate. To add ANOTHER Google account later without overwriting the +first one, authenticate it as a new named profile: + +```bash +gro init --profile work +``` + The wizard will: 1. **Ingest credentials.json.** Pick one of three options when prompted: @@ -255,6 +263,16 @@ gro me --id gro me --extended +# List credential profiles (accounts) and their token state +gro profiles list +gro profiles list --check # live-verify each token + +# Switch the active profile +gro profiles use work + +# Add another account as a new named profile +gro init --profile work + # Check configuration status gro config show @@ -277,6 +295,11 @@ gro -v All Gmail commands are under `gro mail`: ```bash +# List recent inbox messages +gro mail list +gro mail list --max 25 +gro mail list --ids # Output IDs only (for piping) + # Search messages gro mail search "is:unread" gro mail search "from:someone@example.com" --max 20 @@ -509,6 +532,7 @@ Flags: --credentials-file string Path to a downloaded OAuth client JSON (bypasses the wizard) --no-browser Don't try to open the consent URL in a browser --no-verify Skip connectivity verification after setup + --profile string Authenticate a NEW named profile (stored as google-readonly/) without touching the active profile ``` ### gro me @@ -523,6 +547,32 @@ Flags: --extended Add granted scopes, token expiry, and storage backend ``` +### gro profiles list + +List every credential profile stored in the OS keyring: the account email each +holds (as last verified), whether a token is present, and which profile is +active (marked `*`, with where the selection came from). + +``` +Usage: gro profiles list [flags] + +Flags: + -j, --json Emit JSON + --check Live-verify each profile's token against the API + (ok / expired or revoked / error, one API call per profile) +``` + +### gro profiles use + +Switch the active profile (writes `credential_ref` in `config.yml`). Accepts a +bare profile name or a full `google-readonly/` ref. Never touches +stored tokens; switching to a profile with no token warns and points at +`gro init`. + +``` +Usage: gro profiles use +``` + ### gro config show Display current configuration status including credentials and token. @@ -549,6 +599,18 @@ removed without removing anything. Usage: gro config clear [--all] [--dry-run] ``` +### gro mail list + +List the most recent inbox messages (equivalent to `gro mail search "in:inbox"`). + +``` +Usage: gro mail list [flags] + +Flags: + -m, --max int Maximum number of results (default 10) + --ids Output only message IDs (one per line, for piping) +``` + ### gro mail search Search for Gmail messages using Gmail's search syntax. diff --git a/go.mod b/go.mod index 6250355..804b112 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.26.4 require ( github.com/charmbracelet/lipgloss v1.1.0 github.com/muesli/termenv v0.16.0 - github.com/open-cli-collective/cli-common v0.4.1 + github.com/open-cli-collective/cli-common v0.5.0 github.com/spf13/cobra v1.10.2 golang.org/x/oauth2 v0.36.0 google.golang.org/api v0.289.0 @@ -66,7 +66,7 @@ require ( github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/noamcohen97/touchid-go v0.3.0 // indirect - github.com/open-cli-collective/google-cli-common v0.2.0 + github.com/open-cli-collective/google-cli-common v0.3.0 github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect diff --git a/go.sum b/go.sum index 594f62d..16a1cdb 100644 --- a/go.sum +++ b/go.sum @@ -128,10 +128,10 @@ github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/noamcohen97/touchid-go v0.3.0 h1:fcXxVCizysD7KHRR6hrURt3nyNIs5JBGSbOIidD/3wo= github.com/noamcohen97/touchid-go v0.3.0/go.mod h1:X9MRNIBGEmPqwpDm1G3fQOAQX7fwBlhzUbnkDTxuta0= -github.com/open-cli-collective/cli-common v0.4.1 h1:6oNaUVmMtXWi4pJn1X+W6KZVDP/WAJwbyZUyxYpGdbQ= -github.com/open-cli-collective/cli-common v0.4.1/go.mod h1:3AzjCT0V8xgslHlGi1+rUkcV+Vf5wONGwISQkufLZLQ= -github.com/open-cli-collective/google-cli-common v0.2.0 h1:yJ9x1gijQ0rgPTvhuCg74a19XhSteiGWkCcqhcVknLc= -github.com/open-cli-collective/google-cli-common v0.2.0/go.mod h1:EgRDUOb7s6cRa8PncoUrablPL3OcEDjTU8TByE7tsqs= +github.com/open-cli-collective/cli-common v0.5.0 h1:HA+ctKhVMugvVPf3zoke3ewhQvs6ur4H4Z7q7RJewyU= +github.com/open-cli-collective/cli-common v0.5.0/go.mod h1:3AzjCT0V8xgslHlGi1+rUkcV+Vf5wONGwISQkufLZLQ= +github.com/open-cli-collective/google-cli-common v0.3.0 h1:iIxFT8NJXKIfF53Lk940pRofwFfXQg11z10GLOEyXZs= +github.com/open-cli-collective/google-cli-common v0.3.0/go.mod h1:lPrPo5pD9s0eyU8YtTXjsXE8DszelDqGKh35SPUW1Nc= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= diff --git a/internal/cmd/root/root.go b/internal/cmd/root/root.go index 0f12e79..ce45280 100644 --- a/internal/cmd/root/root.go +++ b/internal/cmd/root/root.go @@ -11,6 +11,7 @@ import ( config "github.com/open-cli-collective/google-cli-common/configcmd" "github.com/open-cli-collective/google-cli-common/initcmd" mail "github.com/open-cli-collective/google-cli-common/mailcmd" + "github.com/open-cli-collective/google-cli-common/profilescmd" "github.com/open-cli-collective/google-cli-common/refreshcmd" "github.com/open-cli-collective/google-cli-common/rootutil" "github.com/open-cli-collective/google-cli-common/setcred" @@ -86,6 +87,7 @@ func init() { // Register commands rootCmd.AddCommand(initcmd.NewCommand()) rootCmd.AddCommand(config.NewCommand()) + rootCmd.AddCommand(profilescmd.NewCommand()) rootCmd.AddCommand(setcred.NewCmd()) rootCmd.AddCommand(me.NewCommand()) rootCmd.AddCommand(mail.NewCommand()) diff --git a/internal/cmd/root/root_test.go b/internal/cmd/root/root_test.go index 31e7646..87008bc 100644 --- a/internal/cmd/root/root_test.go +++ b/internal/cmd/root/root_test.go @@ -47,6 +47,7 @@ func TestRootCommand(t *testing.T) { } testutil.SliceContains(t, names, "init") testutil.SliceContains(t, names, "config") + testutil.SliceContains(t, names, "profiles") testutil.SliceContains(t, names, "mail") testutil.SliceContains(t, names, "calendar") testutil.SliceContains(t, names, "contacts")