code-muse 0.0.1__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.
- code_muse/__init__.py +26 -0
- code_muse/__main__.py +10 -0
- code_muse/agents/__init__.py +31 -0
- code_muse/agents/_builder.py +214 -0
- code_muse/agents/_compaction.py +506 -0
- code_muse/agents/_diagnostics.py +171 -0
- code_muse/agents/_history.py +382 -0
- code_muse/agents/_key_listeners.py +148 -0
- code_muse/agents/_non_streaming_render.py +148 -0
- code_muse/agents/_runtime.py +596 -0
- code_muse/agents/agent_creator_agent.py +603 -0
- code_muse/agents/agent_helios.py +47 -0
- code_muse/agents/agent_manager.py +740 -0
- code_muse/agents/agent_muse.py +78 -0
- code_muse/agents/agent_planning.py +44 -0
- code_muse/agents/agent_qa_melpomene.py +207 -0
- code_muse/agents/base_agent.py +194 -0
- code_muse/agents/event_stream_handler.py +361 -0
- code_muse/agents/json_agent.py +201 -0
- code_muse/agents/prompt_v3.py +521 -0
- code_muse/agents/subagent_stream_handler.py +273 -0
- code_muse/callbacks.py +941 -0
- code_muse/chatgpt_codex_client.py +333 -0
- code_muse/claude_cache_client.py +853 -0
- code_muse/cli_runner/__init__.py +319 -0
- code_muse/cli_runner/args.py +63 -0
- code_muse/cli_runner/loop.py +510 -0
- code_muse/cli_runner/resume.py +72 -0
- code_muse/cli_runner/runner.py +161 -0
- code_muse/command_line/__init__.py +1 -0
- code_muse/command_line/add_model_menu.py +1331 -0
- code_muse/command_line/agent_menu.py +674 -0
- code_muse/command_line/attachments.py +397 -0
- code_muse/command_line/autosave_menu.py +709 -0
- code_muse/command_line/clipboard.py +528 -0
- code_muse/command_line/colors_menu.py +530 -0
- code_muse/command_line/command_handler.py +262 -0
- code_muse/command_line/command_registry.py +150 -0
- code_muse/command_line/config_commands.py +711 -0
- code_muse/command_line/core_commands.py +740 -0
- code_muse/command_line/diff_menu.py +865 -0
- code_muse/command_line/file_path_completion.py +73 -0
- code_muse/command_line/load_context_completion.py +57 -0
- code_muse/command_line/model_picker_completion.py +512 -0
- code_muse/command_line/model_settings_menu.py +983 -0
- code_muse/command_line/onboarding_slides.py +162 -0
- code_muse/command_line/onboarding_wizard.py +337 -0
- code_muse/command_line/pagination.py +41 -0
- code_muse/command_line/pin_command_completion.py +329 -0
- code_muse/command_line/prompt_toolkit_completion.py +886 -0
- code_muse/command_line/session_commands.py +304 -0
- code_muse/command_line/shell_passthrough.py +145 -0
- code_muse/command_line/skills_completion.py +158 -0
- code_muse/command_line/types.py +18 -0
- code_muse/command_line/uc_menu.py +908 -0
- code_muse/command_line/utils.py +105 -0
- code_muse/command_line/wiggum_state.py +77 -0
- code_muse/config.py +1138 -0
- code_muse/config_agent.py +168 -0
- code_muse/config_appearance.py +241 -0
- code_muse/config_model.py +357 -0
- code_muse/config_security.py +73 -0
- code_muse/error_logging.py +132 -0
- code_muse/evals/__init__.py +35 -0
- code_muse/evals/eval_helpers.py +81 -0
- code_muse/evals/eval_runner.py +299 -0
- code_muse/evals/sample_evals/__init__.py +1 -0
- code_muse/evals/sample_evals/eval_frugal_reads.py +59 -0
- code_muse/evals/sample_evals/eval_memory_planning.py +31 -0
- code_muse/evals/sample_evals/eval_shell_efficiency.py +39 -0
- code_muse/evals/sample_evals/eval_tool_masking.py +33 -0
- code_muse/fs_scan_cache/__init__.py +31 -0
- code_muse/fs_scan_cache/invalidation_hooks.py +89 -0
- code_muse/fs_scan_cache/scan_cache_core.cpython-314-darwin.so +0 -0
- code_muse/fs_scan_cache/scan_cache_core.pyx +203 -0
- code_muse/fs_scan_cache/tool_integration.py +309 -0
- code_muse/fs_scan_cache/ttl_policy.py +44 -0
- code_muse/gemini_code_assist.py +383 -0
- code_muse/gemini_model.py +838 -0
- code_muse/hook_engine/README.md +105 -0
- code_muse/hook_engine/__init__.py +21 -0
- code_muse/hook_engine/aliases.py +153 -0
- code_muse/hook_engine/engine.py +221 -0
- code_muse/hook_engine/executor.py +347 -0
- code_muse/hook_engine/matcher.py +154 -0
- code_muse/hook_engine/models.py +245 -0
- code_muse/hook_engine/registry.py +114 -0
- code_muse/hook_engine/trust.py +268 -0
- code_muse/hook_engine/validator.py +144 -0
- code_muse/http_utils.py +360 -0
- code_muse/keymap.py +128 -0
- code_muse/list_filtering.py +26 -0
- code_muse/main.py +10 -0
- code_muse/messaging/__init__.py +259 -0
- code_muse/messaging/bus.py +621 -0
- code_muse/messaging/commands.py +166 -0
- code_muse/messaging/markdown_patches.py +57 -0
- code_muse/messaging/message_queue.py +397 -0
- code_muse/messaging/messages.py +591 -0
- code_muse/messaging/queue_console.py +269 -0
- code_muse/messaging/renderers.py +308 -0
- code_muse/messaging/rich_renderer.py +1158 -0
- code_muse/messaging/shimmer.py +154 -0
- code_muse/messaging/spinner/__init__.py +87 -0
- code_muse/messaging/spinner/console_spinner.py +250 -0
- code_muse/messaging/spinner/spinner_base.py +82 -0
- code_muse/messaging/subagent_console.py +458 -0
- code_muse/model_factory.py +1203 -0
- code_muse/model_switching.py +59 -0
- code_muse/model_utils.py +156 -0
- code_muse/models.json +66 -0
- code_muse/models_cache/__init__.py +26 -0
- code_muse/models_cache/blocking_lru_cache.py +98 -0
- code_muse/models_cache/cache_writer.py +86 -0
- code_muse/models_cache/sha256_hash.cpython-314-darwin.so +0 -0
- code_muse/models_cache/sha256_hash.pyx +34 -0
- code_muse/models_cache/startup_integration.py +75 -0
- code_muse/models_dev_api.json +1 -0
- code_muse/models_dev_parser.py +590 -0
- code_muse/motion.py +126 -0
- code_muse/plugins/__init__.py +471 -0
- code_muse/plugins/agent_skills/__init__.py +32 -0
- code_muse/plugins/agent_skills/config.py +176 -0
- code_muse/plugins/agent_skills/discovery.py +309 -0
- code_muse/plugins/agent_skills/downloader.py +389 -0
- code_muse/plugins/agent_skills/installer.py +19 -0
- code_muse/plugins/agent_skills/metadata.py +293 -0
- code_muse/plugins/agent_skills/prompt_builder.py +66 -0
- code_muse/plugins/agent_skills/register_callbacks.py +298 -0
- code_muse/plugins/agent_skills/remote_catalog.py +320 -0
- code_muse/plugins/agent_skills/skill_catalog.py +254 -0
- code_muse/plugins/agent_skills/skills_install_menu.py +690 -0
- code_muse/plugins/agent_skills/skills_menu.py +791 -0
- code_muse/plugins/autonomous_memory/__init__.py +39 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-darwin.so +0 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.pyx +291 -0
- code_muse/plugins/autonomous_memory/consolidation.py +82 -0
- code_muse/plugins/autonomous_memory/extraction.py +382 -0
- code_muse/plugins/autonomous_memory/lease_lock.py +105 -0
- code_muse/plugins/autonomous_memory/memory_injection.py +59 -0
- code_muse/plugins/autonomous_memory/register_callbacks.py +268 -0
- code_muse/plugins/autonomous_memory/secret_scanner.py +62 -0
- code_muse/plugins/autonomous_memory/session_scanner.py +163 -0
- code_muse/plugins/aws_bedrock/__init__.py +14 -0
- code_muse/plugins/aws_bedrock/config.py +99 -0
- code_muse/plugins/aws_bedrock/register_callbacks.py +241 -0
- code_muse/plugins/aws_bedrock/utils.py +153 -0
- code_muse/plugins/azure_foundry/README.md +238 -0
- code_muse/plugins/azure_foundry/__init__.py +15 -0
- code_muse/plugins/azure_foundry/config.py +125 -0
- code_muse/plugins/azure_foundry/discovery.py +187 -0
- code_muse/plugins/azure_foundry/register_callbacks.py +495 -0
- code_muse/plugins/azure_foundry/token.py +180 -0
- code_muse/plugins/azure_foundry/utils.py +345 -0
- code_muse/plugins/build_filter/__init__.py +1 -0
- code_muse/plugins/build_filter/register_callbacks.py +201 -0
- code_muse/plugins/build_filter/strategies/__init__.py +1 -0
- code_muse/plugins/build_filter/strategies/build.py +397 -0
- code_muse/plugins/chatgpt_oauth/__init__.py +6 -0
- code_muse/plugins/chatgpt_oauth/config.py +52 -0
- code_muse/plugins/chatgpt_oauth/oauth_flow.py +338 -0
- code_muse/plugins/chatgpt_oauth/register_callbacks.py +172 -0
- code_muse/plugins/chatgpt_oauth/test_plugin.py +301 -0
- code_muse/plugins/chatgpt_oauth/utils.py +538 -0
- code_muse/plugins/checkpointing/__init__.py +29 -0
- code_muse/plugins/checkpointing/checkpoint_hook.py +51 -0
- code_muse/plugins/checkpointing/conversation_snapshots.py +117 -0
- code_muse/plugins/checkpointing/register_callbacks.py +51 -0
- code_muse/plugins/checkpointing/restore_command.py +263 -0
- code_muse/plugins/checkpointing/rewind_shortcut.py +88 -0
- code_muse/plugins/checkpointing/shadow_git.py +90 -0
- code_muse/plugins/claude_code_hooks/__init__.py +1 -0
- code_muse/plugins/claude_code_hooks/config.py +188 -0
- code_muse/plugins/claude_code_hooks/register_callbacks.py +208 -0
- code_muse/plugins/claude_code_oauth/README.md +167 -0
- code_muse/plugins/claude_code_oauth/SETUP.md +93 -0
- code_muse/plugins/claude_code_oauth/__init__.py +25 -0
- code_muse/plugins/claude_code_oauth/config.py +52 -0
- code_muse/plugins/claude_code_oauth/fast_mode.py +124 -0
- code_muse/plugins/claude_code_oauth/prompt_handler.py +63 -0
- code_muse/plugins/claude_code_oauth/register_callbacks.py +547 -0
- code_muse/plugins/claude_code_oauth/test_fast_mode.py +165 -0
- code_muse/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_muse/plugins/claude_code_oauth/token_refresh_heartbeat.py +237 -0
- code_muse/plugins/claude_code_oauth/utils.py +664 -0
- code_muse/plugins/copilot_auth/__init__.py +11 -0
- code_muse/plugins/copilot_auth/config.py +91 -0
- code_muse/plugins/copilot_auth/reasoning_client.py +409 -0
- code_muse/plugins/copilot_auth/register_callbacks.py +461 -0
- code_muse/plugins/copilot_auth/utils.py +584 -0
- code_muse/plugins/custom_commands/__init__.py +14 -0
- code_muse/plugins/custom_commands/args_injection.py +82 -0
- code_muse/plugins/custom_commands/command_discovery.py +89 -0
- code_muse/plugins/custom_commands/command_toml_schema.py +71 -0
- code_muse/plugins/custom_commands/register_callbacks.py +176 -0
- code_muse/plugins/customizable_commands/__init__.py +0 -0
- code_muse/plugins/customizable_commands/register_callbacks.py +136 -0
- code_muse/plugins/destructive_command_guard/__init__.py +14 -0
- code_muse/plugins/destructive_command_guard/detector.py +375 -0
- code_muse/plugins/destructive_command_guard/register_callbacks.py +148 -0
- code_muse/plugins/example_custom_command/README.md +280 -0
- code_muse/plugins/example_custom_command/register_callbacks.py +51 -0
- code_muse/plugins/file_permission_handler/__init__.py +4 -0
- code_muse/plugins/file_permission_handler/register_callbacks.py +441 -0
- code_muse/plugins/filter_engine/__init__.py +30 -0
- code_muse/plugins/filter_engine/classifier.py +153 -0
- code_muse/plugins/filter_engine/content_detector.py +184 -0
- code_muse/plugins/filter_engine/dispatcher.py +244 -0
- code_muse/plugins/filter_engine/register_callbacks.py +188 -0
- code_muse/plugins/filter_engine/registry.py +279 -0
- code_muse/plugins/filter_engine/strategies/__init__.py +8 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.pyx +348 -0
- code_muse/plugins/filter_engine/strategies/ast_parser.py +167 -0
- code_muse/plugins/filter_engine/strategies/code.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/code.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/code.pyx +584 -0
- code_muse/plugins/filter_engine/strategies/git.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/git.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/git.pyx +438 -0
- code_muse/plugins/filter_engine/strategies/json_compressor.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/json_compressor.pyx +253 -0
- code_muse/plugins/filter_engine/strategies/json_patterns.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/json_patterns.pyx +178 -0
- code_muse/plugins/filter_engine/strategies/lint.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/lint.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/lint.pyx +626 -0
- code_muse/plugins/filter_engine/strategies/test.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/test.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/test.pyx +431 -0
- code_muse/plugins/filter_engine/verbosity.py +63 -0
- code_muse/plugins/force_push_guard/__init__.py +5 -0
- code_muse/plugins/force_push_guard/detector.py +96 -0
- code_muse/plugins/force_push_guard/register_callbacks.py +144 -0
- code_muse/plugins/force_push_guard/test_detector.py +143 -0
- code_muse/plugins/frontend_emitter/__init__.py +25 -0
- code_muse/plugins/frontend_emitter/emitter.py +121 -0
- code_muse/plugins/frontend_emitter/register_callbacks.py +259 -0
- code_muse/plugins/gac/__init__.py +4 -0
- code_muse/plugins/gac/git_ops.py +136 -0
- code_muse/plugins/gac/prompt.py +191 -0
- code_muse/plugins/gac/register_callbacks.py +82 -0
- code_muse/plugins/hook_creator/__init__.py +1 -0
- code_muse/plugins/hook_creator/register_callbacks.py +34 -0
- code_muse/plugins/hook_manager/__init__.py +1 -0
- code_muse/plugins/hook_manager/config.py +289 -0
- code_muse/plugins/hook_manager/hooks_menu.py +563 -0
- code_muse/plugins/hook_manager/register_callbacks.py +227 -0
- code_muse/plugins/hook_monitor/register_callbacks.py +36 -0
- code_muse/plugins/mindpack/__init__.py +0 -0
- code_muse/plugins/mindpack/factory.py +930 -0
- code_muse/plugins/mindpack/judge.py +573 -0
- code_muse/plugins/mindpack/memory.py +100 -0
- code_muse/plugins/mindpack/mindpack_menu.py +1552 -0
- code_muse/plugins/mindpack/orchestration.py +605 -0
- code_muse/plugins/mindpack/register_callbacks.py +175 -0
- code_muse/plugins/mindpack/schemas.py +358 -0
- code_muse/plugins/mindpack/tools.py +387 -0
- code_muse/plugins/oauth_muse_html.py +226 -0
- code_muse/plugins/ollama_setup/__init__.py +5 -0
- code_muse/plugins/ollama_setup/completer.py +36 -0
- code_muse/plugins/ollama_setup/register_callbacks.py +410 -0
- code_muse/plugins/plan_command/__init__.py +0 -0
- code_muse/plugins/plan_command/register_callbacks.py +206 -0
- code_muse/plugins/plan_mode/__init__.py +37 -0
- code_muse/plugins/plan_mode/mode_cycling.py +40 -0
- code_muse/plugins/plan_mode/plan_generation.py +68 -0
- code_muse/plugins/plan_mode/plan_hooks.py +74 -0
- code_muse/plugins/plan_mode/plan_mode_tools.py +138 -0
- code_muse/plugins/plan_mode/register_callbacks.py +121 -0
- code_muse/plugins/plugin_trust/register_callbacks.py +140 -0
- code_muse/plugins/policy_engine/__init__.py +46 -0
- code_muse/plugins/policy_engine/approval_flow_integration.py +59 -0
- code_muse/plugins/policy_engine/policy_evaluator.py +75 -0
- code_muse/plugins/policy_engine/policy_file_discovery.py +90 -0
- code_muse/plugins/policy_engine/policy_toml_schema.py +115 -0
- code_muse/plugins/policy_engine/register_callbacks.py +112 -0
- code_muse/plugins/pop_command/__init__.py +1 -0
- code_muse/plugins/pop_command/register_callbacks.py +189 -0
- code_muse/plugins/prompt_newline/__init__.py +13 -0
- code_muse/plugins/prompt_newline/config.py +19 -0
- code_muse/plugins/prompt_newline/register_callbacks.py +159 -0
- code_muse/plugins/safety_status/__init__.py +0 -0
- code_muse/plugins/safety_status/register_callbacks.py +113 -0
- code_muse/plugins/semantic_compression/__init__.py +6 -0
- code_muse/plugins/semantic_compression/compressor.py +295 -0
- code_muse/plugins/semantic_compression/config.py +123 -0
- code_muse/plugins/semantic_compression/register_callbacks.py +320 -0
- code_muse/plugins/shell_minimizer/__init__.py +50 -0
- code_muse/plugins/shell_minimizer/builtin_filters.toml +393 -0
- code_muse/plugins/shell_minimizer/pipeline.py +556 -0
- code_muse/plugins/shell_minimizer/primitives.py +482 -0
- code_muse/plugins/shell_minimizer/register_callbacks.py +276 -0
- code_muse/plugins/shell_safety/__init__.py +6 -0
- code_muse/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_muse/plugins/shell_safety/command_cache.py +149 -0
- code_muse/plugins/shell_safety/register_callbacks.py +202 -0
- code_muse/plugins/synthetic_status/__init__.py +1 -0
- code_muse/plugins/synthetic_status/register_callbacks.py +128 -0
- code_muse/plugins/synthetic_status/status_api.py +145 -0
- code_muse/plugins/token_caching/__init__.py +21 -0
- code_muse/plugins/token_caching/cache_hit_tracking.py +128 -0
- code_muse/plugins/token_caching/cacheable_prefix_detection.py +28 -0
- code_muse/plugins/token_caching/register_callbacks.py +54 -0
- code_muse/plugins/token_caching/stats_display.py +35 -0
- code_muse/plugins/token_tracking/__init__.py +26 -0
- code_muse/plugins/token_tracking/database.py +381 -0
- code_muse/plugins/token_tracking/edit_analyzer.py +97 -0
- code_muse/plugins/token_tracking/record.py +55 -0
- code_muse/plugins/token_tracking/register_callbacks.py +277 -0
- code_muse/plugins/token_tracking/reports.py +329 -0
- code_muse/plugins/universal_constructor/__init__.py +13 -0
- code_muse/plugins/universal_constructor/models.py +136 -0
- code_muse/plugins/universal_constructor/register_callbacks.py +47 -0
- code_muse/plugins/universal_constructor/registry.py +390 -0
- code_muse/plugins/universal_constructor/runner.py +474 -0
- code_muse/plugins/universal_constructor/safety.py +440 -0
- code_muse/plugins/universal_constructor/sandbox.py +584 -0
- code_muse/provider_identity.py +105 -0
- code_muse/pydantic_patches.py +410 -0
- code_muse/reopenable_async_client.py +233 -0
- code_muse/round_robin_model.py +151 -0
- code_muse/secret_storage.py +74 -0
- code_muse/security/__init__.py +1 -0
- code_muse/security/redaction.cpython-314-darwin.so +0 -0
- code_muse/security/redaction.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/security/redaction.pyx +135 -0
- code_muse/session_storage.py +565 -0
- code_muse/status_display.py +261 -0
- code_muse/stream_parser/__init__.py +76 -0
- code_muse/stream_parser/assistant_text_parser.py +90 -0
- code_muse/stream_parser/citation_parser.py +76 -0
- code_muse/stream_parser/inline_hidden_tag_parser.py +236 -0
- code_muse/stream_parser/proposed_plan_parser.py +158 -0
- code_muse/stream_parser/stream_text_chunk.py +23 -0
- code_muse/stream_parser/stream_text_parser.py +27 -0
- code_muse/stream_parser/tagged_line_parser.cpython-314-darwin.so +0 -0
- code_muse/stream_parser/tagged_line_parser.pyx +251 -0
- code_muse/stream_parser/utf8_stream_parser.cpython-314-darwin.so +0 -0
- code_muse/stream_parser/utf8_stream_parser.pyx +206 -0
- code_muse/summarization_agent.py +308 -0
- code_muse/terminal_utils.cpython-314-darwin.so +0 -0
- code_muse/terminal_utils.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/terminal_utils.pyx +483 -0
- code_muse/tools/__init__.py +459 -0
- code_muse/tools/agent_tools.py +613 -0
- code_muse/tools/ask_user_question/__init__.py +26 -0
- code_muse/tools/ask_user_question/constants.py +73 -0
- code_muse/tools/ask_user_question/demo_tui.py +55 -0
- code_muse/tools/ask_user_question/handler.py +232 -0
- code_muse/tools/ask_user_question/models.py +302 -0
- code_muse/tools/ask_user_question/registration.py +37 -0
- code_muse/tools/ask_user_question/renderers.py +336 -0
- code_muse/tools/ask_user_question/terminal_ui.py +327 -0
- code_muse/tools/ask_user_question/theme.py +156 -0
- code_muse/tools/ask_user_question/tui_loop.py +422 -0
- code_muse/tools/background_jobs.py +99 -0
- code_muse/tools/browser/__init__.py +37 -0
- code_muse/tools/browser/browser_control.py +289 -0
- code_muse/tools/browser/browser_interactions.py +545 -0
- code_muse/tools/browser/browser_locators.py +640 -0
- code_muse/tools/browser/browser_manager.py +376 -0
- code_muse/tools/browser/browser_navigation.py +251 -0
- code_muse/tools/browser/browser_screenshot.py +180 -0
- code_muse/tools/browser/browser_scripts.py +462 -0
- code_muse/tools/browser/browser_workflows.py +222 -0
- code_muse/tools/chrome_cdp/__init__.py +1070 -0
- code_muse/tools/chrome_cdp/register_callbacks.py +61 -0
- code_muse/tools/command_runner.py +1401 -0
- code_muse/tools/common.py +1407 -0
- code_muse/tools/display.py +87 -0
- code_muse/tools/file_modifications.py +1099 -0
- code_muse/tools/file_operations.py +860 -0
- code_muse/tools/image_tools.py +185 -0
- code_muse/tools/meetin_proxy/__init__.py +243 -0
- code_muse/tools/meetin_proxy/capture_addon.py +82 -0
- code_muse/tools/meetin_proxy/proxy_manager.py +326 -0
- code_muse/tools/meetin_proxy/register_callbacks.py +45 -0
- code_muse/tools/path_policy.py +219 -0
- code_muse/tools/skills_tools.py +586 -0
- code_muse/tools/subagent_context.py +158 -0
- code_muse/tools/tools_content.py +50 -0
- code_muse/tools/universal_constructor.py +965 -0
- code_muse/uvx_detection.py +241 -0
- code_muse/version_checker.py +86 -0
- code_muse-0.0.1.data/data/code_muse/models.json +66 -0
- code_muse-0.0.1.data/data/code_muse/models_dev_api.json +1 -0
- code_muse-0.0.1.dist-info/METADATA +845 -0
- code_muse-0.0.1.dist-info/RECORD +394 -0
- code_muse-0.0.1.dist-info/WHEEL +4 -0
- code_muse-0.0.1.dist-info/entry_points.txt +2 -0
- code_muse-0.0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"""ChatGPT OAuth flow closely matching the ChatMock implementation."""
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
import urllib.parse
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
|
|
13
|
+
from code_muse.messaging import emit_error, emit_info, emit_success, emit_warning
|
|
14
|
+
|
|
15
|
+
from ..oauth_muse_html import oauth_failure_html, oauth_success_html
|
|
16
|
+
from .config import CHATGPT_OAUTH_CONFIG
|
|
17
|
+
from .utils import (
|
|
18
|
+
add_models_to_extra_config,
|
|
19
|
+
assign_redirect_uri,
|
|
20
|
+
load_stored_tokens,
|
|
21
|
+
parse_jwt_claims,
|
|
22
|
+
prepare_oauth_context,
|
|
23
|
+
save_tokens,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
REQUIRED_PORT = CHATGPT_OAUTH_CONFIG["required_port"]
|
|
27
|
+
URL_BASE = f"http://127.0.0.1:{REQUIRED_PORT}"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class TokenData:
|
|
32
|
+
id_token: str
|
|
33
|
+
access_token: str
|
|
34
|
+
refresh_token: str
|
|
35
|
+
account_id: str
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class AuthBundle:
|
|
40
|
+
api_key: str | None
|
|
41
|
+
token_data: TokenData
|
|
42
|
+
last_refresh: str
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def success_url(self) -> str:
|
|
46
|
+
"""The URL to redirect to after successful auth."""
|
|
47
|
+
return f"{URL_BASE}/success"
|
|
48
|
+
|
|
49
|
+
def __iter__(self):
|
|
50
|
+
"""Allow unpacking as ``bundle, success_url = exchange_code(...)``."""
|
|
51
|
+
yield self
|
|
52
|
+
yield self.success_url
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class _OAuthServer(HTTPServer):
|
|
56
|
+
allow_reuse_address = True # Reduce port-in-use flakes in tests
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
*,
|
|
61
|
+
client_id: str,
|
|
62
|
+
verbose: bool = False,
|
|
63
|
+
) -> None:
|
|
64
|
+
super().__init__(
|
|
65
|
+
("127.0.0.1", REQUIRED_PORT), _CallbackHandler, bind_and_activate=True
|
|
66
|
+
)
|
|
67
|
+
self.exit_code = 1
|
|
68
|
+
self.verbose = verbose
|
|
69
|
+
self.client_id = client_id
|
|
70
|
+
self.issuer = CHATGPT_OAUTH_CONFIG["issuer"]
|
|
71
|
+
self.token_endpoint = CHATGPT_OAUTH_CONFIG["token_url"]
|
|
72
|
+
|
|
73
|
+
# Create fresh OAuth context for this server instance
|
|
74
|
+
context = prepare_oauth_context()
|
|
75
|
+
self.redirect_uri = assign_redirect_uri(context, REQUIRED_PORT)
|
|
76
|
+
self.context = context
|
|
77
|
+
|
|
78
|
+
def auth_url(self) -> str:
|
|
79
|
+
params = {
|
|
80
|
+
"response_type": "code",
|
|
81
|
+
"client_id": self.client_id,
|
|
82
|
+
"redirect_uri": self.redirect_uri,
|
|
83
|
+
"scope": CHATGPT_OAUTH_CONFIG["scope"],
|
|
84
|
+
"code_challenge": self.context.code_challenge,
|
|
85
|
+
"code_challenge_method": "S256",
|
|
86
|
+
"id_token_add_organizations": "true",
|
|
87
|
+
"codex_cli_simplified_flow": "true",
|
|
88
|
+
"state": self.context.state,
|
|
89
|
+
}
|
|
90
|
+
return f"{self.issuer}/oauth/authorize?" + urllib.parse.urlencode(params)
|
|
91
|
+
|
|
92
|
+
def exchange_code(self, code: str) -> AuthBundle:
|
|
93
|
+
data = {
|
|
94
|
+
"grant_type": "authorization_code",
|
|
95
|
+
"code": code,
|
|
96
|
+
"redirect_uri": self.redirect_uri,
|
|
97
|
+
"client_id": self.client_id,
|
|
98
|
+
"code_verifier": self.context.code_verifier,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
response = httpx.post(
|
|
102
|
+
self.token_endpoint,
|
|
103
|
+
data=data,
|
|
104
|
+
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
105
|
+
timeout=30,
|
|
106
|
+
)
|
|
107
|
+
response.raise_for_status()
|
|
108
|
+
payload = response.json()
|
|
109
|
+
|
|
110
|
+
id_token = payload.get("id_token", "")
|
|
111
|
+
access_token = payload.get("access_token", "")
|
|
112
|
+
refresh_token = payload.get("refresh_token", "")
|
|
113
|
+
|
|
114
|
+
id_token_claims = parse_jwt_claims(id_token) or {}
|
|
115
|
+
|
|
116
|
+
auth_claims = id_token_claims.get("https://api.openai.com/auth") or {}
|
|
117
|
+
chatgpt_account_id = auth_claims.get("chatgpt_account_id", "")
|
|
118
|
+
# Extract org_id from nested auth structure like ChatMock
|
|
119
|
+
organizations = auth_claims.get("organizations", [])
|
|
120
|
+
org_id = None
|
|
121
|
+
if organizations:
|
|
122
|
+
default_org = next(
|
|
123
|
+
(org for org in organizations if org.get("is_default")),
|
|
124
|
+
organizations[0],
|
|
125
|
+
)
|
|
126
|
+
org_id = default_org.get("id")
|
|
127
|
+
# Fallback to top-level org_id if still not found
|
|
128
|
+
if not org_id:
|
|
129
|
+
org_id = id_token_claims.get("organization_id")
|
|
130
|
+
|
|
131
|
+
token_data = TokenData(
|
|
132
|
+
id_token=id_token,
|
|
133
|
+
access_token=access_token,
|
|
134
|
+
refresh_token=refresh_token,
|
|
135
|
+
account_id=chatgpt_account_id,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Use access_token directly as the API key (ChatMock behaviour)
|
|
139
|
+
api_key = token_data.access_token
|
|
140
|
+
|
|
141
|
+
last_refresh = (
|
|
142
|
+
datetime.datetime.now(datetime.UTC).isoformat().replace("+00:00", "Z")
|
|
143
|
+
)
|
|
144
|
+
return AuthBundle(
|
|
145
|
+
api_key=api_key, token_data=token_data, last_refresh=last_refresh
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class _CallbackHandler(BaseHTTPRequestHandler):
|
|
150
|
+
server: _OAuthServer
|
|
151
|
+
|
|
152
|
+
def do_GET(self) -> None: # noqa: N802
|
|
153
|
+
path = urllib.parse.urlparse(self.path).path
|
|
154
|
+
if path == "/success":
|
|
155
|
+
success_html = oauth_success_html(
|
|
156
|
+
"ChatGPT",
|
|
157
|
+
"You can now close this window and return to Muse.",
|
|
158
|
+
)
|
|
159
|
+
self._send_html(success_html)
|
|
160
|
+
self._shutdown_after_delay(2.0)
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
if path != "/auth/callback":
|
|
164
|
+
self._send_failure(404, "Callback endpoint not found for the Muse parade.")
|
|
165
|
+
self._shutdown()
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
query = urllib.parse.urlparse(self.path).query
|
|
169
|
+
params = urllib.parse.parse_qs(query)
|
|
170
|
+
|
|
171
|
+
code = params.get("code", [None])[0]
|
|
172
|
+
if not code:
|
|
173
|
+
self._send_failure(400, "Missing auth code — the token treat rolled away.")
|
|
174
|
+
self._shutdown()
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
state = params.get("state", [None])[0]
|
|
178
|
+
if state != self.server.context.state:
|
|
179
|
+
self._send_failure(400, "Invalid state parameter — possible CSRF attack.")
|
|
180
|
+
self._shutdown()
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
auth_bundle = self.server.exchange_code(code)
|
|
185
|
+
except Exception as exc: # noqa: BLE001
|
|
186
|
+
self._send_failure(500, f"Token exchange failed: {exc}")
|
|
187
|
+
self._shutdown()
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
tokens = {
|
|
191
|
+
"id_token": auth_bundle.token_data.id_token,
|
|
192
|
+
"access_token": auth_bundle.token_data.access_token,
|
|
193
|
+
"refresh_token": auth_bundle.token_data.refresh_token,
|
|
194
|
+
"account_id": auth_bundle.token_data.account_id,
|
|
195
|
+
"last_refresh": auth_bundle.last_refresh,
|
|
196
|
+
}
|
|
197
|
+
if auth_bundle.api_key:
|
|
198
|
+
tokens["api_key"] = auth_bundle.api_key
|
|
199
|
+
|
|
200
|
+
if save_tokens(tokens):
|
|
201
|
+
self.server.exit_code = 0
|
|
202
|
+
# Redirect to a token-free success URL
|
|
203
|
+
self._send_redirect(f"{URL_BASE}/success")
|
|
204
|
+
else:
|
|
205
|
+
self._send_failure(
|
|
206
|
+
500, "Unable to persist auth file — a Muse probably misplaced it."
|
|
207
|
+
)
|
|
208
|
+
self._shutdown()
|
|
209
|
+
self._shutdown_after_delay(2.0)
|
|
210
|
+
|
|
211
|
+
def do_POST(self) -> None: # noqa: N802
|
|
212
|
+
self._send_failure(
|
|
213
|
+
404, "POST not supported — the pups only fetch GET requests."
|
|
214
|
+
)
|
|
215
|
+
self._shutdown()
|
|
216
|
+
|
|
217
|
+
def log_message(self, fmt: str, *args: Any) -> None: # noqa: A003
|
|
218
|
+
if getattr(self.server, "verbose", False):
|
|
219
|
+
safe_args = []
|
|
220
|
+
for arg in args:
|
|
221
|
+
arg_str = str(arg)
|
|
222
|
+
if "?" in arg_str:
|
|
223
|
+
parsed = urllib.parse.urlparse(arg_str)
|
|
224
|
+
if parsed.query:
|
|
225
|
+
arg_str = urllib.parse.urlunparse(
|
|
226
|
+
parsed._replace(query="<redacted>")
|
|
227
|
+
)
|
|
228
|
+
safe_args.append(arg_str)
|
|
229
|
+
super().log_message(fmt, *safe_args)
|
|
230
|
+
|
|
231
|
+
def _send_redirect(self, url: str) -> None:
|
|
232
|
+
self.send_response(302)
|
|
233
|
+
self.send_header("Location", url)
|
|
234
|
+
self.end_headers()
|
|
235
|
+
|
|
236
|
+
def _send_html(self, body: str, status: int = 200) -> None:
|
|
237
|
+
encoded = body.encode("utf-8")
|
|
238
|
+
self.send_response(status)
|
|
239
|
+
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
240
|
+
self.send_header("Content-Length", str(len(encoded)))
|
|
241
|
+
self.end_headers()
|
|
242
|
+
self.wfile.write(encoded)
|
|
243
|
+
|
|
244
|
+
def _send_failure(self, status: int, reason: str) -> None:
|
|
245
|
+
failure_html = oauth_failure_html("ChatGPT", reason)
|
|
246
|
+
self._send_html(failure_html, status)
|
|
247
|
+
|
|
248
|
+
def _shutdown(self) -> None:
|
|
249
|
+
threading.Thread(target=self.server.shutdown, daemon=True).start()
|
|
250
|
+
|
|
251
|
+
def _shutdown_after_delay(self, seconds: float = 2.0) -> None:
|
|
252
|
+
def _later() -> None:
|
|
253
|
+
try:
|
|
254
|
+
time.sleep(seconds)
|
|
255
|
+
finally:
|
|
256
|
+
self._shutdown()
|
|
257
|
+
|
|
258
|
+
threading.Thread(target=_later, daemon=True).start()
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def run_oauth_flow() -> None:
|
|
262
|
+
existing_tokens = load_stored_tokens()
|
|
263
|
+
if existing_tokens and existing_tokens.get("access_token"):
|
|
264
|
+
emit_warning("Existing ChatGPT tokens will be overwritten.")
|
|
265
|
+
|
|
266
|
+
try:
|
|
267
|
+
server = _OAuthServer(client_id=CHATGPT_OAUTH_CONFIG["client_id"])
|
|
268
|
+
except OSError as exc:
|
|
269
|
+
emit_error(f"Could not start OAuth server on port {REQUIRED_PORT}: {exc}")
|
|
270
|
+
emit_info(f"Use `lsof -ti:{REQUIRED_PORT} | xargs kill` to free the port.")
|
|
271
|
+
return
|
|
272
|
+
|
|
273
|
+
auth_url = server.auth_url()
|
|
274
|
+
emit_info(f"Open this URL in your browser: {auth_url}")
|
|
275
|
+
|
|
276
|
+
server_thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
277
|
+
server_thread.start()
|
|
278
|
+
|
|
279
|
+
webbrowser_opened = False
|
|
280
|
+
try:
|
|
281
|
+
import webbrowser
|
|
282
|
+
|
|
283
|
+
from code_muse.tools.common import should_suppress_browser
|
|
284
|
+
|
|
285
|
+
if should_suppress_browser():
|
|
286
|
+
emit_info(f"[HEADLESS MODE] Would normally open: {auth_url}")
|
|
287
|
+
else:
|
|
288
|
+
webbrowser_opened = webbrowser.open(auth_url)
|
|
289
|
+
except Exception as exc: # noqa: BLE001
|
|
290
|
+
emit_warning(f"Could not open browser automatically: {exc}")
|
|
291
|
+
|
|
292
|
+
if not webbrowser_opened and not should_suppress_browser():
|
|
293
|
+
emit_warning("Please open the URL manually if the browser did not open.")
|
|
294
|
+
|
|
295
|
+
emit_info("Waiting for authentication callback…")
|
|
296
|
+
|
|
297
|
+
elapsed = 0.0
|
|
298
|
+
timeout = CHATGPT_OAUTH_CONFIG["callback_timeout"]
|
|
299
|
+
interval = 0.25
|
|
300
|
+
while elapsed < timeout:
|
|
301
|
+
time.sleep(interval)
|
|
302
|
+
elapsed += interval
|
|
303
|
+
if server.exit_code == 0:
|
|
304
|
+
break
|
|
305
|
+
|
|
306
|
+
server.shutdown()
|
|
307
|
+
server_thread.join(timeout=5)
|
|
308
|
+
|
|
309
|
+
if server.exit_code != 0:
|
|
310
|
+
emit_error("Authentication failed or timed out.")
|
|
311
|
+
return
|
|
312
|
+
|
|
313
|
+
tokens = load_stored_tokens()
|
|
314
|
+
if not tokens:
|
|
315
|
+
emit_error("Tokens saved during OAuth flow could not be loaded.")
|
|
316
|
+
return
|
|
317
|
+
|
|
318
|
+
api_key = tokens.get("api_key")
|
|
319
|
+
if api_key:
|
|
320
|
+
emit_success("Successfully obtained OAuth access token for API access.")
|
|
321
|
+
emit_info(
|
|
322
|
+
f"Access token saved and available via {CHATGPT_OAUTH_CONFIG['api_key_env_var']}"
|
|
323
|
+
)
|
|
324
|
+
else:
|
|
325
|
+
emit_warning(
|
|
326
|
+
"No API key obtained. You may need to configure projects at platform.openai.com."
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
if api_key:
|
|
330
|
+
emit_info("Registering ChatGPT Codex models…")
|
|
331
|
+
from .utils import fetch_chatgpt_models
|
|
332
|
+
|
|
333
|
+
account_id = tokens.get("account_id", "")
|
|
334
|
+
models = fetch_chatgpt_models(api_key, account_id)
|
|
335
|
+
if models and add_models_to_extra_config(models):
|
|
336
|
+
emit_success(
|
|
337
|
+
"ChatGPT models registered. Use the `chatgpt-` prefix in /model."
|
|
338
|
+
)
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""ChatGPT OAuth plugin callbacks aligned with ChatMock flow.
|
|
2
|
+
|
|
3
|
+
Provides OAuth authentication for ChatGPT models and registers
|
|
4
|
+
the 'chatgpt_oauth' model type handler.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from code_muse.callbacks import register_callback
|
|
11
|
+
from code_muse.messaging import emit_info, emit_success, emit_warning
|
|
12
|
+
from code_muse.model_switching import set_model_and_reload_agent
|
|
13
|
+
|
|
14
|
+
from .config import CHATGPT_OAUTH_CONFIG, get_token_storage_path
|
|
15
|
+
from .oauth_flow import run_oauth_flow
|
|
16
|
+
from .utils import (
|
|
17
|
+
get_valid_access_token,
|
|
18
|
+
load_chatgpt_models,
|
|
19
|
+
load_stored_tokens,
|
|
20
|
+
remove_chatgpt_models,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _custom_help() -> list[tuple[str, str]]:
|
|
25
|
+
return [
|
|
26
|
+
(
|
|
27
|
+
"chatgpt-auth",
|
|
28
|
+
"Authenticate with ChatGPT via OAuth and import available models",
|
|
29
|
+
),
|
|
30
|
+
(
|
|
31
|
+
"chatgpt-status",
|
|
32
|
+
"Check ChatGPT OAuth authentication status and configured models",
|
|
33
|
+
),
|
|
34
|
+
("chatgpt-logout", "Remove ChatGPT OAuth tokens and imported models"),
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _handle_chatgpt_status() -> None:
|
|
39
|
+
tokens = load_stored_tokens()
|
|
40
|
+
if tokens and tokens.get("access_token"):
|
|
41
|
+
emit_success("🔐 ChatGPT OAuth: Authenticated")
|
|
42
|
+
|
|
43
|
+
api_key = tokens.get("api_key")
|
|
44
|
+
if api_key:
|
|
45
|
+
os.environ[CHATGPT_OAUTH_CONFIG["api_key_env_var"]] = api_key
|
|
46
|
+
emit_info("✅ OAuth access token available for API requests")
|
|
47
|
+
else:
|
|
48
|
+
emit_warning("⚠️ No access token obtained. Authentication may have failed.")
|
|
49
|
+
|
|
50
|
+
chatgpt_models = [
|
|
51
|
+
name
|
|
52
|
+
for name, cfg in load_chatgpt_models().items()
|
|
53
|
+
if cfg.get("oauth_source") == "chatgpt-oauth-plugin"
|
|
54
|
+
]
|
|
55
|
+
if chatgpt_models:
|
|
56
|
+
emit_info(f"🎯 Configured ChatGPT models: {', '.join(chatgpt_models)}")
|
|
57
|
+
else:
|
|
58
|
+
emit_warning("⚠️ No ChatGPT models configured yet.")
|
|
59
|
+
else:
|
|
60
|
+
emit_warning("🔓 ChatGPT OAuth: Not authenticated")
|
|
61
|
+
emit_info("🌐 Run /chatgpt-auth to launch the browser sign-in flow.")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _handle_chatgpt_logout() -> None:
|
|
65
|
+
token_path = get_token_storage_path()
|
|
66
|
+
if token_path.exists():
|
|
67
|
+
token_path.unlink()
|
|
68
|
+
emit_info("Removed ChatGPT OAuth tokens")
|
|
69
|
+
|
|
70
|
+
if CHATGPT_OAUTH_CONFIG["api_key_env_var"] in os.environ:
|
|
71
|
+
del os.environ[CHATGPT_OAUTH_CONFIG["api_key_env_var"]]
|
|
72
|
+
|
|
73
|
+
removed = remove_chatgpt_models()
|
|
74
|
+
if removed:
|
|
75
|
+
emit_info(f"Removed {removed} ChatGPT models from configuration")
|
|
76
|
+
|
|
77
|
+
emit_success("ChatGPT logout complete")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _handle_custom_command(command: str, name: str) -> bool | None:
|
|
81
|
+
if not name:
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
if name == "chatgpt-auth":
|
|
85
|
+
run_oauth_flow()
|
|
86
|
+
set_model_and_reload_agent("chatgpt-gpt-5.4")
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
if name == "chatgpt-status":
|
|
90
|
+
_handle_chatgpt_status()
|
|
91
|
+
return True
|
|
92
|
+
|
|
93
|
+
if name == "chatgpt-logout":
|
|
94
|
+
_handle_chatgpt_logout()
|
|
95
|
+
return True
|
|
96
|
+
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _create_chatgpt_oauth_model(
|
|
101
|
+
model_name: str, model_config: dict, config: dict
|
|
102
|
+
) -> Any:
|
|
103
|
+
"""Create a ChatGPT OAuth model instance.
|
|
104
|
+
|
|
105
|
+
This handler is registered via the 'register_model_type' callback to handle
|
|
106
|
+
models with type='chatgpt_oauth'.
|
|
107
|
+
"""
|
|
108
|
+
from pydantic_ai.models.openai import OpenAIResponsesModel
|
|
109
|
+
from pydantic_ai.providers.openai import OpenAIProvider
|
|
110
|
+
|
|
111
|
+
from code_muse.chatgpt_codex_client import create_codex_async_client
|
|
112
|
+
from code_muse.http_utils import get_cert_bundle_path
|
|
113
|
+
|
|
114
|
+
# Get a valid access token (refreshing if needed)
|
|
115
|
+
access_token = get_valid_access_token()
|
|
116
|
+
if not access_token:
|
|
117
|
+
emit_warning(
|
|
118
|
+
f"Failed to get valid ChatGPT OAuth token; skipping model '{model_config.get('name')}'. "
|
|
119
|
+
"Run /chatgpt-auth to authenticate."
|
|
120
|
+
)
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
# Get account_id from stored tokens (required for ChatGPT-Account-Id header)
|
|
124
|
+
tokens = load_stored_tokens()
|
|
125
|
+
account_id = tokens.get("account_id", "") if tokens else ""
|
|
126
|
+
if not account_id:
|
|
127
|
+
emit_warning(
|
|
128
|
+
f"No account_id found in ChatGPT OAuth tokens; skipping model '{model_config.get('name')}'. "
|
|
129
|
+
"Run /chatgpt-auth to re-authenticate."
|
|
130
|
+
)
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
# Build headers for ChatGPT Codex API
|
|
134
|
+
originator = CHATGPT_OAUTH_CONFIG.get("originator", "codex_cli_rs")
|
|
135
|
+
client_version = CHATGPT_OAUTH_CONFIG.get("client_version", "0.72.0")
|
|
136
|
+
|
|
137
|
+
headers = {
|
|
138
|
+
"ChatGPT-Account-Id": account_id,
|
|
139
|
+
"originator": originator,
|
|
140
|
+
"User-Agent": f"{originator}/{client_version}",
|
|
141
|
+
}
|
|
142
|
+
# Merge with any headers from model config
|
|
143
|
+
config_headers = model_config.get("custom_endpoint", {}).get("headers", {})
|
|
144
|
+
headers.update(config_headers)
|
|
145
|
+
|
|
146
|
+
# Get base URL - Codex API uses chatgpt.com, not api.openai.com
|
|
147
|
+
base_url = model_config.get("custom_endpoint", {}).get(
|
|
148
|
+
"url", CHATGPT_OAUTH_CONFIG["api_base_url"]
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Create HTTP client with Codex interceptor for store=false injection
|
|
152
|
+
verify = get_cert_bundle_path()
|
|
153
|
+
client = create_codex_async_client(headers=headers, verify=verify)
|
|
154
|
+
|
|
155
|
+
provider = OpenAIProvider(
|
|
156
|
+
api_key=access_token,
|
|
157
|
+
base_url=base_url,
|
|
158
|
+
http_client=client,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# ChatGPT Codex API only supports Responses format
|
|
162
|
+
return OpenAIResponsesModel(model_name=model_config["name"], provider=provider)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _register_model_types() -> list[dict[str, Any]]:
|
|
166
|
+
"""Register the chatgpt_oauth model type handler."""
|
|
167
|
+
return [{"type": "chatgpt_oauth", "handler": _create_chatgpt_oauth_model}]
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
register_callback("custom_command_help", _custom_help)
|
|
171
|
+
register_callback("custom_command", _handle_custom_command)
|
|
172
|
+
register_callback("register_model_type", _register_model_types)
|