diff --git a/Makefile b/Makefile index ec312c07d..f095ee094 100644 --- a/Makefile +++ b/Makefile @@ -672,7 +672,15 @@ launchctl-ollama: ## Restart ollama launchd agent. ##@ Systemd Services (Linux) .PHONY: systemctl -systemctl: systemctl-clawdbot systemctl-code-syncer systemctl-dotfiles-updater systemctl-ollama ## Restart all systemd user services. +systemctl: systemctl-cliproxyapi systemctl-clawdbot systemctl-code-syncer systemctl-dotfiles-updater systemctl-ollama ## Restart all systemd user services. + +.PHONY: systemctl-cliproxyapi +systemctl-cliproxyapi: ## Pull latest image and restart cliproxyapi systemd user service. + @echo "🔄 Pulling latest cliproxyapi image..." + @docker pull eceasy/cli-proxy-api:latest || true + @echo "🔄 Restarting cliproxyapi..." + @systemctl --user restart cliproxyapi.service || true + @echo "✅ cliproxyapi restarted" .PHONY: systemctl-clawdbot systemctl-clawdbot: ## Restart clawdbot gateway systemd user service. diff --git a/config/ccs/accounts.json b/config/ccs/accounts.json new file mode 100644 index 000000000..e157d6639 --- /dev/null +++ b/config/ccs/accounts.json @@ -0,0 +1,42 @@ +{ + "version": 1, + "providers": { + "agy": { + "default": "shunkakinoki@gmail.com", + "accounts": { + "shunkakinoki@gmail.com": { + "email": "shunkakinoki@gmail.com", + "nickname": "shunkakinoki", + "tokenFile": "antigravity-shunkakinoki_gmail_com.json", + "createdAt": "2026-01-17T00:00:00.000Z", + "lastUsedAt": "2026-01-17T19:28:18.251Z", + "tier": "paid" + } + } + }, + "gemini": { + "default": "shunkakinoki@gmail.com", + "accounts": { + "shunkakinoki@gmail.com": { + "email": "shunkakinoki@gmail.com", + "nickname": "shunkakinoki", + "tokenFile": "gemini-shunkakinoki@gmail.com-gen-lang-client-0359793614.json", + "createdAt": "2026-01-12T00:00:00.000Z", + "lastUsedAt": "2026-01-12T00:00:00.000Z" + } + } + }, + "codex": { + "default": "shunkakinoki@gmail.com", + "accounts": { + "shunkakinoki@gmail.com": { + "email": "shunkakinoki@gmail.com", + "nickname": "shunkakinoki", + "tokenFile": "codex-shunkakinoki@gmail.com.json", + "createdAt": "2025-12-18T00:00:00.000Z", + "lastUsedAt": "2025-12-18T00:00:00.000Z" + } + } + } + } +} diff --git a/config/ccs/agy.settings.template.json b/config/ccs/agy.settings.template.json new file mode 100644 index 000000000..b84abd727 --- /dev/null +++ b/config/ccs/agy.settings.template.json @@ -0,0 +1,10 @@ +{ + "env": { + "ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/agy", + "ANTHROPIC_AUTH_TOKEN": "__CLIPROXY_API_KEY__", + "ANTHROPIC_MODEL": "claude-opus-4-5-thinking", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-5-thinking", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-opus-4-5-thinking", + "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-sonnet-4-5" + } +} diff --git a/config/ccs/codex.settings.template.json b/config/ccs/codex.settings.template.json new file mode 100644 index 000000000..5a4d6317b --- /dev/null +++ b/config/ccs/codex.settings.template.json @@ -0,0 +1,10 @@ +{ + "env": { + "ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/codex", + "ANTHROPIC_AUTH_TOKEN": "__CLIPROXY_API_KEY__", + "ANTHROPIC_MODEL": "gpt-5.1-codex-max", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "gpt-5.1-codex-max-high", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-5.1-codex-max", + "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-5.1-codex-mini-high" + } +} diff --git a/config/ccs/config.template.yaml b/config/ccs/config.template.yaml new file mode 100644 index 000000000..d21850425 --- /dev/null +++ b/config/ccs/config.template.yaml @@ -0,0 +1,131 @@ +# CCS Unified Configuration +# Docs: https://github.com/kaitranntt/ccs +version: 7 +# ---------------------------------------------------------------------------- +# Accounts: Isolated Claude instances (each with separate auth/sessions) +# Manage with: ccs auth add , ccs auth list, ccs auth remove +# ---------------------------------------------------------------------------- +accounts: {} +# ---------------------------------------------------------------------------- +# Profiles: API-based providers (GLM, GLMT, Kimi, custom endpoints) +# Each profile points to a *.settings.json file containing env vars. +# Edit the settings file directly to customize (ANTHROPIC_MAX_TOKENS, etc.) +# ---------------------------------------------------------------------------- +profiles: {} +# ---------------------------------------------------------------------------- +# CLIProxy: OAuth-based providers (gemini, codex, agy, qwen, iflow) +# Each variant can reference a *.settings.json file for custom env vars. +# Edit the settings file directly to customize model or other settings. +# ---------------------------------------------------------------------------- +cliproxy: + oauth_accounts: {} + providers: + - gemini + - codex + - agy + - qwen + - iflow + - kiro + - ghcp + variants: {} + logging: + enabled: false + request_log: false +# ---------------------------------------------------------------------------- +# CLIProxy Server: Remote proxy connection settings +# Configure via Dashboard (`ccs config`) > Proxy tab. +# +# remote: Connect to a remote CLIProxyAPI instance +# fallback: Use local proxy if remote is unreachable +# local: Local proxy settings (port, auto-start) +# ---------------------------------------------------------------------------- +cliproxy_server: + remote: + enabled: true + host: 127.0.0.1 + port: 8317 + protocol: http + auth_token: __CLIPROXY_API_KEY__ + fallback: + enabled: true + auto_start: false + local: + port: 8317 + auto_start: false +# ---------------------------------------------------------------------------- +# Preferences: User settings +# ---------------------------------------------------------------------------- +preferences: + theme: system + telemetry: false + auto_update: true +# ---------------------------------------------------------------------------- +# WebSearch: CLI-based web search for third-party profiles +# Dashboard (`ccs config`) is the source of truth for provider selection. +# +# Third-party providers (gemini, codex, agy, etc.) do not have access to +# Anthropic's WebSearch tool. These CLI tools provide fallback web search. +# +# Fallback chain: Gemini -> OpenCode -> Grok (tries in order until success) +# +# Gemini models: gemini-2.5-flash (default), gemini-2.5-pro, gemini-2.5-flash-lite +# OpenCode models: opencode/grok-code (default), opencode/gpt-4o, opencode/claude-3.5-sonnet +# +# Install commands: +# gemini: npm i -g @google/gemini-cli (FREE - 1000 req/day) +# opencode: curl -fsSL https://opencode.ai/install | bash (FREE via Zen) +# grok: npm i -g @vibe-kit/grok-cli (requires GROK_API_KEY) +# ---------------------------------------------------------------------------- +websearch: + enabled: true + providers: + gemini: + enabled: true + model: gemini-2.5-flash + timeout: 55 + opencode: + enabled: false + model: opencode/grok-code + timeout: 90 + grok: + enabled: false + timeout: 55 +# ---------------------------------------------------------------------------- +# Copilot: GitHub Copilot API proxy (via copilot-api) +# Uses your existing GitHub Copilot subscription with Claude Code. +# +# !! DISCLAIMER - USE AT YOUR OWN RISK !! +# This uses an UNOFFICIAL reverse-engineered API. +# Excessive usage may trigger GitHub account restrictions. +# CCS provides NO WARRANTY and accepts NO RESPONSIBILITY for consequences. +# +# Setup: npx copilot-api auth (authenticate with GitHub) +# Usage: ccs copilot (switch to copilot profile) +# +# Models: claude-sonnet-4.5, claude-opus-4.5, gpt-5.1, gemini-2.5-pro +# Account types: individual, business, enterprise +# ---------------------------------------------------------------------------- +copilot: + enabled: false + auto_start: false + port: 4141 + account_type: individual + rate_limit: null + wait_on_limit: true + model: gpt-4.1 +# ---------------------------------------------------------------------------- +# Global Environment Variables: Injected into all non-Claude subscription profiles +# These env vars disable telemetry/reporting for third-party providers. +# Configure via Dashboard (`ccs config`) > Global Env tab. +# +# Default variables: +# DISABLE_BUG_COMMAND: Disables /bug command (not supported by proxy) +# DISABLE_ERROR_REPORTING: Disables error reporting to Anthropic +# DISABLE_TELEMETRY: Disables usage telemetry +# ---------------------------------------------------------------------------- +global_env: + enabled: true + env: + DISABLE_BUG_COMMAND: "1" + DISABLE_ERROR_REPORTING: "1" + DISABLE_TELEMETRY: "1" diff --git a/config/ccs/default.nix b/config/ccs/default.nix new file mode 100644 index 000000000..e9bb3d63a --- /dev/null +++ b/config/ccs/default.nix @@ -0,0 +1,31 @@ +{ + config, + lib, + pkgs, + ... +}: +let + dotfilesDir = "${config.home.homeDirectory}/dotfiles"; + + # Hydration script for CCS provider settings + hydrateScript = pkgs.replaceVars ./hydrate.sh { + sed = "${pkgs.gnused}/bin/sed"; + }; +in +{ + # CCS (Claude Code Switcher) account registry + # Maps OAuth token files to registered accounts for cliproxy providers + # Token files are stored separately in ~/.ccs/cliproxy/auth/ (not managed here as they contain secrets) + # Note: Using mkOutOfStoreSymlink with absolute path so CCS can write to the file (updates lastUsedAt) + home.file.".ccs/cliproxy/accounts.json" = { + source = config.lib.file.mkOutOfStoreSymlink "${dotfilesDir}/config/ccs/accounts.json"; + force = true; + }; + + # Hydrate CCS settings templates with secrets from .env + # ANTHROPIC_AUTH_TOKEN is substituted from CLIPROXY_API_KEY at activation time + # Processes all *.settings.template.json files in config/ccs/ + home.activation.hydrateCcsSettings = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + ${pkgs.bash}/bin/bash ${hydrateScript} || true + ''; +} diff --git a/config/ccs/gemini.settings.template.json b/config/ccs/gemini.settings.template.json new file mode 100644 index 000000000..7c3631e34 --- /dev/null +++ b/config/ccs/gemini.settings.template.json @@ -0,0 +1,10 @@ +{ + "env": { + "ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/gemini", + "ANTHROPIC_AUTH_TOKEN": "__CLIPROXY_API_KEY__", + "ANTHROPIC_MODEL": "gemini-2.5-pro", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "gemini-2.5-pro", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "gemini-2.5-pro", + "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gemini-2.5-flash" + } +} diff --git a/config/ccs/hydrate.sh b/config/ccs/hydrate.sh new file mode 100644 index 000000000..f969211c2 --- /dev/null +++ b/config/ccs/hydrate.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# Hydrate CCS provider settings and config from templates +# Substitutes __CLIPROXY_API_KEY__ placeholder with actual API key from .env +# shellcheck source=/dev/null +set -euo pipefail + +DOTFILES_DIR="${HOME}/dotfiles" +CCS_DIR="${HOME}/.ccs" +TEMPLATE_DIR="${DOTFILES_DIR}/config/ccs" +ENV_FILE="${DOTFILES_DIR}/.env" + +# Load environment variables +if [ -f "$ENV_FILE" ]; then + set -a + . "$ENV_FILE" + set +a +fi + +# Check for required API key +if [ -z "${CLIPROXY_API_KEY:-}" ]; then + echo "Warning: CLIPROXY_API_KEY not set in .env, skipping CCS hydration" >&2 + exit 0 +fi + +mkdir -p "$CCS_DIR" + +# Hydrate config.yaml +CONFIG_TEMPLATE="${TEMPLATE_DIR}/config.template.yaml" +if [ -f "$CONFIG_TEMPLATE" ]; then + @sed@ \ + -e "s|__CLIPROXY_API_KEY__|${CLIPROXY_API_KEY}|g" \ + "$CONFIG_TEMPLATE" >"${CCS_DIR}/config.yaml" + echo "Hydrated CCS config.yaml" >&2 +fi + +# Process all provider settings templates +for template in "$TEMPLATE_DIR"/*.settings.template.json; do + [ -f "$template" ] || continue + + # Extract provider name: foo.settings.template.json -> foo + filename=$(basename "$template") + provider="${filename%.settings.template.json}" + output="${CCS_DIR}/${provider}.settings.json" + + # Substitute placeholder and write output + @sed@ \ + -e "s|__CLIPROXY_API_KEY__|${CLIPROXY_API_KEY}|g" \ + "$template" >"$output" + + echo "Hydrated CCS ${provider}.settings.json" >&2 +done diff --git a/config/cliproxyapi/config.yaml b/config/cliproxyapi/config.yaml index 5beeffb52..e848b7235 100644 --- a/config/cliproxyapi/config.yaml +++ b/config/cliproxyapi/config.yaml @@ -12,7 +12,8 @@ remote-management: # Disable the bundled management control panel asset download and HTTP route when true. disable-control-panel: false # Authentication directory (supports ~ for home directory). If you use Windows, please set the directory like this: `C:/cli-proxy-api/` -auth-dir: "~/.cli-proxy-api" +# Use objectstore/auths subdirectory to avoid duplicate scanning (root-level files are legacy) +auth-dir: "~/.cli-proxy-api/objectstore/auths" # API keys for client authentication (optional - leave commented for open access) # api-keys: # - "__CLIPROXY_API_KEY__" diff --git a/config/default.nix b/config/default.nix index a6fec88ca..d760a017d 100644 --- a/config/default.nix +++ b/config/default.nix @@ -1,5 +1,6 @@ [ ./amp + ./ccs ./cliproxyapi ./codex ./crush diff --git a/home-manager/services/cliproxyapi/scripts/start.sh b/home-manager/services/cliproxyapi/scripts/start.sh index d5e65993d..88522333b 100755 --- a/home-manager/services/cliproxyapi/scripts/start.sh +++ b/home-manager/services/cliproxyapi/scripts/start.sh @@ -84,6 +84,16 @@ if [ -f "$TEMPLATE" ]; then -e "s|^# - \"__CLIPROXY_API_KEY__\"| - \"${CLIPROXY_API_KEY}\"|" \ "$CONFIG" fi + + # Disable AMP on Linux (causes routing issues with antigravity provider) + if [ "$(uname)" = "Linux" ]; then + @sed@ -i \ + -e "s|^ampcode:|# ampcode:|" \ + -e "s|^ upstream-url:|# upstream-url:|" \ + -e "s|^ upstream-api-key:|# upstream-api-key:|" \ + -e "s|^ restrict-management-to-localhost:|# restrict-management-to-localhost:|" \ + "$CONFIG" + fi fi cd "$CONFIG_DIR" diff --git a/spec/coverage_spec.sh b/spec/coverage_spec.sh index 95d6a087b..a8bf61c3a 100644 --- a/spec/coverage_spec.sh +++ b/spec/coverage_spec.sh @@ -105,7 +105,8 @@ Describe 'no shell scripts are missing from coverage list' It 'covers all non-spec shell scripts in the repository' # List of all shell scripts that should have tests # Update this list when adding new shell scripts -covered_scripts="config/claude/notify.sh +covered_scripts="config/ccs/hydrate.sh +config/claude/notify.sh config/claude/pushover.sh config/claude/security.sh config/claude/statusline-git.sh