Skip to content

Commit 8619cfa

Browse files
authored
feat: single-run init wizard and gro me (#107) (#108)
* feat: single-run init wizard and gro me Closes #107 Replaces the two-pass `gro init` flow with a huh-driven wizard that ingests credentials.json from clipboard, terminal paste, or file path in a single invocation. Adds a new `gro me` command modeled on jtk me — a token-dense one-liner (resourceName | displayName | primaryEmail) backed by the People API people/me endpoint, with --id (primary email), --extended (scopes + token expiry + storage backend), and --json shapes. Wizard improvements: - Detects existing credentials.json and short-circuits. - --credentials-file path bypass for scripting. - huh.NewConfirm before opening the consent URL; --no-browser opt-out. - huh.NewInput with in-place validation for the redirect URL paste. - Re-auth confirm via huh.NewConfirm when the stored token is expired. - Cache TTL prompt now fires on first runs (snapshot config existence before saving granted scopes — the existing code's ordering bug made the prompt unreachable). - Explicit os.Chmod(path, 0600) after WriteFile so credentials.json can't keep loose perms when overwritten. - Cloud Console instructions now list Gmail, Calendar, Drive, and People APIs. gro me: - Pipe one-liner with `-` for empty fields and `\|` escaping. - People API call site detects insufficient-scope 403s specifically (insufficientPermissions / ACCESS_TOKEN_SCOPE_INSUFFICIENT) and rewrites them to "Run 'gro init' to re-authenticate". Other 403s (service-disabled, project-not-permitted) pass through unchanged. - Token expiry read after the API call so PersistentTokenSource refreshes are reflected. - --id and --extended are mutually exclusive. Adds: - internal/people/ — focused client for people/me + insufficient-scope error detection. - internal/view/ — small Success/Error/Info/Printf/Println helper. - Architecture-test registrations for the new packages. * fix(init): close stale-scope remediation loop and stop lying about success Addresses Codex PR review findings on #108: - Stale-scope remediation loop (BLOCKER): a Gmail-valid but People- insufficient token used to silently pass init's existing-token branch, while gro me told users to "run gro init". Init now verifies People in addition to Gmail when a token exists, runs auth.CheckScopesMigration on the recorded scopes, and triggers re-auth on either failure mode. Extracted tryExistingToken seam to keep runWith readable. - --no-verify regression: existing token path is now selected first, with API calls suppressed when --no-verify is set (rather than forcing a fresh OAuth flow on every invocation). - People-failure-is-fatal: post-OAuth People verify is no longer swallowed as a warning followed by "Setup complete" — init's contract is that gro me works when init returns nil. - Output via Cobra writers / View.Out instead of bare os.Stdout/os.Stderr, so output redirection works and tests can drive both streams. - Removed unused initDeps.GetToken field and the io.Discard import sentinel. New tests cover the stale-scope re-auth, --no-verify-skips-API, fatal People failure, and (in me_test.go) real-config stale/missing/empty scope cases via XDG_CONFIG_HOME. * fix(init): plumb verified profile through finishExisting; don't claim scopes for existing tokens Addresses second-pass Codex PR review: - Existing-token verify only calls People once now: tryExistingToken passes the verified Profile into finishExisting, which renders the one-liner from that result instead of re-fetching. - finishExisting no longer writes GrantedScopes for existing tokens. We don't actually know which scopes the token has — only a fresh OAuth flow does. The pre-fix code was claiming auth.AllScopes for any existing-token-without-config-file run, which would defeat the same stale-scope detection later. - New TestRunWithRecordedStaleScopesReauths covers the loud-and-early CheckScopesMigration branch (asserts DeleteToken happens before any Gmail/People call on the stale token). * test: cover gaps surfaced by TDD assessment on PR #108 Adds tests for the five gaps the TDD review flagged: - ensureCredentials file-path wizard branch (credChoice="file" was the only cred-source path without coverage). - ensureCredentials short-circuits when credentials.json already exists and never invokes the wizard's first prompt. - ConfirmOpenBrowser=true causes OpenBrowser to actually fire (the existing wizard tests all said "no" to the open-browser prompt, so the real branch was never exercised). - Recorded-stale-scope re-auth path now also asserts the TTL prompt fires after the fresh OAuth flow. - run() end-to-end with --extended + --json (regression guard for gatherExtras being skipped when jsonOutput is true). * fix: address daemon Should-Fix findings on PR #108 - tryExistingToken: run CheckScopesMigration BEFORE the --no-verify branch so a stale-scoped token + --no-verify doesn't reopen the same remediation loop the wizard exists to close. Added a "Token accepted" line on the --no-verify path so users don't see a silent return. - gro me: SilenceErrors+SilenceUsage so errReauth's actionable message isn't shadowed by cobra's "Error: re-authentication required" prefix. - RenderJSON --id: normalize empty PrimaryEmail to "-" so JSON and text paths agree on empty-field representation. - grantedScopes(): return nil instead of falsely claiming auth.AllScopes when no scope record exists. Old behavior overstated what the token was actually granted under --extended. - README: clarify that init prints the gro me one-liner automatically; the "run gro me" line was contradicting that. * docs: show -j short form for gro me --json * docs(init): clarify retry-loop scope per daemon review * fix: address daemon Should-Fix findings (round 2) - gatherExtras: add `tok != nil` guard so a nil token from keychain doesn't panic on tok.Expiry.IsZero(). - RenderJSON: revert the --id normalization so all three JSON shapes (default, --id, --extended) consistently emit raw values. Text rendering still uses "-" via normalizeField; JSON keeps empty strings, which is the more machine-friendly convention. - huh.NewText paste prompt: "Press Esc when done" was misleading (Esc cancels in huh). Updated to "Press Tab when done". - Verify cobra SilenceErrors actually suppresses the "Error: ..." prefix end-to-end via TestNewCommandSilencesCobraErrorOnReauth. - TestRunWithExistingTokenNoVerifyStillCatchesStaleScopes: new test asserting --no-verify does NOT bypass the recorded-scope check. - README: document the cache-TTL prompt as wizard step 4. * fix: route 'Warning:' messages to stderr in init wizard Daemon round 3 noted that View.Info writes to stdout, but the three 'Warning: saving ...' callers were misuse — they should be on stderr so scripts redirecting stdout don't capture them. Switching to View.Error matches the existing semantics rather than redefining Info.
1 parent 15db54d commit 8619cfa

15 files changed

Lines changed: 2528 additions & 367 deletions

File tree

README.md

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ A non-destructive command-line interface for Google services. Search, read, and
1212
- **Bulk operations** - Pipe IDs between commands, use search queries inline, or pass IDs as arguments
1313
- **JSON output** - Machine-readable output for scripting
1414
- **Secure storage** - OAuth tokens stored in system keychain (macOS/Linux)
15+
- **Single-run guided setup** - `gro init` reads credentials.json from clipboard / paste / file path and walks you through OAuth in one shot; `gro me` confirms identity afterwards
1516

1617
## Installation
1718

@@ -138,34 +139,40 @@ For personal use with these scopes, publishing is straightforward and doesn't re
138139

139140
> **Note:** If you skip this step, you'll need to run `gro init` every 7 days to re-authenticate.
140141
141-
### 4. Configure gro
142+
### 4. Run the wizard
142143

143-
1. Create the config directory:
144-
```bash
145-
mkdir -p ~/.config/google-readonly
146-
```
147-
148-
2. Move the downloaded credentials file:
149-
```bash
150-
mv ~/Downloads/client_secret_*.json ~/.config/google-readonly/credentials.json
151-
```
152-
153-
### 5. Authenticate
154-
155-
Run the init command to complete OAuth setup:
144+
After creating the OAuth credentials in step 2 (or 3), run:
156145

157146
```bash
158147
gro init
159148
```
160149

161-
1. A URL will be displayed - open it in your browser
162-
2. Sign in with your Google account
163-
3. Grant the requested access
164-
4. Your browser will redirect to a localhost URL (the error page is expected)
165-
5. Copy the entire URL or just the authorization code
166-
6. Paste it back into the terminal
150+
The wizard will:
151+
152+
1. **Ingest credentials.json.** Pick one of three options when prompted:
153+
- **Read from clipboard** — copy the JSON in the Cloud Console and choose this; gro reads, validates, and saves it.
154+
- **Paste in terminal** — paste the JSON directly into the terminal.
155+
- **Point to a file path** — type the path to the downloaded JSON.
156+
157+
2. **Open the consent URL.** Confirm to auto-open your browser, or copy the URL manually.
158+
159+
3. **Sign in and paste the redirect URL back.** After clicking "Allow", your browser redirects to a localhost URL that shows an error — that's expected. Copy the entire URL (or just the `code=` value) and paste it into the wizard.
160+
161+
4. **Set the cache TTL** (first-run only). The wizard asks how many hours to cache Drive metadata. Press Enter to accept the default (24h).
167162

168-
Your token will be saved securely (system keychain on macOS/Linux, or `~/.config/google-readonly/token.json` as fallback).
163+
The token is saved securely (system keychain on macOS, libsecret on Linux, or `~/.config/google-readonly/token.json` as fallback).
164+
165+
When init succeeds, it prints the same `gro me` one-liner as its proof-of-life. You can re-run `gro me` any time after.
166+
167+
**Linux clipboard prerequisites.** The clipboard option requires `xclip` or `xsel` to be installed. If neither is available, the menu falls back to manual paste / file path automatically.
168+
169+
**Useful flags:**
170+
171+
```bash
172+
gro init --credentials-file ~/Downloads/client_secret.json # bypass the wizard
173+
gro init --no-browser # don't auto-open
174+
gro init --no-verify # skip post-setup API check
175+
```
169176

170177
## Commands
171178

@@ -175,6 +182,19 @@ Your token will be saved securely (system keychain on macOS/Linux, or `~/.config
175182
# Guided OAuth setup
176183
gro init
177184

185+
# Show currently authenticated user (resourceName | displayName | primaryEmail)
186+
gro me
187+
188+
# Just the primary email (scriptable)
189+
gro me --id
190+
191+
# Adds granted scopes, token expiry, and storage backend
192+
gro me --extended
193+
194+
# JSON output (combines with --id and --extended; -j is the short form)
195+
gro me --json
196+
gro me --extended -j
197+
178198
# Check configuration status
179199
gro config show
180200

docs/architecture.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ cmd/gro/main.go
99
-> internal/cmd/calendar/ (CalendarClient interface + ClientFactory)
1010
-> internal/cmd/contacts/ (ContactsClient interface + ClientFactory)
1111
-> internal/cmd/drive/ (DriveClient interface + ClientFactory)
12+
-> internal/cmd/me/ (PeopleClient interface + ClientFactory)
1213
-> internal/cmd/initcmd/ (OAuth setup wizard)
1314
-> internal/cmd/config/ (Credential management)
1415
@@ -17,6 +18,7 @@ Each cmd/ package depends on its API client:
1718
internal/cmd/calendar/ -> internal/calendar/
1819
internal/cmd/contacts/ -> internal/contacts/
1920
internal/cmd/drive/ -> internal/drive/
21+
internal/cmd/me/ -> internal/people/
2022
2123
All API clients depend on:
2224
internal/auth/ -> internal/keychain/, internal/config/
@@ -29,6 +31,7 @@ Shared utilities (no internal deps):
2931
internal/errors/ Error types
3032
internal/log/ Logging
3133
internal/cache/ Response caching
34+
internal/view/ Small Success/Error/Info/Printf/Println helper used by initcmd
3235
internal/zip/ Secure zip extraction
3336
internal/version/ Build-time version injection
3437
```
@@ -38,7 +41,7 @@ Shared utilities (no internal deps):
3841
```
3942
User -> cobra command -> ClientFactory(ctx) -> API Client -> auth.GetHTTPClient -> Google API
4043
|
41-
internal/{gmail,calendar,contacts,drive}/
44+
internal/{gmail,calendar,contacts,drive,people}/
4245
```
4346

4447
## Package Responsibilities
@@ -48,7 +51,7 @@ User -> cobra command -> ClientFactory(ctx) -> API Client -> auth.GetHTTPClient
4851
| `cmd/gro/` | Entry point, calls `root.NewCommand()` |
4952
| `internal/cmd/root/` | Root cobra command, registers all domain commands |
5053
| `internal/cmd/{domain}/` | Command handlers, client interface, output formatting |
51-
| `internal/{gmail,calendar,contacts,drive}/` | API client, data models, response parsing |
54+
| `internal/{gmail,calendar,contacts,drive,people}/` | API client, data models, response parsing |
5255
| `internal/auth/` | OAuth2 config loading, HTTP client creation |
5356
| `internal/keychain/` | Platform-specific secure token storage |
5457
| `internal/testutil/` | Test assertions, fixtures, helpers |

go.mod

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ module github.com/open-cli-collective/google-readonly
33
go 1.24.0
44

55
require (
6+
github.com/atotto/clipboard v0.1.4
7+
github.com/charmbracelet/huh v0.8.0
8+
github.com/charmbracelet/lipgloss v1.1.0
9+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
610
github.com/spf13/cobra v1.8.0
711
golang.org/x/oauth2 v0.34.0
812
google.golang.org/api v0.262.0
@@ -12,7 +16,18 @@ require (
1216
cloud.google.com/go/auth v0.18.1 // indirect
1317
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
1418
cloud.google.com/go/compute/metadata v0.9.0 // indirect
19+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
20+
github.com/catppuccin/go v0.3.0 // indirect
1521
github.com/cespare/xxhash/v2 v2.3.0 // indirect
22+
github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect
23+
github.com/charmbracelet/bubbletea v1.3.6 // indirect
24+
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
25+
github.com/charmbracelet/x/ansi v0.9.3 // indirect
26+
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
27+
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect
28+
github.com/charmbracelet/x/term v0.2.1 // indirect
29+
github.com/dustin/go-humanize v1.0.1 // indirect
30+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
1631
github.com/felixge/httpsnoop v1.0.4 // indirect
1732
github.com/go-logr/logr v1.4.3 // indirect
1833
github.com/go-logr/stdr v1.2.2 // indirect
@@ -21,14 +36,25 @@ require (
2136
github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect
2237
github.com/googleapis/gax-go/v2 v2.16.0 // indirect
2338
github.com/inconshreveable/mousetrap v1.1.0 // indirect
39+
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
40+
github.com/mattn/go-isatty v0.0.20 // indirect
41+
github.com/mattn/go-localereader v0.0.1 // indirect
42+
github.com/mattn/go-runewidth v0.0.16 // indirect
43+
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
44+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
45+
github.com/muesli/cancelreader v0.2.2 // indirect
46+
github.com/muesli/termenv v0.16.0 // indirect
47+
github.com/rivo/uniseg v0.4.7 // indirect
2448
github.com/spf13/pflag v1.0.5 // indirect
49+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
2550
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
2651
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
2752
go.opentelemetry.io/otel v1.39.0 // indirect
2853
go.opentelemetry.io/otel/metric v1.39.0 // indirect
2954
go.opentelemetry.io/otel/trace v1.39.0 // indirect
3055
golang.org/x/crypto v0.47.0 // indirect
3156
golang.org/x/net v0.49.0 // indirect
57+
golang.org/x/sync v0.19.0 // indirect
3258
golang.org/x/sys v0.40.0 // indirect
3359
golang.org/x/text v0.33.0 // indirect
3460
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect

go.sum

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,55 @@ cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIi
44
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
55
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
66
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
7+
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
8+
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
9+
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
10+
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
11+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
12+
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
13+
github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY=
14+
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
15+
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
16+
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
717
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
818
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
19+
github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 h1:JFgG/xnwFfbezlUnFMJy0nusZvytYysV4SCS2cYbvws=
20+
github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7/go.mod h1:ISC1gtLcVilLOf23wvTfoQuYbW2q0JevFxPfUzZ9Ybw=
21+
github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU=
22+
github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc=
23+
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
24+
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
25+
github.com/charmbracelet/huh v0.8.0 h1:Xz/Pm2h64cXQZn/Jvele4J3r7DDiqFCNIVteYukxDvY=
26+
github.com/charmbracelet/huh v0.8.0/go.mod h1:5YVc+SlZ1IhQALxRPpkGwwEKftN/+OlJlnJYlDRFqN4=
27+
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
28+
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
29+
github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0=
30+
github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
31+
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
32+
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
33+
github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U=
34+
github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ=
35+
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA=
36+
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
37+
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
38+
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
39+
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4=
40+
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
41+
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
42+
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
43+
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
44+
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
45+
github.com/charmbracelet/x/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI=
46+
github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4=
947
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
48+
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
49+
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
1050
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1151
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
52+
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
53+
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
54+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
55+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
1256
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
1357
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
1458
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -30,15 +74,38 @@ github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5
3074
github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14=
3175
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
3276
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
77+
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
78+
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
79+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
80+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
81+
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
82+
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
83+
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
84+
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
85+
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
86+
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
87+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
88+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
89+
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
90+
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
91+
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
92+
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
93+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
94+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
3395
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
3496
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
97+
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
98+
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
99+
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
35100
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
36101
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
37102
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
38103
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
39104
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
40105
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
41106
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
107+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
108+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
42109
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
43110
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
44111
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
@@ -55,12 +122,17 @@ go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6
55122
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
56123
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
57124
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
125+
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
126+
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
58127
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
59128
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
60129
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
61130
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
62131
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
63132
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
133+
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
134+
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
135+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
64136
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
65137
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
66138
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=

0 commit comments

Comments
 (0)