Kieran's opinionated (and probably slightly dumb) nix config

feat: update crush models

dunkirk.sh 13066323 98907109

verified
Changed files
+269 -561
dots
modules
home
apps
-531
dots/anthropic.sh
···
-
#!/bin/sh
-
-
# Anthropic OAuth client ID
-
CLIENT_ID="9d1c250a-e61b-44d9-88ed-5944d1962f5e"
-
-
# Token cache file location
-
CACHE_DIR="${HOME}/.config/crush/anthropic"
-
CACHE_FILE="${CACHE_DIR}/bearer_token"
-
REFRESH_TOKEN_FILE="${CACHE_DIR}/refresh_token"
-
-
# Function to extract expiration from cached token file
-
extract_expiration() {
-
if [ -f "${CACHE_FILE}.expires" ]; then
-
cat "${CACHE_FILE}.expires"
-
fi
-
}
-
-
# Function to check if token is valid
-
is_token_valid() {
-
local expires="$1"
-
-
if [ -z "$expires" ]; then
-
return 1
-
fi
-
-
local current_time=$(date +%s)
-
# Add 60 second buffer before expiration
-
local buffer_time=$((expires - 60))
-
-
if [ "$current_time" -lt "$buffer_time" ]; then
-
return 0
-
else
-
return 1
-
fi
-
}
-
-
# Function to generate PKCE challenge (requires openssl)
-
generate_pkce() {
-
# Generate 32 random bytes, base64url encode
-
local verifier=$(openssl rand -base64 32 | tr -d "=" | tr "/" "_" | tr "+" "-" | tr -d "\n")
-
# Create SHA256 hash of verifier, base64url encode
-
local challenge=$(printf '%s' "$verifier" | openssl dgst -sha256 -binary | openssl base64 | tr -d "=" | tr "/" "_" | tr "+" "-" | tr -d "\n")
-
-
echo "$verifier|$challenge"
-
}
-
-
# Function to exchange refresh token for new access token
-
exchange_refresh_token() {
-
local refresh_token="$1"
-
-
local bearer_response=$(curl -s -X POST "https://console.anthropic.com/v1/oauth/token" \
-
-H "Content-Type: application/json" \
-
-H "User-Agent: CRUSH/1.0" \
-
-d "{\"grant_type\":\"refresh_token\",\"refresh_token\":\"${refresh_token}\",\"client_id\":\"${CLIENT_ID}\"}")
-
-
# Parse JSON response - try jq first, fallback to sed
-
local access_token=""
-
local new_refresh_token=""
-
local expires_in=""
-
-
if command -v jq >/dev/null 2>&1; then
-
access_token=$(echo "$bearer_response" | jq -r '.access_token // empty')
-
new_refresh_token=$(echo "$bearer_response" | jq -r '.refresh_token // empty')
-
expires_in=$(echo "$bearer_response" | jq -r '.expires_in // empty')
-
else
-
# Fallback to sed parsing
-
access_token=$(echo "$bearer_response" | sed -n 's/.*"access_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
-
new_refresh_token=$(echo "$bearer_response" | sed -n 's/.*"refresh_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
-
expires_in=$(echo "$bearer_response" | sed -n 's/.*"expires_in"[[:space:]]*:[[:space:]]*\([0-9]*\).*/\1/p')
-
fi
-
-
if [ -n "$access_token" ] && [ -n "$expires_in" ]; then
-
# Calculate expiration timestamp
-
local current_time=$(date +%s)
-
local expires_timestamp=$((current_time + expires_in))
-
-
# Cache the new tokens
-
mkdir -p "$CACHE_DIR"
-
echo "$access_token" > "$CACHE_FILE"
-
chmod 600 "$CACHE_FILE"
-
-
if [ -n "$new_refresh_token" ]; then
-
echo "$new_refresh_token" > "$REFRESH_TOKEN_FILE"
-
chmod 600 "$REFRESH_TOKEN_FILE"
-
fi
-
-
# Store expiration for future reference
-
echo "$expires_timestamp" > "${CACHE_FILE}.expires"
-
chmod 600 "${CACHE_FILE}.expires"
-
-
echo "$access_token"
-
return 0
-
fi
-
-
return 1
-
}
-
-
# Function to exchange authorization code for tokens
-
exchange_authorization_code() {
-
local auth_code="$1"
-
local verifier="$2"
-
-
# Split code if it contains state (format: code#state)
-
local code=$(echo "$auth_code" | cut -d'#' -f1)
-
local state=""
-
if echo "$auth_code" | grep -q '#'; then
-
state=$(echo "$auth_code" | cut -d'#' -f2)
-
fi
-
-
# Use the working endpoint
-
local bearer_response=$(curl -s -X POST "https://console.anthropic.com/v1/oauth/token" \
-
-H "Content-Type: application/json" \
-
-H "User-Agent: CRUSH/1.0" \
-
-d "{\"code\":\"${code}\",\"state\":\"${state}\",\"grant_type\":\"authorization_code\",\"client_id\":\"${CLIENT_ID}\",\"redirect_uri\":\"https://console.anthropic.com/oauth/code/callback\",\"code_verifier\":\"${verifier}\"}")
-
-
# Parse JSON response - try jq first, fallback to sed
-
local access_token=""
-
local refresh_token=""
-
local expires_in=""
-
-
if command -v jq >/dev/null 2>&1; then
-
access_token=$(echo "$bearer_response" | jq -r '.access_token // empty')
-
refresh_token=$(echo "$bearer_response" | jq -r '.refresh_token // empty')
-
expires_in=$(echo "$bearer_response" | jq -r '.expires_in // empty')
-
else
-
# Fallback to sed parsing
-
access_token=$(echo "$bearer_response" | sed -n 's/.*"access_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
-
refresh_token=$(echo "$bearer_response" | sed -n 's/.*"refresh_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
-
expires_in=$(echo "$bearer_response" | sed -n 's/.*"expires_in"[[:space:]]*:[[:space:]]*\([0-9]*\).*/\1/p')
-
fi
-
-
if [ -n "$access_token" ] && [ -n "$refresh_token" ] && [ -n "$expires_in" ]; then
-
# Calculate expiration timestamp
-
local current_time=$(date +%s)
-
local expires_timestamp=$((current_time + expires_in))
-
-
# Cache the tokens
-
mkdir -p "$CACHE_DIR"
-
echo "$access_token" > "$CACHE_FILE"
-
echo "$refresh_token" > "$REFRESH_TOKEN_FILE"
-
echo "$expires_timestamp" > "${CACHE_FILE}.expires"
-
chmod 600 "$CACHE_FILE" "$REFRESH_TOKEN_FILE" "${CACHE_FILE}.expires"
-
-
echo "$access_token"
-
return 0
-
else
-
return 1
-
fi
-
}
-
-
# Check for cached bearer token
-
if [ -f "$CACHE_FILE" ] && [ -f "${CACHE_FILE}.expires" ]; then
-
CACHED_TOKEN=$(cat "$CACHE_FILE")
-
CACHED_EXPIRES=$(cat "${CACHE_FILE}.expires")
-
if is_token_valid "$CACHED_EXPIRES"; then
-
# Token is still valid, output and exit
-
echo "$CACHED_TOKEN"
-
exit 0
-
fi
-
fi
-
-
# Bearer token is expired/missing, try to use cached refresh token
-
if [ -f "$REFRESH_TOKEN_FILE" ]; then
-
REFRESH_TOKEN=$(cat "$REFRESH_TOKEN_FILE")
-
if [ -n "$REFRESH_TOKEN" ]; then
-
# Try to exchange refresh token for new bearer token
-
BEARER_TOKEN=$(exchange_refresh_token "$REFRESH_TOKEN")
-
if [ -n "$BEARER_TOKEN" ]; then
-
# Successfully got new bearer token, output and exit
-
echo "$BEARER_TOKEN"
-
exit 0
-
fi
-
fi
-
fi
-
-
# No valid tokens found, start OAuth flow
-
# Check if openssl is available for PKCE
-
if ! command -v openssl >/dev/null 2>&1; then
-
exit 1
-
fi
-
-
# Generate PKCE challenge
-
PKCE_DATA=$(generate_pkce)
-
VERIFIER=$(echo "$PKCE_DATA" | cut -d'|' -f1)
-
CHALLENGE=$(echo "$PKCE_DATA" | cut -d'|' -f2)
-
-
# Build OAuth URL
-
AUTH_URL="https://claude.ai/oauth/authorize"
-
AUTH_URL="${AUTH_URL}?response_type=code"
-
AUTH_URL="${AUTH_URL}&client_id=${CLIENT_ID}"
-
AUTH_URL="${AUTH_URL}&redirect_uri=https://console.anthropic.com/oauth/code/callback"
-
AUTH_URL="${AUTH_URL}&scope=org:create_api_key%20user:profile%20user:inference"
-
AUTH_URL="${AUTH_URL}&code_challenge=${CHALLENGE}"
-
AUTH_URL="${AUTH_URL}&code_challenge_method=S256"
-
AUTH_URL="${AUTH_URL}&state=${VERIFIER}"
-
-
# Create a temporary HTML file with the authentication form
-
TEMP_HTML="/tmp/anthropic_auth_$$.html"
-
cat > "$TEMP_HTML" << EOF
-
<!DOCTYPE html>
-
<html>
-
<head>
-
<title>Anthropic Authentication</title>
-
<style>
-
* {
-
box-sizing: border-box;
-
margin: 0;
-
padding: 0;
-
}
-
-
body {
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
-
background: linear-gradient(135deg, #1a1a1a 0%, #2d1810 100%);
-
color: #ffffff;
-
min-height: 100vh;
-
display: flex;
-
align-items: center;
-
justify-content: center;
-
padding: 20px;
-
}
-
-
.container {
-
background: rgba(40, 40, 40, 0.95);
-
border: 1px solid #4a4a4a;
-
border-radius: 16px;
-
padding: 48px;
-
max-width: 480px;
-
width: 100%;
-
text-align: center;
-
backdrop-filter: blur(10px);
-
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
-
}
-
-
.logo {
-
width: 48px;
-
height: 48px;
-
margin: 0 auto 24px;
-
background: linear-gradient(135deg, #ff6b35 0%, #ff8e53 100%);
-
border-radius: 12px;
-
display: flex;
-
align-items: center;
-
justify-content: center;
-
font-weight: bold;
-
font-size: 24px;
-
color: white;
-
}
-
-
h1 {
-
font-size: 28px;
-
font-weight: 600;
-
margin-bottom: 12px;
-
color: #ffffff;
-
}
-
-
.subtitle {
-
color: #a0a0a0;
-
margin-bottom: 32px;
-
font-size: 16px;
-
line-height: 1.5;
-
}
-
-
.step {
-
margin-bottom: 32px;
-
text-align: left;
-
}
-
-
.step-number {
-
display: inline-flex;
-
align-items: center;
-
justify-content: center;
-
width: 24px;
-
height: 24px;
-
background: #ff6b35;
-
color: white;
-
border-radius: 50%;
-
font-size: 14px;
-
font-weight: 600;
-
margin-right: 12px;
-
}
-
-
.step-title {
-
font-weight: 600;
-
margin-bottom: 8px;
-
color: #ffffff;
-
}
-
-
.step-description {
-
color: #a0a0a0;
-
font-size: 14px;
-
margin-left: 36px;
-
}
-
-
.button {
-
display: inline-block;
-
background: linear-gradient(135deg, #ff6b35 0%, #ff8e53 100%);
-
color: white;
-
padding: 16px 32px;
-
text-decoration: none;
-
border-radius: 12px;
-
font-weight: 600;
-
font-size: 16px;
-
margin-bottom: 24px;
-
transition: all 0.2s ease;
-
box-shadow: 0 4px 12px rgba(255, 107, 53, 0.3);
-
}
-
-
.button:hover {
-
transform: translateY(-2px);
-
box-shadow: 0 8px 20px rgba(255, 107, 53, 0.4);
-
}
-
-
.input-group {
-
margin-bottom: 24px;
-
text-align: left;
-
}
-
-
label {
-
display: block;
-
margin-bottom: 8px;
-
font-weight: 500;
-
color: #ffffff;
-
}
-
-
textarea {
-
width: 100%;
-
background: #2a2a2a;
-
border: 2px solid #4a4a4a;
-
border-radius: 8px;
-
padding: 16px;
-
color: #ffffff;
-
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
-
font-size: 14px;
-
line-height: 1.4;
-
resize: vertical;
-
min-height: 120px;
-
transition: border-color 0.2s ease;
-
}
-
-
textarea:focus {
-
outline: none;
-
border-color: #ff6b35;
-
box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.1);
-
}
-
-
textarea::placeholder {
-
color: #666;
-
}
-
-
.submit-btn {
-
background: linear-gradient(135deg, #ff6b35 0%, #ff8e53 100%);
-
color: white;
-
border: none;
-
padding: 16px 32px;
-
border-radius: 12px;
-
font-weight: 600;
-
font-size: 16px;
-
cursor: pointer;
-
transition: all 0.2s ease;
-
box-shadow: 0 4px 12px rgba(255, 107, 53, 0.3);
-
width: 100%;
-
}
-
-
.submit-btn:hover {
-
transform: translateY(-2px);
-
box-shadow: 0 8px 20px rgba(255, 107, 53, 0.4);
-
}
-
-
.submit-btn:disabled {
-
opacity: 0.6;
-
cursor: not-allowed;
-
transform: none;
-
}
-
-
.status {
-
margin-top: 16px;
-
padding: 12px;
-
border-radius: 8px;
-
font-size: 14px;
-
display: none;
-
}
-
-
.status.success {
-
background: rgba(52, 168, 83, 0.1);
-
border: 1px solid rgba(52, 168, 83, 0.3);
-
color: #34a853;
-
}
-
-
.status.error {
-
background: rgba(234, 67, 53, 0.1);
-
border: 1px solid rgba(234, 67, 53, 0.3);
-
color: #ea4335;
-
}
-
</style>
-
</head>
-
<body>
-
<div class="container">
-
<div class="logo">A</div>
-
<h1>Anthropic Authentication</h1>
-
<p class="subtitle">Connect your Anthropic account to continue</p>
-
-
<div class="step">
-
<div class="step-title">
-
<span class="step-number">1</span>
-
Authorize with Anthropic
-
</div>
-
<div class="step-description">
-
Click the button below to open the Anthropic authorization page
-
</div>
-
</div>
-
-
<a href="$AUTH_URL" class="button" target="_blank">
-
Open Anthropic Authorization
-
</a>
-
-
<div class="step">
-
<div class="step-title">
-
<span class="step-number">2</span>
-
Paste your authorization token
-
</div>
-
<div class="step-description">
-
After authorizing, copy the token and paste it below
-
</div>
-
</div>
-
-
<form id="tokenForm">
-
<div class="input-group">
-
<label for="token">Authorization Token:</label>
-
<textarea
-
id="token"
-
name="token"
-
placeholder="Paste your token here..."
-
required
-
></textarea>
-
</div>
-
<button type="submit" class="submit-btn" id="submitBtn">
-
Complete Authentication
-
</button>
-
</form>
-
-
<div id="status" class="status"></div>
-
</div>
-
-
<script>
-
document.getElementById('tokenForm').addEventListener('submit', function(e) {
-
e.preventDefault();
-
-
const token = document.getElementById('token').value.trim();
-
if (!token) {
-
showStatus('Please paste your authorization token', 'error');
-
return;
-
}
-
-
// Ensure token has content before creating file
-
if (token.length > 0) {
-
// Save the token as a downloadable file
-
const blob = new Blob([token], { type: 'text/plain' });
-
const a = document.createElement('a');
-
a.href = URL.createObjectURL(blob);
-
a.download = "anthropic_token.txt";
-
document.body.appendChild(a); // Append to body to ensure it works in all browsers
-
a.click();
-
document.body.removeChild(a); // Clean up
-
-
// Verify file creation
-
console.log("Token file created with content length: " + token.length);
-
} else {
-
showStatus('Empty token detected, please provide a valid token', 'error');
-
return;
-
}
-
-
document.getElementById('submitBtn').disabled = true;
-
document.getElementById('submitBtn').textContent = "Token saved, you may close this tab.";
-
showStatus('Token file downloaded! You can close this window.', 'success');
-
-
// setTimeout(() => {
-
// window.close();
-
// }, 2000);
-
});
-
-
function showStatus(message, type) {
-
const status = document.getElementById('status');
-
status.textContent = message;
-
status.className = 'status ' + type;
-
status.style.display = 'block';
-
}
-
-
// Auto-close after 10 minutes
-
setTimeout(() => {
-
window.close();
-
}, 600000);
-
</script>
-
</body>
-
</html>
-
EOF
-
-
# Open the HTML file
-
if command -v xdg-open >/dev/null 2>&1; then
-
xdg-open "$TEMP_HTML" >/dev/null 2>&1 &
-
elif command -v open >/dev/null 2>&1; then
-
open "$TEMP_HTML" >/dev/null 2>&1 &
-
elif command -v start >/dev/null 2>&1; then
-
start "$TEMP_HTML" >/dev/null 2>&1 &
-
fi
-
-
# Wait for user to download the token file
-
TOKEN_FILE="$HOME/Downloads/anthropic_token.txt"
-
-
for i in $(seq 1 60); do
-
if [ -f "$TOKEN_FILE" ]; then
-
AUTH_CODE=$(cat "$TOKEN_FILE" | tr -d '\r\n')
-
rm -f "$TOKEN_FILE"
-
break
-
fi
-
sleep 2
-
done
-
-
# Clean up the temporary HTML file
-
rm -f "$TEMP_HTML"
-
-
if [ -z "$AUTH_CODE" ]; then
-
exit 1
-
fi
-
-
# Exchange code for tokens
-
ACCESS_TOKEN=$(exchange_authorization_code "$AUTH_CODE" "$VERIFIER")
-
if [ -n "$ACCESS_TOKEN" ]; then
-
echo "$ACCESS_TOKEN"
-
exit 0
-
else
-
exit 1
-
fi
···
+136
dots/update-crush-models.sh
···
···
+
#!/usr/bin/env bash
+
+
set -euo pipefail
+
+
# Configuration
+
COPILOT_TOKEN=$(bash ~/.config/crush/copilot.sh)
+
ANTHROPIC_TOKEN=$(bunx anthropic-api-key)
+
+
# Colors for output
+
RED='\033[0;31m'
+
GREEN='\033[0;32m'
+
YELLOW='\033[1;33m'
+
BLUE='\033[0;34m'
+
NC='\033[0m' # No Color
+
+
echo -e "${GREEN}Fetching latest models from APIs...${NC}"
+
+
# Fetch Copilot models
+
echo " → Fetching Copilot models..."
+
COPILOT_MODELS=$(curl -s https://api.githubcopilot.com/models \
+
-H "Authorization: Bearer $COPILOT_TOKEN" \
+
-H "Editor-Version: CRUSH/1.0" \
+
-H "Editor-Plugin-Version: CRUSH/1.0" \
+
-H "Copilot-Integration-Id: vscode-chat")
+
+
# Fetch Anthropic models
+
echo " → Fetching Anthropic models..."
+
ANTHROPIC_MODELS=$(curl -s https://api.anthropic.com/v1/models \
+
-H "Authorization: Bearer $ANTHROPIC_TOKEN" \
+
-H "anthropic-version: 2023-06-01" \
+
-H "anthropic-beta: oauth-2025-04-20")
+
+
# Extract model-picker enabled Copilot models
+
echo -e "\n${GREEN}Copilot models (model_picker_enabled):${NC}"
+
echo "$COPILOT_MODELS" | jq -r '.data[] | select(.model_picker_enabled == true) | " ✓ \(.id) - \(.name)"'
+
+
# Extract all Anthropic models
+
echo -e "\n${GREEN}Anthropic models:${NC}"
+
echo "$ANTHROPIC_MODELS" | jq -r '.data[] | " ✓ \(.id) - \(.display_name)"'
+
+
# Generate Copilot models Nix config
+
generate_copilot_models() {
+
echo "$COPILOT_MODELS" | jq -r '.data[] | select(.model_picker_enabled == true) |
+
(if .capabilities.supports.tool_calls then true else false end) as $can_reason |
+
(if .capabilities.supports.vision then true else false end) as $supports_attachments |
+
" {\n" +
+
" id = \"\(.id)\";\n" +
+
" name = \"Copilot: \(.name)\";\n" +
+
" cost_per_1m_in = 0;\n" +
+
" cost_per_1m_out = 0;\n" +
+
" cost_per_1m_in_cached = 0;\n" +
+
" cost_per_1m_out_cached = 0;\n" +
+
" context_window = \(.capabilities.limits.max_context_window_tokens);\n" +
+
" default_max_tokens = \(.capabilities.limits.max_output_tokens);\n" +
+
" can_reason = \($can_reason);\n" +
+
" has_reasoning_efforts = false;\n" +
+
" supports_attachments = \($supports_attachments);\n" +
+
" }"'
+
}
+
+
# Generate Anthropic models Nix config with pricing
+
generate_anthropic_models() {
+
echo "$ANTHROPIC_MODELS" | jq -r '.data[] |
+
# Determine pricing based on model family
+
(if (.id | contains("opus-4")) then
+
{in: 15.0, out: 75.0, in_cached: 1.5, out_cached: 75.0}
+
elif (.id | contains("sonnet-4")) then
+
{in: 3.0, out: 15.0, in_cached: 0.225, out_cached: 15.0}
+
elif (.id | contains("3-7-sonnet")) then
+
{in: 2.5, out: 12.0, in_cached: 0.187, out_cached: 12.0}
+
elif (.id | contains("3-5-sonnet")) then
+
{in: 3.0, out: 15.0, in_cached: 0.225, out_cached: 15.0}
+
elif (.id | contains("3-5-haiku")) then
+
{in: 0.8, out: 4.0, in_cached: 0.06, out_cached: 4.0}
+
elif (.id | contains("3-haiku")) then
+
{in: 0.25, out: 1.25, in_cached: 0.03, out_cached: 1.25}
+
elif (.id | contains("3-opus")) then
+
{in: 15.0, out: 75.0, in_cached: 1.5, out_cached: 75.0}
+
else
+
{in: 3.0, out: 15.0, in_cached: 0.225, out_cached: 15.0}
+
end) as $pricing |
+
+
# Determine context window and max tokens
+
(if (.id | contains("opus-4")) or (.id | contains("sonnet-4")) or (.id | contains("3-7-sonnet")) then
+
{context: 200000, max_tokens: (if (.id | contains("sonnet-4-5")) then 50000 else 50000 end)}
+
elif (.id | contains("3-5")) then
+
{context: 200000, max_tokens: 8192}
+
elif (.id | contains("3-haiku")) or (.id | contains("3-opus")) then
+
{context: 200000, max_tokens: 4096}
+
else
+
{context: 200000, max_tokens: 8192}
+
end) as $limits |
+
+
# Determine has_reasoning_efforts
+
(if (.id | contains("opus-4")) then true else false end) as $has_reasoning |
+
+
" {\n" +
+
" id = \"\(.id)\";\n" +
+
" name = \"\(.display_name)\";\n" +
+
" cost_per_1m_in = \($pricing.in);\n" +
+
" cost_per_1m_out = \($pricing.out);\n" +
+
" cost_per_1m_in_cached = \($pricing.in_cached);\n" +
+
" cost_per_1m_out_cached = \($pricing.out_cached);\n" +
+
" context_window = \($limits.context);\n" +
+
" default_max_tokens = \($limits.max_tokens);\n" +
+
" can_reason = true;\n" +
+
" has_reasoning_efforts = \($has_reasoning);\n" +
+
" supports_attachments = true;\n" +
+
" }"'
+
}
+
+
# Generate formatted model arrays
+
COPILOT_MODELS_NIX=$(generate_copilot_models)
+
ANTHROPIC_MODELS_NIX=$(generate_anthropic_models)
+
+
# Output results
+
echo -e "\n${BLUE}═══════════════════════════════════════════════════════════${NC}"
+
echo -e "${YELLOW}COPILOT MODELS${NC}"
+
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
+
echo -e "\nPaste this into your copilot.models array:\n"
+
echo " models = ["
+
echo "$COPILOT_MODELS_NIX"
+
echo " ];"
+
+
echo -e "\n${BLUE}═══════════════════════════════════════════════════════════${NC}"
+
echo -e "${YELLOW}ANTHROPIC MODELS${NC}"
+
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
+
echo -e "\nPaste this into your claude-pro.models array:\n"
+
echo " models = ["
+
echo "$ANTHROPIC_MODELS_NIX"
+
echo " ];"
+
+
echo -e "\n${BLUE}═══════════════════════════════════════════════════════════${NC}"
+
echo -e "\n${GREEN}Summary:${NC}"
+
echo " • Copilot models: $(echo "$COPILOT_MODELS" | jq '[.data[] | select(.model_picker_enabled == true)] | length')"
+
echo " • Anthropic models: $(echo "$ANTHROPIC_MODELS" | jq '.data | length')"
+133 -30
modules/home/apps/crush.nix
···
cost_per_1m_out_cached = 0;
context_window = 128000;
default_max_tokens = 16384;
-
can_reason = false;
has_reasoning_efforts = false;
-
supports_attachments = false;
}
{
id = "gpt-5-mini";
-
name = "Copilot: GPT-5 mini (Preview)";
cost_per_1m_in = 0;
cost_per_1m_out = 0;
cost_per_1m_in_cached = 0;
cost_per_1m_out_cached = 0;
-
context_window = 128000;
default_max_tokens = 64000;
-
can_reason = false;
has_reasoning_efforts = false;
-
supports_attachments = false;
}
{
id = "gpt-5";
-
name = "Copilot: GPT-5 (Preview)";
cost_per_1m_in = 0;
cost_per_1m_out = 0;
cost_per_1m_in_cached = 0;
cost_per_1m_out_cached = 0;
-
context_window = 128000;
default_max_tokens = 64000;
-
can_reason = false;
has_reasoning_efforts = false;
-
supports_attachments = false;
}
{
id = "gpt-4o";
···
cost_per_1m_out_cached = 0;
context_window = 128000;
default_max_tokens = 4096;
-
can_reason = false;
has_reasoning_efforts = false;
-
supports_attachments = false;
}
{
id = "o3-mini";
···
cost_per_1m_out_cached = 0;
context_window = 200000;
default_max_tokens = 100000;
-
can_reason = false;
has_reasoning_efforts = false;
supports_attachments = false;
}
{
id = "claude-3.5-sonnet";
name = "Copilot: Claude Sonnet 3.5";
cost_per_1m_in = 0;
···
cost_per_1m_out_cached = 0;
context_window = 200000;
default_max_tokens = 16384;
-
can_reason = true;
has_reasoning_efforts = false;
supports_attachments = true;
}
···
cost_per_1m_out = 0;
cost_per_1m_in_cached = 0;
cost_per_1m_out_cached = 0;
-
context_window = 128000;
default_max_tokens = 16000;
can_reason = true;
has_reasoning_efforts = false;
···
cost_per_1m_out_cached = 0;
context_window = 1000000;
default_max_tokens = 8192;
-
can_reason = true;
has_reasoning_efforts = false;
supports_attachments = true;
}
{
id = "gemini-2.5-pro";
-
name = "Copilot: Gemini 2.5 Pro (Preview)";
cost_per_1m_in = 0;
cost_per_1m_out = 0;
cost_per_1m_in_cached = 0;
···
cost_per_1m_out_cached = 0;
context_window = 128000;
default_max_tokens = 16384;
-
can_reason = false;
has_reasoning_efforts = false;
-
supports_attachments = false;
}
];
};
···
};
models = [
{
id = "claude-opus-4-20250514";
name = "Claude Opus 4";
cost_per_1m_in = 15.0;
···
{
id = "claude-sonnet-4-20250514";
name = "Claude Sonnet 4";
-
cost_per_1m_in = 3000;
-
cost_per_1m_out = 15000;
-
cost_per_1m_in_cached = 225;
-
cost_per_1m_out_cached = 15000;
context_window = 200000;
default_max_tokens = 50000;
can_reason = true;
···
}
{
id = "claude-3-7-sonnet-20250219";
-
name = "Claude 3.7 Sonnet";
cost_per_1m_in = 2.5;
cost_per_1m_out = 12.0;
cost_per_1m_in_cached = 0.187;
cost_per_1m_out_cached = 12.0;
context_window = 200000;
-
default_max_tokens = 64000;
can_reason = true;
has_reasoning_efforts = false;
supports_attachments = true;
}
{
id = "claude-3-5-sonnet-20241022";
-
name = "Claude 3.5 Sonnet (Latest)";
cost_per_1m_in = 3.0;
cost_per_1m_out = 15.0;
cost_per_1m_in_cached = 0.225;
cost_per_1m_out_cached = 15.0;
context_window = 200000;
default_max_tokens = 8192;
-
can_reason = false;
has_reasoning_efforts = false;
supports_attachments = true;
}
{
id = "claude-3-5-haiku-20241022";
-
name = "Claude 3.5 Haiku";
cost_per_1m_in = 0.8;
cost_per_1m_out = 4.0;
cost_per_1m_in_cached = 0.06;
cost_per_1m_out_cached = 4.0;
context_window = 200000;
default_max_tokens = 8192;
-
can_reason = false;
has_reasoning_efforts = false;
supports_attachments = true;
}
···
};
xdg.configFile."crush/copilot.sh".source = ../../../dots/copilot.sh;
-
xdg.configFile."crush/anthropic.sh".source = ../../../dots/anthropic.sh;
};
}
···
cost_per_1m_out_cached = 0;
context_window = 128000;
default_max_tokens = 16384;
+
can_reason = true;
has_reasoning_efforts = false;
+
supports_attachments = true;
}
{
id = "gpt-5-mini";
+
name = "Copilot: GPT-5 mini";
cost_per_1m_in = 0;
cost_per_1m_out = 0;
cost_per_1m_in_cached = 0;
cost_per_1m_out_cached = 0;
+
context_window = 264000;
default_max_tokens = 64000;
+
can_reason = true;
has_reasoning_efforts = false;
+
supports_attachments = true;
}
{
id = "gpt-5";
+
name = "Copilot: GPT-5";
cost_per_1m_in = 0;
cost_per_1m_out = 0;
cost_per_1m_in_cached = 0;
cost_per_1m_out_cached = 0;
+
context_window = 264000;
default_max_tokens = 64000;
+
can_reason = true;
has_reasoning_efforts = false;
+
supports_attachments = true;
}
{
id = "gpt-4o";
···
cost_per_1m_out_cached = 0;
context_window = 128000;
default_max_tokens = 4096;
+
can_reason = true;
has_reasoning_efforts = false;
+
supports_attachments = true;
}
{
id = "o3-mini";
···
cost_per_1m_out_cached = 0;
context_window = 200000;
default_max_tokens = 100000;
+
can_reason = true;
has_reasoning_efforts = false;
supports_attachments = false;
}
{
+
id = "grok-code-fast-1";
+
name = "Copilot: Grok Code Fast 1 (Preview)";
+
cost_per_1m_in = 0;
+
cost_per_1m_out = 0;
+
cost_per_1m_in_cached = 0;
+
cost_per_1m_out_cached = 0;
+
context_window = 128000;
+
default_max_tokens = 64000;
+
can_reason = true;
+
has_reasoning_efforts = false;
+
supports_attachments = false;
+
}
+
{
+
id = "gpt-5-codex";
+
name = "Copilot: GPT-5-Codex (Preview)";
+
cost_per_1m_in = 0;
+
cost_per_1m_out = 0;
+
cost_per_1m_in_cached = 0;
+
cost_per_1m_out_cached = 0;
+
context_window = 200000;
+
default_max_tokens = 64000;
+
can_reason = true;
+
has_reasoning_efforts = false;
+
supports_attachments = true;
+
}
+
{
id = "claude-3.5-sonnet";
name = "Copilot: Claude Sonnet 3.5";
cost_per_1m_in = 0;
···
cost_per_1m_out_cached = 0;
context_window = 200000;
default_max_tokens = 16384;
+
can_reason = false;
has_reasoning_efforts = false;
supports_attachments = true;
}
···
cost_per_1m_out = 0;
cost_per_1m_in_cached = 0;
cost_per_1m_out_cached = 0;
+
context_window = 216000;
+
default_max_tokens = 16000;
+
can_reason = true;
+
has_reasoning_efforts = false;
+
supports_attachments = true;
+
}
+
{
+
id = "claude-sonnet-4.5";
+
name = "Copilot: Claude Sonnet 4.5 (Preview)";
+
cost_per_1m_in = 0;
+
cost_per_1m_out = 0;
+
cost_per_1m_in_cached = 0;
+
cost_per_1m_out_cached = 0;
+
context_window = 144000;
default_max_tokens = 16000;
can_reason = true;
has_reasoning_efforts = false;
···
cost_per_1m_out_cached = 0;
context_window = 1000000;
default_max_tokens = 8192;
+
can_reason = false;
has_reasoning_efforts = false;
supports_attachments = true;
}
{
id = "gemini-2.5-pro";
+
name = "Copilot: Gemini 2.5 Pro";
cost_per_1m_in = 0;
cost_per_1m_out = 0;
cost_per_1m_in_cached = 0;
···
cost_per_1m_out_cached = 0;
context_window = 128000;
default_max_tokens = 16384;
+
can_reason = true;
has_reasoning_efforts = false;
+
supports_attachments = true;
}
];
};
···
};
models = [
{
+
id = "claude-sonnet-4-5-20250929";
+
name = "Claude Sonnet 4.5";
+
cost_per_1m_in = 3.0;
+
cost_per_1m_out = 15.0;
+
cost_per_1m_in_cached = 0.225;
+
cost_per_1m_out_cached = 15.0;
+
context_window = 200000;
+
default_max_tokens = 50000;
+
can_reason = true;
+
has_reasoning_efforts = false;
+
supports_attachments = true;
+
}
+
{
+
id = "claude-opus-4-1-20250805";
+
name = "Claude Opus 4.1";
+
cost_per_1m_in = 15.0;
+
cost_per_1m_out = 75.0;
+
cost_per_1m_in_cached = 1.5;
+
cost_per_1m_out_cached = 75.0;
+
context_window = 200000;
+
default_max_tokens = 50000;
+
can_reason = true;
+
has_reasoning_efforts = true;
+
supports_attachments = true;
+
}
+
{
id = "claude-opus-4-20250514";
name = "Claude Opus 4";
cost_per_1m_in = 15.0;
···
{
id = "claude-sonnet-4-20250514";
name = "Claude Sonnet 4";
+
cost_per_1m_in = 3.0;
+
cost_per_1m_out = 15.0;
+
cost_per_1m_in_cached = 0.225;
+
cost_per_1m_out_cached = 15.0;
context_window = 200000;
default_max_tokens = 50000;
can_reason = true;
···
}
{
id = "claude-3-7-sonnet-20250219";
+
name = "Claude Sonnet 3.7";
cost_per_1m_in = 2.5;
cost_per_1m_out = 12.0;
cost_per_1m_in_cached = 0.187;
cost_per_1m_out_cached = 12.0;
context_window = 200000;
+
default_max_tokens = 50000;
can_reason = true;
has_reasoning_efforts = false;
supports_attachments = true;
}
{
id = "claude-3-5-sonnet-20241022";
+
name = "Claude Sonnet 3.5 (New)";
cost_per_1m_in = 3.0;
cost_per_1m_out = 15.0;
cost_per_1m_in_cached = 0.225;
cost_per_1m_out_cached = 15.0;
context_window = 200000;
default_max_tokens = 8192;
+
can_reason = true;
has_reasoning_efforts = false;
supports_attachments = true;
}
{
id = "claude-3-5-haiku-20241022";
+
name = "Claude Haiku 3.5";
cost_per_1m_in = 0.8;
cost_per_1m_out = 4.0;
cost_per_1m_in_cached = 0.06;
cost_per_1m_out_cached = 4.0;
context_window = 200000;
default_max_tokens = 8192;
+
can_reason = true;
+
has_reasoning_efforts = false;
+
supports_attachments = true;
+
}
+
{
+
id = "claude-3-5-sonnet-20240620";
+
name = "Claude Sonnet 3.5 (Old)";
+
cost_per_1m_in = 3.0;
+
cost_per_1m_out = 15.0;
+
cost_per_1m_in_cached = 0.225;
+
cost_per_1m_out_cached = 15.0;
+
context_window = 200000;
+
default_max_tokens = 8192;
+
can_reason = true;
+
has_reasoning_efforts = false;
+
supports_attachments = true;
+
}
+
{
+
id = "claude-3-haiku-20240307";
+
name = "Claude Haiku 3";
+
cost_per_1m_in = 0.25;
+
cost_per_1m_out = 1.25;
+
cost_per_1m_in_cached = 0.03;
+
cost_per_1m_out_cached = 1.25;
+
context_window = 200000;
+
default_max_tokens = 4096;
+
can_reason = true;
+
has_reasoning_efforts = false;
+
supports_attachments = true;
+
}
+
{
+
id = "claude-3-opus-20240229";
+
name = "Claude Opus 3";
+
cost_per_1m_in = 15.0;
+
cost_per_1m_out = 75.0;
+
cost_per_1m_in_cached = 1.5;
+
cost_per_1m_out_cached = 75.0;
+
context_window = 200000;
+
default_max_tokens = 4096;
+
can_reason = true;
has_reasoning_efforts = false;
supports_attachments = true;
}
···
};
xdg.configFile."crush/copilot.sh".source = ../../../dots/copilot.sh;
};
}