0.99.57

Bump version to 0.99.57; add Claude CLI proxy mode

- Add aisbf/cli_mode.py: detect claude binary in PATH at startup
- Add ClaudeCliSessionManager: per-user isolated temp config dirs with
  10-minute idle cleanup and asyncio subprocess-based request proxying
- Add ClaudeProviderHandler CLI methods: _get_cli_credentials,
  _messages_to_cli_prompt, _handle_cli_streaming_request,
  _handle_cli_request, _oauth_tokens_to_cli_credentials
- Wire CLI mode check into _handle_request_with_model; falls through to
  HTTP API mode when no CLI credentials are configured
- Add 'Use Claude CLI mode' checkbox in provider config (both admin and
  user templates): derives credentials from existing OAuth2 tokens
- Add explicit CLI credentials file upload (file_type=cli_credentials);
  DB users: stored in user_oauth2_credentials; admin: path in providers.json
- Update Claude provider warning notices to scope risk to HTTP API mode
  and clarify that claude -p is permitted by Claude's terms of service
- Update CHANGELOG.md, DOCUMENTATION.md, README.md
parent 8fed50ce
......@@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.99.57] - 2026-04-23
### Added
- **Claude CLI Mode** (`aisbf/cli_mode.py`, `aisbf/providers/claude.py`)
- At startup, AISBF checks for the `claude` binary in PATH via `shutil.which`; if found, CLI proxy mode is enabled globally
- New `ClaudeCliSessionManager` class manages per-user isolated temporary config directories (`CLAUDE_CONFIG_DIR`) containing `settings.json` (onboarding bypass) and `.credentials.json`
- Session directories are reused across requests and automatically cleaned up after 10 minutes of idle time; the background cleanup task runs every 60 seconds
- Each request spawns a dedicated `asyncio.create_subprocess_exec` subprocess running `claude -p --input-format stream-json --output-format stream-json --tools "" --dangerously-skip-permissions --no-session-persistence --verbose --model <model>`; multiple parallel requests are fully non-blocking
- Streaming output is parsed from the CLI's `stream-json` format and re-emitted as OpenAI-compatible SSE chunks
- Non-streaming requests collect all `text_delta` events and return a standard `chat.completion` response
- Messages are converted to a flat text prompt (system instructions as prefix, then `Human:`/`Assistant:` turns) — no Anthropic billing-header injection needed in CLI mode
- CLI mode falls through gracefully to HTTP API mode if no CLI credentials are configured for the requesting user
- **"Use Claude CLI mode" checkbox** (provider configuration UI)
- New `claude_config.use_cli_mode` boolean field in the Claude provider configuration
- When checked, AISBF derives CLI credentials automatically from the user's existing OAuth2 tokens already stored in AISBF, converting them to the `claudeAiOauth` schema expected by the CLI (`expires_at` seconds → `expiresAt` milliseconds, `scope` string → `scopes` list)
- No separate file upload required when OAuth2 authentication has already been completed
- Available in both admin (`providers.html`) and user (`user_providers.html`) provider configuration pages, shown only when the `claude` binary is detected at startup
- **Explicit CLI credentials file upload**
- Users can upload their own `~/.claude/.credentials.json` to use a specific Claude account independently of their AISBF OAuth2 session
- Uploaded file takes priority over OAuth2-derived credentials
- Admin: file path saved in `providers.json` under `claude_config.cli_credentials_file`
- Database users (all roles): credentials JSON content stored in `user_oauth2_credentials` with `auth_type='claude_cli_credentials'`; temp file removed after content is persisted to DB
- **`ClaudeProviderHandler._oauth_tokens_to_cli_credentials()`** static method
- Converts AISBF's internal OAuth2 token dict to the `claudeAiOauth` credentials schema used by the Claude CLI
- Handles missing `scope` by defaulting to the standard five-scope set: `user:inference`, `user:file_upload`, `user:profile`, `user:mcp_servers`, `user:sessions:claude_code`
### Changed
- **Claude provider warning notices** updated in both `providers.html` and `user_providers.html`
- Warning now explicitly scopes the "unofficial client" risk to HTTP API / OAuth2 mode
- Added a green ✅ note clarifying that CLI mode uses the official `claude -p` interface, which is the intended programmatic use of the Anthropic CLI and is permitted by Claude's terms of service
## [0.99.49] - 2026-04-21
### Added
......
......@@ -6,9 +6,10 @@ AISBF is a modular proxy server for managing multiple AI provider integrations.
### Key Features
- **Multi-Provider Support**: Unified interface for Google, OpenAI, Anthropic, Claude Code (OAuth2), Ollama, Kiro (Amazon Q Developer), Kilocode (OAuth2), Codex (OAuth2), and Qwen (API Key/OAuth2)
- **Multi-Provider Support**: Unified interface for Google, OpenAI, Anthropic, Claude Code (OAuth2 or CLI), Ollama, Kiro (Amazon Q Developer), Kilocode (OAuth2), Codex (OAuth2), and Qwen (API Key/OAuth2)
- **Unified Wallet System**: Fiat wallet with crypto/PayPal/Stripe top-ups and auto top-up for subscription renewals
- **Claude OAuth2 Authentication**: Full OAuth2 PKCE flow for Claude Code with automatic token refresh, Chrome extension for remote servers, and curl_cffi TLS fingerprinting support
- **Claude CLI Mode**: When the `claude` binary is present in PATH at startup, AISBF automatically enables CLI proxy mode — requests are piped through the official Anthropic CLI (`claude -p`) instead of the HTTP API, using per-user isolated config directories with a 10-minute idle cleanup
- **Kiro-cli Support**: Full support for Amazon Q Developer CLI authentication with Device Authorization Grant
- **Kilocode OAuth2 Authentication**: OAuth2 Device Authorization Grant for Kilo Code with automatic token refresh
- **Codex OAuth2 Authentication**: OAuth2 Device Authorization Grant for OpenAI Codex with automatic token refresh and API key exchange
......@@ -519,12 +520,46 @@ AISBF automatically extracts and tracks model metadata from provider responses:
- Static model list (no dynamic model discovery)
- cache_control support for cost reduction
### Claude Code (OAuth2)
### Claude Code (OAuth2 / CLI)
AISBF supports two modes for the Claude provider, selected automatically at runtime:
#### HTTP API / OAuth2 mode (default)
- Full OAuth2 PKCE authentication flow
- Automatic token refresh with refresh token rotation
- Chrome extension for remote server OAuth2 callback interception
- Proxy-aware extension serving: automatically detects reverse proxy deployments
- Supports all Claude models with streaming, tool calling, vision, and extended thinking
- **Note**: this mode uses an unofficial client interface; use at your own risk as per Claude's terms of service
#### CLI proxy mode (requires `claude` in PATH)
- Activated automatically at startup when the `claude` binary is found in PATH
- Requests are proxied through the official Anthropic CLI using `claude -p --input-format stream-json --output-format stream-json`
- Uses the official CLI as intended by Anthropic — permitted by Claude's terms of service
- Per-user isolated temporary config directories (`CLAUDE_CONFIG_DIR`) prevent credential cross-contamination
- Idle session cleanup: temp dirs are removed after 10 minutes of inactivity; active requests always get a fresh subprocess
- Multiple parallel requests are handled concurrently via `asyncio.create_subprocess_exec`
- Credentials can be supplied in two ways:
- **"Use Claude CLI mode" checkbox**: derives credentials automatically from existing OAuth2 tokens already stored in AISBF, converting them to the `claudeAiOauth` schema expected by the CLI
- **Explicit upload**: users can upload their own `~/.claude/.credentials.json` file to use a specific account (takes priority over OAuth2-derived credentials)
- Admin (config-file user): CLI credentials file path stored in `providers.json` under `claude_config.cli_credentials_file`
- Database users (all roles): CLI credentials stored in `user_oauth2_credentials` with `auth_type='claude_cli_credentials'`
##### Credentials file schema
The CLI expects a `.credentials.json` with this structure:
```json
{
"claudeAiOauth": {
"accessToken": "sk-ant-oat01-...",
"refreshToken": "sk-ant-ort01-...",
"expiresAt": 1776940481301,
"scopes": ["user:inference", "user:file_upload", "user:profile", "user:mcp_servers", "user:sessions:claude_code"],
"subscriptionType": "pro",
"rateLimitTier": "default_claude_ai"
}
}
```
When "Use Claude CLI mode" is checked without an explicit file upload, AISBF converts the stored OAuth2 tokens to this format automatically (`expires_at` seconds → `expiresAt` milliseconds, `scope` string → `scopes` list).
### Ollama
- Uses direct HTTP API
......
......@@ -19,7 +19,8 @@ Also available via TOR for privacy-first access:
## Key Features
- **Multi-Provider Support**: Unified interface for Google, OpenAI, Anthropic, Claude Code (OAuth2), Ollama, Kiro, Kilocode, Codex, and Qwen
- **Multi-Provider Support**: Unified interface for Google, OpenAI, Anthropic, Claude Code (OAuth2 or CLI), Ollama, Kiro, Kilocode, Codex, and Qwen
- **Claude CLI Mode**: When the `claude` binary is in PATH, requests are proxied through the official Anthropic CLI (`claude -p`) — uses each user's own account, fully permitted by Claude's terms of service
- **Unified Wallet System**: Fiat wallet with crypto/PayPal/Stripe top-ups and auto top-up for subscription renewals
- **Intelligent Routing**: Weighted load balancing and AI-assisted model selection
- **Streaming Support**: Full support for streaming responses from all providers
......
......@@ -54,7 +54,7 @@ from .auth.qwen import QwenOAuth2
from .handlers import RequestHandler, RotationHandler, AutoselectHandler
from .utils import count_messages_tokens, split_messages_into_chunks, get_max_request_tokens_for_model
__version__ = "0.99.56"
__version__ = "0.99.57"
__all__ = [
# Config
"config",
......
"""
Copyleft (C) 2026 Stefy Lanza <stefy@nexlab.net>
AISBF - AI Service Broker Framework || AI Should Be Free
Claude CLI mode detection module.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import shutil
from typing import Optional
CLAUDE_CLI_MODE: bool = False
CLAUDE_CLI_PATH: Optional[str] = None
def detect_claude_cli() -> bool:
global CLAUDE_CLI_MODE, CLAUDE_CLI_PATH
path = shutil.which('claude')
if path:
CLAUDE_CLI_MODE = True
CLAUDE_CLI_PATH = path
return True
return False
This diff is collapsed.
......@@ -663,6 +663,7 @@ autoselect_handler = None
server_config = None
config = None
_initialized = False
_claude_cli_mode = False
# Cache for user-specific handlers to avoid recreating them
_user_handlers_cache = {}
......@@ -712,7 +713,7 @@ _background_tasks: set = set()
def initialize_app(custom_config_dir=None):
"""Initialize app globals. Called by startup event or main()."""
global config, request_handler, rotation_handler, autoselect_handler, server_config, _initialized
global config, request_handler, rotation_handler, autoselect_handler, server_config, _initialized, _claude_cli_mode
if _initialized:
return
......@@ -722,6 +723,16 @@ def initialize_app(custom_config_dir=None):
set_config_dir(custom_config_dir)
logger.info(f"Using custom config directory: {custom_config_dir}")
# Detect claude CLI in PATH and enable CLI proxy mode if found
import shutil as _shutil
from aisbf.cli_mode import detect_claude_cli
if detect_claude_cli():
_claude_cli_mode = True
import aisbf.cli_mode as _cli_mode_mod
logger.info(f"Claude CLI detected at {_cli_mode_mod.CLAUDE_CLI_PATH} – CLI proxy mode enabled")
else:
logger.info("Claude CLI not found in PATH – using HTTP API mode")
# Import config
from aisbf.config import config as cfg
from aisbf.handlers import RequestHandler, RotationHandler, AutoselectHandler
......@@ -4387,6 +4398,7 @@ async def dashboard_providers(request: Request):
"session": request.session,
"__version__": __version__,
"providers_json": json.dumps(providers_data),
"claude_cli_mode": _claude_cli_mode,
"success": "Configuration saved successfully! Restart server for changes to take effect." if success else None
}
)
......@@ -4401,6 +4413,7 @@ async def dashboard_providers(request: Request):
"__version__": __version__,
"user_providers_json": json.dumps(providers_data),
"user_id": current_user_id,
"claude_cli_mode": _claude_cli_mode,
"success": "Configuration saved successfully!" if success else None
}
)
......@@ -4607,6 +4620,7 @@ async def dashboard_providers_save(request: Request, config: str = Form(...)):
"session": request.session,
"__version__": __version__,
"providers_json": json.dumps(providers_data),
"claude_cli_mode": _claude_cli_mode,
"success": success_msg
}
)
......@@ -4622,6 +4636,7 @@ async def dashboard_providers_save(request: Request, config: str = Form(...)):
"__version__": __version__,
"user_providers_json": json.dumps(providers_data),
"user_id": current_user_id,
"claude_cli_mode": _claude_cli_mode,
"success": success_msg
}
)
......@@ -4651,6 +4666,7 @@ async def dashboard_providers_save(request: Request, config: str = Form(...)):
"session": request.session,
"__version__": __version__,
"providers_json": json.dumps(providers_data),
"claude_cli_mode": _claude_cli_mode,
"error": f"Invalid JSON: {str(e)}"
}
)
......@@ -4667,6 +4683,7 @@ async def dashboard_providers_save(request: Request, config: str = Form(...)):
"__version__": __version__,
"user_providers_json": json.dumps(user_providers),
"user_id": current_user_id,
"claude_cli_mode": _claude_cli_mode,
"error": f"Invalid JSON: {str(e)}"
}
)
......@@ -6321,7 +6338,7 @@ async def dashboard_provider_upload(
try:
# Validate file type
allowed_types = ['credentials', 'database', 'config', 'kiro_credentials', 'claude_credentials', 'sqlite_db', 'creds_file']
allowed_types = ['credentials', 'database', 'config', 'kiro_credentials', 'claude_credentials', 'sqlite_db', 'creds_file', 'cli_credentials']
if file_type not in allowed_types:
return JSONResponse(
status_code=400,
......@@ -6486,7 +6503,7 @@ async def dashboard_provider_upload_form(
try:
# Validate file type
allowed_types = ['credentials', 'database', 'config', 'kiro_credentials', 'claude_credentials', 'sqlite_db', 'creds_file']
allowed_types = ['credentials', 'database', 'config', 'kiro_credentials', 'claude_credentials', 'sqlite_db', 'creds_file', 'cli_credentials']
if file_type not in allowed_types:
return JSONResponse(
status_code=400,
......@@ -6582,7 +6599,7 @@ async def dashboard_provider_upload_chunk(
try:
# Validate file type
allowed_types = ['credentials', 'database', 'config', 'kiro_credentials', 'claude_credentials', 'sqlite_db', 'creds_file']
allowed_types = ['credentials', 'database', 'config', 'kiro_credentials', 'claude_credentials', 'sqlite_db', 'creds_file', 'cli_credentials']
if file_type not in allowed_types:
return JSONResponse(
status_code=400,
......@@ -6636,6 +6653,28 @@ async def dashboard_provider_upload_chunk(
# Save metadata to database if not admin
if not is_config_admin:
db = DatabaseRegistry.get_config_database()
# CLI credentials for claude providers go directly into
# user_oauth2_credentials so the provider handler can read them
# without touching the filesystem at request time.
if file_type == 'cli_credentials':
try:
with open(file_path, 'r') as fh:
cli_creds_content = json.load(fh)
db.save_user_oauth2_credentials(
user_id=current_user_id,
provider_id=provider_key,
auth_type='claude_cli_credentials',
credentials={'credentials': cli_creds_content},
)
file_path.unlink(missing_ok=True)
logger.info(
f"Stored CLI credentials for user {current_user_id} "
f"provider {provider_key} in user_oauth2_credentials"
)
except Exception as exc:
logger.error(f"Failed to save CLI credentials to DB: {exc}")
else:
db.save_user_auth_file(
user_id=current_user_id,
provider_id=provider_key,
......@@ -6705,6 +6744,13 @@ async def dashboard_provider_upload_chunk(
providers[provider_key]['codex_config']['credentials_file'] = relative_path
logger.info(f"Updated providers.json: {provider_key}.codex_config.credentials_file = {relative_path}")
# For Claude CLI credentials – store path in claude_config.cli_credentials_file
elif provider_type == 'claude' and file_type == 'cli_credentials':
if 'claude_config' not in providers[provider_key]:
providers[provider_key]['claude_config'] = {}
providers[provider_key]['claude_config']['cli_credentials_file'] = relative_path
logger.info(f"Updated providers.json: {provider_key}.claude_config.cli_credentials_file = {relative_path}")
# Fallback: update top-level field
else:
providers[provider_key][file_type] = relative_path
......
......@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "aisbf"
version = "0.99.56"
version = "0.99.57"
description = "AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
readme = "README.md"
license = "GPL-3.0-or-later"
......
......@@ -49,7 +49,7 @@ class InstallCommand(_install):
setup(
name="aisbf",
version="0.99.56",
version="0.99.57",
author="AISBF Contributors",
author_email="stefy@nexlab.net",
description="AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations",
......
......@@ -64,11 +64,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<div id="new-provider-claude-warning" style="background: #ffdd57; border: 2px solid #ff6b35; border-radius: 8px; padding: 20px; margin-bottom: 15px; display: none;">
<h4 style="margin: 0 0 10px 0; color: #d63031;">⚠️ Important Notice for Claude Provider</h4>
<p style="margin: 0; color: #2d3436;">
Claude.ai policies state that unofficial clients are not allowed. By using AISBF as a client, you acknowledge that:
Claude.ai policies state that unofficial clients are not allowed. By using AISBF in <strong>HTTP API / OAuth2 mode</strong>, you acknowledge that:
<br><br>• While we do our best to mimic the claude-code CLI, using an unofficial client can lead to account suspension or cancellation
<br>• Claude.ai detects prompt patterns to identify unofficial client usage, so it may not work with complex agents and prompts
<br>• You use this software at your own risk and responsibility
<br>• We are not affiliated with Anthropic and cannot guarantee compatibility or continued functionality
<br><br><strong>CLI mode is different:</strong> when "Use Claude CLI mode" is enabled, AISBF proxies requests through the official <code>claude</code> binary using <code>claude -p</code>, which is the intended programmatic use of the official Anthropic CLI and is permitted by Claude's terms of service.
</p>
</div>
......@@ -134,6 +135,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
{% block extra_js %}
<script>
const CLAUDE_CLI_MODE = {{ 'true' if claude_cli_mode else 'false' }};
let providersData = {{ providers_json | replace("</script>", "<\\/script>") | safe }};
let expandedProviders = new Set();
......@@ -227,6 +229,41 @@ async function uploadClaudeFile(providerKey, file) {
await uploadFileChunked(providerKey, 'credentials_file', file, 'claude_config');
}
// Upload handler for Claude CLI credentials (.credentials.json from ~/.claude/)
async function uploadClaudeCliFile(providerKey, file) {
if (!file) return;
if (!expandedProviders.has(providerKey)) toggleProvider(providerKey);
const statusEl = document.getElementById(`claude-cli-upload-status-${providerKey}`);
if (!statusEl) return;
statusEl.innerHTML = '<div style="color: #4a9eff;">Uploading CLI credentials: 0%</div>';
try {
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
for (let chunkNumber = 1; chunkNumber <= totalChunks; chunkNumber++) {
const start = (chunkNumber - 1) * CHUNK_SIZE;
const chunk = file.slice(start, Math.min(start + CHUNK_SIZE, file.size));
const progress = Math.round((chunkNumber / totalChunks) * 100);
statusEl.innerHTML = `<div style="color: #4a9eff;">Uploading CLI credentials: ${progress}%</div>`;
const formData = new FormData();
formData.append('provider_key', providerKey);
formData.append('file_type', 'cli_credentials');
formData.append('file_name', file.name);
formData.append('total_size', file.size);
formData.append('chunk_number', chunkNumber);
formData.append('total_chunks', totalChunks);
formData.append('file', chunk);
const response = await fetch('/dashboard/providers/upload-auth-file/chunk', { method: 'POST', body: formData });
const result = await response.json();
if (!result.success) throw new Error(result.error || 'Upload failed');
if (result.complete) {
statusEl.innerHTML = '<div style="color: #4ade80;">CLI credentials saved to server!</div>';
return;
}
}
} catch (e) {
statusEl.innerHTML = `<div style="color: #f87171;">Upload failed: ${e.message}</div>`;
}
}
// Upload handler for Qwen credential files
async function uploadQwenFile(providerKey, file) {
await uploadFileChunked(providerKey, 'credentials_file', file, 'qwen_config');
......@@ -505,21 +542,60 @@ function renderProviderDetails(key) {
<h5 style="margin: 20px 0 10px 0; color: #8ec8ff;">Or Upload Credentials File</h5>
<div class="form-group">
<label>Upload Credentials File</label>
<label>Upload OAuth2 Credentials File</label>
<input type="file" id="claude-creds-file-${key}" accept=".json" onchange="uploadClaudeFile('${key}', this.files[0])">
<small style="color: #a0a0a0; display: block; margin-top: 5px;">Upload Claude OAuth2 credentials JSON file</small>
</div>
<div id="claude-upload-status-${key}" style="margin-top: 10px;"></div>
${CLAUDE_CLI_MODE ? `
<div style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #1e3a5f;">
<h5 style="margin: 0 0 8px 0; color: #4ade80;">Claude CLI Mode Active</h5>
<small style="color: #4ade80; display: block; margin-bottom: 14px;">
The claude CLI was detected at startup. When enabled, requests are piped
through the local claude binary instead of the HTTP API.
</small>
<div class="form-group" style="margin-bottom: 16px;">
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer;">
<input type="checkbox"
id="claude-use-cli-mode-${key}"
${claudeConfig.use_cli_mode ? 'checked' : ''}
onchange="updateClaudeConfig('${key}', 'use_cli_mode', this.checked)"
style="width: 18px; height: 18px; cursor: pointer; accent-color: #4ade80;">
<span style="color: #e0e0e0; font-weight: 500;">Use Claude CLI mode</span>
</label>
<small style="color: #a0a0a0; display: block; margin-top: 6px; padding-left: 28px;">
When checked and you have already authenticated via OAuth2 above,
your existing tokens are used automatically — no separate file upload needed.
You can also upload an explicit <code style="background:#0f2840;padding:1px 4px;border-radius:3px;">.credentials.json</code>
below to override.
</small>
</div>
<div class="form-group">
<label>Override: Upload CLI Credentials File</label>
<input type="file" id="claude-cli-creds-file-${key}" accept=".json" onchange="uploadClaudeCliFile('${key}', this.files[0])">
<small style="color: #a0a0a0; display: block; margin-top: 5px;">
Optional — upload a <code style="background:#0f2840;padding:1px 4px;border-radius:3px;">~/.claude/.credentials.json</code>
to use a specific account. Path saved in
<code style="background:#0f2840;padding:1px 4px;border-radius:3px;">claude_config.cli_credentials_file</code>.
</small>
</div>
<div id="claude-cli-upload-status-${key}" style="margin-top: 10px;"></div>
</div>
` : ''}
</div>
<div style="background: #ffdd57; border: 2px solid #ff6b35; border-radius: 8px; padding: 20px; margin-bottom: 15px;">
<h4 style="margin: 0 0 10px 0; color: #d63031;">⚠️ Important Notice</h4>
<p style="margin: 0; color: #2d3436;">
Claude.ai policies state that unofficial clients are not allowed. By using AISBF as a client, you acknowledge that:
Claude.ai policies state that unofficial clients are not allowed. By using AISBF in <strong>HTTP API / OAuth2 mode</strong>, you acknowledge that:
<br><br>• While we do our best to mimic the claude-code CLI, using an unofficial client can lead to account suspension or cancellation
<br>• Claude.ai detects prompt patterns to identify unofficial client usage, so it may not work with complex agents and prompts
<br>• You use this software at your own risk and responsibility
<br>• We are not affiliated with Anthropic and cannot guarantee compatibility or continued functionality
<br><br><strong>CLI mode is different:</strong> when "Use Claude CLI mode" is enabled, AISBF proxies requests through the official <code style="background:#e8e0b0;padding:1px 4px;border-radius:3px;">claude</code> binary using <code style="background:#e8e0b0;padding:1px 4px;border-radius:3px;">claude -p</code>, which is the intended programmatic use of the official Anthropic CLI and is permitted by Claude's terms of service.
</p>
</div>
`;
......
......@@ -64,11 +64,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<div id="new-provider-claude-warning" style="background: #ffdd57; border: 2px solid #ff6b35; border-radius: 8px; padding: 20px; margin-bottom: 15px; display: none;">
<h4 style="margin: 0 0 10px 0; color: #d63031;">⚠️ Important Notice for Claude Provider</h4>
<p style="margin: 0; color: #2d3436;">
Claude.ai policies state that unofficial clients are not allowed. By using AISBF as a client, you acknowledge that:
Claude.ai policies state that unofficial clients are not allowed. By using AISBF as a client in <strong>HTTP API / OAuth2 mode</strong>, you acknowledge that:
<br><br>• While we do our best to mimic the claude-code CLI, using an unofficial client can lead to account suspension or cancellation
<br>• Claude.ai detects prompt patterns to identify unofficial client usage, so it may not work with complex agents and prompts
<br>• You use this software at your own risk and responsibility
<br>• We are not affiliated with Anthropic and cannot guarantee compatibility or continued functionality
<br><br><strong>CLI mode is different:</strong> when "Use Claude CLI mode" is enabled, AISBF proxies requests through the official <code>claude</code> binary using <code>claude -p</code>, which is the intended programmatic use of the official Anthropic CLI and is permitted by Claude's terms of service.
</p>
</div>
......@@ -134,6 +135,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
{% block extra_js %}
<script>
const CLAUDE_CLI_MODE = {{ 'true' if claude_cli_mode else 'false' }};
// Global cache settings cache (pun intended)
let cacheSettings = [];
......@@ -353,6 +356,41 @@ async function uploadClaudeFile(providerKey, file) {
await uploadFileChunked(providerKey, 'credentials_file', file, 'claude_config');
}
// Upload handler for Claude CLI credentials file (.credentials.json from ~/.claude/)
async function uploadClaudeCliFile(providerKey, file) {
if (!file) return;
if (!expandedProviders.has(providerKey)) toggleProvider(providerKey);
const statusEl = document.getElementById(`claude-cli-upload-status-${providerKey}`);
if (!statusEl) return;
statusEl.innerHTML = '<div style="color: #4a9eff;">Uploading CLI credentials: 0%</div>';
try {
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
for (let chunkNumber = 1; chunkNumber <= totalChunks; chunkNumber++) {
const start = (chunkNumber - 1) * CHUNK_SIZE;
const chunk = file.slice(start, Math.min(start + CHUNK_SIZE, file.size));
const progress = Math.round((chunkNumber / totalChunks) * 100);
statusEl.innerHTML = `<div style="color: #4a9eff;">Uploading CLI credentials: ${progress}%</div>`;
const formData = new FormData();
formData.append('provider_key', providerKey);
formData.append('file_type', 'cli_credentials');
formData.append('file_name', file.name);
formData.append('total_size', file.size);
formData.append('chunk_number', chunkNumber);
formData.append('total_chunks', totalChunks);
formData.append('file', chunk);
const response = await fetch('/dashboard/providers/upload-auth-file/chunk', { method: 'POST', body: formData });
const result = await response.json();
if (!result.success) throw new Error(result.error || 'Upload failed');
if (result.complete) {
statusEl.innerHTML = '<div style="color: #4ade80;">CLI credentials uploaded and saved!</div>';
return;
}
}
} catch (e) {
statusEl.innerHTML = `<div style="color: #f87171;">Upload failed: ${e.message}</div>`;
}
}
// Upload handler for Qwen credential files
async function uploadQwenFile(providerKey, file) {
await uploadFileChunked(providerKey, 'credentials_file', file, 'qwen_config');
......@@ -596,23 +634,61 @@ function renderProviderDetails(key) {
<!-- Auth status will be displayed here -->
</div>
<h5 style="margin: 20px 0 10px 0; color: #8ec8ff;">Or Upload Credentials File</h5>
<h5 style="margin: 20px 0 10px 0; color: #8ec8ff;">Or Upload OAuth2 Credentials File</h5>
<div class="form-group">
<label>Upload Credentials File</label>
<label>Upload OAuth2 Credentials File</label>
<input type="file" id="claude-creds-file-${key}" accept=".json" onchange="uploadClaudeFile('${key}', this.files[0])">
<small style="color: #a0a0a0; display: block; margin-top: 5px;">Upload Claude OAuth2 credentials JSON file</small>
</div>
<div id="claude-upload-status-${key}" style="margin-top: 10px;"></div>
${CLAUDE_CLI_MODE ? `
<div style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #1e3a5f;">
<h5 style="margin: 0 0 8px 0; color: #4ade80;">Claude CLI Mode Active</h5>
<small style="color: #4ade80; display: block; margin-bottom: 14px;">
The claude CLI was detected at startup. When enabled, requests are piped
through the local claude binary instead of the HTTP API.
</small>
<div class="form-group" style="margin-bottom: 16px;">
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer;">
<input type="checkbox"
id="claude-use-cli-mode-${key}"
${claudeConfig.use_cli_mode ? 'checked' : ''}
onchange="updateClaudeConfig('${key}', 'use_cli_mode', this.checked)"
style="width: 18px; height: 18px; cursor: pointer; accent-color: #4ade80;">
<span style="color: #e0e0e0; font-weight: 500;">Use Claude CLI mode</span>
</label>
<small style="color: #a0a0a0; display: block; margin-top: 6px; padding-left: 28px;">
When checked and you have already authenticated via OAuth2 above,
your existing tokens will be used automatically — no separate file upload needed.
You can also upload an explicit <code style="background:#0f2840;padding:1px 4px;border-radius:3px;">.credentials.json</code>
below to override.
</small>
</div>
<div class="form-group">
<label>Override: Upload CLI Credentials File</label>
<input type="file" id="claude-cli-creds-file-${key}" accept=".json" onchange="uploadClaudeCliFile('${key}', this.files[0])">
<small style="color: #a0a0a0; display: block; margin-top: 5px;">
Optional — upload a <code style="background:#0f2840;padding:1px 4px;border-radius:3px;">~/.claude/.credentials.json</code>
to use a specific account instead of your OAuth2 tokens.
</small>
</div>
<div id="claude-cli-upload-status-${key}" style="margin-top: 10px;"></div>
</div>
` : ''}
</div>
<div style="background: #ffdd57; border: 2px solid #ff6b35; border-radius: 8px; padding: 20px; margin-bottom: 15px;">
<h4 style="margin: 0 0 10px 0; color: #d63031;">⚠️ Important Notice</h4>
<p style="margin: 0; color: #2d3436;">
Claude.ai policies state that unofficial clients are not allowed. By using AISBF as a client, you acknowledge that:
Claude.ai policies state that unofficial clients are not allowed. By using AISBF in <strong>HTTP API / OAuth2 mode</strong>, you acknowledge that:
<br><br>• While we do our best to mimic the claude-code CLI, using an unofficial client can lead to account suspension or cancellation
<br>• Claude.ai detects prompt patterns to identify unofficial client usage, so it may not work with complex agents and prompts
<br>• You use this software at your own risk and responsibility
<br>• We are not affiliated with Anthropic and cannot guarantee compatibility or continued functionality
<br><br><strong>CLI mode is different:</strong> when "Use Claude CLI mode" is enabled, AISBF proxies requests through the official <code style="background:#e8e0b0;padding:1px 4px;border-radius:3px;">claude</code> binary using <code style="background:#e8e0b0;padding:1px 4px;border-radius:3px;">claude -p</code>, which is the intended programmatic use of the official Anthropic CLI and is permitted by Claude's terms of service.
</p>
</div>
`;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment