From a970f0385eb40f4b01a701fd171c2f3cfff115cd Mon Sep 17 00:00:00 2001 From: BeLazy167 Date: Thu, 20 Aug 2026 21:39:32 -0500 Subject: [PATCH] Stop showing a raw org id, and shut the proxy down on exit Both from the first outside dogfood (Ryan, #software): - "the organization is the ID not the name which a user probably wouldn't recognize". An API key genuinely cannot read its organization's NAME: /organizations/current needs a JWT org context and /organizations rejects key auth (both verified against prod). So asking someone to confirm 507f1f77bcf86cd799439011 was worse than not asking. The step now shows plan and credits, names the organization only when it resolves, and makes the escape hatch an actionable sentence instead of a hex string. - "if I quit does the gpt proxy shut off automatically?" It didn't. When the launcher starts the proxy it now stops it on exit, including Ctrl+C and terminal close (trap). A proxy that was ALREADY running belongs to another session and is left alone. Verified live: gpt session leaves no process behind, a second run starts a fresh one. --- README.md | 4 ++-- install.sh | 44 ++++++++++++++++++++++++++---------------- qbraid-code | 51 +++++++++++++++++++++++++++++++++++++++++-------- qbraid-code.cmd | 8 +++++++- 4 files changed, 80 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 574eaff..642ce04 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,8 @@ OpenAI-compatible surface. Because it is one endpoint, `/model ` works for **any** of the models mid-session, e.g. `/model gpt-5.6-sol`. If the proxy is missing, Claude models automatically fall back to the gateway -directly — they can never break because of it. `qbraid-code --stop` shuts the -proxy down. +directly — they can never break because of it. It shuts down by itself when +you quit the session that started it; `qbraid-code --stop` stops it by hand. One caveat: GPT models accept at most 128 tools, so with many MCP servers add `--strict-mcp-config`. diff --git a/install.sh b/install.sh index 0b84a2f..2a8ee69 100755 --- a/install.sh +++ b/install.sh @@ -300,36 +300,48 @@ else CREDITS="unknown" fi -# /organizations/current returns the organization document, so `name` is the -# organization's. /organizations/me returns MEMBERSHIP, whose first `name` is -# the USER's — labelling the confirmation with that would be worse than -# showing nothing. Scope the match to the data object, and print the id -# alongside so a bad parse cannot quietly point someone at the wrong org. +# An API key cannot read its organization's NAME: /organizations/current needs +# a JWT org context and /organizations rejects key auth outright (both verified +# 2026-08-20). Showing a raw Mongo id and asking "is this right?" is worse than +# not asking — nobody recognises 507f1f77bcf86cd799439011. Show what the key +# DOES tell us (plan, credits), name the organization only when it resolves, +# and make the escape hatch the actionable sentence. ORG_NAME="" if [ -n "$ORG_ID" ]; then api_get "$API_BASE/organizations/current" "$API_KEY" "$ORG_ID" - ORG_DATA=$(printf '%s' "$API_BODY" | sed 's/.*"data"[[:space:]]*:[[:space:]]*{//') - ORG_NAME=$(json_str "$ORG_DATA" name) + if [ "$HTTP_STATUS" = 200 ]; then + ORG_DATA=$(printf '%s' "$API_BODY" | sed 's/.*"data"[[:space:]]*:[[:space:]]*{//') + ORG_NAME=$(json_str "$ORG_DATA" name) + fi fi +PLAN="" +api_get "$GATEWAY_URL/quota" "$API_KEY" +[ "$HTTP_STATUS" = 200 ] && PLAN=$(json_str "$API_BODY" plan) + +printf '\n' if [ -n "$ORG_NAME" ]; then - printf '\n Organization: %s%s%s %s(%s)%s\n' \ - "$bold" "$ORG_NAME" "$rst" "$dim" "$ORG_ID" "$rst" -else - printf '\n Organization: %s%s%s\n' "$bold" "${ORG_ID:-unknown}" "$rst" + printf ' Organization: %s%s%s\n' "$bold" "$ORG_NAME" "$rst" +fi +[ -n "$PLAN" ] && printf ' Plan: %s%s%s\n' "$bold" "$PLAN" "$rst" +printf ' Credits: %s%s%s\n' "$bold" "$CREDITS" "$rst" +if [ -z "$ORG_NAME" ]; then + printf '\n These are the credits this API key can spend.\n' + printf ' Using a different organization means creating a key under it at\n' + printf ' %s%s%s\n' "$bold" "$KEYS_URL" "$rst" fi -printf ' Credits: %s%s%s\n\n' "$bold" "$CREDITS" "$rst" +printf '\n' -if ! confirm "Is this the right organization?" y; then +if ! confirm "Continue with this account?" y; then cat </dev/null } +PROXY_STARTED_BY_US=0 + start_proxy() { proxy_listening && return 0 [ -n "$PROXY_BIN" ] && [ -x "$PROXY_BIN" ] || { @@ -52,7 +54,10 @@ start_proxy() { >> "$HOME_DIR/proxy.log" 2>&1 & i=0 while [ "$i" -lt 40 ]; do - proxy_listening && return 0 + if proxy_listening; then + PROXY_STARTED_BY_US=1 + return 0 + fi sleep 0.3 i=$((i + 1)) done @@ -244,10 +249,40 @@ fi # FORCED, not defaulted: an inherited MAX_THINKING_TOKENS from the user's # shell re-enables thinking and turns every request into that 400. Relax to # Claude-only once the gateway accepts "adaptive". -MAX_THINKING_TOKENS=0 \ -ANTHROPIC_BASE_URL="$RUN_BASE" \ -ANTHROPIC_AUTH_TOKEN="$RUN_TOKEN" \ -ANTHROPIC_MODEL="$RUN_MODEL" \ -ANTHROPIC_SMALL_FAST_MODEL="$RUN_MODEL" \ -CLAUDE_CODE_SUBAGENT_MODEL="$RUN_MODEL" \ -exec claude "$@" +run_claude() { + MAX_THINKING_TOKENS=0 \ + ANTHROPIC_BASE_URL="$RUN_BASE" \ + ANTHROPIC_AUTH_TOKEN="$RUN_TOKEN" \ + ANTHROPIC_MODEL="$RUN_MODEL" \ + ANTHROPIC_SMALL_FAST_MODEL="$RUN_MODEL" \ + CLAUDE_CODE_SUBAGENT_MODEL="$RUN_MODEL" \ + claude "$@" +} + +if [ "$PROXY_STARTED_BY_US" = 1 ]; then + # We started the proxy for this session, so we clean it up — leaving a + # background process running after the user quits is our bug, not theirs. + # A proxy that was ALREADY running belongs to someone else: leave it. + # No `exec` here, so this shell survives to do the cleanup; the trap also + # covers Ctrl+C and terminal close. + stop_our_proxy() { + pkill -f "$PROXY_BIN -config $HOME_DIR/proxy-config.yaml" 2>/dev/null || true + } + trap 'stop_our_proxy' EXIT INT TERM HUP + run_claude "$@" + STATUS=$? + stop_our_proxy + trap - EXIT INT TERM HUP + exit "$STATUS" +fi + +exec_claude() { + MAX_THINKING_TOKENS=0 \ + ANTHROPIC_BASE_URL="$RUN_BASE" \ + ANTHROPIC_AUTH_TOKEN="$RUN_TOKEN" \ + ANTHROPIC_MODEL="$RUN_MODEL" \ + ANTHROPIC_SMALL_FAST_MODEL="$RUN_MODEL" \ + CLAUDE_CODE_SUBAGENT_MODEL="$RUN_MODEL" \ + exec claude "$@" +} +exec_claude "$@" diff --git a/qbraid-code.cmd b/qbraid-code.cmd index 4a1f63b..eaf96f0 100644 --- a/qbraid-code.cmd +++ b/qbraid-code.cmd @@ -59,6 +59,7 @@ rem Unified route: the proxy serves every model on one endpoint (Claude rem passthrough, GPT translated). Fall back to the direct gateway for Claude rem models when the proxy is unavailable; GPT models require it. if not exist "%QC_HOME%\proxy-config.yaml" goto :noproxy +powershell -NoProfile -ExecutionPolicy Bypass -File "%QC_HOME%\qbraid-proxy.ps1" status | find "not running" >nul && set "QC_PROXY_STARTED=1" powershell -NoProfile -ExecutionPolicy Bypass -File "%QC_HOME%\qbraid-proxy.ps1" ensure if errorlevel 1 goto :noproxy set /p RUNTOKEN=<"%QC_HOME%\proxy.key" @@ -103,8 +104,13 @@ set "ANTHROPIC_MODEL=%RUNMODEL%" set "ANTHROPIC_SMALL_FAST_MODEL=%RUNMODEL%" set "CLAUDE_CODE_SUBAGENT_MODEL=%RUNMODEL%" +rem If we started the proxy for this session, stop it when claude exits — +rem leaving a background process behind after the user quits is our bug. +rem A proxy that was already running belongs to someone else: leave it. claude %* -exit /b !ERRORLEVEL! +set "CLAUDE_RC=!ERRORLEVEL!" +if defined QC_PROXY_STARTED powershell -NoProfile -ExecutionPolicy Bypass -File "%QC_HOME%\qbraid-proxy.ps1" stop >nul 2>&1 +exit /b !CLAUDE_RC! :incomplete echo qbraid-code: "%QC_HOME%\env" is incomplete. 1>&2