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,272 @@
|
|
|
1
|
+
"""Task execution for claims returned by messaging tree aggregates."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import contextlib
|
|
5
|
+
from collections.abc import Awaitable, Callable
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
from loguru import logger
|
|
9
|
+
|
|
10
|
+
from ..safe_diagnostics import format_exception_for_log
|
|
11
|
+
from .runtime import MessageTree
|
|
12
|
+
from .transitions import CancellationReason, NodeClaim, QueueEntry
|
|
13
|
+
|
|
14
|
+
NodeProcessor = Callable[[NodeClaim], Awaitable[None]]
|
|
15
|
+
QueueUpdateCallback = Callable[[tuple[QueueEntry, ...]], Awaitable[None]]
|
|
16
|
+
NodeStartedCallback = Callable[[NodeClaim], Awaitable[None]]
|
|
17
|
+
ClaimFailureCallback = Callable[[NodeClaim], Awaitable[None]]
|
|
18
|
+
ClaimFinishedCallback = Callable[[MessageTree, NodeClaim], Awaitable[None]]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(slots=True)
|
|
22
|
+
class _TaskSlot:
|
|
23
|
+
tree: MessageTree
|
|
24
|
+
claim: NodeClaim
|
|
25
|
+
task: asyncio.Task[None] | None = None
|
|
26
|
+
runner_started: bool = False
|
|
27
|
+
transitioned: bool = False
|
|
28
|
+
recovery_task: asyncio.Task[None] | None = None
|
|
29
|
+
cancellation_requested: bool = False
|
|
30
|
+
cancellation_reason: CancellationReason | None = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True, slots=True)
|
|
34
|
+
class CancelledTask:
|
|
35
|
+
"""Task handle plus whether the node runner owns cancellation UI."""
|
|
36
|
+
|
|
37
|
+
task: asyncio.Task[None]
|
|
38
|
+
runner_started: bool
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TreeQueueProcessor:
|
|
42
|
+
"""Own asyncio tasks while MessageTree owns scheduling state."""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
node_processor: NodeProcessor,
|
|
47
|
+
*,
|
|
48
|
+
claim_failure_callback: ClaimFailureCallback,
|
|
49
|
+
claim_finished_callback: ClaimFinishedCallback,
|
|
50
|
+
queue_update_callback: QueueUpdateCallback | None = None,
|
|
51
|
+
node_started_callback: NodeStartedCallback | None = None,
|
|
52
|
+
log_messaging_error_details: bool = False,
|
|
53
|
+
) -> None:
|
|
54
|
+
self._node_processor = node_processor
|
|
55
|
+
self._claim_failure_callback = claim_failure_callback
|
|
56
|
+
self._claim_finished_callback = claim_finished_callback
|
|
57
|
+
self._queue_update_callback = queue_update_callback
|
|
58
|
+
self._node_started_callback = node_started_callback
|
|
59
|
+
self._log_messaging_error_details = log_messaging_error_details
|
|
60
|
+
self._tasks: dict[str, _TaskSlot] = {}
|
|
61
|
+
self._completion_failures: list[Exception] = []
|
|
62
|
+
self._idle = asyncio.Event()
|
|
63
|
+
self._idle.set()
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def _key(claim: NodeClaim) -> str:
|
|
67
|
+
return claim.claim_id
|
|
68
|
+
|
|
69
|
+
def launch(
|
|
70
|
+
self,
|
|
71
|
+
tree: MessageTree,
|
|
72
|
+
claim: NodeClaim,
|
|
73
|
+
*,
|
|
74
|
+
announce_started: bool = False,
|
|
75
|
+
queue: tuple[QueueEntry, ...] = (),
|
|
76
|
+
) -> None:
|
|
77
|
+
"""Attach a task synchronously before another coroutine can cancel it."""
|
|
78
|
+
key = self._key(claim)
|
|
79
|
+
if key in self._tasks:
|
|
80
|
+
raise RuntimeError(f"Claim {key} already has a task")
|
|
81
|
+
slot = _TaskSlot(tree=tree, claim=claim)
|
|
82
|
+
self._tasks[key] = slot
|
|
83
|
+
self._idle.clear()
|
|
84
|
+
ownership_ready = asyncio.Event()
|
|
85
|
+
claim_runner = self._run_claim(
|
|
86
|
+
slot,
|
|
87
|
+
ownership_ready=ownership_ready,
|
|
88
|
+
announce_started=announce_started,
|
|
89
|
+
queue=queue,
|
|
90
|
+
)
|
|
91
|
+
try:
|
|
92
|
+
task = asyncio.create_task(
|
|
93
|
+
claim_runner,
|
|
94
|
+
name=(f"messaging-claim-{claim.identity.root_id}-{claim.claim_id[:8]}"),
|
|
95
|
+
)
|
|
96
|
+
except BaseException:
|
|
97
|
+
claim_runner.close()
|
|
98
|
+
if self._tasks.get(key) is slot:
|
|
99
|
+
self._tasks.pop(key)
|
|
100
|
+
if not self._tasks:
|
|
101
|
+
self._idle.set()
|
|
102
|
+
raise
|
|
103
|
+
slot.task = task
|
|
104
|
+
task.add_done_callback(lambda _task, claim_key=key: self._task_done(claim_key))
|
|
105
|
+
ownership_ready.set()
|
|
106
|
+
|
|
107
|
+
def _task_done(self, key: str) -> None:
|
|
108
|
+
"""Recover a claim if its task was cancelled before entering its body."""
|
|
109
|
+
slot = self._tasks.get(key)
|
|
110
|
+
if slot is None or slot.transitioned or slot.recovery_task is not None:
|
|
111
|
+
return
|
|
112
|
+
slot.recovery_task = asyncio.create_task(
|
|
113
|
+
self._recover_unentered_task(slot),
|
|
114
|
+
name=f"messaging-claim-recovery-{key[:8]}",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
async def _recover_unentered_task(self, slot: _TaskSlot) -> None:
|
|
118
|
+
task = slot.task
|
|
119
|
+
if task is not None:
|
|
120
|
+
with contextlib.suppress(asyncio.CancelledError, Exception):
|
|
121
|
+
await task
|
|
122
|
+
if not slot.transitioned:
|
|
123
|
+
await self._finish_and_continue(slot)
|
|
124
|
+
|
|
125
|
+
async def _notify_queue_updated(self, queue: tuple[QueueEntry, ...]) -> None:
|
|
126
|
+
if self._queue_update_callback is None:
|
|
127
|
+
return
|
|
128
|
+
try:
|
|
129
|
+
await self._queue_update_callback(queue)
|
|
130
|
+
except Exception as exc:
|
|
131
|
+
logger.warning(
|
|
132
|
+
"Queue update callback failed: {}",
|
|
133
|
+
format_exception_for_log(
|
|
134
|
+
exc,
|
|
135
|
+
log_full_message=self._log_messaging_error_details,
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
async def notify_queue_updated(self, queue: tuple[QueueEntry, ...]) -> None:
|
|
140
|
+
"""Publish a transition-owned queue snapshot."""
|
|
141
|
+
await self._notify_queue_updated(queue)
|
|
142
|
+
|
|
143
|
+
async def _notify_node_started(self, claim: NodeClaim) -> None:
|
|
144
|
+
if self._node_started_callback is None:
|
|
145
|
+
return
|
|
146
|
+
try:
|
|
147
|
+
await self._node_started_callback(claim)
|
|
148
|
+
except Exception as exc:
|
|
149
|
+
logger.warning(
|
|
150
|
+
"Node started callback failed: {}",
|
|
151
|
+
format_exception_for_log(
|
|
152
|
+
exc,
|
|
153
|
+
log_full_message=self._log_messaging_error_details,
|
|
154
|
+
),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
async def _run_claim(
|
|
158
|
+
self,
|
|
159
|
+
slot: _TaskSlot,
|
|
160
|
+
*,
|
|
161
|
+
ownership_ready: asyncio.Event,
|
|
162
|
+
announce_started: bool,
|
|
163
|
+
queue: tuple[QueueEntry, ...],
|
|
164
|
+
) -> None:
|
|
165
|
+
await ownership_ready.wait()
|
|
166
|
+
claim = slot.claim
|
|
167
|
+
try:
|
|
168
|
+
if announce_started:
|
|
169
|
+
await self._notify_node_started(claim)
|
|
170
|
+
await self._notify_queue_updated(queue)
|
|
171
|
+
if slot.cancellation_requested:
|
|
172
|
+
if slot.cancellation_reason is None:
|
|
173
|
+
raise asyncio.CancelledError
|
|
174
|
+
raise asyncio.CancelledError(slot.cancellation_reason)
|
|
175
|
+
slot.runner_started = True
|
|
176
|
+
await self._node_processor(claim)
|
|
177
|
+
except asyncio.CancelledError:
|
|
178
|
+
logger.info("Task for node {} was cancelled", claim.node.node_id)
|
|
179
|
+
raise
|
|
180
|
+
except Exception as exc:
|
|
181
|
+
logger.error(
|
|
182
|
+
"Error processing node {}: {}",
|
|
183
|
+
claim.node.node_id,
|
|
184
|
+
format_exception_for_log(
|
|
185
|
+
exc,
|
|
186
|
+
log_full_message=self._log_messaging_error_details,
|
|
187
|
+
),
|
|
188
|
+
)
|
|
189
|
+
await self._claim_failure_callback(claim)
|
|
190
|
+
finally:
|
|
191
|
+
if not slot.transitioned:
|
|
192
|
+
await self._finish_and_continue(slot)
|
|
193
|
+
|
|
194
|
+
async def _finish_and_continue(self, slot: _TaskSlot) -> None:
|
|
195
|
+
current = asyncio.current_task()
|
|
196
|
+
if current is not None:
|
|
197
|
+
while current.cancelling():
|
|
198
|
+
current.uncancel()
|
|
199
|
+
try:
|
|
200
|
+
while True:
|
|
201
|
+
try:
|
|
202
|
+
await self._claim_finished_callback(slot.tree, slot.claim)
|
|
203
|
+
slot.transitioned = True
|
|
204
|
+
break
|
|
205
|
+
except asyncio.CancelledError:
|
|
206
|
+
if current is not None:
|
|
207
|
+
while current.cancelling():
|
|
208
|
+
current.uncancel()
|
|
209
|
+
continue
|
|
210
|
+
except Exception as exc:
|
|
211
|
+
self._completion_failures.append(exc)
|
|
212
|
+
logger.error(
|
|
213
|
+
"Claim completion callback failed for node {}: {}",
|
|
214
|
+
slot.claim.node.node_id,
|
|
215
|
+
format_exception_for_log(
|
|
216
|
+
exc,
|
|
217
|
+
log_full_message=self._log_messaging_error_details,
|
|
218
|
+
),
|
|
219
|
+
)
|
|
220
|
+
finally:
|
|
221
|
+
key = self._key(slot.claim)
|
|
222
|
+
if self._tasks.get(key) is slot:
|
|
223
|
+
self._tasks.pop(key)
|
|
224
|
+
if not self._tasks:
|
|
225
|
+
self._idle.set()
|
|
226
|
+
|
|
227
|
+
def cancel(
|
|
228
|
+
self,
|
|
229
|
+
claim: NodeClaim,
|
|
230
|
+
reason: CancellationReason | None,
|
|
231
|
+
) -> CancelledTask | None:
|
|
232
|
+
"""Cancel exactly the task bound to one aggregate claim."""
|
|
233
|
+
slot = self._tasks.get(self._key(claim))
|
|
234
|
+
if slot is None:
|
|
235
|
+
return None
|
|
236
|
+
slot.cancellation_requested = True
|
|
237
|
+
slot.cancellation_reason = reason
|
|
238
|
+
task = slot.task
|
|
239
|
+
if task is None or task.done():
|
|
240
|
+
return None
|
|
241
|
+
if reason is None:
|
|
242
|
+
task.cancel()
|
|
243
|
+
else:
|
|
244
|
+
task.cancel(reason)
|
|
245
|
+
|
|
246
|
+
if slot.runner_started:
|
|
247
|
+
return CancelledTask(task=task, runner_started=True)
|
|
248
|
+
|
|
249
|
+
if slot.recovery_task is None:
|
|
250
|
+
slot.recovery_task = asyncio.create_task(
|
|
251
|
+
self._recover_unentered_task(slot),
|
|
252
|
+
name=f"messaging-claim-recovery-{claim.claim_id[:8]}",
|
|
253
|
+
)
|
|
254
|
+
return CancelledTask(task=slot.recovery_task, runner_started=False)
|
|
255
|
+
|
|
256
|
+
def task_count(self) -> int:
|
|
257
|
+
"""Return the number of attached claims for observability."""
|
|
258
|
+
return len(self._tasks)
|
|
259
|
+
|
|
260
|
+
async def wait_idle(self) -> None:
|
|
261
|
+
"""Wait for every task and hand completion failures to the caller once."""
|
|
262
|
+
await self._idle.wait()
|
|
263
|
+
if not self._completion_failures:
|
|
264
|
+
return
|
|
265
|
+
failures = self._completion_failures
|
|
266
|
+
self._completion_failures = []
|
|
267
|
+
if len(failures) == 1:
|
|
268
|
+
raise failures[0]
|
|
269
|
+
raise ExceptionGroup("Messaging claim completion failures", failures)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
__all__ = ["CancelledTask", "TreeQueueProcessor"]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""FIFO queue state for one messaging conversation tree."""
|
|
2
|
+
|
|
3
|
+
from collections import deque
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MessageNodeQueue:
|
|
7
|
+
"""Queue with snapshot/remove helpers, backed by a deque and a set index."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, items: list[str] | None = None) -> None:
|
|
10
|
+
self._deque: deque[str] = deque()
|
|
11
|
+
self._set: set[str] = set()
|
|
12
|
+
for item in items or []:
|
|
13
|
+
self.put(item)
|
|
14
|
+
|
|
15
|
+
def put(self, item: str) -> bool:
|
|
16
|
+
"""Append a unique item and report whether the queue changed."""
|
|
17
|
+
if item in self._set:
|
|
18
|
+
return False
|
|
19
|
+
self._deque.append(item)
|
|
20
|
+
self._set.add(item)
|
|
21
|
+
return True
|
|
22
|
+
|
|
23
|
+
def pop(self) -> str | None:
|
|
24
|
+
if not self._deque:
|
|
25
|
+
return None
|
|
26
|
+
item = self._deque.popleft()
|
|
27
|
+
self._set.discard(item)
|
|
28
|
+
return item
|
|
29
|
+
|
|
30
|
+
def qsize(self) -> int:
|
|
31
|
+
return len(self._deque)
|
|
32
|
+
|
|
33
|
+
def items(self) -> tuple[str, ...]:
|
|
34
|
+
return tuple(self._deque)
|
|
35
|
+
|
|
36
|
+
def remove(self, item: str) -> bool:
|
|
37
|
+
if item not in self._set:
|
|
38
|
+
return False
|
|
39
|
+
self._set.discard(item)
|
|
40
|
+
self._deque = deque(x for x in self._deque if x != item)
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
def drain(self) -> tuple[str, ...]:
|
|
44
|
+
items = tuple(self._deque)
|
|
45
|
+
self._deque.clear()
|
|
46
|
+
self._set.clear()
|
|
47
|
+
return items
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Manager-owned index of messaging tree aggregates."""
|
|
2
|
+
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
from ..models import MessageScope
|
|
6
|
+
from .identity import TreeIdentity
|
|
7
|
+
from .runtime import MessageTree
|
|
8
|
+
from .snapshot import ConversationSnapshot
|
|
9
|
+
from .transitions import NodeUiTarget
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TreeRepository:
|
|
13
|
+
"""Store aggregates and map node/status references to their root."""
|
|
14
|
+
|
|
15
|
+
def __init__(self) -> None:
|
|
16
|
+
self._trees: dict[TreeIdentity, MessageTree] = {}
|
|
17
|
+
self._reference_to_tree: dict[tuple[MessageScope, str], TreeIdentity] = {}
|
|
18
|
+
|
|
19
|
+
def get_tree(self, identity: TreeIdentity) -> MessageTree | None:
|
|
20
|
+
return self._trees.get(identity)
|
|
21
|
+
|
|
22
|
+
def get_tree_for_reference(
|
|
23
|
+
self,
|
|
24
|
+
scope: MessageScope,
|
|
25
|
+
reference_id: str,
|
|
26
|
+
) -> MessageTree | None:
|
|
27
|
+
identity = self._reference_to_tree.get((scope, reference_id))
|
|
28
|
+
return self._trees.get(identity) if identity is not None else None
|
|
29
|
+
|
|
30
|
+
def has_reference(self, scope: MessageScope, reference_id: str) -> bool:
|
|
31
|
+
return (scope, reference_id) in self._reference_to_tree
|
|
32
|
+
|
|
33
|
+
def add_tree(
|
|
34
|
+
self,
|
|
35
|
+
tree: MessageTree,
|
|
36
|
+
*,
|
|
37
|
+
node_id: str,
|
|
38
|
+
status_message_id: str,
|
|
39
|
+
) -> None:
|
|
40
|
+
identity = tree.identity
|
|
41
|
+
if identity in self._trees:
|
|
42
|
+
raise ValueError(f"Tree {identity} already exists")
|
|
43
|
+
self.register_node(
|
|
44
|
+
identity=identity,
|
|
45
|
+
node_id=node_id,
|
|
46
|
+
status_message_id=status_message_id,
|
|
47
|
+
)
|
|
48
|
+
self._trees[identity] = tree
|
|
49
|
+
logger.debug("TREE_REPO: add_tree identity={}", identity)
|
|
50
|
+
|
|
51
|
+
def register_node(
|
|
52
|
+
self,
|
|
53
|
+
*,
|
|
54
|
+
identity: TreeIdentity,
|
|
55
|
+
node_id: str,
|
|
56
|
+
status_message_id: str,
|
|
57
|
+
) -> None:
|
|
58
|
+
if self.has_reference(identity.scope, node_id) or self.has_reference(
|
|
59
|
+
identity.scope, status_message_id
|
|
60
|
+
):
|
|
61
|
+
raise ValueError("Node or status message is already registered")
|
|
62
|
+
self._reference_to_tree[(identity.scope, node_id)] = identity
|
|
63
|
+
self._reference_to_tree[(identity.scope, status_message_id)] = identity
|
|
64
|
+
|
|
65
|
+
def unregister_references(
|
|
66
|
+
self,
|
|
67
|
+
identity: TreeIdentity,
|
|
68
|
+
reference_ids: set[str],
|
|
69
|
+
) -> None:
|
|
70
|
+
for reference_id in reference_ids:
|
|
71
|
+
key = (identity.scope, reference_id)
|
|
72
|
+
if self._reference_to_tree.get(key) == identity:
|
|
73
|
+
self._reference_to_tree.pop(key)
|
|
74
|
+
|
|
75
|
+
def remove_tree(
|
|
76
|
+
self,
|
|
77
|
+
identity: TreeIdentity,
|
|
78
|
+
) -> MessageTree | None:
|
|
79
|
+
tree = self._trees.pop(identity, None)
|
|
80
|
+
if tree is None:
|
|
81
|
+
return None
|
|
82
|
+
self._reference_to_tree = {
|
|
83
|
+
key: owner
|
|
84
|
+
for key, owner in self._reference_to_tree.items()
|
|
85
|
+
if owner != identity
|
|
86
|
+
}
|
|
87
|
+
logger.debug("TREE_REPO: remove_tree identity={}", identity)
|
|
88
|
+
return tree
|
|
89
|
+
|
|
90
|
+
def trees(self) -> tuple[MessageTree, ...]:
|
|
91
|
+
return tuple(self._trees.values())
|
|
92
|
+
|
|
93
|
+
def tree_count(self) -> int:
|
|
94
|
+
return len(self._trees)
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def from_snapshot(
|
|
98
|
+
cls, snapshot: ConversationSnapshot
|
|
99
|
+
) -> tuple[
|
|
100
|
+
TreeRepository,
|
|
101
|
+
ConversationSnapshot,
|
|
102
|
+
tuple[NodeUiTarget, ...],
|
|
103
|
+
]:
|
|
104
|
+
repo = cls()
|
|
105
|
+
normalized = ConversationSnapshot()
|
|
106
|
+
stale_targets: list[NodeUiTarget] = []
|
|
107
|
+
for tree_snapshot in snapshot.trees.values():
|
|
108
|
+
try:
|
|
109
|
+
tree = MessageTree.from_snapshot(tree_snapshot)
|
|
110
|
+
except (KeyError, TypeError, ValueError) as exc:
|
|
111
|
+
logger.warning(
|
|
112
|
+
"Skipping invalid messaging tree snapshot: {}",
|
|
113
|
+
type(exc).__name__,
|
|
114
|
+
)
|
|
115
|
+
continue
|
|
116
|
+
references = tree_snapshot.lookup_ids()
|
|
117
|
+
if tree.identity in repo._trees or any(
|
|
118
|
+
repo.has_reference(tree.identity.scope, reference)
|
|
119
|
+
for reference in references
|
|
120
|
+
):
|
|
121
|
+
logger.warning("Skipping duplicate messaging tree {}", tree.identity)
|
|
122
|
+
continue
|
|
123
|
+
repo._trees[tree.identity] = tree
|
|
124
|
+
for reference in references:
|
|
125
|
+
repo._reference_to_tree[(tree.identity.scope, reference)] = (
|
|
126
|
+
tree.identity
|
|
127
|
+
)
|
|
128
|
+
stale_targets.extend(tree.restored_stale_targets)
|
|
129
|
+
normalized_tree = tree.restored_snapshot
|
|
130
|
+
if normalized_tree is not None:
|
|
131
|
+
normalized = normalized.with_tree(normalized_tree)
|
|
132
|
+
return repo, normalized, tuple(stale_targets)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
__all__ = ["TreeRepository"]
|