ccproxy-api 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccproxy/__init__.py +4 -0
- ccproxy/__main__.py +7 -0
- ccproxy/_version.py +21 -0
- ccproxy/adapters/__init__.py +11 -0
- ccproxy/adapters/base.py +80 -0
- ccproxy/adapters/openai/__init__.py +43 -0
- ccproxy/adapters/openai/adapter.py +915 -0
- ccproxy/adapters/openai/models.py +412 -0
- ccproxy/adapters/openai/streaming.py +449 -0
- ccproxy/api/__init__.py +28 -0
- ccproxy/api/app.py +225 -0
- ccproxy/api/dependencies.py +140 -0
- ccproxy/api/middleware/__init__.py +11 -0
- ccproxy/api/middleware/auth.py +0 -0
- ccproxy/api/middleware/cors.py +55 -0
- ccproxy/api/middleware/errors.py +703 -0
- ccproxy/api/middleware/headers.py +51 -0
- ccproxy/api/middleware/logging.py +175 -0
- ccproxy/api/middleware/request_id.py +69 -0
- ccproxy/api/middleware/server_header.py +62 -0
- ccproxy/api/responses.py +84 -0
- ccproxy/api/routes/__init__.py +16 -0
- ccproxy/api/routes/claude.py +181 -0
- ccproxy/api/routes/health.py +489 -0
- ccproxy/api/routes/metrics.py +1033 -0
- ccproxy/api/routes/proxy.py +238 -0
- ccproxy/auth/__init__.py +75 -0
- ccproxy/auth/bearer.py +68 -0
- ccproxy/auth/credentials_adapter.py +93 -0
- ccproxy/auth/dependencies.py +229 -0
- ccproxy/auth/exceptions.py +79 -0
- ccproxy/auth/manager.py +102 -0
- ccproxy/auth/models.py +118 -0
- ccproxy/auth/oauth/__init__.py +26 -0
- ccproxy/auth/oauth/models.py +49 -0
- ccproxy/auth/oauth/routes.py +396 -0
- ccproxy/auth/oauth/storage.py +0 -0
- ccproxy/auth/storage/__init__.py +12 -0
- ccproxy/auth/storage/base.py +57 -0
- ccproxy/auth/storage/json_file.py +159 -0
- ccproxy/auth/storage/keyring.py +192 -0
- ccproxy/claude_sdk/__init__.py +20 -0
- ccproxy/claude_sdk/client.py +169 -0
- ccproxy/claude_sdk/converter.py +331 -0
- ccproxy/claude_sdk/options.py +120 -0
- ccproxy/cli/__init__.py +14 -0
- ccproxy/cli/commands/__init__.py +8 -0
- ccproxy/cli/commands/auth.py +553 -0
- ccproxy/cli/commands/config/__init__.py +14 -0
- ccproxy/cli/commands/config/commands.py +766 -0
- ccproxy/cli/commands/config/schema_commands.py +119 -0
- ccproxy/cli/commands/serve.py +630 -0
- ccproxy/cli/docker/__init__.py +34 -0
- ccproxy/cli/docker/adapter_factory.py +157 -0
- ccproxy/cli/docker/params.py +278 -0
- ccproxy/cli/helpers.py +144 -0
- ccproxy/cli/main.py +193 -0
- ccproxy/cli/options/__init__.py +14 -0
- ccproxy/cli/options/claude_options.py +216 -0
- ccproxy/cli/options/core_options.py +40 -0
- ccproxy/cli/options/security_options.py +48 -0
- ccproxy/cli/options/server_options.py +117 -0
- ccproxy/config/__init__.py +40 -0
- ccproxy/config/auth.py +154 -0
- ccproxy/config/claude.py +124 -0
- ccproxy/config/cors.py +79 -0
- ccproxy/config/discovery.py +87 -0
- ccproxy/config/docker_settings.py +265 -0
- ccproxy/config/loader.py +108 -0
- ccproxy/config/observability.py +158 -0
- ccproxy/config/pricing.py +88 -0
- ccproxy/config/reverse_proxy.py +31 -0
- ccproxy/config/scheduler.py +89 -0
- ccproxy/config/security.py +14 -0
- ccproxy/config/server.py +81 -0
- ccproxy/config/settings.py +534 -0
- ccproxy/config/validators.py +231 -0
- ccproxy/core/__init__.py +274 -0
- ccproxy/core/async_utils.py +675 -0
- ccproxy/core/constants.py +97 -0
- ccproxy/core/errors.py +256 -0
- ccproxy/core/http.py +328 -0
- ccproxy/core/http_transformers.py +428 -0
- ccproxy/core/interfaces.py +247 -0
- ccproxy/core/logging.py +189 -0
- ccproxy/core/middleware.py +114 -0
- ccproxy/core/proxy.py +143 -0
- ccproxy/core/system.py +38 -0
- ccproxy/core/transformers.py +259 -0
- ccproxy/core/types.py +129 -0
- ccproxy/core/validators.py +288 -0
- ccproxy/docker/__init__.py +67 -0
- ccproxy/docker/adapter.py +588 -0
- ccproxy/docker/docker_path.py +207 -0
- ccproxy/docker/middleware.py +103 -0
- ccproxy/docker/models.py +228 -0
- ccproxy/docker/protocol.py +192 -0
- ccproxy/docker/stream_process.py +264 -0
- ccproxy/docker/validators.py +173 -0
- ccproxy/models/__init__.py +123 -0
- ccproxy/models/errors.py +42 -0
- ccproxy/models/messages.py +243 -0
- ccproxy/models/requests.py +85 -0
- ccproxy/models/responses.py +227 -0
- ccproxy/models/types.py +102 -0
- ccproxy/observability/__init__.py +51 -0
- ccproxy/observability/access_logger.py +400 -0
- ccproxy/observability/context.py +447 -0
- ccproxy/observability/metrics.py +539 -0
- ccproxy/observability/pushgateway.py +366 -0
- ccproxy/observability/sse_events.py +303 -0
- ccproxy/observability/stats_printer.py +755 -0
- ccproxy/observability/storage/__init__.py +1 -0
- ccproxy/observability/storage/duckdb_simple.py +665 -0
- ccproxy/observability/storage/models.py +55 -0
- ccproxy/pricing/__init__.py +19 -0
- ccproxy/pricing/cache.py +212 -0
- ccproxy/pricing/loader.py +267 -0
- ccproxy/pricing/models.py +106 -0
- ccproxy/pricing/updater.py +309 -0
- ccproxy/scheduler/__init__.py +39 -0
- ccproxy/scheduler/core.py +335 -0
- ccproxy/scheduler/exceptions.py +34 -0
- ccproxy/scheduler/manager.py +186 -0
- ccproxy/scheduler/registry.py +150 -0
- ccproxy/scheduler/tasks.py +484 -0
- ccproxy/services/__init__.py +10 -0
- ccproxy/services/claude_sdk_service.py +614 -0
- ccproxy/services/credentials/__init__.py +55 -0
- ccproxy/services/credentials/config.py +105 -0
- ccproxy/services/credentials/manager.py +562 -0
- ccproxy/services/credentials/oauth_client.py +482 -0
- ccproxy/services/proxy_service.py +1536 -0
- ccproxy/static/.keep +0 -0
- ccproxy/testing/__init__.py +34 -0
- ccproxy/testing/config.py +148 -0
- ccproxy/testing/content_generation.py +197 -0
- ccproxy/testing/mock_responses.py +262 -0
- ccproxy/testing/response_handlers.py +161 -0
- ccproxy/testing/scenarios.py +241 -0
- ccproxy/utils/__init__.py +6 -0
- ccproxy/utils/cost_calculator.py +210 -0
- ccproxy/utils/streaming_metrics.py +199 -0
- ccproxy_api-0.1.0.dist-info/METADATA +253 -0
- ccproxy_api-0.1.0.dist-info/RECORD +148 -0
- ccproxy_api-0.1.0.dist-info/WHEEL +4 -0
- ccproxy_api-0.1.0.dist-info/entry_points.txt +2 -0
- ccproxy_api-0.1.0.dist-info/licenses/LICENSE +21 -0
ccproxy/cli/main.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Main entry point for CCProxy API Server."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import secrets
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Optional, cast
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from click import get_current_context
|
|
11
|
+
from structlog import get_logger
|
|
12
|
+
from typer import Typer
|
|
13
|
+
|
|
14
|
+
from ccproxy._version import __version__
|
|
15
|
+
from ccproxy.api.middleware.cors import setup_cors_middleware
|
|
16
|
+
from ccproxy.cli.helpers import (
|
|
17
|
+
get_rich_toolkit,
|
|
18
|
+
is_running_in_docker,
|
|
19
|
+
warning,
|
|
20
|
+
)
|
|
21
|
+
from ccproxy.config.settings import (
|
|
22
|
+
ConfigurationError,
|
|
23
|
+
Settings,
|
|
24
|
+
config_manager,
|
|
25
|
+
get_settings,
|
|
26
|
+
)
|
|
27
|
+
from ccproxy.core.async_utils import get_package_dir, get_root_package_name
|
|
28
|
+
from ccproxy.core.logging import setup_logging
|
|
29
|
+
from ccproxy.models.responses import (
|
|
30
|
+
PermissionToolAllowResponse,
|
|
31
|
+
PermissionToolDenyResponse,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
from .commands.auth import app as auth_app
|
|
35
|
+
from .commands.config import app as config_app
|
|
36
|
+
from .commands.serve import api, get_config_path_from_context
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def version_callback(value: bool) -> None:
|
|
40
|
+
"""Print version and exit."""
|
|
41
|
+
if value:
|
|
42
|
+
toolkit = get_rich_toolkit()
|
|
43
|
+
toolkit.print(f"ccproxy {__version__}", tag="version")
|
|
44
|
+
raise typer.Exit()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
app = typer.Typer(
|
|
48
|
+
rich_markup_mode="rich",
|
|
49
|
+
add_completion=True,
|
|
50
|
+
no_args_is_help=False,
|
|
51
|
+
pretty_exceptions_enable=False,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Logger will be configured by configuration manager
|
|
55
|
+
logger = get_logger(__name__)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Add global options
|
|
59
|
+
@app.callback()
|
|
60
|
+
def app_main(
|
|
61
|
+
ctx: typer.Context,
|
|
62
|
+
version: bool = typer.Option(
|
|
63
|
+
False,
|
|
64
|
+
"--version",
|
|
65
|
+
"-V",
|
|
66
|
+
callback=version_callback,
|
|
67
|
+
is_eager=True,
|
|
68
|
+
help="Show version and exit.",
|
|
69
|
+
),
|
|
70
|
+
config: Path | None = typer.Option(
|
|
71
|
+
None,
|
|
72
|
+
"--config",
|
|
73
|
+
"-c",
|
|
74
|
+
help="Path to configuration file (TOML, JSON, or YAML)",
|
|
75
|
+
exists=True,
|
|
76
|
+
file_okay=True,
|
|
77
|
+
dir_okay=False,
|
|
78
|
+
readable=True,
|
|
79
|
+
),
|
|
80
|
+
) -> None:
|
|
81
|
+
"""CCProxy API Server - Anthropic and OpenAI compatible interface for Claude."""
|
|
82
|
+
# Store config path for commands to use
|
|
83
|
+
ctx.ensure_object(dict)
|
|
84
|
+
ctx.obj["config_path"] = config
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# Register config command
|
|
88
|
+
app.add_typer(config_app)
|
|
89
|
+
|
|
90
|
+
# Register auth command
|
|
91
|
+
app.add_typer(auth_app)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# Register imported commands
|
|
95
|
+
app.command(name="serve")(api)
|
|
96
|
+
# Claude command removed - functionality moved to serve command
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@app.command()
|
|
100
|
+
def permission_tool(
|
|
101
|
+
tool_name: str = typer.Argument(
|
|
102
|
+
..., help="Name of the tool to check permissions for"
|
|
103
|
+
),
|
|
104
|
+
tool_input: str = typer.Argument(..., help="JSON string of the tool input"),
|
|
105
|
+
) -> None:
|
|
106
|
+
"""
|
|
107
|
+
MCP permission prompt tool for Claude Code SDK.
|
|
108
|
+
|
|
109
|
+
This tool is used by the Claude Code SDK to check permissions for tool calls.
|
|
110
|
+
It returns a JSON response indicating whether the tool call should be allowed or denied.
|
|
111
|
+
|
|
112
|
+
Response format:
|
|
113
|
+
- Allow: {"behavior": "allow", "updatedInput": {...}}
|
|
114
|
+
- Deny: {"behavior": "deny", "message": "reason"}
|
|
115
|
+
|
|
116
|
+
Examples:
|
|
117
|
+
ccproxy permission_tool "bash" '{"command": "ls -la"}'
|
|
118
|
+
ccproxy permission_tool "edit_file" '{"path": "/etc/passwd", "content": "..."}'
|
|
119
|
+
"""
|
|
120
|
+
toolkit = get_rich_toolkit()
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
# Parse the tool input JSON
|
|
124
|
+
try:
|
|
125
|
+
input_data = json.loads(tool_input)
|
|
126
|
+
except json.JSONDecodeError as e:
|
|
127
|
+
response = PermissionToolDenyResponse(message=f"Invalid JSON input: {e}")
|
|
128
|
+
toolkit.print(response.model_dump_json(by_alias=True), tag="result")
|
|
129
|
+
raise typer.Exit(1) from e
|
|
130
|
+
|
|
131
|
+
# Load settings to get permission configuration
|
|
132
|
+
settings = config_manager.load_settings(
|
|
133
|
+
config_path=get_config_path_from_context()
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Basic permission checking logic
|
|
137
|
+
# This can be extended with more sophisticated rules
|
|
138
|
+
|
|
139
|
+
# Check for potentially dangerous commands
|
|
140
|
+
dangerous_patterns = [
|
|
141
|
+
"rm -rf",
|
|
142
|
+
"sudo",
|
|
143
|
+
"passwd",
|
|
144
|
+
"chmod 777",
|
|
145
|
+
"/etc/passwd",
|
|
146
|
+
"/etc/shadow",
|
|
147
|
+
"format",
|
|
148
|
+
"mkfs",
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
# Convert input to string for pattern matching
|
|
152
|
+
input_str = json.dumps(input_data).lower()
|
|
153
|
+
|
|
154
|
+
# Check for dangerous patterns
|
|
155
|
+
for pattern in dangerous_patterns:
|
|
156
|
+
if pattern in input_str:
|
|
157
|
+
response = PermissionToolDenyResponse(
|
|
158
|
+
message=f"Tool call contains potentially dangerous pattern: {pattern}"
|
|
159
|
+
)
|
|
160
|
+
toolkit.print(response.model_dump_json(by_alias=True), tag="result")
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
# Check for specific tool restrictions
|
|
164
|
+
restricted_tools = {"exec", "system", "shell", "subprocess"}
|
|
165
|
+
|
|
166
|
+
if tool_name.lower() in restricted_tools:
|
|
167
|
+
response = PermissionToolDenyResponse(
|
|
168
|
+
message=f"Tool {tool_name} is restricted for security reasons"
|
|
169
|
+
)
|
|
170
|
+
toolkit.print(response.model_dump_json(by_alias=True), tag="result")
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
# Allow the tool call with original input
|
|
174
|
+
allow_response = PermissionToolAllowResponse(updated_input=input_data)
|
|
175
|
+
toolkit.print(allow_response.model_dump_json(by_alias=True), tag="result")
|
|
176
|
+
|
|
177
|
+
except Exception as e:
|
|
178
|
+
error_response = PermissionToolDenyResponse(
|
|
179
|
+
message=f"Error processing permission request: {e}"
|
|
180
|
+
)
|
|
181
|
+
toolkit.print(error_response.model_dump_json(by_alias=True), tag="result")
|
|
182
|
+
raise typer.Exit(1) from e
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def main() -> None:
|
|
186
|
+
"""Entry point for the CLI application."""
|
|
187
|
+
app()
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
if __name__ == "__main__":
|
|
191
|
+
import sys
|
|
192
|
+
|
|
193
|
+
sys.exit(app())
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""CLI option modules for organized command-line argument handling."""
|
|
2
|
+
|
|
3
|
+
from .claude_options import ClaudeOptions
|
|
4
|
+
from .core_options import CoreOptions
|
|
5
|
+
from .security_options import SecurityOptions
|
|
6
|
+
from .server_options import ServerOptions
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"CoreOptions",
|
|
11
|
+
"ServerOptions",
|
|
12
|
+
"ClaudeOptions",
|
|
13
|
+
"SecurityOptions",
|
|
14
|
+
]
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Claude-specific CLI options."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def validate_max_thinking_tokens(
|
|
10
|
+
ctx: typer.Context, param: typer.CallbackParam, value: int | None
|
|
11
|
+
) -> int | None:
|
|
12
|
+
"""Validate max thinking tokens."""
|
|
13
|
+
if value is None:
|
|
14
|
+
return None
|
|
15
|
+
|
|
16
|
+
if value < 0:
|
|
17
|
+
raise typer.BadParameter("Max thinking tokens must be non-negative")
|
|
18
|
+
|
|
19
|
+
return value
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def validate_max_turns(
|
|
23
|
+
ctx: typer.Context, param: typer.CallbackParam, value: int | None
|
|
24
|
+
) -> int | None:
|
|
25
|
+
"""Validate max turns."""
|
|
26
|
+
if value is None:
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
if value < 1:
|
|
30
|
+
raise typer.BadParameter("Max turns must be at least 1")
|
|
31
|
+
|
|
32
|
+
return value
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def validate_permission_mode(
|
|
36
|
+
ctx: typer.Context, param: typer.CallbackParam, value: str | None
|
|
37
|
+
) -> str | None:
|
|
38
|
+
"""Validate permission mode."""
|
|
39
|
+
if value is None:
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
valid_modes = {"default", "acceptEdits", "bypassPermissions"}
|
|
43
|
+
if value not in valid_modes:
|
|
44
|
+
raise typer.BadParameter(
|
|
45
|
+
f"Permission mode must be one of: {', '.join(valid_modes)}"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return value
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def validate_claude_cli_path(
|
|
52
|
+
ctx: typer.Context, param: typer.CallbackParam, value: str | None
|
|
53
|
+
) -> str | None:
|
|
54
|
+
"""Validate Claude CLI path."""
|
|
55
|
+
if value is None:
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
path = Path(value)
|
|
59
|
+
if not path.exists():
|
|
60
|
+
raise typer.BadParameter(f"Claude CLI path does not exist: {value}")
|
|
61
|
+
|
|
62
|
+
return value
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def validate_cwd(
|
|
66
|
+
ctx: typer.Context, param: typer.CallbackParam, value: str | None
|
|
67
|
+
) -> str | None:
|
|
68
|
+
"""Validate working directory."""
|
|
69
|
+
if value is None:
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
path = Path(value)
|
|
73
|
+
if not path.exists():
|
|
74
|
+
raise typer.BadParameter(f"Working directory does not exist: {value}")
|
|
75
|
+
if not path.is_dir():
|
|
76
|
+
raise typer.BadParameter(f"Working directory is not a directory: {value}")
|
|
77
|
+
|
|
78
|
+
return value
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def max_thinking_tokens_option() -> Any:
|
|
82
|
+
"""Max thinking tokens parameter."""
|
|
83
|
+
return typer.Option(
|
|
84
|
+
None,
|
|
85
|
+
"--max-thinking-tokens",
|
|
86
|
+
help="Maximum thinking tokens for Claude Code",
|
|
87
|
+
callback=validate_max_thinking_tokens,
|
|
88
|
+
rich_help_panel="Claude Settings",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def allowed_tools_option() -> Any:
|
|
93
|
+
"""Allowed tools parameter."""
|
|
94
|
+
return typer.Option(
|
|
95
|
+
None,
|
|
96
|
+
"--allowed-tools",
|
|
97
|
+
help="List of allowed tools (comma-separated)",
|
|
98
|
+
rich_help_panel="Claude Settings",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def disallowed_tools_option() -> Any:
|
|
103
|
+
"""Disallowed tools parameter."""
|
|
104
|
+
return typer.Option(
|
|
105
|
+
None,
|
|
106
|
+
"--disallowed-tools",
|
|
107
|
+
help="List of disallowed tools (comma-separated)",
|
|
108
|
+
rich_help_panel="Claude Settings",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def claude_cli_path_option() -> Any:
|
|
113
|
+
"""Claude CLI path parameter."""
|
|
114
|
+
return typer.Option(
|
|
115
|
+
None,
|
|
116
|
+
"--claude-cli-path",
|
|
117
|
+
help="Path to Claude CLI executable",
|
|
118
|
+
callback=validate_claude_cli_path,
|
|
119
|
+
rich_help_panel="Claude Settings",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def append_system_prompt_option() -> Any:
|
|
124
|
+
"""Append system prompt parameter."""
|
|
125
|
+
return typer.Option(
|
|
126
|
+
None,
|
|
127
|
+
"--append-system-prompt",
|
|
128
|
+
help="Additional system prompt to append",
|
|
129
|
+
rich_help_panel="Claude Settings",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def permission_mode_option() -> Any:
|
|
134
|
+
"""Permission mode parameter."""
|
|
135
|
+
return typer.Option(
|
|
136
|
+
None,
|
|
137
|
+
"--permission-mode",
|
|
138
|
+
help="Permission mode: default, acceptEdits, or bypassPermissions",
|
|
139
|
+
callback=validate_permission_mode,
|
|
140
|
+
rich_help_panel="Claude Settings",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def max_turns_option() -> Any:
|
|
145
|
+
"""Max turns parameter."""
|
|
146
|
+
return typer.Option(
|
|
147
|
+
None,
|
|
148
|
+
"--max-turns",
|
|
149
|
+
help="Maximum conversation turns",
|
|
150
|
+
callback=validate_max_turns,
|
|
151
|
+
rich_help_panel="Claude Settings",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def cwd_option() -> Any:
|
|
156
|
+
"""Working directory parameter."""
|
|
157
|
+
return typer.Option(
|
|
158
|
+
None,
|
|
159
|
+
"--cwd",
|
|
160
|
+
help="Working directory path",
|
|
161
|
+
callback=validate_cwd,
|
|
162
|
+
rich_help_panel="Claude Settings",
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def permission_prompt_tool_name_option() -> Any:
|
|
167
|
+
"""Permission prompt tool name parameter."""
|
|
168
|
+
return typer.Option(
|
|
169
|
+
None,
|
|
170
|
+
"--permission-prompt-tool-name",
|
|
171
|
+
help="Permission prompt tool name",
|
|
172
|
+
rich_help_panel="Claude Settings",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class ClaudeOptions:
|
|
177
|
+
"""Container for all Claude-related CLI options.
|
|
178
|
+
|
|
179
|
+
This class provides a convenient way to include all Claude-related
|
|
180
|
+
options in a command using typed attributes.
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
def __init__(
|
|
184
|
+
self,
|
|
185
|
+
max_thinking_tokens: int | None = None,
|
|
186
|
+
allowed_tools: str | None = None,
|
|
187
|
+
disallowed_tools: str | None = None,
|
|
188
|
+
claude_cli_path: str | None = None,
|
|
189
|
+
append_system_prompt: str | None = None,
|
|
190
|
+
permission_mode: str | None = None,
|
|
191
|
+
max_turns: int | None = None,
|
|
192
|
+
cwd: str | None = None,
|
|
193
|
+
permission_prompt_tool_name: str | None = None,
|
|
194
|
+
):
|
|
195
|
+
"""Initialize Claude options.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
max_thinking_tokens: Maximum thinking tokens for Claude Code
|
|
199
|
+
allowed_tools: List of allowed tools (comma-separated)
|
|
200
|
+
disallowed_tools: List of disallowed tools (comma-separated)
|
|
201
|
+
claude_cli_path: Path to Claude CLI executable
|
|
202
|
+
append_system_prompt: Additional system prompt to append
|
|
203
|
+
permission_mode: Permission mode
|
|
204
|
+
max_turns: Maximum conversation turns
|
|
205
|
+
cwd: Working directory path
|
|
206
|
+
permission_prompt_tool_name: Permission prompt tool name
|
|
207
|
+
"""
|
|
208
|
+
self.max_thinking_tokens = max_thinking_tokens
|
|
209
|
+
self.allowed_tools = allowed_tools
|
|
210
|
+
self.disallowed_tools = disallowed_tools
|
|
211
|
+
self.claude_cli_path = claude_cli_path
|
|
212
|
+
self.append_system_prompt = append_system_prompt
|
|
213
|
+
self.permission_mode = permission_mode
|
|
214
|
+
self.max_turns = max_turns
|
|
215
|
+
self.cwd = cwd
|
|
216
|
+
self.permission_prompt_tool_name = permission_prompt_tool_name
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Core CLI options for configuration and global settings."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def config_option() -> Any:
|
|
10
|
+
"""Configuration file parameter."""
|
|
11
|
+
return typer.Option(
|
|
12
|
+
None,
|
|
13
|
+
"--config",
|
|
14
|
+
"-c",
|
|
15
|
+
help="Path to configuration file (TOML, JSON, or YAML)",
|
|
16
|
+
exists=True,
|
|
17
|
+
file_okay=True,
|
|
18
|
+
dir_okay=False,
|
|
19
|
+
readable=True,
|
|
20
|
+
rich_help_panel="Configuration",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class CoreOptions:
|
|
25
|
+
"""Container for core CLI options.
|
|
26
|
+
|
|
27
|
+
This class provides a convenient way to include core options
|
|
28
|
+
in a command using typed attributes.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
config: Path | None = None,
|
|
34
|
+
):
|
|
35
|
+
"""Initialize core options.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
config: Path to configuration file
|
|
39
|
+
"""
|
|
40
|
+
self.config = config
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Security-related CLI options."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def validate_auth_token(
|
|
9
|
+
ctx: typer.Context, param: typer.CallbackParam, value: str | None
|
|
10
|
+
) -> str | None:
|
|
11
|
+
"""Validate auth token."""
|
|
12
|
+
if value is None:
|
|
13
|
+
return None
|
|
14
|
+
|
|
15
|
+
if not value.strip():
|
|
16
|
+
raise typer.BadParameter("Auth token cannot be empty")
|
|
17
|
+
|
|
18
|
+
return value
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def auth_token_option() -> Any:
|
|
22
|
+
"""Auth token parameter."""
|
|
23
|
+
return typer.Option(
|
|
24
|
+
None,
|
|
25
|
+
"--auth-token",
|
|
26
|
+
help="Bearer token for API authentication",
|
|
27
|
+
callback=validate_auth_token,
|
|
28
|
+
rich_help_panel="Security Settings",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SecurityOptions:
|
|
33
|
+
"""Container for all security-related CLI options.
|
|
34
|
+
|
|
35
|
+
This class provides a convenient way to include all security-related
|
|
36
|
+
options in a command using typed attributes.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
auth_token: str | None = None,
|
|
42
|
+
):
|
|
43
|
+
"""Initialize security options.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
auth_token: Bearer token for API authentication
|
|
47
|
+
"""
|
|
48
|
+
self.auth_token = auth_token
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Server-related CLI options."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def validate_port(
|
|
9
|
+
ctx: typer.Context, param: typer.CallbackParam, value: int | None
|
|
10
|
+
) -> int | None:
|
|
11
|
+
"""Validate port number."""
|
|
12
|
+
if value is None:
|
|
13
|
+
return None
|
|
14
|
+
|
|
15
|
+
if value < 1 or value > 65535:
|
|
16
|
+
raise typer.BadParameter("Port must be between 1 and 65535")
|
|
17
|
+
|
|
18
|
+
return value
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def validate_log_level(
|
|
22
|
+
ctx: typer.Context, param: typer.CallbackParam, value: str | None
|
|
23
|
+
) -> str | None:
|
|
24
|
+
"""Validate log level."""
|
|
25
|
+
if value is None:
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
valid_levels = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
|
|
29
|
+
if value.upper() not in valid_levels:
|
|
30
|
+
raise typer.BadParameter(f"Log level must be one of: {', '.join(valid_levels)}")
|
|
31
|
+
|
|
32
|
+
return value.upper()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def port_option() -> Any:
|
|
36
|
+
"""Port parameter."""
|
|
37
|
+
return typer.Option(
|
|
38
|
+
None,
|
|
39
|
+
"--port",
|
|
40
|
+
"-p",
|
|
41
|
+
help="Port to run the server on",
|
|
42
|
+
callback=validate_port,
|
|
43
|
+
rich_help_panel="Server Settings",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def host_option() -> Any:
|
|
48
|
+
"""Host parameter."""
|
|
49
|
+
return typer.Option(
|
|
50
|
+
None,
|
|
51
|
+
"--host",
|
|
52
|
+
"-h",
|
|
53
|
+
help="Host to bind the server to",
|
|
54
|
+
rich_help_panel="Server Settings",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def reload_option() -> Any:
|
|
59
|
+
"""Reload parameter."""
|
|
60
|
+
return typer.Option(
|
|
61
|
+
None,
|
|
62
|
+
"--reload/--no-reload",
|
|
63
|
+
help="Enable auto-reload for development",
|
|
64
|
+
rich_help_panel="Server Settings",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def log_level_option() -> Any:
|
|
69
|
+
"""Log level parameter."""
|
|
70
|
+
return typer.Option(
|
|
71
|
+
None,
|
|
72
|
+
"--log-level",
|
|
73
|
+
help="Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)",
|
|
74
|
+
callback=validate_log_level,
|
|
75
|
+
rich_help_panel="Server Settings",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def log_file_option() -> Any:
|
|
80
|
+
"""Log file parameter."""
|
|
81
|
+
return typer.Option(
|
|
82
|
+
None,
|
|
83
|
+
"--log-file",
|
|
84
|
+
help="Path to JSON log file. If specified, logs will be written to this file in JSON format",
|
|
85
|
+
rich_help_panel="Server Settings",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class ServerOptions:
|
|
90
|
+
"""Container for all server-related CLI options.
|
|
91
|
+
|
|
92
|
+
This class provides a convenient way to include all server-related
|
|
93
|
+
options in a command using typed attributes.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
port: int | None = None,
|
|
99
|
+
host: str | None = None,
|
|
100
|
+
reload: bool | None = None,
|
|
101
|
+
log_level: str | None = None,
|
|
102
|
+
log_file: str | None = None,
|
|
103
|
+
):
|
|
104
|
+
"""Initialize server options.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
port: Port to run the server on
|
|
108
|
+
host: Host to bind the server to
|
|
109
|
+
reload: Enable auto-reload for development
|
|
110
|
+
log_level: Logging level
|
|
111
|
+
log_file: Path to JSON log file
|
|
112
|
+
"""
|
|
113
|
+
self.port = port
|
|
114
|
+
self.host = host
|
|
115
|
+
self.reload = reload
|
|
116
|
+
self.log_level = log_level
|
|
117
|
+
self.log_file = log_file
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Configuration module for Claude Proxy API Server."""
|
|
2
|
+
|
|
3
|
+
from .auth import AuthSettings, CredentialStorageSettings, OAuthSettings
|
|
4
|
+
from .docker_settings import DockerSettings
|
|
5
|
+
from .loader import ConfigLoader, load_config
|
|
6
|
+
from .reverse_proxy import ReverseProxySettings
|
|
7
|
+
from .settings import Settings, get_settings
|
|
8
|
+
from .validators import (
|
|
9
|
+
ConfigValidationError,
|
|
10
|
+
validate_config_dict,
|
|
11
|
+
validate_cors_origins,
|
|
12
|
+
validate_host,
|
|
13
|
+
validate_log_level,
|
|
14
|
+
validate_path,
|
|
15
|
+
validate_port,
|
|
16
|
+
validate_timeout,
|
|
17
|
+
validate_url,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"Settings",
|
|
23
|
+
"get_settings",
|
|
24
|
+
"AuthSettings",
|
|
25
|
+
"OAuthSettings",
|
|
26
|
+
"CredentialStorageSettings",
|
|
27
|
+
"ReverseProxySettings",
|
|
28
|
+
"DockerSettings",
|
|
29
|
+
"ConfigLoader",
|
|
30
|
+
"load_config",
|
|
31
|
+
"ConfigValidationError",
|
|
32
|
+
"validate_config_dict",
|
|
33
|
+
"validate_cors_origins",
|
|
34
|
+
"validate_host",
|
|
35
|
+
"validate_log_level",
|
|
36
|
+
"validate_path",
|
|
37
|
+
"validate_port",
|
|
38
|
+
"validate_timeout",
|
|
39
|
+
"validate_url",
|
|
40
|
+
]
|