Skip to content

Tags: pinecone-io/cli

Tags

v0.4.5

Toggle v0.4.5's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Agentic auth improvements: lazy login, `--json` consistency, and `pc …

…target` fixes (#83)

## Problem
After previous changes to daemonize the login flow and improve overall
I/O, there were still some rough edges when working with the CLI through
an agent. Lack of consistent `--json` flag representation was still an
issue for a few flags, primarily around not supporting a shorthand
option. The `pc target` command also needed changes to better support
"headless", or agentic operation effectively. In-client documentation
around the specifics of some of these operations also need to be
improved to better guide users and agents when authenticating.

## Solution
This PR improves the CLI's behavior in agentic/non-TTY contexts (Claude
Code, Cursor, other AI coding agents). It builds on the daemon-backed
login flow from #82

### `-j` shorthand added to login commands

`pc login` and `pc auth login` were missing the `-j` shorthand for
`--json`, inconsistent with every other command. Fixed.

### Lazy auth completion

Added `login.EnsureAuthenticated`, called from a `PersistentPreRun` hook
on the root command. After a successful browser login, the next command
run — any command — automatically detects the completed session, reloads
credentials, and initializes the target org/project context. A second
`pc login --json` call is no longer required before running other
commands.

Commands that don't require authentication are explicitly exempted: `pc
login`, `pc logout`, `pc auth
login/logout/configure/clear/status/_daemon`, `pc auth
local-keys`/`list`, `pc target` (which handles its own auth after
local-state early returns), `pc version`, and all `pc config`
subcommands.

### `pc target` fixes

- **TTY auto-detection** — `pc target` now detects non-TTY stdout and
enables JSON mode automatically, consistent with `pc login`.
- **No-flags JSON mode** — `pc target --json` with no targeting flags
now returns the current target context as JSON (equivalent to `--show
--json`) instead of an error. This early return is correctly placed
before auth and API calls, so it works for API-key users and requires no
credentials.
- **`--show` and `--clear` unblocked** — These are local-state-only
operations; they now return before the auth gate and before any API
calls.
- **Re-auth URL stays on stderr** — When `pc target` triggers an
org-switch re-auth, the auth URL is printed to stderr only. Stdout
remains a single JSON document, keeping output compatible with `jq .`
and other single-document parsers.
- **Missing `return` after `exit.Error`** — Added `return` guards after
all `exit.Error` calls in target and root pre-run, preventing
fall-through when the exit handler is mocked in tests.

### `applyAuthContext` / `RunPostAuthSetup` refactor

Extracted the org/project state-setting logic from `RunPostAuthSetup`
into a new `applyAuthContext` helper that returns the user's email. This
eliminates a redundant `oauth.Token` + `ParseClaimsUnverified` call that
`RunPostAuthSetup` was making immediately after `applyAuthContext` had
already fetched the same data. `applyAuthContext` now also always writes
`state.TargetProj` (clearing it when the org has no projects),
preventing stale project data from a previous session from appearing in
the authenticated JSON response.

### Root pre-run JSON error output

`EnsureAuthenticated` errors in the root `PersistentPreRun` now check
both TTY state and the command's own `--json`/`-j` flag, so auth errors
are machine-readable when a caller explicitly requests JSON even in a
TTY context.

### Deduplication

Extracted `printTargetContextJSON()` in `target.go`, replacing three
identical blocks that each read `state.GetTargetContext()`, masked the
API key, and printed JSON.

### Documentation

Updated `pc login`, `pc auth login`, and `pc target` help text to
document the interactive vs. agentic flows, the two-call
pending/authenticated pattern, session resumability, and the lazy
context initialization behavior. Also fixed a typo in `pc target`'s
example (`-project` → `--project`).

## Type of Change
- [X] Bug fix (non-breaking change which fixes an issue)
- [X] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan
### Interactive login (TTY)
- [ ] `pc login` opens a browser and completes auth, sets target
org/project, prints success message
- [ ] `pc auth login` same as above
### Agentic login (non-TTY / `--json`)
- [ ] `pc login --json` returns
`{"status":"pending","url":"...","session_id":"..."}` and exits
immediately
- [ ] `-j` shorthand works identically on both `pc login` and `pc auth
login`
- [ ] Second `pc login --json` after completing browser auth returns
`{"status":"authenticated",...}` with org/project populated
- [ ] Running any other command (e.g. `pc index list --json`) after
browser auth — without a second `pc login` call — succeeds and sets
target context automatically
- [ ] Piping output (`pc login | cat`) triggers non-TTY path
automatically without needing `--json`
### Lazy auth / `EnsureAuthenticated`
- [ ] After `pc login --json` + browser auth, `pc index list --json`
works without a second login call
- [ ] `pc auth status` works with no credentials (not blocked by auth
gate)
- [ ] `pc auth local-keys list` works with no credentials (not blocked
by auth gate)
- [ ] `pc version` works with no credentials
- [ ] All `pc config` subcommands work with no credentials
                                                                     
### `pc target`
  - [ ] `pc target --show` works with no credentials                 
- [ ] `pc target --clear` works with no credentials
- [ ] `pc target --json` (no flags) returns current target context as
JSON, works with no credentials
- [ ] `pc target --json --org "name"` sets org and returns updated
context as JSON
- [ ] `pc target --json --org "name" --project "name"` sets both and
returns updated context
- [ ] `pc target` in a TTY launches interactive selector as before
- [ ] `pc target` in a non-TTY (or with `--json`) without targeting
flags returns current context rather than erroring
### Auth errors
- [ ] Running a command requiring auth with no credentials returns
`{"error":"..."}` to stdout when stdout is non-TTY
- [ ] Running a command requiring auth with no credentials and `--json`
flag returns `{"error":"..."}` to stdout even in a TTY
- [ ] "authentication in progress" error includes the auth URL when
daemon is still running

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Adds a global authentication pre-check for most commands and changes
login/target behavior in non-TTY JSON mode, which could affect
automation and command execution paths if the skip list or auth
detection is incorrect.
> 
> **Overview**
> **Adds a root-level auth gate for most commands.** `pc` now runs
`login.EnsureAuthenticated()` in `PersistentPreRun`, with a curated skip
list for commands that manage credentials/local state, and auto-detects
JSON mode (non-TTY stdout or `--json/-j`) to format auth errors
appropriately.
> 
> **Improves non-interactive/agentic UX.** `login` and `auth login` gain
`-j` shorthand and updated help/examples; `target` now auto-enables JSON
mode in non-TTY contexts, supports `pc target --json` as an auth-free
“show current context” path, and centralizes JSON context printing.
> 
> **Refactors and extends post-auth setup.** Login utilities split
context initialization into `applyAuthContext()`, ensure target project
state is cleared when no projects exist, keep stdout clean during
wait-mode by printing auth URLs to stderr, and introduce
`EnsureAuthenticated()` to lazily complete pending daemon sessions and
auto-initialize org/project context after successful browser auth.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
ddb7ecd. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

v0.4.3

Toggle v0.4.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Daemonize `login` OAuth listener (#82)

Reworks the OAuth login flow to work correctly in agentic contexts
(Claude Code, Cursor, other AI coding agents) where stdin/stdout are not
interactive TTYs.
### Problem                                                        

The previous `pc login` flow blocked on stdin waiting for `[Enter]`, and
the auth URL was either hidden or percent-encoded in ways that made it
hard for agents to extract. There was no machine-readable way to surface
the URL and get a non-blocking result.
                                                                   
### Solution: daemon-backed two-call pattern
 
**First call** (`pc login --json`): spawns a detached background daemon
that owns the OAuth callback server on `127.0.0.1:59049`, emits
`{"status":"pending","url":"...","session_id":"..."}` to stdout, and
exits immediately. The daemon keeps listening for the browser redirect.
                                                                   
**Second call** (`pc login --json`): detects the pending session, polls
the daemon's result file until auth completes, then emits
`{"status":"authenticated","email":"...","org_id":"...","org_name":"...","project_id":"...","project_name":"..."}`.
                                                                   
If the second call is killed before the daemon finishes, a third call
resumes seamlessly — the daemon keeps running regardless.

Non-agentic users (`pc login` without `--json`, TTY stdout) are
completely unaffected.
 
### Key details
- **Session state** is stored in `~/.config/pinecone/sessions/` (mode
0600, 5-minute TTL) — PKCE verifier is passed to the daemon via
environment variable and never touches disk.
- **Atomic result writes** use write-to-temp-then-rename to prevent
partial reads in the polling loop.
- **`pc target` re-auth** uses a `Wait: true` mode that blocks until the
token is acquired (daemon path, but synchronous), printing the auth URL
to stderr so it's visible without polluting stdout.
- **Windows** daemon uses `CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS`
so it survives terminal close.
- **TTY auto-detection** is applied in `GetAndSetAccessToken` so all
callers (including `pc target`) correctly use the daemon path in non-TTY
environments.
- **Port conflict detection** in the interactive path surfaces a clear
error with the existing auth URL if a daemon is already holding the
callback port.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes the CLI OAuth login flow to spawn a detached background
process and persist session state/results on disk, which can impact
authentication reliability across platforms and failure modes. Also
modifies re-auth behavior in `pc target` to block until tokens are
acquired.
> 
> **Overview**
> **Daemonizes `pc login --json` OAuth flow** by introducing a hidden
`pc auth _daemon` subcommand that runs the local callback server in a
detached process, exchanges the auth code, stores the token, and writes
a session result file.
> 
> **Adds resumable, file-backed login sessions** (`sessions/`
state+result) so JSON login can return immediately with
`{"status":"pending"...}` and be polled/resumed on subsequent
invocations, with cleanup/expiry handling and cross-platform process
detachment.
> 
> **Refactors token acquisition and post-auth setup**: splits
interactive vs JSON paths, adds `Options.Wait` for callers that must
block until credentials exist, and updates `pc target` re-auth to use
`Wait: true` while keeping JSON output behavior separate via
`RunPostAuthSetup`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
596f676. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

v0.4.2

Toggle v0.4.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Post-cask migration: quarantine fix and documentation updates (#81)

## Problem
While testing the formula-to-cask migration, macOS Gatekeeper was
blocking the `pc` binary after a cask install. This is expected behavior
— Homebrew casks explicitly apply the `com.apple.quarantine` attribute,
unlike formula installs. The postflight block strips this attribute
after installation.

## Solution
- Adds `postflight` block to cask config to strip macOS quarantine
attribute, resolving Gatekeeper prompt on first run after cask install.
- Updates `README` to reflect cask-based installation across all
Homebrew commands and adds install script as a first class installation
method.

The quarantine removal is guarded with `OS.mac?` since `xattr` is
macOS-only and Homebrew 4.5.0+ supports casks on Linux. This is a
stopgap until the binary is code-signed and notarized via Apple
Developer.

## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update
- [X] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan
Re-tag and release, test installation flow.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Moderate risk because it changes Homebrew cask post-install behavior
(runs `xattr` on macOS) and could affect end-user install experience,
though the change is small and OS-guarded.
> 
> **Overview**
> Resolves macOS Gatekeeper prompts after Homebrew **cask** installs by
adding a `postflight` step in `.goreleaser.yaml` that recursively
removes `com.apple.quarantine` from the staged cask payload on macOS.
> 
> Updates `README.md` installation guidance to emphasize cask-based
Homebrew commands, notes the Linux Homebrew cask version requirement,
and adds an *install script* option with examples for pinning versions
and custom install paths.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
302c409. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

v0.4.1

Toggle v0.4.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
add preflight block to custom_block in goreleaser.yaml to allow clean…

…ing up old binaries before installing (#80)

## Problem
When reinstalling the CLI, you may run into conflicts with previously
existing binaries since these may not be properly cleaned up by
homebrew. For us, this seems like an issue with the binary alias install
for `pc`:

```
brew uninstall pinecone-io/tap/pinecone && brew install --cask pinecone-io/tap/pinecone
Warning: Treating pinecone-io/tap/pinecone as a formula. For the cask, use pinecone-io/tap/pinecone or specify the `--cask` flag. To silence this message, use the `--formula` flag.
Uninstalling /opt/homebrew/Cellar/pinecone/0.0.60... (107 files, 49.2MB)
==> Auto-updating Homebrew...
Adjust how often this is run with `$HOMEBREW_AUTO_UPDATE_SECS` or disable with
`$HOMEBREW_NO_AUTO_UPDATE=1`. Hide these hints with `$HOMEBREW_NO_ENV_HINTS=1` (see `man brew`).
==> Fetching downloads for: pinecone-io/tap/pinecone
✔︎ Cask pinecone (0.4.0)                                                                           Verified     25.9MB/ 25.9MB
==> Installing Cask pinecone
==> Purging files for version 0.4.0 of Cask pinecone
Error: It seems there is already a Binary at '/opt/homebrew/bin/pc'.
```

## Solution
add preflight block to custom_block in goreleaser.yaml to allow cleaning
up old binaries before installing

## Type of Change

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update
- [X] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan
Rerun the installation.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: only adjusts release tooling by adding a cask `preflight`
cleanup, with no runtime code changes. Potential impact is limited to
Homebrew installs if the delete logic removes unexpected files in
`HOMEBREW_PREFIX/bin`.
> 
> **Overview**
> Prevents Homebrew cask reinstall failures by adding a `preflight`
block to the GoReleaser `homebrew_casks.custom_block` that deletes any
existing `pc` or `pinecone` entries under `HOMEBREW_PREFIX/bin`
(including symlinks) before installing the cask binary alias.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
fa65cdc. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

v0.4.0

Toggle v0.4.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Remove publishing dev version on `publish.yaml` (#78)

## Problem
There was some initial setup done for a `pinecone-dev` version of the
CLI that gets released via goreleaser -> brew. This looks like it was
relying on a previous `homebrew-pinecone` repo which no longer exists. I
don't think we've ever utilized the dev build of the CLI for anything,
and since we're changing the overall goreleaser -> brew process, it's a
good time to clean this up.

## Solution
Remove dev release from publish.yaml workflow for now.

## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update
- [X] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan
N/A

Make sure this isn't run at all when the publish workflow is triggered.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk CI-only change that deletes an unused workflow trigger; main
release/publish steps remain unchanged.
> 
> **Overview**
> Removes the `publish.yaml` step that extracted the released version
from GoReleaser metadata and used a PAT to dispatch
`update-dev-formula.yaml` in the `homebrew-pinecone` repo.
> 
> The publish workflow now only runs GoReleaser (plus existing setup)
without attempting any dev/Homebrew formula update automation.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
440ec30. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

v0.3.1

Toggle v0.3.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
make sure NewIndexConnection uses index.private_host if it's available (

#65)

## Problem
There was a bug caught while testing BYOC. The `sdk.NewIndexConnection`
function does not properly account for `index.private_host` if it
exists, and only uses `index.host` to resolve the connection.

## Solution
Update `sdk.NewIndexConnection` to use `index.PrivateHost > index.Host`
when one is present.

## Type of Change
- [X] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan
Test data plane operations with a BYOC index.

v0.3.0

Toggle v0.3.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Allow attribution tags through `go-pinecone` (`PINECONE_CLI_ATTRIBUTI…

…ON_TAG`) (#63)

## Problem
We need a mechanism for being able to further tag usage of the CLI.

## Solution
- Allow appending attribution tags to network operations from the CLI
via the `go-pinecone` SDK `SourceTag`.
- Read from `PINECONE_CLI_ATTRIBUTION_TAG` and append to the existing
`pinecone-cli` source tag.
- Add a small unit test for the function reading from env.

## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [X] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan
CI - unit & integration tests

v0.2.0

Toggle v0.2.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Clean up `presenters` pointer handling (#59)

## Problem
While testing I ran into instances where an empty response, such as in
`query` could result in a nil reference error. A lot of our presenters
for table-output, and non-json output, don't do enough nil-checking on
the inbound pointers.

## Solution
Clean up the presenters package and individual presentational functions
to better deal with nils, add a small `PrintEmptyState` function and
test, and try and cover cases where we're accessing pointers without
explicitly checking.

## Type of Change

- [X] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan
CI - unit & integration tests

v0.1.3

Toggle v0.1.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Refactor / fix `exit` package (#53)

## Problem
Previously, I refactored the `exit` package a bit to expose functions
that wrapped `zerolog`. This seemed clever at the time, but I ended up
introducing a bug where chaining `exit.Error().Err(error)...` would
return a `zerolog.Event` instead of an exitEvent, which meant the
process wouldn't actually exit. This could lead to odd logging and
behavior include sigsev violations.

## Solution
Reflecting on this code, the implementation seemed a bit too fancy and
error-prone. I simplified things down to functions that do the same
thing as the previous builder pattern-style `exitEvent`.

Replaced all usages of `exit.Error()` and `exit.Success()`, updated unit
tests to validate new functions.

There larger scale changes I'd like to take on involving error handling
and logging in later PRs.

## Type of Change
- [X] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan
CI - unit and integration tests

Testing involves generally triggering error and success messages that
are meant to end the process.

A good example is calling `$ pc target --org my-org --project
invalid-project-name` - this will throw an error which would cause a
sigsev:

Before:
<img width="839" height="426" alt="Screenshot 2025-11-14 at 6 47 44 PM"
src="proxy.php?url=https%3A%2F%2Fgithub.com%2F%3Ca+href%3D"https://github.com/user-attachments/assets/e41482d4-f321-4026-8672-0f3ef6bee6f1">https://github.com/user-attachments/assets/e41482d4-f321-4026-8672-0f3ef6bee6f1"
/>

After:
<img width="845" height="160" alt="Screenshot 2025-11-14 at 6 48 28 PM"
src="proxy.php?url=https%3A%2F%2Fgithub.com%2F%3Ca+href%3D"https://github.com/user-attachments/assets/d39bc37a-e711-4d2e-b555-bdae4db6cca1">https://github.com/user-attachments/assets/d39bc37a-e711-4d2e-b555-bdae4db6cca1"
/>

v0.1.2

Toggle v0.1.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Add `TokenError`, improve `exit` package utilities, improve error log…

…ging (#50)

## Problem
With the CLI public preview release last week, we've had some reports of
errors come through. I noticed we had some deficiencies around both
logging / error output (inconsistent output, duplicate log output, etc).

We also were not properly handling the oauth token API responses from
auth0. Since these HTTP requests are not going through the SDK and are
handled manually, there was a need to add more robust inspection of
token response payloads, and properly surface user-friendly error
messages on issues with the authentication service. The biggest issue
here is the lack of proper error messaging when a user token has expired
(24 hours total or 12 hours inactive).

## Solution

- Implement new `TokenError` struct and various utilities for parsing an
error from auth responses, and then bubbling that error up through the
call stack so it's presented in a reasonable way. I did some testing
against what we get back from the auth service on token expiration, and
followed [RFC6749 The OAuth 2.0 Authorization
Framework](https://datatracker.ietf.org/doc/html/rfc6749) in terms of
the shape of the response and expected error codes from the token
endpoints. This may have been a bit overkill, but since the CLI will
continue handling it's own client authentication, we should probably
start from as robust a place as we can.
- Refactor the `exit` package a bit. This was mainly for ergonomics,
since logging was also tied to calling exit. This was the cause of a lot
of logging duplication in spots which I was responsible for
implementing. I basically wanted to wrap a zerolog.Event to allow easier
chaining at callsites, etc.
- General clean up of logging and usage around `exit.Error` or
`exit.Success`. For example, we don't generally need to return after
calling exit. There were places where errors were being swallowed or not
properly bubbled up, etc.
- Add unit tests for `/oauth/error.go` and `exit.go`. There's still a
lot of unit test coverage missing in general, but trying to add in
coverage to new features and things as I go seems prudent.

## Type of Change
- [X] Bug fix (non-breaking change which fixes an issue)
- [X] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan
For general logging and error cases, test to make sure user-facing
messages display as expected. Also validate logging for info, debug, etc
using `PINECONE_LOG_LEVEL="debug"`.

Testing `TokenError` is difficult as we don't have a ton of direct
control over the authentication server and its responses. The most
consistent testing I was able to do was keeping a stale/expired user
token in my cache, and making calls with it. This will attempt a token
refresh call, which will return `"error": "invalid_grant"`. This case is
consistently testable, so that's what I focused on. The rest of the
functionality I hopefully covered through unit testing suites.

||Expired user token error|
|---|---|
|Before|<img width="960" height="164" alt="Screenshot 2025-10-24 at 6 17
10 PM"
src="proxy.php?url=https%3A%2F%2Fgithub.com%2F%3Ca+href%3D"https://github.com/user-attachments/assets/d4417383-41b8-4c53-bd57-12b9a2fab03f">https://github.com/user-attachments/assets/d4417383-41b8-4c53-bd57-12b9a2fab03f"
/>|
|After|<img width="1118" height="148" alt="Screenshot 2025-10-24 at 6 18
35 PM"
src="proxy.php?url=https%3A%2F%2Fgithub.com%2F%3Ca+href%3D"https://github.com/user-attachments/assets/fb471210-e7ae-4a2b-bc42-028c854831b8">https://github.com/user-attachments/assets/fb471210-e7ae-4a2b-bc42-028c854831b8"
/>|


---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211580374844618