Skip to content

Surface platform 'upgrade' offers in bench CLI - #441

Open
HeyGarrison wants to merge 1 commit into
masterfrom
devin/1789511756-cli-upgrade-notices
Open

HeyGarrison wants to merge 1 commit into
masterfrom
devin/1789511756-cli-upgrade-notices

Conversation

@HeyGarrison

Copy link
Copy Markdown
Collaborator

Summary

Companion to computesdk/benchmarks-platform#290, which adds a machine-readable upgrade offer to v1 API read responses (and to 403s on benchmarks locked behind the per-category daily subscription). This makes bench and @benchsdk/api consumers — including LLM agents driving the CLI — aware the paid daily tier exists when they're reading weekly-stale data.

  • BenchmarkClientConfig.onUpgradeNotice is invoked from request() whenever a response body carries upgrade (top-level on success, details.upgrade on errors). A config hook rather than per-method plumbing so list endpoints whose methods return data.items (e.g. listBenchmarks, listRuns) still surface the notice.
  • BenchmarkUpgradeNotice is exported from @benchsdk/api, and upgrade? is added to BenchmarkResultsOverview / BenchmarkRunResults so JSON consumers see it in-band.
  • The CLI collects notices during the request and prints a deduped (per category:message) footer to stderr after output:
Note: This public benchmark refreshes weekly. Upgrade to daily Dax benchmark runs for fresh results every day, on-demand reruns, and full run logs.
  Upgrade: https://platform.computesdk.com/<org>/settings/billing?category=dax

It also fires on the 403 error path before printErrorAndExit, so locked benchmarks still advertise the offer. stripUpgradeField removes the top-level upgrade key in table mode only; --format json keeps it in-band for programmatic consumers.

Verification

Against a local platform build with a seeded unentitled org: bench runs list <daily-slug> → stderr notice + API 403; bench runs list <weekly-slug> → table + stderr notice; entitled org → no notice. pnpm --filter @benchsdk/cli test green (32 tests); client.test.ts assertion updated for the new onUpgradeNotice config arg.

Known pre-existing: pnpm --filter @benchsdk/cli typecheck reports vitest MockInstance type errors in src/__tests__/output.test.ts on an untouched file — same on main.

Link to Devin session: https://app.devin.ai/sessions/2b242a1e747c4dfc8237cd2ad36e50aa
Open in Devin Desktop: https://app.devin.ai/desktop/session/2b242a1e747c4dfc8237cd2ad36e50aa?variant=devin
Requested by: @HeyGarrison

The v1 API can now attach a machine-readable 'upgrade' block to read
responses (and to 403s on subscription-locked benchmarks). Thread it
through so LLM agents using the CLI/API learn the paid daily tier exists:

- @benchsdk/api: new BenchmarkUpgradeNotice type; responses may include
  'upgrade'; BenchmarkClientConfig gains onUpgradeNotice, invoked for
  every response body that carries one (covers list endpoints whose
  methods return only data.items, and error bodies via details.upgrade).
- bench CLI: collects notices during the request, prints a deduped
  'Note: ... / Upgrade: <billingUrl>' to stderr after output, including
  on the 403 failure path; the upgrade key is stripped from table output
  but kept in JSON for programmatic consumers.
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@HeyGarrison
HeyGarrison marked this pull request as ready for review September 17, 2026 20:38
@open-cla

open-cla Bot commented Sep 17, 2026

Copy link
Copy Markdown

Contributor License Agreement

All contributors are covered by a CLA.

@superagent-security superagent-security Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superagent found 1 security concern(s).

pendingUpgradeNotices.set(`${notice.category}:${notice.message}`, notice);
}

export function printUpgradeNotices(): void {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Sanitize server-controlled upgrade text before printing it to the terminal

API-controlled message and URL are printed directly to stderr without validation or terminal escaping.

Validate trusted URL origins and strip terminal control characters before printing upgrade notices.

AI prompt
Check if this security scanner issue is valid. If so, understand the root cause and fix it. If appropriate, update or add tests. Keep the change focused and preserve intended behavior.

<file name="packages/benchsdk-cli/src/output.ts">
<violation number="1" location="packages/benchsdk-cli/src/output.ts:21">
<priority>P2</priority>
<title>Sanitize server-controlled upgrade text before printing it to the terminal</title>
<evidence>printUpgradeNotices() writes notice.message and notice.billingUrl/learnMoreUrl directly with console.error. These values originate from the API response and are only narrowed to object shape by the client, so control characters or an attacker-controlled URL could be emitted to a user's terminal and used for terminal manipulation or phishing.</evidence>
<recommendation>Validate the upgrade notice fields before invoking the callback, require billing and learn-more URLs to use an expected HTTPS origin, and strip or escape terminal control characters from message and URL text before printing. Preserve the raw structured value only for explicitly requested JSON output.</recommendation>
</violation>
</file>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment on lines +120 to +121
if (notice && typeof notice === 'object') {
config.onUpgradeNotice(notice as BenchmarkUpgradeNotice);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Upgrade callback exceptions replace responses

When onUpgradeNotice throws, emitUpgradeNotice rejects successful requests and replaces failed requests' BenchmarkApiError. Callers lose valid results or structured error details.

Learn more

The notice hook runs inside the request's success and failure paths. A thrown callback exception escapes before the successful value returns or the normal BenchmarkApiError is constructed. This makes an observational hook alter the API operation's established result contract.

Example: A client configures onUpgradeNotice to write telemetry, but its telemetry library throws. A 200 response now rejects with the telemetry error. A 403 response also rejects with that error instead of exposing status 403 and the response body.

Recommended fix: Invoke config.onUpgradeNotice inside an exception boundary that preserves the request result. If hook failures need visibility, route them to a separate error callback without replacing the HTTP result.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

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