One-line installer: Claude Code on the qBraid AI gateway - #2
Merged
Conversation
Installs Claude Code if absent, wires it to the qBraid AI gateway, registers the qBraid MCP, and adds a credit statusline. macOS, Linux and Windows. No proxy: the gateway serves an Anthropic-compatible surface at /api/v1/ai/v1/messages, so the launcher just sets ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN and execs claude.
Portability, the two platforms that were never executed: - statusline.sh used `stat -f %m`, which is BSD-only. On GNU coreutils it fails but still prints, so the balance froze at the first cached value forever and the fallback was dead code. Try the GNU form first, reject a non-numeric answer. - install.ps1 built the model list as `@(...) | Where-Object`, so a single-model response unrolled to a String and the chosen model became one character. - GNU sed rejects line address 0, so typing 0 at the model prompt killed the installer. - qbraid-code.cmd now ships CRLF (.gitattributes) and uses delayed expansion; `exit /b %ERRORLEVEL%` inside an if-block expanded before the command ran. - The gh fallback wrote a UTF-8 BOM, which cmd.exe rejects on line 1. Secrets: - --global wrote the API key into a world-readable settings.json; chmod 600 on POSIX, an owner-only ACL on Windows. - The pasted key is no longer echoed to the terminal. - statusline.ps1 passed the key on a child process command line every 60s; the child now reads the env file itself. Honest failures: - A network failure was reported as a rejected key, in both the installer and --doctor. Transport and auth are now distinguished by HTTP status. - A smoke-test failure no longer discards a finished install, and an empty wallet is named as such rather than reported as a broken gateway. - confirm() says out loud when it auto-answers with no TTY, instead of printing "organization confirmed" when nobody confirmed anything. - The final banner only claims --global worked if it actually did. - write_settings distinguishes "no python3" from "python3 failed". Also: fetch companion files from qbraid.com first, so the proxy covers the whole install and not just its first step; resolve QBRAID_CODE_HOME once and bake it into the launcher; write ~/.claude.json atomically and never fatally; scope the org name to the data object; round the balance everywhere. Adds tests/statusline.sh (12 cases) and CI on ubuntu + macos + windows — every defect above was platform-specific and nothing ran them before.
Re-running with a key from a different organization kept rendering the old organization's wallet until the cache expired. The README now states plainly that the installer is fetched from an unpinned `main` with no signature, which makes branch protection load-bearing, and shows the fetch-then-read form for anyone who wants it.
macOS still ships bash 3.2, which cannot parse `case ... esac` inside a command substitution. Moved the mtime probe into a function. Caught by the macos-latest leg of the new CI matrix, which is what it is for.
The check asserted correctly and printed its success line, but `echo` does not reset ERRORLEVEL in cmd, so the expected exit 1 from the launcher leaked out and failed the step.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Installs Claude Code on a personal laptop and leaves the user in a working
session against qBraid — aimed at someone who should not have to configure
anything (a professor, a workshop attendee).
What it does
$QBRAID_API_KEY→~/.qbraid/qbraidrc→ opens the key page and takes one paste, not echoed). Validates it, and distinguishes a rejected key from an unreachable network.GET /api/v1/ai/v1/models, so new gateway models appear without a release here.claude mcp loginthere and then./v1/messagesrequest — and warns rather than discarding a finished install if it fails.No proxy
claudeseekneeded CLIProxyAPI because vLLM speaks only OpenAI chat-completions.This does not: the gateway already serves an Anthropic-compatible surface at
/api/v1/ai/v1/messages, andapikey-strategy.service.tsacceptsAuthorization: Bearer qbr_.... The launcher sets four environment variables andexecs
claude. Nothing runs in the background, nothing listens on a port.ANTHROPIC_AUTH_TOKENis used rather thanANTHROPIC_API_KEYspecifically toavoid Claude Code's custom-API-key approval prompt on first run.
Statusline
Credits come from
GET /api/v1/billing/credits/balance, cached for 60s andrefreshed out of band, so a slow network never stalls the prompt. The refresh
stamps its attempt, not its success, so a revoked key cannot spawn a
background fetch on every keystroke.
Windows
A
.cmdshim on PATH, not a PowerShell profile function: it works from cmd.exe,PowerShell and Windows Terminal with no profile edit and no execution-policy
change. Ships CRLF and BOM-free, both enforced in CI.
--doctordelegates todoctor.ps1because batch cannot parse JSON.Testing
CI runs on ubuntu, macOS and windows — every portability defect found in
review broke on exactly one platform.
shellcheck -S warningandbash -non all four shell scripts, both platforms.tests/statusline.sh— 12 cases: context boundaries, missing fields, emptystdin, escape-sequence injection, zero and rounded balances, and that file
mtimes are readable on this platform.
.ps1, CRLF/BOMenforcement, and the launcher's missing-install path.
curl <proxy>/code.sh | bashruns; bad key, unreachable host andshell-metacharacter key all produce distinct, correct errors.
Notable fixes from review
stat -f %mis BSD-only; on GNU coreutils it fails but still prints, so thecredit balance froze at the first cached value forever on Linux.
@(...) | Where-Objectunrolls a one-element pipeline to a String, so asingle-model response made the chosen model one character.
--globalwrote the API key into a world-readablesettings.json.sedrejects line address 0, so typing0at the model prompt killed the installer.exit /b %ERRORLEVEL%inside a.cmdif-block expands before the command runs.Trust
The installer is fetched from an unpinned
mainwith no signature — the normaltrade-off for a one-line installer that must stay current. Branch protection
on
mainis therefore load-bearing and is not yet enabled. The README says soand shows the fetch-then-read form for anyone who wants it.
Companion files are served from
qbraid.comfirst (seeqbraid-external-site#107), so the install works on networks that block
raw.githubusercontent.com.