diff --git a/config/amp/default.nix b/config/amp/default.nix new file mode 100644 index 000000000..6bb2c0af3 --- /dev/null +++ b/config/amp/default.nix @@ -0,0 +1,6 @@ +{ config, ... }: +{ + xdg.configFile."amp/settings.json" = { + source = config.lib.file.mkOutOfStoreSymlink ./settings.json; + }; +} diff --git a/config/amp/settings.json b/config/amp/settings.json new file mode 100644 index 000000000..34b9efe55 --- /dev/null +++ b/config/amp/settings.json @@ -0,0 +1,3 @@ +{ + "amp.url": "http://localhost:8317" +} diff --git a/config/cliproxyapi/config.yaml b/config/cliproxyapi/config.yaml new file mode 100644 index 000000000..99c36479f --- /dev/null +++ b/config/cliproxyapi/config.yaml @@ -0,0 +1,93 @@ +# Server port +port: 8317 +# Management API settings +remote-management: + # Whether to allow remote (non-localhost) management access. + # When false, only localhost can access management endpoints (a key is still required). + allow-remote: false + # Management key. If a plaintext value is provided here, it will be hashed on startup. + # All management requests (even from localhost) require this key. + # Leave empty to disable the Management API entirely (404 for all /v0/management routes). + secret-key: "" + # 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" +# API keys for authentication +# api-keys: +# - "your-api-key-1" +# - "your-api-key-2" + +# Enable debug logging +debug: true +# When true, write application logs to rotating files instead of stdout +logging-to-file: true +# When false, disable in-memory usage statistics aggregation +usage-statistics-enabled: true +# Proxy URL. Supports socks5/http/https protocols. Example: socks5://user:pass@192.168.1.1:1080/ +proxy-url: "" +# Number of times to retry a request. Retries will occur if the HTTP response code is 403, 408, 500, 502, 503, or 504. +request-retry: 3 +# Quota exceeded behavior +quota-exceeded: + switch-project: true # Whether to automatically switch to another project when a quota is exceeded + switch-preview-model: true # Whether to automatically switch to a preview model when a quota is exceeded +# When true, enable authentication for the WebSocket API (/v1/ws). +ws-auth: false +# AMP +ampcode: + upstream-url: "https://ampcode.com" + restrict-management-to-localhost: true +# amp-upstream-api-key: "" # Optional - use AMP_API_KEY env var or ~/.local/share/amp/secrets.json + +# Gemini API keys (preferred) +# gemini-api-key: +# - api-key: "AIzaSy...01" +# base-url: "https://generativelanguage.googleapis.com" +# headers: +# X-Custom-Header: "custom-value" +# proxy-url: "socks5://proxy.example.com:1080" +# - api-key: "AIzaSy...02" + +# Codex API keys +# codex-api-key: +# - api-key: "sk-atSM..." +# base-url: "https://www.example.com" # use the custom codex API endpoint +# headers: +# X-Custom-Header: "custom-value" +# proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override + +# Claude API keys +# claude-api-key: +# - api-key: "sk-atSM..." # use the official claude API key, no need to set the base url +# - api-key: "sk-atSM..." +# base-url: "https://www.example.com" # use the custom claude API endpoint +# headers: +# X-Custom-Header: "custom-value" +# proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override +# models: +# - name: "claude-3-5-sonnet-20241022" # upstream model name +# alias: "claude-sonnet-latest" # client alias mapped to the upstream model + +# OpenAI compatibility providers +openai-compatibility: + - name: "openrouter" + base-url: "https://openrouter.ai/api/v1" + api-keys: + - "__OPENROUTER_API_KEY__" + models: + - name: "z-ai/glm-4.6" + +# payload: # Optional payload configuration +# default: # Default rules only set parameters when they are missing in the payload. +# - models: +# - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*") +# protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex +# params: # JSON path (gjson/sjson syntax) -> value +# "generationConfig.thinkingConfig.thinkingBudget": 32768 +# override: # Override rules always set parameters, overwriting any existing values. +# - models: +# - name: "gpt-*" # Supports wildcards (e.g., "gpt-*") +# protocol: "codex" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex +# params: # JSON path (gjson/sjson syntax) -> value +# "reasoning.effort": "high" diff --git a/config/cliproxyapi/default.nix b/config/cliproxyapi/default.nix new file mode 100644 index 000000000..4f0d8e3a0 --- /dev/null +++ b/config/cliproxyapi/default.nix @@ -0,0 +1,7 @@ +{ config, ... }: +{ + # Template config - the service wrapper injects secrets and writes to config.yaml + home.file.".cli-proxy-api/config.template.yaml" = { + source = config.lib.file.mkOutOfStoreSymlink ./config.yaml; + }; +} diff --git a/config/codex/config.toml b/config/codex/config.toml index a5a216020..5382b37e4 100644 --- a/config/codex/config.toml +++ b/config/codex/config.toml @@ -8,6 +8,10 @@ notify = [ [features] web_search_request = true +[model_providers.cliproxyapi] +name = "CLIProxyAPI" +base_url = "http://localhost:8317/v1" + [model_providers.lmstudio] name = "LMStudio" base_url = "http://127.0.0.1:1234/v1" @@ -32,3 +36,7 @@ model_provider = "lmstudio" [profiles.qwen3-coder-30b] model = "qwen/qwen3-coder-30b" model_provider = "lmstudio" + +[profiles.kimi-k2] +model = "kimi-k2" +model_provider = "cliproxyapi" diff --git a/config/default.nix b/config/default.nix index 71e275753..7dc66b8d3 100644 --- a/config/default.nix +++ b/config/default.nix @@ -1,8 +1,11 @@ [ + ./amp + ./cliproxyapi ./codex ./crush ./claude ./direnv + ./factory ./ghostty ./hammerspoon ./karabiner diff --git a/config/factory/config.json b/config/factory/config.json new file mode 100644 index 000000000..cf82abcc8 --- /dev/null +++ b/config/factory/config.json @@ -0,0 +1,40 @@ +{ + "custom_models": [ + { + "model": "gemini-3-pro-preview", + "base_url": "http://127.0.0.1:8317/v1", + "api_key": "sk-dummy", + "provider": "openai" + }, + { + "model": "gpt-5.1", + "base_url": "http://127.0.0.1:8317/v1", + "api_key": "sk-dummy", + "provider": "openai" + }, + { + "model": "gpt-5.1-codex", + "base_url": "http://127.0.0.1:8317/v1", + "api_key": "sk-dummy", + "provider": "openai" + }, + { + "model": "gpt-5.1-codex-max", + "base_url": "http://127.0.0.1:8317/v1", + "api_key": "sk-dummy", + "provider": "openai" + }, + { + "model": "claude-opus-4-5-20251101", + "base_url": "http://127.0.0.1:8317/v1", + "api_key": "sk-dummy", + "provider": "anthropic" + }, + { + "model": "claude-haiku-4-5-20251001", + "base_url": "http://127.0.0.1:8317/v1", + "api_key": "sk-dummy", + "provider": "anthropic" + } + ] +} diff --git a/config/factory/default.nix b/config/factory/default.nix new file mode 100644 index 000000000..21d67e666 --- /dev/null +++ b/config/factory/default.nix @@ -0,0 +1,6 @@ +{ config, ... }: +{ + home.file.".factory/config.json" = { + source = config.lib.file.mkOutOfStoreSymlink ./config.json; + }; +} diff --git a/config/opencode/opencode.jsonc b/config/opencode/opencode.jsonc index 65dfd0aff..775e56da5 100644 --- a/config/opencode/opencode.jsonc +++ b/config/opencode/opencode.jsonc @@ -17,6 +17,39 @@ } }, "provider": { + "cliproxyapi": { + "npm": "@ai-sdk/openai-compatible", + "name": "CLIProxyAPI (local)", + "options": { + "baseURL": "http://localhost:8317/v1" + }, + "models": { + "gemini-3-pro-preview": { + "name": "Gemini 3.0 Pro (via CLIProxyAPI)" + }, + "gpt-5.1": { + "name": "GPT-5.1 (via CLIProxyAPI)" + }, + "gpt-5.1-codex": { + "name": "GPT-5.1 Codex (via CLIProxyAPI)" + }, + "gpt-5.1-codex-max": { + "name": "GPT-5.1 Codex Max (via CLIProxyAPI)" + }, + "claude-opus-4-5-20251101": { + "name": "Claude Opus 4.5 (via CLIProxyAPI)" + }, + "claude-haiku-4-5-20251001": { + "name": "Claude Haiku 4.5 (via CLIProxyAPI)" + }, + "z-ai/glm-4.6": { + "name": "GLM-4.6 (via OpenRouter)", + "options": { + "stream": false + } + } + } + }, "lmstudio": { "npm": "@ai-sdk/openai-compatible", "name": "LM Studio (local)", @@ -158,7 +191,6 @@ } } }, - "plugin": ["opencode-openai-codex-auth"], "tui": { "scroll_speed": 3 } diff --git a/home-manager/programs/bash/default.nix b/home-manager/programs/bash/default.nix index 97a511edd..26ccdc4f4 100644 --- a/home-manager/programs/bash/default.nix +++ b/home-manager/programs/bash/default.nix @@ -43,6 +43,7 @@ export PATH="$HOME/.nix-profile/bin:$PATH" export PATH="/nix/var/nix/profiles/default/bin:$PATH" export PATH="/opt/homebrew/bin:$PATH" + export PATH="/opt/homebrew/opt/postgresql@18/bin:$PATH" ''; profileExtra = '' diff --git a/home-manager/programs/fish/default.nix b/home-manager/programs/fish/default.nix index bb2a2b1ec..a29e9f73b 100644 --- a/home-manager/programs/fish/default.nix +++ b/home-manager/programs/fish/default.nix @@ -17,6 +17,7 @@ fish_add_path -p /nix/var/nix/profiles/default/bin fish_add_path -p ~/.foundry/bin fish_add_path -p /opt/homebrew/bin + fish_add_path -p /opt/homebrew/opt/postgresql@18/bin fish_add_path -p /etc/profiles/per-user/${config.home.username}/bin ''; interactiveShellInit = '' @@ -30,6 +31,7 @@ fish_add_path -p /nix/var/nix/profiles/default/bin fish_add_path -p ~/.foundry/bin fish_add_path -p /opt/homebrew/bin + fish_add_path -p /opt/homebrew/opt/postgresql@18/bin fish_add_path -p /etc/profiles/per-user/${config.home.username}/bin set -a fish_complete_path ~/.nix-profile/share/fish/completions/ ~/.nix-profile/share/fish/vendor_completions.d/ set -x FISH_HISTFILE fish diff --git a/home-manager/programs/zsh/default.nix b/home-manager/programs/zsh/default.nix index 2f55f7cdc..28add0a6b 100644 --- a/home-manager/programs/zsh/default.nix +++ b/home-manager/programs/zsh/default.nix @@ -29,6 +29,7 @@ export PATH="$HOME/.nix-profile/bin:$PATH" export PATH="/nix/var/nix/profiles/default/bin:$PATH" export PATH="/opt/homebrew/bin:$PATH" + export PATH="/opt/homebrew/opt/postgresql@18/bin:$PATH" # FNM (Fast Node Manager) configuration export FNM_DIR="$HOME/Library/Application Support/fnm" diff --git a/home-manager/services/cliproxyapi/default.nix b/home-manager/services/cliproxyapi/default.nix new file mode 100644 index 000000000..d21380c1e --- /dev/null +++ b/home-manager/services/cliproxyapi/default.nix @@ -0,0 +1,19 @@ +{ pkgs, ... }: +{ + launchd.agents.cliproxyapi = pkgs.lib.mkIf pkgs.stdenv.isDarwin { + enable = true; + config = { + ProgramArguments = [ + "${pkgs.bash}/bin/bash" + "${./start.sh}" + ]; + Environment = { + PATH = "${pkgs.lib.makeBinPath [ pkgs.gnused ]}:/opt/homebrew/bin:/usr/local/bin"; + }; + KeepAlive = true; + RunAtLoad = true; + StandardOutPath = "/tmp/cliproxyapi.log"; + StandardErrorPath = "/tmp/cliproxyapi.error.log"; + }; + }; +} diff --git a/home-manager/services/cliproxyapi/start.sh b/home-manager/services/cliproxyapi/start.sh new file mode 100755 index 000000000..d3cf15bac --- /dev/null +++ b/home-manager/services/cliproxyapi/start.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +set -euo pipefail + +CONFIG_DIR="$HOME/.cli-proxy-api" +TEMPLATE="$CONFIG_DIR/config.template.yaml" +CONFIG="$CONFIG_DIR/config.yaml" +ENV_FILE="$HOME/dotfiles/.env" + +# Source .env file to get API keys +if [ -f "$ENV_FILE" ]; then + set -a + # shellcheck source=/dev/null + source "$ENV_FILE" + set +a +fi + +# Generate config from template with secrets injected +if [ -f "$TEMPLATE" ]; then + sed "s|__OPENROUTER_API_KEY__|${OPENROUTER_API_KEY:-}|g" "$TEMPLATE" >"$CONFIG" +fi + +# Change to config dir so logs are created there +cd "$CONFIG_DIR" + +# Export S3-compatible object storage env vars (for R2 or any S3-compatible storage) +export OBJECTSTORE_ENDPOINT="${OBJECTSTORE_ENDPOINT:-${AWS_S3_ENDPOINT:-}}" +export OBJECTSTORE_BUCKET="${OBJECTSTORE_BUCKET:-${AWS_S3_BUCKET:-}}" +export OBJECTSTORE_ACCESS_KEY="${OBJECTSTORE_ACCESS_KEY:-${AWS_ACCESS_KEY_ID:-}}" +export OBJECTSTORE_SECRET_KEY="${OBJECTSTORE_SECRET_KEY:-${AWS_SECRET_ACCESS_KEY:-}}" + +# Find and exec cliproxyapi with config file +if [ -x /opt/homebrew/bin/cliproxyapi ]; then + exec /opt/homebrew/bin/cliproxyapi -config "$CONFIG" "$@" +elif [ -x /usr/local/bin/cliproxyapi ]; then + exec /usr/local/bin/cliproxyapi -config "$CONFIG" "$@" +else + echo 'cliproxyapi binary not found; install it with "brew install cliproxyapi"' >&2 + exit 1 +fi diff --git a/home-manager/services/default.nix b/home-manager/services/default.nix index 2a6a32fc7..d227270f5 100644 --- a/home-manager/services/default.nix +++ b/home-manager/services/default.nix @@ -1,5 +1,6 @@ { pkgs }: let + cliproxyapi = import ./cliproxyapi { inherit pkgs; }; codeSyncer = import ./code-syncer { inherit pkgs; }; dotfilesUpdater = import ./dotfiles-updater { inherit pkgs; }; neversslKeepalive = import ./neverssl-keepalive { inherit pkgs; }; @@ -8,6 +9,7 @@ let in [ brewUpgrader + cliproxyapi codeSyncer dotfilesUpdater neversslKeepalive diff --git a/nix-darwin/config/homebrew.nix b/nix-darwin/config/homebrew.nix index b26e38b7f..6ee1772ca 100644 --- a/nix-darwin/config/homebrew.nix +++ b/nix-darwin/config/homebrew.nix @@ -18,7 +18,9 @@ brews = [ "bun" "claude-squad" + "cliproxyapi" "cmake" + "coder" "colima" "coreutils" "ffmpeg" @@ -35,6 +37,7 @@ "pinentry-mac" "pnpm" "postgresql" + "postgresql@18" "protobuf" "reth" "sheldon"