Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,13 @@ adopt_legacy_profile() { # adopt_legacy_profile <root>
if [ -n "$token" ]; then
if [ "${OS:-linux}" = darwin ]; then
account="${USER:-$(id -un)}"; secret_ref='qbraid-code:default'
printf '%s\n%s\n' "$token" "$token" | security add-generic-password -U -a "$account" -s "$secret_ref" -w >/dev/null 2>&1 \
# `security -i` takes the whole command on stdin. Bare `-w` relies on
# security reading the password from stdin, which is undocumented and
# falls back to a /dev/tty prompt when it does not — under `curl | bash`
# that surfaces as "password data for new item:" and blocks the install.
# -i also keeps the secret out of the process argument list.
printf 'add-generic-password -U -a %s -s %s -w %s\n' "$account" "$secret_ref" "$token" \
| security -i >/dev/null 2>&1 \
|| { rm -rf "$stage"; die "could not migrate the default key into macOS Keychain."; }
printf 'QBRAID_CODE_SECRET_BACKEND=keychain\nQBRAID_CODE_SECRET_REF=%s\n' "$secret_ref" >> "$stage/env"
elif command -v secret-tool >/dev/null 2>&1 && printf '%s' "$token" | secret-tool store --label='qbraid-code default profile' service qbraid-code ref qbraid-code:default >/dev/null 2>&1; then
Expand Down Expand Up @@ -755,8 +761,11 @@ store_profile_secret() {
SECRET_REF="qbraid-code:$PROFILE:$GENERATION"
if [ "$OS" = darwin ]; then
command -v security >/dev/null 2>&1 || die "macOS Keychain is unavailable."
printf '%s\n%s\n' "$API_KEY" "$API_KEY" | security add-generic-password -U -a "${USER:-$(id -un)}" -s "$SECRET_REF" -w >/dev/null 2>&1 \
|| die "could not store the profile key in macOS Keychain."
# See the migration path above: `security -i` cannot fall back to a tty
# prompt, and keeps the key out of argv.
printf 'add-generic-password -U -a %s -s %s -w %s\n' "${USER:-$(id -un)}" "$SECRET_REF" "$API_KEY" \
| security -i >/dev/null 2>&1 \
|| die "could not store the profile key in macOS Keychain. If your login keychain is locked, unlock it in Keychain Access and re-run."
SECRET_BACKEND="keychain"
elif command -v secret-tool >/dev/null 2>&1 && printf '%s' "$API_KEY" | secret-tool store --label="qbraid-code $PROFILE profile" service qbraid-code ref "qbraid-code:$PROFILE:$GENERATION" >/dev/null 2>&1; then
SECRET_REF="qbraid-code:$PROFILE:$GENERATION"
Expand Down Expand Up @@ -1024,7 +1033,10 @@ MODEL="${QBRAID_CODE_MODEL:-}"
if [ "$UPDATE_KEY" -eq 1 ] && [ -z "$MODEL" ]; then MODEL="$OLD_MODEL"; fi
if [ -z "$MODEL" ]; then
# The list is fetched live so new gateway models appear without a release here.
api_get "$GATEWAY_URL/v1/models" "$API_KEY"
# /models (OpenAI-compat surface), NOT /v1/models (Anthropic surface): the
# Anthropic one lists only the Claude models, so the chooser silently hid
# every GPT model the launcher can actually run.
api_get "$GATEWAY_URL/models" "$API_KEY"
MODEL_IDS=$(set +o pipefail; printf '%s' "$API_BODY" | grep -o '"id":"[^"]*"' | sed 's/"id":"//; s/"$//')
if [ -z "$MODEL_IDS" ]; then
warn "could not list models — defaulting to claude-sonnet-4-6"
Expand Down
43 changes: 35 additions & 8 deletions tests/install-profiles.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,41 @@ EOF
chmod +x "$FAKE_BIN/secret-tool"
cat > "$FAKE_BIN/security" <<'EOF'
#!/usr/bin/env bash
action="$1"; shift; service=""
while [ "$#" -gt 0 ]; do case "$1" in -s) service="$2"; shift 2;; -w) want_password=1; shift;; *) shift;; esac; done
file="$HOME/.fake-key.$(printf '%s' "$service" | tr ':/' '__')"
case "$action" in
add-generic-password) IFS= read -r value; printf '%s\n' "$value" > "$file" ;;
find-generic-password) cat "$file" ;;
delete-generic-password) rm -f "$file" ;;
esac
# Models both call shapes the installer uses:
# security <action> -s <ref> -w (password on stdin)
# security -i (whole command on stdin, with
# the password as the -w value)
# The second is what the real tool needs so it can never fall back to a tty
# prompt; a stub that only knew the first silently stored nothing.
run_command() { # run_command <args...>
local action="$1"; shift
local service="" password="" have_password=0
while [ "$#" -gt 0 ]; do
case "$1" in
-s) service="$2"; shift 2 ;;
-w) if [ "$#" -ge 2 ] && [ "${2#-}" = "$2" ]; then password="$2"; have_password=1; shift 2; else shift; fi ;;
*) shift ;;
esac
done
local file="$HOME/.fake-key.$(printf '%s' "$service" | tr ':/' '__')"
case "$action" in
add-generic-password)
[ "$have_password" -eq 1 ] || IFS= read -r password
printf '%s\n' "$password" > "$file" ;;
find-generic-password) cat "$file" ;;
delete-generic-password) rm -f "$file" ;;
esac
}

if [ "$1" = "-i" ]; then
while IFS= read -r line; do
[ -n "$line" ] || continue
# shellcheck disable=SC2086
run_command $line
done
else
run_command "$@"
fi
EOF
chmod +x "$FAKE_BIN/claude" "$FAKE_BIN/curl" "$FAKE_BIN/security" "$FAKE_BIN/cliproxyapi"
export HOME="$HOME_ROOT" QBRAID_CODE_HOME="$QC_HOME" QBRAID_CODE_BIN_DIR="$BIN_DIR"
Expand Down
Loading