free-claude-code 6.2.64__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.
- free_claude_code/__init__.py +1 -0
- free_claude_code/api/__init__.py +1 -0
- free_claude_code/api/admin_cache.py +49 -0
- free_claude_code/api/admin_routes.py +501 -0
- free_claude_code/api/admin_security.py +67 -0
- free_claude_code/api/admin_static/admin.css +980 -0
- free_claude_code/api/admin_static/admin.js +1934 -0
- free_claude_code/api/admin_static/code_sessions.css +109 -0
- free_claude_code/api/admin_static/code_sessions.js +1693 -0
- free_claude_code/api/admin_static/form_controls.js +15 -0
- free_claude_code/api/admin_static/index.html +201 -0
- free_claude_code/api/admin_static/model_combobox.js +194 -0
- free_claude_code/api/admin_static/providers/LICENSE-llamacpp.txt +21 -0
- free_claude_code/api/admin_static/providers/LICENSE-lobe-icons.txt +21 -0
- free_claude_code/api/admin_static/providers/SOURCES.md +56 -0
- free_claude_code/api/admin_static/providers/agnesai.svg +1 -0
- free_claude_code/api/admin_static/providers/azure-color.svg +1 -0
- free_claude_code/api/admin_static/providers/bedrock-color.svg +1 -0
- free_claude_code/api/admin_static/providers/cerebras-color.svg +1 -0
- free_claude_code/api/admin_static/providers/chutes.png +0 -0
- free_claude_code/api/admin_static/providers/cline.svg +1 -0
- free_claude_code/api/admin_static/providers/cloudflare-color.svg +1 -0
- free_claude_code/api/admin_static/providers/cohere-color.svg +1 -0
- free_claude_code/api/admin_static/providers/deepinfra-color.svg +1 -0
- free_claude_code/api/admin_static/providers/deepseek-color.svg +1 -0
- free_claude_code/api/admin_static/providers/experiential.svg +9 -0
- free_claude_code/api/admin_static/providers/featherless-color.svg +1 -0
- free_claude_code/api/admin_static/providers/fireworks-color.svg +1 -0
- free_claude_code/api/admin_static/providers/gemini-color.svg +1 -0
- free_claude_code/api/admin_static/providers/githubcopilot.svg +1 -0
- free_claude_code/api/admin_static/providers/groq.svg +1 -0
- free_claude_code/api/admin_static/providers/huggingface-color.svg +1 -0
- free_claude_code/api/admin_static/providers/kilocode.svg +1 -0
- free_claude_code/api/admin_static/providers/kimi-color.svg +1 -0
- free_claude_code/api/admin_static/providers/lightning.png +0 -0
- free_claude_code/api/admin_static/providers/llamacpp.svg +77 -0
- free_claude_code/api/admin_static/providers/llm7.png +0 -0
- free_claude_code/api/admin_static/providers/lmstudio.svg +1 -0
- free_claude_code/api/admin_static/providers/minimax-color.svg +1 -0
- free_claude_code/api/admin_static/providers/mistral-color.svg +1 -0
- free_claude_code/api/admin_static/providers/nararoute.png +0 -0
- free_claude_code/api/admin_static/providers/nebius.svg +1 -0
- free_claude_code/api/admin_static/providers/novita-color.svg +1 -0
- free_claude_code/api/admin_static/providers/nvidia-color.svg +1 -0
- free_claude_code/api/admin_static/providers/ollama.svg +1 -0
- free_claude_code/api/admin_static/providers/openai.svg +1 -0
- free_claude_code/api/admin_static/providers/opencode.svg +1 -0
- free_claude_code/api/admin_static/providers/openrouter.svg +1 -0
- free_claude_code/api/admin_static/providers/poolside-color.svg +1 -0
- free_claude_code/api/admin_static/providers/qwen-color.svg +1 -0
- free_claude_code/api/admin_static/providers/sambanova-color.svg +1 -0
- free_claude_code/api/admin_static/providers/scaleway.svg +11 -0
- free_claude_code/api/admin_static/providers/siliconcloud-color.svg +1 -0
- free_claude_code/api/admin_static/providers/together-color.svg +1 -0
- free_claude_code/api/admin_static/providers/tokenrouter.png +0 -0
- free_claude_code/api/admin_static/providers/vercel.svg +1 -0
- free_claude_code/api/admin_static/providers/vertexai-color.svg +1 -0
- free_claude_code/api/admin_static/providers/wafer.png +0 -0
- free_claude_code/api/admin_static/providers/wandb.png +0 -0
- free_claude_code/api/admin_static/providers/xai.svg +1 -0
- free_claude_code/api/admin_static/providers/zai.svg +1 -0
- free_claude_code/api/admin_static/providers/zenmux.svg +1 -0
- free_claude_code/api/admin_static/session_layout.css +552 -0
- free_claude_code/api/admin_static/session_ui.js +202 -0
- free_claude_code/api/app.py +149 -0
- free_claude_code/api/code_sessions_routes.py +386 -0
- free_claude_code/api/command_utils.py +164 -0
- free_claude_code/api/dependencies.py +128 -0
- free_claude_code/api/detection.py +185 -0
- free_claude_code/api/handlers/__init__.py +7 -0
- free_claude_code/api/handlers/classifier_response.py +66 -0
- free_claude_code/api/handlers/messages.py +359 -0
- free_claude_code/api/handlers/responses.py +163 -0
- free_claude_code/api/handlers/token_count.py +87 -0
- free_claude_code/api/markdown.py +71 -0
- free_claude_code/api/model_catalog.py +282 -0
- free_claude_code/api/optimization_handlers.py +162 -0
- free_claude_code/api/ports.py +97 -0
- free_claude_code/api/request_errors.py +100 -0
- free_claude_code/api/request_ids.py +100 -0
- free_claude_code/api/request_lifetime.py +130 -0
- free_claude_code/api/response_streams.py +422 -0
- free_claude_code/api/routes.py +269 -0
- free_claude_code/api/validation_log.py +46 -0
- free_claude_code/application/__init__.py +1 -0
- free_claude_code/application/code_sessions/__init__.py +33 -0
- free_claude_code/application/code_sessions/models.py +227 -0
- free_claude_code/application/code_sessions/ports.py +197 -0
- free_claude_code/application/code_sessions/service.py +1123 -0
- free_claude_code/application/code_sessions/state.py +659 -0
- free_claude_code/application/connected_accounts.py +96 -0
- free_claude_code/application/errors.py +41 -0
- free_claude_code/application/execution.py +467 -0
- free_claude_code/application/model_catalog.py +95 -0
- free_claude_code/application/model_metadata.py +26 -0
- free_claude_code/application/ports.py +127 -0
- free_claude_code/application/readiness.py +43 -0
- free_claude_code/application/reasoning.py +134 -0
- free_claude_code/application/routing.py +291 -0
- free_claude_code/application/session_events.py +140 -0
- free_claude_code/application/web_tools/__init__.py +1 -0
- free_claude_code/application/web_tools/ports.py +35 -0
- free_claude_code/application/web_tools/request.py +348 -0
- free_claude_code/application/web_tools/service.py +481 -0
- free_claude_code/assets/app-icon.icns +0 -0
- free_claude_code/assets/app-icon.ico +0 -0
- free_claude_code/assets/app-icon.png +0 -0
- free_claude_code/assets/app-icon.svg +14 -0
- free_claude_code/cli/__init__.py +5 -0
- free_claude_code/cli/commands.py +379 -0
- free_claude_code/cli/desktop.py +156 -0
- free_claude_code/cli/desktop_assets.py +31 -0
- free_claude_code/cli/desktop_entrypoint.py +29 -0
- free_claude_code/cli/desktop_tray.py +67 -0
- free_claude_code/cli/entrypoints.py +25 -0
- free_claude_code/cli/launchers/__init__.py +1 -0
- free_claude_code/cli/launchers/aider.py +53 -0
- free_claude_code/cli/launchers/aider_config.py +80 -0
- free_claude_code/cli/launchers/catalog_http.py +134 -0
- free_claude_code/cli/launchers/claude.py +34 -0
- free_claude_code/cli/launchers/cline.py +60 -0
- free_claude_code/cli/launchers/cline_config.py +121 -0
- free_claude_code/cli/launchers/codex.py +47 -0
- free_claude_code/cli/launchers/common.py +103 -0
- free_claude_code/cli/launchers/dsh.py +69 -0
- free_claude_code/cli/launchers/dsh_config.py +124 -0
- free_claude_code/cli/launchers/grok.py +116 -0
- free_claude_code/cli/launchers/hermes.py +85 -0
- free_claude_code/cli/launchers/hermes_config.py +134 -0
- free_claude_code/cli/launchers/muse.py +68 -0
- free_claude_code/cli/launchers/opencode.py +83 -0
- free_claude_code/cli/launchers/opencode_config.py +107 -0
- free_claude_code/cli/launchers/pi.py +70 -0
- free_claude_code/cli/launchers/pi_extension.ts +190 -0
- free_claude_code/cli/launchers/runner.py +155 -0
- free_claude_code/cli/local_http.py +12 -0
- free_claude_code/cli/managed/__init__.py +6 -0
- free_claude_code/cli/managed/claude.py +225 -0
- free_claude_code/cli/managed/diagnostics.py +47 -0
- free_claude_code/cli/managed/manager.py +207 -0
- free_claude_code/cli/managed/session.py +293 -0
- free_claude_code/cli/process_registry.py +76 -0
- free_claude_code/cli/server_socket.py +71 -0
- free_claude_code/cli/uvicorn_server.py +41 -0
- free_claude_code/config/__init__.py +10 -0
- free_claude_code/config/admin/__init__.py +1 -0
- free_claude_code/config/admin/manifest.py +610 -0
- free_claude_code/config/admin/persistence.py +202 -0
- free_claude_code/config/admin/provider_manifest.py +443 -0
- free_claude_code/config/admin/specs.py +77 -0
- free_claude_code/config/admin/state.py +18 -0
- free_claude_code/config/admin/status.py +116 -0
- free_claude_code/config/admin/validation.py +40 -0
- free_claude_code/config/admin/values.py +115 -0
- free_claude_code/config/constants.py +9 -0
- free_claude_code/config/env_files.py +74 -0
- free_claude_code/config/env_migrations.py +457 -0
- free_claude_code/config/loader.py +196 -0
- free_claude_code/config/logging_config.py +197 -0
- free_claude_code/config/model_refs.py +118 -0
- free_claude_code/config/nim.py +121 -0
- free_claude_code/config/paths.py +104 -0
- free_claude_code/config/provider_catalog.py +746 -0
- free_claude_code/config/provider_proxies.py +44 -0
- free_claude_code/config/reasoning.py +24 -0
- free_claude_code/config/server_urls.py +55 -0
- free_claude_code/config/settings.py +812 -0
- free_claude_code/core/__init__.py +15 -0
- free_claude_code/core/anthropic/__init__.py +99 -0
- free_claude_code/core/anthropic/content.py +31 -0
- free_claude_code/core/anthropic/conversion.py +876 -0
- free_claude_code/core/anthropic/errors.py +97 -0
- free_claude_code/core/anthropic/image_sources.py +77 -0
- free_claude_code/core/anthropic/models.py +193 -0
- free_claude_code/core/anthropic/native.py +184 -0
- free_claude_code/core/anthropic/native_stream.py +306 -0
- free_claude_code/core/anthropic/request_serialization.py +57 -0
- free_claude_code/core/anthropic/request_snapshot.py +35 -0
- free_claude_code/core/anthropic/server_tool_sse.py +234 -0
- free_claude_code/core/anthropic/server_tool_types.py +9 -0
- free_claude_code/core/anthropic/sse_aggregation.py +144 -0
- free_claude_code/core/anthropic/stream_contracts.py +208 -0
- free_claude_code/core/anthropic/streaming/__init__.py +43 -0
- free_claude_code/core/anthropic/streaming/decoder.py +49 -0
- free_claude_code/core/anthropic/streaming/emitter.py +62 -0
- free_claude_code/core/anthropic/streaming/ledger.py +538 -0
- free_claude_code/core/anthropic/streaming/recovery.py +211 -0
- free_claude_code/core/anthropic/thinking.py +140 -0
- free_claude_code/core/anthropic/tokens.py +111 -0
- free_claude_code/core/anthropic/tool_results.py +63 -0
- free_claude_code/core/anthropic/tool_schema.py +94 -0
- free_claude_code/core/anthropic/tools.py +493 -0
- free_claude_code/core/anthropic/usage.py +38 -0
- free_claude_code/core/anthropic/utils.py +9 -0
- free_claude_code/core/async_iterators.py +26 -0
- free_claude_code/core/async_tasks.py +26 -0
- free_claude_code/core/data/README.md +12 -0
- free_claude_code/core/data/TIKTOKEN-LICENSE +21 -0
- free_claude_code/core/data/cl100k_base.tiktoken +100256 -0
- free_claude_code/core/diagnostics.py +319 -0
- free_claude_code/core/failures.py +50 -0
- free_claude_code/core/gateway_model_ids.py +73 -0
- free_claude_code/core/history_replay.py +561 -0
- free_claude_code/core/interprocess_lock.py +90 -0
- free_claude_code/core/json_types.py +7 -0
- free_claude_code/core/model_capabilities.py +10 -0
- free_claude_code/core/openai_chat.py +54 -0
- free_claude_code/core/openai_responses/__init__.py +90 -0
- free_claude_code/core/openai_responses/chat_request.py +747 -0
- free_claude_code/core/openai_responses/errors.py +78 -0
- free_claude_code/core/openai_responses/events.py +82 -0
- free_claude_code/core/openai_responses/ids.py +32 -0
- free_claude_code/core/openai_responses/items.py +35 -0
- free_claude_code/core/openai_responses/messages_request.py +624 -0
- free_claude_code/core/openai_responses/messages_stream.py +404 -0
- free_claude_code/core/openai_responses/models.py +33 -0
- free_claude_code/core/openai_responses/native.py +147 -0
- free_claude_code/core/openai_responses/provider_input.py +409 -0
- free_claude_code/core/openai_responses/provider_stream.py +282 -0
- free_claude_code/core/openai_responses/reasoning.py +73 -0
- free_claude_code/core/openai_responses/streaming/__init__.py +1 -0
- free_claude_code/core/openai_responses/streaming/blocks.py +36 -0
- free_claude_code/core/openai_responses/streaming/completion.py +169 -0
- free_claude_code/core/openai_responses/streaming/error_mapping.py +28 -0
- free_claude_code/core/openai_responses/streaming/event_builders.py +198 -0
- free_claude_code/core/openai_responses/streaming/ledger.py +96 -0
- free_claude_code/core/openai_responses/tokens.py +133 -0
- free_claude_code/core/openai_responses/tool_adaptation.py +828 -0
- free_claude_code/core/openai_responses/tool_search.py +204 -0
- free_claude_code/core/openai_responses/tools.py +169 -0
- free_claude_code/core/openai_tool_names.py +178 -0
- free_claude_code/core/rate_limit.py +72 -0
- free_claude_code/core/reasoning.py +136 -0
- free_claude_code/core/token_estimation.py +76 -0
- free_claude_code/core/tool_schema_patterns.py +168 -0
- free_claude_code/core/trace.py +197 -0
- free_claude_code/core/version.py +15 -0
- free_claude_code/core/web_tools.py +17 -0
- free_claude_code/core/windows_dpi.py +27 -0
- free_claude_code/harnesses/__init__.py +1 -0
- free_claude_code/harnesses/claude.py +42 -0
- free_claude_code/harnesses/claude_desktop_integration.py +418 -0
- free_claude_code/harnesses/claude_integration.py +167 -0
- free_claude_code/harnesses/codex.py +94 -0
- free_claude_code/harnesses/codex_integration.py +147 -0
- free_claude_code/harnesses/codex_model_catalog.py +109 -0
- free_claude_code/harnesses/config_file.py +33 -0
- free_claude_code/harnesses/environment.py +78 -0
- free_claude_code/harnesses/jetbrains_acp_integration.py +242 -0
- free_claude_code/harnesses/launch.py +21 -0
- free_claude_code/harnesses/resources.py +42 -0
- free_claude_code/messaging/__init__.py +16 -0
- free_claude_code/messaging/cli_event_constants.py +67 -0
- free_claude_code/messaging/command_context.py +99 -0
- free_claude_code/messaging/command_dispatcher.py +31 -0
- free_claude_code/messaging/commands.py +182 -0
- free_claude_code/messaging/event_parser.py +184 -0
- free_claude_code/messaging/limiter.py +342 -0
- free_claude_code/messaging/managed_protocols.py +35 -0
- free_claude_code/messaging/models.py +57 -0
- free_claude_code/messaging/node_event_pipeline.py +120 -0
- free_claude_code/messaging/node_runner.py +408 -0
- free_claude_code/messaging/platforms/__init__.py +20 -0
- free_claude_code/messaging/platforms/discord.py +319 -0
- free_claude_code/messaging/platforms/discord_inbound.py +112 -0
- free_claude_code/messaging/platforms/discord_io.py +167 -0
- free_claude_code/messaging/platforms/factory.py +109 -0
- free_claude_code/messaging/platforms/outbox.py +143 -0
- free_claude_code/messaging/platforms/ports.py +99 -0
- free_claude_code/messaging/platforms/telegram.py +300 -0
- free_claude_code/messaging/platforms/telegram_inbound.py +133 -0
- free_claude_code/messaging/platforms/telegram_io.py +287 -0
- free_claude_code/messaging/platforms/voice_flow.py +388 -0
- free_claude_code/messaging/rendering/__init__.py +3 -0
- free_claude_code/messaging/rendering/discord_markdown.py +318 -0
- free_claude_code/messaging/rendering/markdown_tables.py +47 -0
- free_claude_code/messaging/rendering/profiles.py +53 -0
- free_claude_code/messaging/rendering/telegram_markdown.py +331 -0
- free_claude_code/messaging/safe_diagnostics.py +15 -0
- free_claude_code/messaging/session/__init__.py +5 -0
- free_claude_code/messaging/session/managed_message_log.py +135 -0
- free_claude_code/messaging/session/persistence.py +161 -0
- free_claude_code/messaging/session/store.py +162 -0
- free_claude_code/messaging/transcript/__init__.py +6 -0
- free_claude_code/messaging/transcript/buffer.py +220 -0
- free_claude_code/messaging/transcript/context.py +18 -0
- free_claude_code/messaging/transcript/renderer.py +65 -0
- free_claude_code/messaging/transcript/segments.py +176 -0
- free_claude_code/messaging/transcript/subagents.py +108 -0
- free_claude_code/messaging/transcription.py +132 -0
- free_claude_code/messaging/trees/__init__.py +41 -0
- free_claude_code/messaging/trees/graph.py +250 -0
- free_claude_code/messaging/trees/identity.py +16 -0
- free_claude_code/messaging/trees/manager.py +587 -0
- free_claude_code/messaging/trees/node.py +56 -0
- free_claude_code/messaging/trees/processor.py +272 -0
- free_claude_code/messaging/trees/queue.py +47 -0
- free_claude_code/messaging/trees/repository.py +135 -0
- free_claude_code/messaging/trees/runtime.py +487 -0
- free_claude_code/messaging/trees/snapshot.py +221 -0
- free_claude_code/messaging/trees/transitions.py +174 -0
- free_claude_code/messaging/turn_intake.py +241 -0
- free_claude_code/messaging/ui_updates.py +99 -0
- free_claude_code/messaging/voice.py +376 -0
- free_claude_code/messaging/workflow.py +714 -0
- free_claude_code/providers/__init__.py +12 -0
- free_claude_code/providers/admission.py +942 -0
- free_claude_code/providers/anthropic_messages/__init__.py +1 -0
- free_claude_code/providers/anthropic_messages/request_policy.py +162 -0
- free_claude_code/providers/anthropic_messages/transport.py +499 -0
- free_claude_code/providers/base.py +73 -0
- free_claude_code/providers/cloudflare/__init__.py +8 -0
- free_claude_code/providers/cloudflare/client.py +166 -0
- free_claude_code/providers/credential_validation.py +314 -0
- free_claude_code/providers/deepseek/__init__.py +5 -0
- free_claude_code/providers/deepseek/client.py +117 -0
- free_claude_code/providers/deepseek/compat.py +241 -0
- free_claude_code/providers/endpoint.py +21 -0
- free_claude_code/providers/endpoint_types.py +21 -0
- free_claude_code/providers/failure_policy.py +574 -0
- free_claude_code/providers/gemini/__init__.py +5 -0
- free_claude_code/providers/gemini/client.py +42 -0
- free_claude_code/providers/github_copilot/__init__.py +1 -0
- free_claude_code/providers/github_copilot/auth.py +502 -0
- free_claude_code/providers/github_copilot/broker.py +248 -0
- free_claude_code/providers/github_copilot/lifecycle.py +18 -0
- free_claude_code/providers/github_copilot/login.py +112 -0
- free_claude_code/providers/github_copilot/native_cli.py +82 -0
- free_claude_code/providers/github_copilot/provider.py +367 -0
- free_claude_code/providers/github_copilot/request_policy.py +119 -0
- free_claude_code/providers/github_copilot/responses_events.py +90 -0
- free_claude_code/providers/github_copilot/sdk.py +496 -0
- free_claude_code/providers/github_copilot/types.py +67 -0
- free_claude_code/providers/google_openai/__init__.py +17 -0
- free_claude_code/providers/google_openai/provider.py +73 -0
- free_claude_code/providers/google_openai/reasoning.py +150 -0
- free_claude_code/providers/google_openai/thought_signatures.py +117 -0
- free_claude_code/providers/groq/__init__.py +5 -0
- free_claude_code/providers/groq/client.py +342 -0
- free_claude_code/providers/groq/tpm.py +114 -0
- free_claude_code/providers/history_replay.py +260 -0
- free_claude_code/providers/http.py +105 -0
- free_claude_code/providers/kilo/__init__.py +5 -0
- free_claude_code/providers/kilo/client.py +45 -0
- free_claude_code/providers/kilo/models.py +60 -0
- free_claude_code/providers/lmstudio/__init__.py +5 -0
- free_claude_code/providers/lmstudio/client.py +220 -0
- free_claude_code/providers/mistral/__init__.py +5 -0
- free_claude_code/providers/mistral/client.py +91 -0
- free_claude_code/providers/mistral/reasoning.py +410 -0
- free_claude_code/providers/model_listing.py +479 -0
- free_claude_code/providers/nvidia_nim/__init__.py +5 -0
- free_claude_code/providers/nvidia_nim/client.py +224 -0
- free_claude_code/providers/nvidia_nim/native_tool_stream.py +693 -0
- free_claude_code/providers/nvidia_nim/request_options.py +141 -0
- free_claude_code/providers/nvidia_nim/retry.py +85 -0
- free_claude_code/providers/nvidia_nim/tool_schema.py +255 -0
- free_claude_code/providers/nvidia_nim/voice.py +107 -0
- free_claude_code/providers/open_router/__init__.py +5 -0
- free_claude_code/providers/open_router/client.py +113 -0
- free_claude_code/providers/openai_api/__init__.py +5 -0
- free_claude_code/providers/openai_api/provider.py +150 -0
- free_claude_code/providers/openai_chat/__init__.py +72 -0
- free_claude_code/providers/openai_chat/base_url.py +7 -0
- free_claude_code/providers/openai_chat/behavior.py +133 -0
- free_claude_code/providers/openai_chat/client.py +40 -0
- free_claude_code/providers/openai_chat/extra_body.py +63 -0
- free_claude_code/providers/openai_chat/output_cap.py +161 -0
- free_claude_code/providers/openai_chat/profiles.py +778 -0
- free_claude_code/providers/openai_chat/provider.py +244 -0
- free_claude_code/providers/openai_chat/reasoning.py +169 -0
- free_claude_code/providers/openai_chat/reasoning_details.py +128 -0
- free_claude_code/providers/openai_chat/request_policy.py +140 -0
- free_claude_code/providers/openai_chat/stream_output.py +752 -0
- free_claude_code/providers/openai_chat/tool_calls.py +462 -0
- free_claude_code/providers/openai_chat/transport.py +1643 -0
- free_claude_code/providers/openai_chat/usage.py +117 -0
- free_claude_code/providers/openai_client.py +74 -0
- free_claude_code/providers/openai_codex/__init__.py +1 -0
- free_claude_code/providers/openai_codex/auth.py +626 -0
- free_claude_code/providers/openai_codex/endpoint.py +53 -0
- free_claude_code/providers/openai_codex/login.py +265 -0
- free_claude_code/providers/openai_codex/provider.py +259 -0
- free_claude_code/providers/openai_responses/__init__.py +5 -0
- free_claude_code/providers/openai_responses/presentation.py +115 -0
- free_claude_code/providers/openai_responses/transport.py +621 -0
- free_claude_code/providers/openai_stream.py +21 -0
- free_claude_code/providers/opencode/__init__.py +5 -0
- free_claude_code/providers/opencode/catalog.py +340 -0
- free_claude_code/providers/opencode/provider.py +359 -0
- free_claude_code/providers/reasoning_compatibility.py +199 -0
- free_claude_code/providers/request_recovery.py +122 -0
- free_claude_code/providers/runtime/__init__.py +10 -0
- free_claude_code/providers/runtime/config.py +91 -0
- free_claude_code/providers/runtime/discovery.py +63 -0
- free_claude_code/providers/runtime/factory.py +282 -0
- free_claude_code/providers/runtime/model_cache.py +93 -0
- free_claude_code/providers/runtime/runtime.py +108 -0
- free_claude_code/providers/stream_recovery.py +164 -0
- free_claude_code/providers/vertex/__init__.py +5 -0
- free_claude_code/providers/vertex/auth.py +105 -0
- free_claude_code/providers/vertex/client.py +140 -0
- free_claude_code/providers/vertex/endpoint.py +50 -0
- free_claude_code/providers/vertex/models.py +52 -0
- free_claude_code/runtime/__init__.py +1 -0
- free_claude_code/runtime/application.py +1121 -0
- free_claude_code/runtime/asgi.py +63 -0
- free_claude_code/runtime/bootstrap.py +153 -0
- free_claude_code/runtime/code_sessions_sqlite.py +785 -0
- free_claude_code/runtime/codex_app_server.py +775 -0
- free_claude_code/runtime/codex_catalog.py +62 -0
- free_claude_code/runtime/codex_protocol.py +662 -0
- free_claude_code/runtime/configuration.py +53 -0
- free_claude_code/runtime/folder_picker.py +134 -0
- free_claude_code/runtime/native_folder_dialog.py +123 -0
- free_claude_code/runtime/provider_manager.py +716 -0
- free_claude_code/runtime/retired_chat.py +47 -0
- free_claude_code/runtime/web_tools/__init__.py +1 -0
- free_claude_code/runtime/web_tools/client.py +238 -0
- free_claude_code/runtime/web_tools/constants.py +17 -0
- free_claude_code/runtime/web_tools/egress.py +89 -0
- free_claude_code/runtime/web_tools/parsers.py +78 -0
- free_claude_code-6.2.64.data/scripts/fcc-update +4 -0
- free_claude_code-6.2.64.data/scripts/fcc-update.cmd +5 -0
- free_claude_code-6.2.64.dist-info/METADATA +555 -0
- free_claude_code-6.2.64.dist-info/RECORD +429 -0
- free_claude_code-6.2.64.dist-info/WHEEL +4 -0
- free_claude_code-6.2.64.dist-info/entry_points.txt +15 -0
- free_claude_code-6.2.64.dist-info/licenses/LICENSE +239 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""Loguru-based structured logging configuration.
|
|
2
|
+
|
|
3
|
+
Structured logs are written as JSON lines to a configurable path (default
|
|
4
|
+
``logs/server.log``). Stdlib logging is intercepted and funneled to loguru.
|
|
5
|
+
Context vars (request_id, node_id, chat_id) from contextualize() are
|
|
6
|
+
included at top level for easy grep/filter.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import re
|
|
12
|
+
import threading
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from loguru import logger
|
|
16
|
+
|
|
17
|
+
_configured = False
|
|
18
|
+
_current_path: Path | None = None
|
|
19
|
+
_current_level = "INFO"
|
|
20
|
+
_current_verbose: bool | None = None
|
|
21
|
+
_sink_id: int | None = None
|
|
22
|
+
|
|
23
|
+
_THIRD_PARTY_LOGGERS = (
|
|
24
|
+
"openai",
|
|
25
|
+
"httpx",
|
|
26
|
+
"httpcore",
|
|
27
|
+
"httpcore.http11",
|
|
28
|
+
"httpcore.connection",
|
|
29
|
+
"telegram",
|
|
30
|
+
"telegram.ext",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Loguru ``logger.bind()`` key used by structured TRACE payloads; ``core/trace.py``
|
|
34
|
+
# uses the identical string constant ``TRACE_PAYLOAD_BINDING``.
|
|
35
|
+
_TRACE_PAYLOAD_BINDING = "trace_payload"
|
|
36
|
+
|
|
37
|
+
# Context keys we promote to top-level JSON for traceability / grep
|
|
38
|
+
_CONTEXT_KEYS = (
|
|
39
|
+
"request_id",
|
|
40
|
+
"node_id",
|
|
41
|
+
"chat_id",
|
|
42
|
+
"claude_session_id",
|
|
43
|
+
"http_method",
|
|
44
|
+
"http_path",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
_TELEGRAM_BOT_RE = re.compile(
|
|
48
|
+
r"(https?://api\.telegram\.org/)bot([0-9]+:[A-Za-z0-9_-]+)(/?)",
|
|
49
|
+
re.IGNORECASE,
|
|
50
|
+
)
|
|
51
|
+
# Authorization: Bearer <token> (HTTP client / proxy debug lines)
|
|
52
|
+
_AUTH_BEARER_RE = re.compile(
|
|
53
|
+
r"(\bAuthorization\s*:\s*Bearer\s+)([^\s'\"]+)",
|
|
54
|
+
re.IGNORECASE,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _redact_sensitive_substrings(message: str) -> str:
|
|
59
|
+
"""Remove obvious API tokens and secrets before JSON log line emission."""
|
|
60
|
+
text = _TELEGRAM_BOT_RE.sub(r"\1bot<redacted>\3", message)
|
|
61
|
+
return _AUTH_BEARER_RE.sub(r"\1<redacted>", text)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _serialize_with_context(record) -> str:
|
|
65
|
+
"""Format record as JSON with context vars at top level.
|
|
66
|
+
Returns a format template; we inject _json into record for output.
|
|
67
|
+
"""
|
|
68
|
+
extra = record.get("extra", {})
|
|
69
|
+
out = {
|
|
70
|
+
"time": str(record["time"]),
|
|
71
|
+
"level": record["level"].name,
|
|
72
|
+
"message": _redact_sensitive_substrings(str(record["message"])),
|
|
73
|
+
"module": record["name"],
|
|
74
|
+
"function": record["function"],
|
|
75
|
+
"line": record["line"],
|
|
76
|
+
}
|
|
77
|
+
trace_payload = extra.get(_TRACE_PAYLOAD_BINDING)
|
|
78
|
+
for key in _CONTEXT_KEYS:
|
|
79
|
+
if key in extra and extra[key] is not None:
|
|
80
|
+
out[key] = extra[key]
|
|
81
|
+
if isinstance(trace_payload, dict):
|
|
82
|
+
for tk, tv in trace_payload.items():
|
|
83
|
+
if tk in out:
|
|
84
|
+
continue
|
|
85
|
+
out[tk] = tv
|
|
86
|
+
out["trace"] = True
|
|
87
|
+
record["_json"] = json.dumps(out, default=str)
|
|
88
|
+
return "{_json}\n"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class InterceptHandler(logging.Handler):
|
|
92
|
+
"""Redirect stdlib logging to loguru."""
|
|
93
|
+
|
|
94
|
+
def __init__(self) -> None:
|
|
95
|
+
super().__init__()
|
|
96
|
+
self._local = threading.local()
|
|
97
|
+
|
|
98
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
99
|
+
if getattr(self._local, "active", False):
|
|
100
|
+
# Avoid deadlock when nested stdlib records fire during a loguru emit.
|
|
101
|
+
return
|
|
102
|
+
self._local.active = True
|
|
103
|
+
try:
|
|
104
|
+
try:
|
|
105
|
+
level = logger.level(record.levelname).name
|
|
106
|
+
except ValueError:
|
|
107
|
+
level = record.levelno
|
|
108
|
+
|
|
109
|
+
frame, depth = logging.currentframe(), 2
|
|
110
|
+
while frame is not None and frame.f_code.co_filename == logging.__file__:
|
|
111
|
+
frame = frame.f_back
|
|
112
|
+
depth += 1
|
|
113
|
+
|
|
114
|
+
logger.opt(depth=depth, exception=record.exc_info).log(
|
|
115
|
+
level, record.getMessage()
|
|
116
|
+
)
|
|
117
|
+
finally:
|
|
118
|
+
self._local.active = False
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _set_third_party_levels(verbose: bool) -> None:
|
|
122
|
+
level = logging.NOTSET if verbose else logging.WARNING
|
|
123
|
+
for name in _THIRD_PARTY_LOGGERS:
|
|
124
|
+
logging.getLogger(name).setLevel(level)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _add_file_sink(log_file: str | Path, level: str) -> int:
|
|
128
|
+
log_path = Path(log_file)
|
|
129
|
+
return logger.add(
|
|
130
|
+
log_path,
|
|
131
|
+
level=level,
|
|
132
|
+
format=_serialize_with_context,
|
|
133
|
+
encoding="utf-8",
|
|
134
|
+
mode="a",
|
|
135
|
+
rotation="50 MB",
|
|
136
|
+
retention=5,
|
|
137
|
+
enqueue=True,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def configure_logging(
|
|
142
|
+
log_file: str | Path,
|
|
143
|
+
*,
|
|
144
|
+
force: bool = False,
|
|
145
|
+
verbose_third_party: bool = False,
|
|
146
|
+
level: str = "INFO",
|
|
147
|
+
) -> None:
|
|
148
|
+
"""Configure loguru with JSON output to log_file and intercept stdlib logging.
|
|
149
|
+
|
|
150
|
+
Idempotent: skips if already configured with the same path, level, and verbosity.
|
|
151
|
+
On path or level change, replaces only the file sink without truncating.
|
|
152
|
+
On verbosity change alone, updates only the third-party logger levels.
|
|
153
|
+
Use force=True to reconfigure from scratch.
|
|
154
|
+
|
|
155
|
+
When ``verbose_third_party`` is false, managed noisy third-party loggers
|
|
156
|
+
are capped at WARNING unless explicitly configured otherwise.
|
|
157
|
+
"""
|
|
158
|
+
global _configured, _current_path, _current_level, _current_verbose, _sink_id
|
|
159
|
+
|
|
160
|
+
log_path = Path(log_file).expanduser().resolve()
|
|
161
|
+
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
162
|
+
|
|
163
|
+
if (
|
|
164
|
+
_configured
|
|
165
|
+
and not force
|
|
166
|
+
and log_path == _current_path
|
|
167
|
+
and level == _current_level
|
|
168
|
+
and verbose_third_party == _current_verbose
|
|
169
|
+
):
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
if not _configured or force:
|
|
173
|
+
_configured = True
|
|
174
|
+
|
|
175
|
+
logger.remove()
|
|
176
|
+
|
|
177
|
+
log_path.write_text("")
|
|
178
|
+
|
|
179
|
+
_sink_id = _add_file_sink(log_path, level)
|
|
180
|
+
|
|
181
|
+
intercept = InterceptHandler()
|
|
182
|
+
logging.root.handlers = [intercept]
|
|
183
|
+
logging.root.setLevel(logging.DEBUG)
|
|
184
|
+
|
|
185
|
+
_set_third_party_levels(verbose_third_party)
|
|
186
|
+
elif log_path != _current_path or level != _current_level:
|
|
187
|
+
if _sink_id is not None:
|
|
188
|
+
logger.remove(_sink_id)
|
|
189
|
+
_sink_id = _add_file_sink(log_path, level)
|
|
190
|
+
if verbose_third_party != _current_verbose:
|
|
191
|
+
_set_third_party_levels(verbose_third_party)
|
|
192
|
+
else:
|
|
193
|
+
_set_third_party_levels(verbose_third_party)
|
|
194
|
+
|
|
195
|
+
_current_path = log_path
|
|
196
|
+
_current_level = level
|
|
197
|
+
_current_verbose = verbose_third_party
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Provider-prefixed model reference helpers."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Protocol
|
|
6
|
+
|
|
7
|
+
from .constants import DEFAULT_MODEL
|
|
8
|
+
|
|
9
|
+
RETIRED_PROVIDER_IDS = frozenset({"github_models"})
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def is_retired_model_ref(model_ref: str) -> bool:
|
|
13
|
+
"""Recognize complete references owned by a retired provider."""
|
|
14
|
+
|
|
15
|
+
provider, separator, model = model_ref.strip().partition("/")
|
|
16
|
+
return provider in RETIRED_PROVIDER_IDS and bool(separator and model.strip())
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def parse_model_fallbacks(value: object) -> object:
|
|
20
|
+
if value is None:
|
|
21
|
+
return None
|
|
22
|
+
if isinstance(value, str):
|
|
23
|
+
if not value.strip():
|
|
24
|
+
return None
|
|
25
|
+
return tuple(part.strip() for part in value.split(","))
|
|
26
|
+
if isinstance(value, list | tuple) and not value:
|
|
27
|
+
return None
|
|
28
|
+
return value
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def normalize_retired_model_settings(
|
|
32
|
+
values: Mapping[str, str], *, preserve_empty_overrides: bool
|
|
33
|
+
) -> dict[str, str]:
|
|
34
|
+
"""Repair one source without changing its precedence or unrelated validation."""
|
|
35
|
+
|
|
36
|
+
normalized = dict(values)
|
|
37
|
+
for key in ("MODEL", "MODEL_FABLE", "MODEL_OPUS", "MODEL_SONNET", "MODEL_HAIKU"):
|
|
38
|
+
if is_retired_model_ref(normalized.get(key, "")):
|
|
39
|
+
if preserve_empty_overrides:
|
|
40
|
+
normalized[key] = DEFAULT_MODEL if key == "MODEL" else ""
|
|
41
|
+
else:
|
|
42
|
+
normalized.pop(key)
|
|
43
|
+
fallbacks = parse_model_fallbacks(normalized.get("MODEL_FALLBACKS"))
|
|
44
|
+
if isinstance(fallbacks, tuple) and all(fallbacks):
|
|
45
|
+
retained = tuple(ref for ref in fallbacks if not is_retired_model_ref(ref))
|
|
46
|
+
if retained != fallbacks:
|
|
47
|
+
if retained or preserve_empty_overrides:
|
|
48
|
+
normalized["MODEL_FALLBACKS"] = ",".join(retained)
|
|
49
|
+
else:
|
|
50
|
+
normalized.pop("MODEL_FALLBACKS")
|
|
51
|
+
return normalized
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass(frozen=True, slots=True)
|
|
55
|
+
class ConfiguredChatModelRef:
|
|
56
|
+
"""A unique configured chat model reference."""
|
|
57
|
+
|
|
58
|
+
model_ref: str
|
|
59
|
+
provider_id: str
|
|
60
|
+
model_id: str
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ChatModelConfig(Protocol):
|
|
64
|
+
model: str
|
|
65
|
+
model_fable: str | None
|
|
66
|
+
model_opus: str | None
|
|
67
|
+
model_sonnet: str | None
|
|
68
|
+
model_haiku: str | None
|
|
69
|
+
model_fallbacks: tuple[str, ...] | None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def split_provider_model_ref(model_ref: str) -> tuple[str, str]:
|
|
73
|
+
"""Split one complete ``provider/model`` reference."""
|
|
74
|
+
|
|
75
|
+
provider_id, separator, model_id = model_ref.partition("/")
|
|
76
|
+
if not separator or not provider_id or not model_id:
|
|
77
|
+
raise ValueError("Model reference must contain provider and model names.")
|
|
78
|
+
return provider_id, model_id
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def parse_provider_type(model_ref: str) -> str:
|
|
82
|
+
"""Extract provider type from any 'provider/model' string."""
|
|
83
|
+
|
|
84
|
+
return split_provider_model_ref(model_ref)[0]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def parse_model_name(model_ref: str) -> str:
|
|
88
|
+
"""Extract model name from any 'provider/model' string."""
|
|
89
|
+
|
|
90
|
+
return split_provider_model_ref(model_ref)[1]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def configured_chat_model_refs(
|
|
94
|
+
settings: ChatModelConfig,
|
|
95
|
+
) -> tuple[ConfiguredChatModelRef, ...]:
|
|
96
|
+
"""Return unique configured chat provider/model refs."""
|
|
97
|
+
|
|
98
|
+
model_refs = dict.fromkeys(
|
|
99
|
+
model_ref
|
|
100
|
+
for model_ref in (
|
|
101
|
+
settings.model,
|
|
102
|
+
settings.model_fable,
|
|
103
|
+
settings.model_opus,
|
|
104
|
+
settings.model_sonnet,
|
|
105
|
+
settings.model_haiku,
|
|
106
|
+
*(settings.model_fallbacks or ()),
|
|
107
|
+
)
|
|
108
|
+
if model_ref is not None
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return tuple(
|
|
112
|
+
ConfiguredChatModelRef(
|
|
113
|
+
model_ref=model_ref,
|
|
114
|
+
provider_id=parse_provider_type(model_ref),
|
|
115
|
+
model_id=parse_model_name(model_ref),
|
|
116
|
+
)
|
|
117
|
+
for model_ref in model_refs
|
|
118
|
+
)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""NVIDIA NIM settings (fixed values, no env config)."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator
|
|
4
|
+
|
|
5
|
+
from free_claude_code.config.constants import ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class NimSettings(BaseModel):
|
|
9
|
+
"""Fixed NVIDIA NIM settings (not configurable via env)."""
|
|
10
|
+
|
|
11
|
+
temperature: float = Field(
|
|
12
|
+
1.0, ge=0.0, le=2.0, description="Sampling temperature, must be >=0 and <=2."
|
|
13
|
+
)
|
|
14
|
+
top_p: float = Field(
|
|
15
|
+
0.95,
|
|
16
|
+
ge=0.95,
|
|
17
|
+
le=0.95,
|
|
18
|
+
description="FCC's fixed NVIDIA NIM nucleus sampling probability.",
|
|
19
|
+
)
|
|
20
|
+
top_k: int = -1
|
|
21
|
+
max_tokens: int = Field(
|
|
22
|
+
ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
|
|
23
|
+
ge=1,
|
|
24
|
+
description="Maximum number of tokens in output.",
|
|
25
|
+
)
|
|
26
|
+
presence_penalty: float = Field(0.0, ge=-2.0, le=2.0)
|
|
27
|
+
frequency_penalty: float = Field(0.0, ge=-2.0, le=2.0)
|
|
28
|
+
min_p: float = Field(
|
|
29
|
+
0.0, ge=0.0, le=1.0, description="Minimum probability threshold [0,1]."
|
|
30
|
+
)
|
|
31
|
+
repetition_penalty: float = Field(
|
|
32
|
+
1.0, ge=0.0, description="Penalty for repeated tokens. Must be >=0."
|
|
33
|
+
)
|
|
34
|
+
seed: int | None = None
|
|
35
|
+
stop: str | None = None
|
|
36
|
+
parallel_tool_calls: bool = True
|
|
37
|
+
ignore_eos: bool = False
|
|
38
|
+
min_tokens: int = Field(0, ge=0, description="Minimum tokens in the response.")
|
|
39
|
+
chat_template: str | None = None
|
|
40
|
+
request_id: str | None = None
|
|
41
|
+
|
|
42
|
+
model_config = ConfigDict(extra="forbid")
|
|
43
|
+
|
|
44
|
+
@field_validator("top_k", mode="before")
|
|
45
|
+
@classmethod
|
|
46
|
+
def validate_top_k(cls, v, info: ValidationInfo):
|
|
47
|
+
if v is None or v == "":
|
|
48
|
+
return -1
|
|
49
|
+
int_v = int(v)
|
|
50
|
+
if int_v < -1:
|
|
51
|
+
raise ValueError(f"{info.field_name} must be -1 or >= 0")
|
|
52
|
+
return int_v
|
|
53
|
+
|
|
54
|
+
@field_validator(
|
|
55
|
+
"temperature",
|
|
56
|
+
"top_p",
|
|
57
|
+
"min_p",
|
|
58
|
+
"presence_penalty",
|
|
59
|
+
"frequency_penalty",
|
|
60
|
+
"repetition_penalty",
|
|
61
|
+
mode="before",
|
|
62
|
+
)
|
|
63
|
+
@classmethod
|
|
64
|
+
def validate_float_fields(cls, v, info: ValidationInfo):
|
|
65
|
+
field_defaults = {
|
|
66
|
+
"temperature": 1.0,
|
|
67
|
+
"top_p": 0.95,
|
|
68
|
+
"min_p": 0.0,
|
|
69
|
+
"presence_penalty": 0.0,
|
|
70
|
+
"frequency_penalty": 0.0,
|
|
71
|
+
"repetition_penalty": 1.0,
|
|
72
|
+
}
|
|
73
|
+
if v is None or v == "":
|
|
74
|
+
key = info.field_name or "temperature"
|
|
75
|
+
return field_defaults.get(key, 1.0)
|
|
76
|
+
try:
|
|
77
|
+
val = float(v)
|
|
78
|
+
except (TypeError, ValueError) as err:
|
|
79
|
+
raise ValueError(
|
|
80
|
+
f"{info.field_name} must be a float. Got {type(v).__name__}."
|
|
81
|
+
) from err
|
|
82
|
+
return val
|
|
83
|
+
|
|
84
|
+
@field_validator("max_tokens", "min_tokens", mode="before")
|
|
85
|
+
@classmethod
|
|
86
|
+
def validate_int_fields(cls, v, info: ValidationInfo):
|
|
87
|
+
field_defaults = {
|
|
88
|
+
"max_tokens": ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
|
|
89
|
+
"min_tokens": 0,
|
|
90
|
+
}
|
|
91
|
+
if v is None or v == "":
|
|
92
|
+
key = info.field_name or "max_tokens"
|
|
93
|
+
return field_defaults.get(key, ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS)
|
|
94
|
+
try:
|
|
95
|
+
val = int(v)
|
|
96
|
+
except (TypeError, ValueError) as err:
|
|
97
|
+
raise ValueError(
|
|
98
|
+
f"{info.field_name} must be an int. Got {type(v).__name__}."
|
|
99
|
+
) from err
|
|
100
|
+
return val
|
|
101
|
+
|
|
102
|
+
@field_validator("seed", mode="before")
|
|
103
|
+
@classmethod
|
|
104
|
+
def parse_optional_int(cls, v, info: ValidationInfo):
|
|
105
|
+
if v == "" or v is None:
|
|
106
|
+
return None
|
|
107
|
+
try:
|
|
108
|
+
return int(v)
|
|
109
|
+
except (TypeError, ValueError) as err:
|
|
110
|
+
raise ValueError(
|
|
111
|
+
f"{info.field_name} must be an int or empty/None."
|
|
112
|
+
) from err
|
|
113
|
+
|
|
114
|
+
@field_validator("stop", "chat_template", "request_id", mode="before")
|
|
115
|
+
@classmethod
|
|
116
|
+
def parse_optional_str(cls, v, info: ValidationInfo):
|
|
117
|
+
if v == "":
|
|
118
|
+
return None
|
|
119
|
+
if v is not None and not isinstance(v, str):
|
|
120
|
+
return str(v)
|
|
121
|
+
return v
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Shared filesystem paths for Free Claude Code configuration."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
FCC_CONFIG_DIRNAME = ".fcc"
|
|
6
|
+
FCC_ENV_FILENAME = ".env"
|
|
7
|
+
LEGACY_REPO_DIRNAME = "free-claude-code"
|
|
8
|
+
LEGACY_XDG_CONFIG_DIRNAME = ".config"
|
|
9
|
+
MESSAGING_STATE_DIRNAME = "agent_workspace"
|
|
10
|
+
FCC_LOGS_DIRNAME = "logs"
|
|
11
|
+
SERVER_LOG_FILENAME = "server.log"
|
|
12
|
+
CODEX_MODEL_CATALOG_FILENAME = "codex-model-catalog.json"
|
|
13
|
+
AUTH_DIRNAME = "auth"
|
|
14
|
+
OPENAI_AUTH_FILENAME = "openai.json"
|
|
15
|
+
OPENAI_AUTH_LOCK_FILENAME = "openai.lock"
|
|
16
|
+
CONFIG_LOCK_FILENAME = "config.lock"
|
|
17
|
+
FCC_TEMP_DIRNAME = "tmp"
|
|
18
|
+
LAUNCHER_TEMP_DIRNAME = "launchers"
|
|
19
|
+
CODE_STATE_DIRNAME = "code"
|
|
20
|
+
CODE_DATABASE_FILENAME = "code.db"
|
|
21
|
+
CODE_LOCK_FILENAME = "code.lock"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def config_dir_path() -> Path:
|
|
25
|
+
"""Return the default user config directory."""
|
|
26
|
+
|
|
27
|
+
return Path.home() / FCC_CONFIG_DIRNAME
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def managed_env_path() -> Path:
|
|
31
|
+
"""Return the default user-managed env file path."""
|
|
32
|
+
|
|
33
|
+
return config_dir_path() / FCC_ENV_FILENAME
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def config_lock_path() -> Path:
|
|
37
|
+
"""Return the cross-process managed-config migration lock path."""
|
|
38
|
+
|
|
39
|
+
return config_dir_path() / CONFIG_LOCK_FILENAME
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def launcher_temp_dir_path() -> Path:
|
|
43
|
+
"""Return the base directory for private native launcher configuration."""
|
|
44
|
+
|
|
45
|
+
return config_dir_path() / FCC_TEMP_DIRNAME / LAUNCHER_TEMP_DIRNAME
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def code_database_path() -> Path:
|
|
49
|
+
"""Return the managed Code sessions database path."""
|
|
50
|
+
return config_dir_path() / CODE_STATE_DIRNAME / CODE_DATABASE_FILENAME
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def code_lock_path() -> Path:
|
|
54
|
+
"""Return the exclusive Code sessions process-lock path."""
|
|
55
|
+
return config_dir_path() / CODE_STATE_DIRNAME / CODE_LOCK_FILENAME
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def legacy_env_paths() -> tuple[Path, ...]:
|
|
59
|
+
"""Return legacy user env paths that can be migrated to ~/.fcc/.env."""
|
|
60
|
+
|
|
61
|
+
home = Path.home()
|
|
62
|
+
return (
|
|
63
|
+
home / LEGACY_REPO_DIRNAME / FCC_ENV_FILENAME,
|
|
64
|
+
home / LEGACY_XDG_CONFIG_DIRNAME / LEGACY_REPO_DIRNAME / FCC_ENV_FILENAME,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def messaging_state_dir_path() -> Path:
|
|
69
|
+
"""Return the managed messaging state directory."""
|
|
70
|
+
|
|
71
|
+
return config_dir_path() / MESSAGING_STATE_DIRNAME
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def server_log_path() -> Path:
|
|
75
|
+
"""Return the canonical server log path."""
|
|
76
|
+
|
|
77
|
+
return config_dir_path() / FCC_LOGS_DIRNAME / SERVER_LOG_FILENAME
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def codex_model_catalog_path() -> Path:
|
|
81
|
+
"""Return the generated Codex model catalog path."""
|
|
82
|
+
|
|
83
|
+
return config_dir_path() / CODEX_MODEL_CATALOG_FILENAME
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def claude_desktop_disconnect_path() -> Path:
|
|
87
|
+
return config_dir_path() / "claude-desktop-disconnect.json"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def openai_auth_path() -> Path:
|
|
91
|
+
"""Return FCC's private ChatGPT credential file path."""
|
|
92
|
+
|
|
93
|
+
return config_dir_path() / AUTH_DIRNAME / OPENAI_AUTH_FILENAME
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def openai_auth_lock_path() -> Path:
|
|
97
|
+
"""Return the cross-process lock path for ChatGPT credentials."""
|
|
98
|
+
|
|
99
|
+
return config_dir_path() / AUTH_DIRNAME / OPENAI_AUTH_LOCK_FILENAME
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def github_copilot_auth_path() -> Path:
|
|
103
|
+
"""Return FCC connection state; native Copilot retains credentials."""
|
|
104
|
+
return config_dir_path() / AUTH_DIRNAME / "github_copilot.json"
|