Skip to content

fix(cli): real whoami auth, --json on provision verbs, headless login#32

Merged
mastermanas805 merged 1 commit into
masterfrom
fix/cli-agent-dx-whoami-json-headless
Jun 10, 2026
Merged

fix(cli): real whoami auth, --json on provision verbs, headless login#32
mastermanas805 merged 1 commit into
masterfrom
fix/cli-agent-dx-whoami-json-headless

Conversation

@mastermanas805

Copy link
Copy Markdown
Member

A dogfood pass found three agent-facing issues in the instant CLI. All three are fixed here in one PR.

B1 (High) — whoami false-positive authentication

Symptom: instant whoami --token <bogus> --json returned {"authenticated":true,"tier":"flag-token"} exit 0 for ANY non-empty token (and tier:"env-token" via INSTANT_TOKEN). cmd/whoami.go was a pure local-credential reflector that never validated against the server.

Fix: whoami now calls GET /auth/me (named const authMePath) and sets authenticated:true only when the server confirms the token; tier/email/team_name come from the /auth/me response. Offline handling: a transport failure reports authenticated:false with a distinct error note (exit 1, "could not reach the server…") so a flaky network never masquerades as logged-out; a server 401/403 → authenticated:false + exit 3 (session expired). No-token path is unchanged (clean anonymous, exit 0, no network). In --json mode stdout is a single JSON object; the human-readable reason goes to stderr (main.go), so whoami --json | jq .authenticated parses cleanly.

Symptom:        whoami --json reports authenticated:true for a bogus token
Enumeration:    rg -F 'flag-token' / 'env-token' / 'IsAuthenticated' in cmd/whoami.go (single reflector, 1 site)
Sites found:    1
Sites touched:  1   (cmd/whoami.go rewritten to validate via GET /auth/me)
Coverage test:  TestWhoami_BogusToken_ReportsNotAuthenticated (+ valid/offline/5xx/no-token/--token variants)
Live verified:  local mock /auth/me → bogus token: {"authenticated":false} exit 3; real token: {"authenticated":true,"tier":"pro"} exit 0

B2 (Medium) — provision verbs reject --json

Symptom: instant db new --name x --jsonunknown flag: --json exit 1. The flag existed on resource/resources/whoami/status/etc. but NOT on the provision new verbs.

Fix: --json added to every provision new verb (db/cache/nosql/queue/storage/webhook/vector — all route through makeProvisionCmd). Success emits the full structured response (token, connection_url/receive_url, env + environment, tier, env_override_reason, note). Errors funnel through the shared JSON envelope (provisionJSON added to jsonModeOn).

Symptom:        `<verb> new --json` → unknown flag: --json
Enumeration:    rg -F 'makeProvisionCmd' — all 7 verbs share one builder; flag registered in 2 init blocks (monitor.go, extras.go)
Sites found:    7 verbs / 2 init sites
Sites touched:  7 / 2  (single emit path in makeProvisionCmd → emitProvisionJSON)
Coverage test:  TestProvision_JSON_AllVerbs (table over all 7) + error-envelope + env-override + omitted-env tests
Live verified:  local mock → `db new --json` emits {ok,token,connection_url,environment,...} exit 0

U2 (Medium) — login unusable on headless/agent boxes

Symptom: browserLauncherForGOOS hardcoded open/xdg-open/rundll32, ignored $BROWSER; on a headless box it printed "Could not open browser automatically" then blocked polling 5 min with no escape.

Fix: (a) $BROWSER (named const browserEnvVar) takes precedence over the per-GOOS default on every platform, treated as a bare command name (no shell parsing). (b) --no-browser flag prints the login URL + session id to stdout and continues polling without ever attempting to launch a browser — the most useful behavior for a supervising agent (URL is captured, login still completes hands-free if a human/agent opens it). Interactive happy path unchanged.

Symptom:        headless login spawns a useless browser + blocks 5 min silently
Enumeration:    rg -F 'browserLauncherForGOOS' / 'openBrowser' (login.go, 1 launcher site)
Sites found:    1 launcher + 1 login flow
Sites touched:  1 / 1
Coverage test:  TestBrowserLauncher_HonorsBROWSER (+ whitespace), TestLogin_NoBrowser_PrintsURLAndPolls
Live verified:  `login --help` shows --no-browser + $BROWSER note; flag prints URL+session and polls to completion

Gate

  • make ci (build + vet + -race tests) green
  • golangci-lint run0 issues
  • 100%-patch coverage: every added line covered (verified by parsing the diff against origin/master + the cover profile; mirrors the CI diff-cover --fail-under=100 gate)
  • Named consts (authMePath, browserEnvVar, defaultAPIBaseURL) — no scattered literals

🤖 Generated with Claude Code

Three agent-DX fixes found in a dogfood pass:

B1: whoami now validates the bearer token against GET /auth/me instead of
reflecting local config. A bogus/expired token reports authenticated:false
(exit 3) so an agent gating on `whoami --json | jq .authenticated` can no
longer get a false positive. Network failures surface a distinct offline
state (authenticated:false + error note, exit 1) rather than masquerading
as logged-out. tier/email/team_name come from the server response.

B2: every provisioning `new` verb (db/cache/nosql/queue/storage/webhook/
vector) honors --json, emitting the full structured response (token,
connection_url/receive_url, environment, tier, env_override_reason) — the
wedge action an agent most needs machine-readable. Errors funnel through
the shared JSON envelope.

U2: login honors $BROWSER on every platform and adds --no-browser, which
prints the auth URL + session id and polls without attempting to spawn a
browser — usable on headless/agent boxes.

Named consts authMePath / browserEnvVar / defaultAPIBaseURL avoid scattered
literals. 100%-patch coverage via httptest-mock tests for all new lines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mastermanas805 mastermanas805 enabled auto-merge (squash) June 10, 2026 19:46
@mastermanas805 mastermanas805 merged commit f98d9b1 into master Jun 10, 2026
9 checks passed
mastermanas805 added a commit that referenced this pull request Jun 10, 2026
…e, full token in list (#33)

Four agent-DX follow-ups found in a live cohort dogfood (round 2, building on #32):

F1 (P0): add `instant resource creds <token>` (alias `credentials`) — GETs
  /api/v1/resources/:id/credentials and prints the connection_url (webhook
  receive_url fallback; --json for the full response). Closes the broken
  first-provision recovery loop: `db new` frequently hits the 60s client
  timeout and the URL — printed only by `new` — was otherwise lost forever.
  Path fragment is the named const resourceCredsSuffix.

F2 (P1): `webhook new` printed a blank `url` line — the human path read
  creds.ConnectionURL only, but /webhook/new returns receive_url. Fall back
  to ReceiveURL like the local token-store code already does. (--json already
  emits receive_url via emitProvisionJSON; verified.)

F3 (P1): a 401 with INSTANT_TOKEN set advised `instant login`, which is
  useless — the env var SHADOWS any saved login. errSessionExpired() +
  classifyError() now branch on token SOURCE (new authFromEnvToken helper):
  an env-sourced reject tells the user to fix/unset INSTANT_TOKEN. This also
  fixes the dead session_expired JSON code (a 401 was mis-coded auth_required
  because errSessionExpired carries ExitAuthRequired).

F4 (P2): `instant resources` truncated the token (`d3cef90f-a75…`) — the
  exact value every other command needs as an argument, so it was
  un-copyable. Print the FULL token; truncate the NAME column instead
  (new truncateName + nameDisplayMaxLen). --json unchanged.

Tests: cmd/agent_dx_followups_test.go covers all four (creds happy/alias/json/
webhook-fallback/unauth/missing-token/empty/parse/server-error + token
fallback; webhook receive_url human+json; env-vs-saved 401 advice + json
action + flag-over-env; full-token + name-truncation + truncateName unit).
100%-patch on the diff; make ci green (build+vet+race+lint).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant