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