diff --git a/bin/xclaude b/bin/xclaude index b22d215..e354ed7 100755 --- a/bin/xclaude +++ b/bin/xclaude @@ -5,15 +5,22 @@ import argparse import os import sys +import urllib.parse env = os.environ def die(msg): - print(msg, file=sys.stderr) + print(f"Error: {msg}", file=sys.stderr) sys.exit(1) -p = argparse.ArgumentParser( + +def validate_url(url): + parsed = urllib.parse.urlparse(url) + if parsed.scheme not in ("http", "https"): + die(f"invalid URL scheme '{parsed.scheme}' in --url (expected http or https)") + +parser = argparse.ArgumentParser( prog="xclaude", description="Run Claude Code with any OpenAI-compatible API backend.", epilog="""\ @@ -26,6 +33,8 @@ If neither URL nor token is set, defaults to Ollama: OLLAMA_HOST - Ollama server address (default: nvidia.hell), or use --host OLLAMA_MODEL - Ollama model (default: glm-5:cloud) +Any extra arguments are passed through directly to the claude CLI. + Examples: xclaude xclaude --model qwen3-coder @@ -33,24 +42,26 @@ Examples: """, formatter_class=argparse.RawDescriptionHelpFormatter, ) -p.add_argument("--url", default=env.get("CLAUDE_URL", "")) -p.add_argument("--token", default=env.get("CLAUDE_TOKEN", "")) -p.add_argument("--model", default=env.get("CLAUDE_MODEL", "")) -p.add_argument("--host", default=env.get("OLLAMA_HOST", "nvidia.hell")) -args, rest = p.parse_known_args() +parser.add_argument("--url", default=env.get("CLAUDE_URL", "")) +parser.add_argument("--token", default=env.get("CLAUDE_TOKEN", "")) +parser.add_argument("--model", default=env.get("CLAUDE_MODEL", "")) +parser.add_argument("--host", default=env.get("OLLAMA_HOST", "nvidia.hell")) +args, rest = parser.parse_known_args() env["DISABLE_TELEMETRY"] = "1" is_external_api = bool(args.url or args.token) if is_external_api: if not args.url: - die("Warning: no URL specified") + die("no URL specified (--url or CLAUDE_URL)") + validate_url(args.url) if not args.token: - die("Warning: no token specified") + die("no token specified (--token or CLAUDE_TOKEN)") if not args.model: - die("Warning: no model specified") + die("no model specified (--model or CLAUDE_MODEL)") env["ANTHROPIC_BASE_URL"] = args.url env["ANTHROPIC_API_KEY"] = args.token + # Remove Ollama auth token if switching to external API env.pop("ANTHROPIC_AUTH_TOKEN", None) model = args.model else: @@ -58,7 +69,7 @@ else: model = args.model or env.get("OLLAMA_MODEL", "glm-5:cloud") env["ANTHROPIC_BASE_URL"] = f"http://{host}:11434" env["ANTHROPIC_AUTH_TOKEN"] = "ollama" - env["ANTHROPIC_API_KEY"] = "" + env.pop("ANTHROPIC_API_KEY", None) cmd = ["claude", "--model", model] + rest os.execvp("claude", cmd)