Skip to content

Build account awareness across the CLI#5

Merged
kshahbw merged 10 commits intomainfrom
feature/build-cli-improvements
Apr 30, 2026
Merged

Build account awareness across the CLI#5
kshahbw merged 10 commits intomainfrom
feature/build-cli-improvements

Conversation

@kshahbw
Copy link
Copy Markdown
Contributor

@kshahbw kshahbw commented Apr 29, 2026

Summary

Makes the CLI aware of Bandwidth Build accounts so users (and agents) get clear, actionable signals when they hit a Build feature limit, instead of misleading "API error 403" or "no working callback URL" errors. Builds purely on the JWT — no new API surface exposed.

  • New `auth status` capability surface: human output adds Build account type and a `Capable of:` line; `--plain` now actually returns parseable JSON (previously printed the same text and ignored the flag) including a derived `capabilities` map.
  • 403 audit: every feature-gate 403 (`vcp list`, `subaccount list`, `number search`, `number list`, `tendlc *`, `tfv *`, `shortcode list`) now produces a consistent Build-aware (or role-name-aware) message and exits 4. True 401 auth failures still exit 2.
  • New exit code 7 (`ExitRateLimit`) for HTTP 429; HTTP 402 (Payment Required) maps to exit 4 with the API's description preserved. Previously both fell through to exit 1.
  • `message send` short-circuits on Build before the dashboard preflight, so users no longer get pointed at a callback-URL fix when the actual problem is that messaging isn't on Build.
  • README and AGENTS.md updated to match — without enumerating credit prices, trial limits, or other things that will rot. Those live at dev.bandwidth.com.

Eight commits, each independently reviewable. No breaking changes for non-Build accounts (verified end-to-end against a full Bandwidth account during development).

Test plan

  • `make build` and `make test` pass
  • `make lint` reports 0 issues
  • Live verified against a real Bandwidth Build account: `auth status` (both formats), `vcp list`, `subaccount list`, `number search`, `number list`, `tendlc campaigns`, `message send` — all produce Build-aware messages and exit 4
  • Live verified against a full Bandwidth account: same commands all succeed (exit 0); `auth status` shows no Build-specific lines
  • Reviewer to spot-check the JWT struct tag boundary in `cmd/auth/login.go` — the only remaining `json:"express"` reference, by design
  • Reviewer to confirm the capability matcher's substring-based mapping is acceptable as best-effort (some role names like `messaging_insights` will trip the `messaging` cap to true; documented as a known imprecision)

kshahbw added 8 commits April 28, 2026 16:05
Step 1 of Build CLI improvements. The JWT already exposes whether a
credential belongs to a Bandwidth Build account (express: true) and
which roles it carries; today login parses these and discards them.
Persist them on the active profile so subsequent commands can render
capability hints, gate Build-restricted operations locally, and produce
Build-aware error messages.

No user-visible behavior change in this commit — plumbing only.
The login flow's guidance for credentials without an account binding
echoed an internal field value back to the user. Replace it with a
direct, descriptive message that gives the same actionable advice
without surfacing implementation details. Tighten the AGENTS.md
section that described the same flow.
Human output adds a Type and "Capable of" line for Bandwidth Build
accounts so users immediately see that voice-only commands work and
messaging/numbers/etc. don't. Full accounts see the same output as
before — no new noise.

--plain now emits structured JSON (it previously printed the same
human text, breaking any agent or script that took the flag at its
word). The shape includes express, roles, and a derived capabilities
map for branching logic.

Capabilities() is a pure role-to-feature mapping with unit tests
covering Build, no-roles, messaging-capable, and campaign+TFV cases.
The CLI's 403 paths today are inconsistent: some commands wrap the
error with a tailored message, others let the raw "API error 403:
Forbidden" through. Exit codes are split between 1, 2, and 4 for
morally-equivalent failures, so an agent can't reliably branch on
"feature limit vs. real auth failure."

Introduce a typed FeatureLimitError in cmdutil that maps to
ExitConflict (4), and route every 403-on-feature-gate path through
it: vcp/list, site/list, number/search, number/list, tendlc/*,
tfv get, tfv submit, shortcode/list. True auth failures (token
expired, bad credentials) keep returning a raw APIError that maps
to ExitAuth (2) — so an agent can still tell the two apart.

When the active profile is an express account, Wrap403 produces a
plan-aware message pointing the user at the upgrade path. Otherwise
it points at the role to request. number/list keeps its richer
endpoint-specific text (the pre-provisioned number is reachable via
the account portal) but is now parameterized on isBuild for
deterministic testing.

Tests cover both branches plus the underlying-error preservation
contract that ExitCodeForError relies on.
Two new exit-code mappings:

* HTTP 402 (Payment Required) — out of credits, declined card, or no
  payment method on file. Maps to ExitConflict (4) — non-retryable;
  the user must act before the CLI can succeed.

* HTTP 429 (Too Many Requests) — transient quota or rate-limit
  failure. Mapped to a new ExitRateLimit (7) so an agent can branch
  on "back off and retry" rather than treating it as a generic error.

Previously both fell through to ExitGeneral (1), which conflated
billing/quota issues with bad input or unexpected failures and gave
agents no way to react differently. Tests cover the full mapping
matrix including FeatureLimitError precedence and wrapped errors.
Build accounts ship with a pre-provisioned voice application whose
callback URL points at a Bandwidth-hosted sample endpoint. The
existing CheckCallbackURL preflight uses isPlaceholderURL, which
treats anything containing "bandwidth.com" as fake — so the sample
callback gets flagged and the user is told to fix something that
isn't the actual problem.

The actual problem is that Build accounts are voice-only and can't
send messages at all. Detect that case before the preflight runs and
return a FeatureLimitError pointing at the upgrade path. Failure now
exits 4 (consistent with other Build feature gates) rather than 1
with misleading guidance.
Profile and status now expose the field as Build / "build", matching
how the product is named in user-facing material. Helper renames to
match: ActiveBuild() in cmdutil. The JWT struct tag still maps the
server's field name to the new Go field, isolating that detail to a
single line of translation at the auth boundary.

The Go field rename lands in a separate commit from the doc updates
that depend on it.
The CLI now detects Build accounts at login, surfaces the account
type and capabilities through auth status, and routes Build-gated
failures through a consistent exit code with tailored messages.
The user-facing docs need to point at those signals so humans and
agents know how to read them.

README:
- Replace the vague "usage limits and terms apply" callout with a
  concrete one: which commands work, which fail with exit 4, and a
  link to dev.bandwidth.com for current pricing/credit/limit detail
  rather than enumerating numbers that will rot.
- Drop the broken "run band number list" instruction — number list
  isn't supported on Build yet.
- Exit codes table: expand 4 to cover feature limits and payment
  required, add 7 for rate limiting.

AGENTS.md:
- New "Account Type and Capabilities" subsection under Authentication
  describing the build flag and capabilities map in auth status JSON,
  so agents can branch on plan limits before attempting work.
- Same fix to the "Build Registration" workflow's number-list line.
- Exit codes table: rename 2 to "Auth error" (true 401), expand 4 to
  cover plan/role/feature gates and 402, add 7 for 429.
- Error patterns table: add rows for the new Build-aware messages,
  402, and 429.
- Limitations section: add a Build account entry calling out the
  feature scope, the runtime limits that aren't statically detectable
  (verified-number-only on Free Trial, 30-min cap, 5-concurrent),
  and the exit-code mapping for billing/quota errors.

No new content duplicates the FAQ — current pricing, credit costs,
and trial limits live at dev.bandwidth.com so they don't go stale in
this repo.
@kshahbw kshahbw requested review from a team as code owners April 29, 2026 18:22
@bwappsec
Copy link
Copy Markdown

bwappsec commented Apr 29, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Comment thread cmd/auth/auth_test.go Outdated
Comment thread README.md Outdated
michaela-band
michaela-band previously approved these changes Apr 29, 2026
- Rename TestCapabilities case "messaging-capable" → "messaging and
  voice" (per @brianluisgomez): the case sets both Messaging and
  HttpVoice roles, so the name should reflect both. Matches the style
  of the adjacent "campaign and tfv" case.

- Tighten the README "What you get" line (per @michaela-band):
  add a "then" for sentence flow before the make-a-call link, and
  refer to the "Bandwidth App" rather than the "Bandwidth account
  portal" (the canonical product name).
@kshahbw kshahbw merged commit 92a5327 into main Apr 30, 2026
9 checks passed
@kshahbw kshahbw deleted the feature/build-cli-improvements branch April 30, 2026 17:42
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.

6 participants