Skip to content
Merged
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
256 changes: 237 additions & 19 deletions LICENSE

Large diffs are not rendered by default.

115 changes: 40 additions & 75 deletions README.md

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions assets/package-manager-uv.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 6 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ build-backend = "hatchling.build"

[project]
name = "free-claude-code"
version = "6.2.60"
version = "6.2.62"
description = "Local proxy connecting coding agents to OpenAI-compatible AI providers"
readme = "README.md"
license = "AGPL-3.0-only"
license-files = ["LICENSE"]
requires-python = ">=3.14.0"
dependencies = [
"anyio>=4.15.1",
Expand All @@ -32,6 +34,9 @@ dependencies = [
"Pillow>=12.0.0; sys_platform == 'win32' or sys_platform == 'darwin'",
"github-copilot-sdk==1.0.13",
"simplejson>=4.1.2",
"grpcio>=1.83.1",
"grpcio-tools>=1.81.1",
"nvidia-riva-client>=2.26.0",
]

[project.scripts]
Expand All @@ -51,11 +56,6 @@ fcc-aider = "free_claude_code.cli.launchers.aider:launch"
fcc-desktop = "free_claude_code.cli.desktop_entrypoint:launch"

[project.optional-dependencies]
voice = [
"grpcio>=1.83.1",
"grpcio-tools>=1.81.1",
"nvidia-riva-client>=2.26.0",
]
voice_local = [
"torch>=2.14.0",
"transformers>=5.17.0",
Expand Down Expand Up @@ -151,12 +151,10 @@ python-version = "3.14"

[tool.ty.analysis]
# Optional voice_local extra: torch, transformers, librosa for local whisper transcription
# Optional voice extra: nvidia-riva-client for nvidia_nim transcription provider
allowed-unresolved-imports = [
"torch",
"transformers",
"librosa",
"riva.client",
"pystray",
"PIL",
]
24 changes: 3 additions & 21 deletions scripts/install.ps1
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
param(
[switch] $VoiceNim,
[switch] $VoiceLocal,
[switch] $VoiceAll,
[string] $TorchBackend = "",
[switch] $Rtk,
[switch] $DryRun,
Expand Down Expand Up @@ -71,9 +69,7 @@ Usage: install.ps1 [options]
Installs or updates Free Claude Code and lets you choose which coding agents to install or verify.

Options:
-VoiceNim Install NVIDIA NIM voice transcription support.
-VoiceLocal Install local Whisper voice transcription support.
-VoiceAll Install all voice transcription backends.
-TorchBackend VALUE Use a uv PyTorch backend, such as cu130. Requires local voice.
-Rtk Install and configure RTK for the selected coding agents.
-DryRun Print commands without running them.
Expand Down Expand Up @@ -1398,21 +1394,7 @@ function Ensure-Uv {
}

function Get-PackageSpec {
$includeNim = $VoiceNim
$includeLocal = $VoiceLocal

if ($VoiceAll) {
$includeNim = $true
$includeLocal = $true
}

if ($includeNim -and $includeLocal) {
return "free-claude-code[voice,voice_local] @ $RepoArchiveUrl"
}
if ($includeNim) {
return "free-claude-code[voice] @ $RepoArchiveUrl"
}
if ($includeLocal) {
if ($VoiceLocal) {
return "free-claude-code[voice_local] @ $RepoArchiveUrl"
}
return "free-claude-code @ $RepoArchiveUrl"
Expand Down Expand Up @@ -1605,8 +1587,8 @@ if ($RemainingArgs.Count -gt 0) {
throw "Unknown option: $($RemainingArgs -join ' ')"
}

if ((-not [string]::IsNullOrWhiteSpace($TorchBackend)) -and (-not ($VoiceLocal -or $VoiceAll))) {
throw "-TorchBackend requires -VoiceLocal or -VoiceAll."
if ((-not [string]::IsNullOrWhiteSpace($TorchBackend)) -and (-not $VoiceLocal)) {
throw "-TorchBackend requires -VoiceLocal."
}

# Preserve the user's winning command before adding installer search paths.
Expand Down
33 changes: 3 additions & 30 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ FCC_MACOS_OWNER_FILE=".free-claude-code-owner"
FCC_COMMANDS="fcc-desktop fcc-server fcc-claude fcc-codex fcc-pi fcc-opencode fcc-cline fcc-hermes fcc-dsh fcc-grok fcc-muse fcc-aider fcc-update fcc-init free-claude-code"

dry_run=0
voice_nim=0
voice_local=0
voice_all=0
install_claude=1
install_codex=1
install_pi=1
Expand All @@ -50,9 +48,7 @@ Usage: install.sh [options]
Installs or updates Free Claude Code and lets you choose which coding agents to install or verify.

Options:
--voice-nim Install NVIDIA NIM voice transcription support.
--voice-local Install local Whisper voice transcription support.
--voice-all Install all voice transcription backends.
--torch-backend VALUE Use a uv PyTorch backend, such as cu130. Requires local voice.
--rtk Install and configure RTK for the selected coding agents.
--dry-run Print commands without running them.
Expand Down Expand Up @@ -1261,15 +1257,9 @@ ensure_uv() {
parse_args() {
while [ "$#" -gt 0 ]; do
case "$1" in
--voice-nim)
voice_nim=1
;;
--voice-local)
voice_local=1
;;
--voice-all)
voice_all=1
;;
--torch-backend)
shift
[ "$#" -gt 0 ] || fail "--torch-backend requires a value."
Expand Down Expand Up @@ -1300,30 +1290,13 @@ parse_args() {
}

validate_args() {
include_local=$voice_local
if [ "$voice_all" -eq 1 ]; then
include_local=1
fi

if [ -n "$torch_backend" ] && [ "$include_local" -ne 1 ]; then
fail "--torch-backend requires --voice-local or --voice-all."
if [ -n "$torch_backend" ] && [ "$voice_local" -ne 1 ]; then
fail "--torch-backend requires --voice-local."
fi
}

package_spec() {
include_nim=$voice_nim
include_local=$voice_local

if [ "$voice_all" -eq 1 ]; then
include_nim=1
include_local=1
fi

if [ "$include_nim" -eq 1 ] && [ "$include_local" -eq 1 ]; then
printf 'free-claude-code[voice,voice_local] @ %s' "$REPO_ARCHIVE_URL"
elif [ "$include_nim" -eq 1 ]; then
printf 'free-claude-code[voice] @ %s' "$REPO_ARCHIVE_URL"
elif [ "$include_local" -eq 1 ]; then
if [ "$voice_local" -eq 1 ]; then
printf 'free-claude-code[voice_local] @ %s' "$REPO_ARCHIVE_URL"
else
printf 'free-claude-code @ %s' "$REPO_ARCHIVE_URL"
Expand Down
24 changes: 0 additions & 24 deletions src/free_claude_code/config/admin/provider_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,38 +255,20 @@ class ProviderFieldOverride(TypedDict, total=False):
"TokenRouter OpenAI-compatible gateway API key for api.tokenrouter.com/v1."
),
},
"TOKENROUTER_BASE_URL": {
"description": (
"TokenRouter OpenAI-compatible Chat Completions base URL. "
"Defaults to https://api.tokenrouter.com/v1."
),
},
"NARAROUTE_API_KEY": {
"label": "NaraRoute API Key",
"description": (
"NaraRoute OpenAI-compatible gateway API key for router.bynara.id/v1. "
"Keys begin with sk-nry-; create one at router.bynara.id/keys."
),
},
"NARAROUTE_BASE_URL": {
"description": (
"NaraRoute OpenAI-compatible Chat Completions base URL. "
"Defaults to https://router.bynara.id/v1."
),
},
"LIGHTNING_API_KEY": {
"label": "Lightning AI API Key",
"description": (
"Lightning AI Model APIs key for the OpenAI-compatible endpoint at "
"lightning.ai/api/v1. Create one on lightning.ai under Model APIs."
),
},
"LIGHTNING_BASE_URL": {
"description": (
"Lightning AI OpenAI-compatible Chat Completions base URL. "
"Defaults to https://lightning.ai/api/v1."
),
},
"AGNES_API_KEY": {
"label": "Agnes AI API Key",
"description": (
Expand Down Expand Up @@ -315,12 +297,6 @@ class ProviderFieldOverride(TypedDict, total=False):
"hex characters; mint one at platform.experientiallabs.ai/settings/api-keys."
),
},
"EXPLABS_BASE_URL": {
"description": (
"Experiential Labs OpenAI-compatible Chat Completions base URL. "
"Defaults to https://api.experientiallabs.ai/v1."
),
},
}


Expand Down
4 changes: 0 additions & 4 deletions src/free_claude_code/config/provider_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,6 @@ def configuration_attrs(self) -> tuple[str, ...]:
credential_url="https://www.tokenrouter.com/",
credential_attr="tokenrouter_api_key",
default_base_url=TOKENROUTER_DEFAULT_BASE,
base_url_attr="tokenrouter_base_url",
proxy_attr="tokenrouter_proxy",
),
"nararoute": ProviderDescriptor(
Expand All @@ -631,7 +630,6 @@ def configuration_attrs(self) -> tuple[str, ...]:
credential_url="https://router.bynara.id/keys",
credential_attr="nararoute_api_key",
default_base_url=NARAROUTE_DEFAULT_BASE,
base_url_attr="nararoute_base_url",
proxy_attr="nararoute_proxy",
),
"poolside": ProviderDescriptor(
Expand Down Expand Up @@ -676,7 +674,6 @@ def configuration_attrs(self) -> tuple[str, ...]:
credential_url="https://lightning.ai/lightning-ai/model-apis/models",
credential_attr="lightning_api_key",
default_base_url=LIGHTNING_DEFAULT_BASE,
base_url_attr="lightning_base_url",
proxy_attr="lightning_proxy",
),
"experiential": ProviderDescriptor(
Expand All @@ -688,7 +685,6 @@ def configuration_attrs(self) -> tuple[str, ...]:
credential_url="https://platform.experientiallabs.ai/settings/api-keys",
credential_attr="experiential_api_key",
default_base_url=EXPERIENTIAL_DEFAULT_BASE,
base_url_attr="experiential_base_url",
proxy_attr="experiential_proxy",
),
"ollama_cloud": ProviderDescriptor(
Expand Down
22 changes: 1 addition & 21 deletions src/free_claude_code/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@
from .nim import NimSettings
from .provider_catalog import (
BEDROCK_DEFAULT_BASE,
EXPERIENTIAL_DEFAULT_BASE,
LIGHTNING_DEFAULT_BASE,
NARAROUTE_DEFAULT_BASE,
SUPPORTED_PROVIDER_IDS,
TOKENROUTER_DEFAULT_BASE,
)
from .reasoning import ReasoningPreference

Expand Down Expand Up @@ -173,19 +169,11 @@ class Settings(BaseModel):
tokenrouter_api_key: OptionalNonEmptyString = Field(
default=None, validation_alias="TOKENROUTER_API_KEY"
)
tokenrouter_base_url: NonEmptyString = Field(
default=TOKENROUTER_DEFAULT_BASE,
validation_alias="TOKENROUTER_BASE_URL",
)

# ==================== NaraRoute Config ====================
nararoute_api_key: OptionalNonEmptyString = Field(
default=None, validation_alias="NARAROUTE_API_KEY"
)
nararoute_base_url: NonEmptyString = Field(
default=NARAROUTE_DEFAULT_BASE,
validation_alias="NARAROUTE_BASE_URL",
)

# ==================== Poolside AI (OpenAI-compatible) ====================
poolside_api_key: OptionalNonEmptyString = Field(
Expand All @@ -201,19 +189,11 @@ class Settings(BaseModel):
lightning_api_key: OptionalNonEmptyString = Field(
default=None, validation_alias="LIGHTNING_API_KEY"
)
lightning_base_url: NonEmptyString = Field(
default=LIGHTNING_DEFAULT_BASE,
validation_alias="LIGHTNING_BASE_URL",
)

# ==================== Experiential Labs (OpenAI-compatible) ====================
experiential_api_key: OptionalNonEmptyString = Field(
default=None, validation_alias="EXPLABS_API_KEY"
)
experiential_base_url: NonEmptyString = Field(
default=EXPERIENTIAL_DEFAULT_BASE,
validation_alias="EXPLABS_BASE_URL",
)

# ==================== Fireworks AI Config ====================
fireworks_api_key: OptionalNonEmptyString = Field(
Expand Down Expand Up @@ -678,7 +658,7 @@ class Settings(BaseModel):
)
# Device: "cpu" | "cuda" | "nvidia_nim"
# - "cpu"/"cuda": local Whisper (requires voice_local extra: uv sync --extra voice_local)
# - "nvidia_nim": NVIDIA NIM Whisper API (requires voice extra: uv sync --extra voice)
# - "nvidia_nim": NVIDIA NIM Whisper API (included in the standard installation)
whisper_device: NonEmptyString = Field(
default="cpu", validation_alias="WHISPER_DEVICE"
)
Expand Down
8 changes: 1 addition & 7 deletions src/free_claude_code/providers/nvidia_nim/voice.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,7 @@ def _transcribe_sync(self, file_path: Path) -> str:
f"Supported models: {', '.join(_NIM_ASR_MODEL_MAP)}"
)
function_id, language_code = model_config
try:
import riva.client
except ImportError as exc:
raise ImportError(
"NVIDIA NIM transcription requires the voice extra. "
"Install with: uv sync --extra voice"
) from exc
import riva.client

auth = riva.client.Auth(
use_ssl=True,
Expand Down
Loading
Loading