#!/bin/bash # # Ditto Free-Tier API Key Setup # Run with: curl -sL https://app.askditto.io/scripts/free-tier-auth.sh | bash # set -e DITTO_API_BASE_URL="https://app.askditto.io" CALLBACK_PORT=53682 CALLBACK_URI="http://127.0.0.1:${CALLBACK_PORT}/oauth/callback" CALLBACK_FILE="/tmp/ditto_oauth_callback_$$.json" ENV_FILE="${HOME}/.ditto_free_tier.env" echo "" echo "Ditto Free-Tier API Setup" echo "=========================" echo "" # Check dependencies command -v curl >/dev/null 2>&1 || { echo "ERROR: curl is required"; exit 1; } command -v python3 >/dev/null 2>&1 || { echo "ERROR: python3 is required"; exit 1; } # Check if already have valid key if [ -f "$ENV_FILE" ]; then source "$ENV_FILE" if [ -n "$DITTO_FREE_TIER_API_KEY" ]; then echo "OK: API key already configured: ${DITTO_FREE_TIER_API_KEY:0:20}..." echo "" echo "To re-authenticate, run:" echo " rm ~/.ditto_free_tier.env && curl -sL https://app.askditto.io/scripts/free-tier-auth.sh | bash" echo "" exit 0 fi fi # Step 1: Start OAuth session echo "[1/4] Starting OAuth session..." START_RESPONSE=$(curl -s -X POST "${DITTO_API_BASE_URL}/cli/auth/start" \ -H "Content-Type: application/json" \ -d "{\"provider\":\"google\",\"redirect_uri\":\"${CALLBACK_URI}\"}") SESSION_ID=$(echo "$START_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('session_id',''))" 2>/dev/null) AUTH_URL=$(echo "$START_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('authorization_url',''))" 2>/dev/null) if [ -z "$SESSION_ID" ] || [ -z "$AUTH_URL" ]; then echo "ERROR: Failed to start OAuth session" echo "Response: $START_RESPONSE" exit 1 fi # Step 2: Start callback listener echo "[2/4] Starting callback listener on port ${CALLBACK_PORT}..." rm -f "$CALLBACK_FILE" python3 << PYTHON_EOF & import http.server, socketserver, urllib.parse, json class Handler(http.server.BaseHTTPRequestHandler): def do_GET(self): params = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query) with open("$CALLBACK_FILE", 'w') as f: json.dump({ 'code': params.get('code', [''])[0], 'state': params.get('state', [''])[0], 'error': params.get('error', [''])[0] }, f) self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write(b'''
You can close this window and return to your terminal.
''') def log_message(self, *args): pass with socketserver.TCPServer(("127.0.0.1", $CALLBACK_PORT), Handler) as httpd: httpd.handle_request() PYTHON_EOF LISTENER_PID=$! sleep 1 # Verify listener started if ! kill -0 $LISTENER_PID 2>/dev/null; then echo "ERROR: Failed to start callback listener (port ${CALLBACK_PORT} may be in use)" exit 1 fi # Step 3: Open browser echo "[3/4] Opening browser for Google authentication..." echo "" echo " If the browser doesn't open, visit:" echo " $AUTH_URL" echo "" if [[ "$OSTYPE" == "darwin"* ]]; then open "$AUTH_URL" 2>/dev/null elif [[ "$OSTYPE" == "linux-gnu"* ]]; then xdg-open "$AUTH_URL" 2>/dev/null || true elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin"* ]]; then start "$AUTH_URL" 2>/dev/null || true fi # Step 4: Wait for callback echo " Waiting for authentication..." TIMEOUT=120 ELAPSED=0 while [ ! -s "$CALLBACK_FILE" ] && [ $ELAPSED -lt $TIMEOUT ]; do sleep 1 ELAPSED=$((ELAPSED + 1)) done # Clean up listener kill $LISTENER_PID 2>/dev/null || true if [ ! -s "$CALLBACK_FILE" ]; then echo "ERROR: Timeout waiting for authentication" rm -f "$CALLBACK_FILE" exit 1 fi # Read callback data CALLBACK_DATA=$(cat "$CALLBACK_FILE") rm -f "$CALLBACK_FILE" CODE=$(echo "$CALLBACK_DATA" | python3 -c "import sys,json; print(json.load(sys.stdin).get('code',''))" 2>/dev/null) STATE=$(echo "$CALLBACK_DATA" | python3 -c "import sys,json; print(json.load(sys.stdin).get('state',''))" 2>/dev/null) ERROR=$(echo "$CALLBACK_DATA" | python3 -c "import sys,json; print(json.load(sys.stdin).get('error',''))" 2>/dev/null) if [ -n "$ERROR" ]; then echo "ERROR: Authentication failed: $ERROR" exit 1 fi if [ -z "$CODE" ]; then echo "ERROR: No authorization code received" exit 1 fi # Step 5: Complete OAuth echo "[4/4] Completing authentication..." COMPLETE_RESPONSE=$(curl -s -X POST "${DITTO_API_BASE_URL}/cli/auth/complete" \ -H "Content-Type: application/json" \ -d "{\"session_id\":\"${SESSION_ID}\",\"code\":\"${CODE}\",\"state\":\"${STATE}\"}") STATUS=$(echo "$COMPLETE_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('status',''))" 2>/dev/null) API_KEY=$(echo "$COMPLETE_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('api_key',''))" 2>/dev/null) if [ "$STATUS" = "login_required" ]; then LOGIN_URL=$(echo "$COMPLETE_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('login_url',''))" 2>/dev/null) echo "" echo "INFO: You have an existing Ditto organization account." echo " Please log in at: ${LOGIN_URL}" echo " Use your organization's API key instead of the free tier." exit 0 fi if [ -z "$API_KEY" ]; then echo "ERROR: Failed to obtain API key" echo "Response: $COMPLETE_RESPONSE" exit 1 fi # Save API key echo "DITTO_FREE_TIER_API_KEY=\"${API_KEY}\"" > "$ENV_FILE" chmod 600 "$ENV_FILE" echo "" echo "============================" echo "SUCCESS: Setup complete" echo "============================" echo "" echo "Your API key: ${API_KEY:0:25}..." echo "Saved to: ~/.ditto_free_tier.env" echo "" echo "To use in your current terminal session:" echo " source ~/.ditto_free_tier.env" echo "" echo "Try a research question now:" echo "" echo " curl -s -X POST \"https://app.askditto.io/v1/free/questions\" \\" echo " -H \"Authorization: Bearer \\$DITTO_FREE_TIER_API_KEY\" \\" echo " -H \"Content-Type: application/json\" \\" echo " -d '{\"question\": \"What matters most when choosing a coffee brand?\"}'" echo ""