code-puppy 0.0.613__tar.gz → 0.0.615__tar.gz
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_puppy-0.0.613 → code_puppy-0.0.615}/PKG-INFO +1 -1
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/autosave_menu.py +8 -56
- code_puppy-0.0.615/code_puppy/plugins/grok_oauth/__init__.py +6 -0
- code_puppy-0.0.615/code_puppy/plugins/grok_oauth/config.py +49 -0
- code_puppy-0.0.615/code_puppy/plugins/grok_oauth/oauth_flow.py +213 -0
- code_puppy-0.0.615/code_puppy/plugins/grok_oauth/register_callbacks.py +132 -0
- code_puppy-0.0.615/code_puppy/plugins/grok_oauth/test_plugin.py +160 -0
- code_puppy-0.0.615/code_puppy/plugins/grok_oauth/utils.py +195 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/session_storage.py +9 -75
- {code_puppy-0.0.613 → code_puppy-0.0.615}/pyproject.toml +1 -1
- {code_puppy-0.0.613 → code_puppy-0.0.615}/.gitignore +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/LICENSE +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/README.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/__main__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_builder.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_compaction.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_diagnostics.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_history.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_key_listeners.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_non_streaming_render.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_run_signals.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_runtime.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/_steer_processor.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/agent_code_puppy.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/agent_creator_agent.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/agent_helios.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/agent_manager.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/agent_planning.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/agent_qa_kitten.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/base_agent.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/event_stream_handler.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/json_agent.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/run_stats.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/smooth_stream.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/agents/subagent_stream_handler.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/chatgpt_codex_client.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/claude_cache_client.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/cli_runner.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/add_model_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/agent_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/attachments.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/autosave_search.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/clipboard.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/colors_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/command_handler.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/command_registry.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/config_apply.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/config_commands.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/core_commands.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/diff_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/file_index.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/file_path_completion.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/judges_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/load_context_completion.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/base.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/custom_server_form.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/edit_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/handler.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/help_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/install_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/install_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/list_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/logs_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/remove_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/restart_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/search_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/silence_warning_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/start_all_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/start_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/status_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/stop_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/utils.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp_binding_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/mcp_completion.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/model_picker_completion.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/model_settings_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/onboarding_slides.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/onboarding_wizard.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/pagination.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/pin_command_completion.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/prompt_toolkit_completion.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/session_commands.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu_catalog.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu_render.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu_schema.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu_settings.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu_shims.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/set_menu_values.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/shell_passthrough.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/skills_completion.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/uc_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/utils.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/command_line/wiggum_state.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/error_logging.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/gemini_code_assist.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/gemini_model.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/README.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/aliases.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/engine.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/executor.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/matcher.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/models.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/registry.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/hook_engine/validator.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/http_utils.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/keymap.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/list_filtering.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/main.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/agent_bindings.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/async_lifecycle.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/blocking_startup.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/captured_stdio_server.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/circuit_breaker.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/config_wizard.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/dashboard.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/error_isolation.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/examples/retry_example.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/health_monitor.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/managed_server.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/manager.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/mcp_logs.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/registry.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/retry_manager.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/server_registry_catalog.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/status_tracker.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/system_tools.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_/tool_arg_coercion.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_prompts/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/mcp_prompts/hook_creator.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/bar_painters.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/bar_rendering.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/bottom_bar.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/bus.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/commands.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/editor_actions.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/editor_completion.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/editor_display.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/editor_history.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/editor_keys.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/editor_paste.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/line_editor.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/markdown_patches.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/message_queue.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/messages.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/pause_controller.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/prompt_prefix_style.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/queue_console.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/renderers.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/rich_renderer.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/run_ui.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/run_ui_wiring.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/spinner/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/subagent_console.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/messaging/transcript_guard.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/model_descriptions.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/model_factory.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/model_switching.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/model_utils.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/models.json +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/models_dev_parser.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/discovery.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/downloader.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/enabled_skills.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/installer.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/metadata.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/prompt_builder.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/remote_catalog.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/skill_catalog.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/skill_commands.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/skills_install_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/agent_skills/skills_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/aws_bedrock/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/aws_bedrock/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/aws_bedrock/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/aws_bedrock/utils.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/README.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/discovery.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/token.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/azure_foundry/utils.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/btw/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/btw/inline_view.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/btw/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/btw/side_query.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/chatgpt_oauth/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/chatgpt_oauth/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/chatgpt_oauth/oauth_flow.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/chatgpt_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/chatgpt_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/chatgpt_oauth/utils.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_hooks/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_hooks/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_hooks/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/README.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/SETUP.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/fast_mode.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/prompt_handler.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/test_fast_mode.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/claude_code_oauth/utils.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/code_puppy_agent/SKILL.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/code_puppy_agent/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/code_puppy_agent/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/context_indicator/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/context_indicator/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/context_indicator/usage.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/copilot_auth/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/copilot_auth/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/copilot_auth/reasoning_client.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/copilot_auth/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/copilot_auth/utils.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/customizable_commands/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/customizable_commands/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/cancel.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/commands.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/lifecycle.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/runtime.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/startup_lock.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/workflow_ids.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/dbos_durable_exec/wrapper.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/destructive_command_guard/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/destructive_command_guard/detector.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/destructive_command_guard/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/emoji_filter/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/emoji_filter/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/emoji_filter/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/emoji_filter/stripper.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/example_custom_command/README.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/example_custom_command/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/file_permission_handler/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/file_permission_handler/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/force_push_guard/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/force_push_guard/detector.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/force_push_guard/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/force_push_guard/test_detector.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/fork/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/fork/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/frontend_emitter/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/frontend_emitter/emitter.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/frontend_emitter/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/frontend_emitter/session_context.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/herdr/README.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/herdr/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/herdr/client.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/herdr/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/herdr/reporter.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/hook_creator/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/hook_creator/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/hook_manager/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/hook_manager/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/hook_manager/hooks_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/hook_manager/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/no_tools/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/no_tools/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/oauth_puppy_html.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/obsidian_agent/README.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/obsidian_agent/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/obsidian_agent/agent_obsidian.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/obsidian_agent/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/ollama/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/ollama/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/ollama_setup/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/ollama_setup/completer.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/ollama_setup/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/plugin_contributions.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/plugin_meta.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/plugin_text_utils.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/plugins_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/plugins_menu_layout.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/plugins_menu_render.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/project_trust_flow.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/plugin_list/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/pop_command/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/pop_command/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prompt_newline/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prompt_newline/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prompt_newline/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prune/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prune/prune_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prune/prune_model.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prune/prune_render.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/prune/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/README.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/commands.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/kennel.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/packer.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/recorder.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/retriever.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/schema.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/state.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/tools.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_kennel/wings.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/README.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/builtin_frames.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/commands.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/picker.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/puppy_spinner/spinners.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/quick_resume/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/quick_resume/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/review_pr/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/review_pr/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/shell_safety/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/shell_safety/agent_shell_safety.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/shell_safety/command_cache.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/shell_safety/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/stack_dump/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/stack_dump/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/payload.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/prompt_patch.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/runner.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/statusline/statusline_command.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/steer_queue/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/steer_queue/queue_menu.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/steer_queue/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/README.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/coalesce_patch.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/panel_render.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/resume_repaint.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/subagent_panel/state.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/switch_agent_resume/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/switch_agent_resume/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/README.md +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/bundled_palettes.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/content_styles.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/osc_palette.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/picker.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/rich_themes.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/theme/themes.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/token_ratio_learner/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/token_ratio_learner/ratios.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/token_ratio_learner/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/trust.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/trust_notice.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/universal_constructor/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/universal_constructor/models.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/universal_constructor/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/universal_constructor/registry.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/universal_constructor/sandbox.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wide_completion_menu/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wide_completion_menu/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wiggum/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wiggum/judge.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wiggum/judge_config.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wiggum/register_callbacks.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/plugins/wiggum/state.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/provider_credentials.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/provider_identity.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/pydantic_patches.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/reopenable_async_client.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/round_robin_model.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/session_lifecycle.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/session_migration.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/status_display.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/summarization_agent.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/terminal_utils.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/agent_tools.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/constants.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/demo_tui.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/handler.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/models.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/registration.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/renderers.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/terminal_ui.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/theme.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/ask_user_question/tui_loop.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/__init__.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_control.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_interactions.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_locator_resolver.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_locators.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_manager.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_navigation.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_page_snapshot.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_screenshot.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_scripts.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_semantic_interactions.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/browser/browser_workflows.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/command_runner.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/common.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/display.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/file_modifications.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/file_operations.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/image_tools.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/model_tools.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/skills_tools.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/subagent_context.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/subagent_invocation.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/tools_content.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/tools/universal_constructor.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/undo_manager.py +0 -0
- {code_puppy-0.0.613 → code_puppy-0.0.615}/code_puppy/version_checker.py +0 -0
|
@@ -48,26 +48,8 @@ def _get_session_metadata(base_dir: Path, session_name: str) -> dict:
|
|
|
48
48
|
return {}
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
def _is_auto_flavored(session_name: str) -> bool:
|
|
52
|
-
"""Return True for system-generated session names.
|
|
53
|
-
|
|
54
|
-
Matches the ``auto_session_`` prefix reserved by
|
|
55
|
-
``code_puppy.session_lifecycle.is_valid_session_name``. Pre-unification,
|
|
56
|
-
all autosaves carried this prefix; the unified-autosave migration introduces user-named
|
|
57
|
-
entries (from ``-r NAME``, ``/dump_context``, ``/load_context``) into
|
|
58
|
-
the same store, hence the need to distinguish.
|
|
59
|
-
"""
|
|
60
|
-
return session_name.startswith("auto_session_")
|
|
61
|
-
|
|
62
|
-
|
|
63
51
|
def _get_session_entries(base_dir: Path) -> List[Tuple[str, dict]]:
|
|
64
|
-
"""Get all sessions with their metadata.
|
|
65
|
-
|
|
66
|
-
Returns entries grouped "named first, auto-flavored second", each group
|
|
67
|
-
sorted mtime-descending. Section headers (if any) are inserted at
|
|
68
|
-
render time -- see ``_render_menu_panel`` -- to keep this function's
|
|
69
|
-
return shape stable and pagination math straightforward.
|
|
70
|
-
"""
|
|
52
|
+
"""Get all sessions with their metadata, most recent first."""
|
|
71
53
|
try:
|
|
72
54
|
sessions = list_sessions(base_dir)
|
|
73
55
|
except (FileNotFoundError, PermissionError):
|
|
@@ -93,11 +75,8 @@ def _get_session_entries(base_dir: Path) -> List[Tuple[str, dict]]:
|
|
|
93
75
|
return datetime.min
|
|
94
76
|
return datetime.min
|
|
95
77
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
named.sort(key=sort_key, reverse=True)
|
|
99
|
-
auto.sort(key=sort_key, reverse=True)
|
|
100
|
-
return named + auto
|
|
78
|
+
entries.sort(key=sort_key, reverse=True)
|
|
79
|
+
return entries
|
|
101
80
|
|
|
102
81
|
|
|
103
82
|
def _extract_last_user_message(history: list) -> str:
|
|
@@ -237,24 +216,9 @@ def _render_menu_panel(
|
|
|
237
216
|
lines.append(("", "Cancel"))
|
|
238
217
|
return lines
|
|
239
218
|
|
|
240
|
-
# Show sessions for current page. We inject visual section headers
|
|
241
|
-
# at named->auto boundaries within the visible page (and at the very
|
|
242
|
-
# top if the page starts in either section). Section headers do NOT
|
|
243
|
-
# consume entry indices -- they're cosmetic only -- so selection and
|
|
244
|
-
# pagination math stay untouched.
|
|
245
|
-
prev_section = None
|
|
246
219
|
for i in range(start_idx, end_idx):
|
|
247
220
|
session_name, metadata = entries[i]
|
|
248
221
|
is_selected = i == selected_idx
|
|
249
|
-
section = "auto" if _is_auto_flavored(session_name) else "named"
|
|
250
|
-
|
|
251
|
-
if section != prev_section:
|
|
252
|
-
header = "Named sessions" if section == "named" else "Auto-saved sessions"
|
|
253
|
-
# Blank line between sections (skip for the very first one).
|
|
254
|
-
if prev_section is not None:
|
|
255
|
-
lines.append(("", "\n"))
|
|
256
|
-
lines.append(("fg:ansibrightcyan bold", f" {header}\n"))
|
|
257
|
-
prev_section = section
|
|
258
222
|
|
|
259
223
|
# Format timestamp
|
|
260
224
|
timestamp = metadata.get("timestamp", "unknown")
|
|
@@ -264,20 +228,13 @@ def _render_menu_panel(
|
|
|
264
228
|
except Exception:
|
|
265
229
|
time_str = "unknown time"
|
|
266
230
|
|
|
267
|
-
# Format message count
|
|
231
|
+
# Format message count. auto_session_* names are opaque noise — hide them.
|
|
232
|
+
# User-named sessions keep the parenthetical so they stay distinguishable.
|
|
268
233
|
msg_count = metadata.get("message_count", "?")
|
|
269
|
-
|
|
270
|
-
# For named sessions, include the name so users can tell them
|
|
271
|
-
# apart at a glance. Auto-flavored sessions get the deterministic
|
|
272
|
-
# label persisted at save time (#246) -- '[project] first prompt' --
|
|
273
|
-
# since the auto_session_* names are noise.
|
|
274
|
-
if section == "named":
|
|
275
|
-
label = f"{time_str} \u2022 {msg_count} msgs ({session_name})"
|
|
276
|
-
else:
|
|
234
|
+
if session_name.startswith("auto_session_"):
|
|
277
235
|
label = f"{time_str} \u2022 {msg_count} msgs"
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
label = f"{label} \u2014 {derived}"
|
|
236
|
+
else:
|
|
237
|
+
label = f"{time_str} \u2022 {msg_count} msgs ({session_name})"
|
|
281
238
|
|
|
282
239
|
# Highlight selected item
|
|
283
240
|
if is_selected:
|
|
@@ -427,11 +384,6 @@ def _render_preview_panel(base_dir: Path, entry: Optional[Tuple[str, dict]]) ->
|
|
|
427
384
|
lines.append(("", session_name))
|
|
428
385
|
lines.append(("", "\n"))
|
|
429
386
|
|
|
430
|
-
derived = metadata.get("label")
|
|
431
|
-
if derived:
|
|
432
|
-
lines.append(("fg:ansibrightblack", f" Label: {derived}"))
|
|
433
|
-
lines.append(("", "\n"))
|
|
434
|
-
|
|
435
387
|
timestamp = metadata.get("timestamp", "unknown")
|
|
436
388
|
try:
|
|
437
389
|
dt = datetime.fromisoformat(timestamp)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Configuration for the Grok (x.ai) OAuth plugin.
|
|
2
|
+
|
|
3
|
+
Endpoints, client id, and scopes mirror the official Grok CLI OAuth flow
|
|
4
|
+
(as implemented by https://pi.dev/packages/pi-xai-oauth).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
|
|
10
|
+
from code_puppy import config
|
|
11
|
+
|
|
12
|
+
GROK_OAUTH_CONFIG: Dict[str, Any] = {
|
|
13
|
+
# OIDC issuer + discovery. Authorization/token endpoints are resolved
|
|
14
|
+
# dynamically from the discovery document and validated against *.x.ai.
|
|
15
|
+
"issuer": "https://auth.x.ai",
|
|
16
|
+
"discovery_url": "https://auth.x.ai/.well-known/openid-configuration",
|
|
17
|
+
# Public OAuth client used by the official Grok CLI.
|
|
18
|
+
"client_id": "b1a00492-073a-47ea-816f-4c329264a828",
|
|
19
|
+
"scope": "openid profile email offline_access grok-cli:access api:access",
|
|
20
|
+
# Localhost callback. xAI allows an ephemeral-port fallback when the
|
|
21
|
+
# preferred port is busy.
|
|
22
|
+
"redirect_host": "127.0.0.1",
|
|
23
|
+
"redirect_port": 56121,
|
|
24
|
+
"redirect_path": "/callback",
|
|
25
|
+
"callback_timeout": 180,
|
|
26
|
+
# Refresh this many seconds before the token actually expires.
|
|
27
|
+
"refresh_skew_seconds": 120,
|
|
28
|
+
# Grok models are served from the regular xAI API with the OAuth
|
|
29
|
+
# access token as the bearer key.
|
|
30
|
+
"api_base_url": "https://api.x.ai/v1",
|
|
31
|
+
# Registered model names are prefixed to avoid catalogue collisions.
|
|
32
|
+
"prefix": "xai-",
|
|
33
|
+
"default_model": "grok-4.5",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Grok models made available after authentication.
|
|
37
|
+
GROK_MODELS: Dict[str, Dict[str, Any]] = {
|
|
38
|
+
"grok-4.5": {
|
|
39
|
+
"context_length": 500_000,
|
|
40
|
+
"supported_settings": ["reasoning_effort"],
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_token_storage_path() -> Path:
|
|
46
|
+
"""Path for storing OAuth tokens (uses XDG_DATA_HOME)."""
|
|
47
|
+
data_dir = Path(config.DATA_DIR)
|
|
48
|
+
data_dir.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
49
|
+
return data_dir / "grok_oauth.json"
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""Grok (x.ai) OAuth flow matching the official Grok CLI / pi-xai-oauth."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import secrets
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
import urllib.parse
|
|
9
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
|
|
13
|
+
|
|
14
|
+
from ..oauth_puppy_html import oauth_failure_html, oauth_success_html
|
|
15
|
+
from .config import GROK_OAUTH_CONFIG
|
|
16
|
+
from .utils import (
|
|
17
|
+
exchange_token,
|
|
18
|
+
fetch_discovery,
|
|
19
|
+
generate_pkce_pair,
|
|
20
|
+
load_stored_tokens,
|
|
21
|
+
save_tokens,
|
|
22
|
+
tokens_from_payload,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
_ALLOWED_CORS_ORIGINS = ("https://accounts.x.ai", "https://auth.x.ai")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class _OAuthServer(HTTPServer):
|
|
29
|
+
"""Localhost callback server holding the state for one OAuth attempt."""
|
|
30
|
+
|
|
31
|
+
def __init__(self) -> None:
|
|
32
|
+
host = GROK_OAUTH_CONFIG["redirect_host"]
|
|
33
|
+
preferred_port = GROK_OAUTH_CONFIG["redirect_port"]
|
|
34
|
+
try:
|
|
35
|
+
super().__init__((host, preferred_port), _CallbackHandler)
|
|
36
|
+
except OSError:
|
|
37
|
+
# Preferred port busy — xAI accepts ephemeral localhost ports.
|
|
38
|
+
super().__init__((host, 0), _CallbackHandler)
|
|
39
|
+
|
|
40
|
+
self.exit_code = 1
|
|
41
|
+
discovery = fetch_discovery()
|
|
42
|
+
self.authorization_endpoint = discovery["authorization_endpoint"]
|
|
43
|
+
self.token_endpoint = discovery["token_endpoint"]
|
|
44
|
+
self.state = secrets.token_hex(16)
|
|
45
|
+
self.nonce = secrets.token_hex(16)
|
|
46
|
+
self.code_verifier, self.code_challenge = generate_pkce_pair()
|
|
47
|
+
port = self.server_address[1]
|
|
48
|
+
self.redirect_uri = f"http://{host}:{port}{GROK_OAUTH_CONFIG['redirect_path']}"
|
|
49
|
+
|
|
50
|
+
def auth_url(self) -> str:
|
|
51
|
+
params = {
|
|
52
|
+
"response_type": "code",
|
|
53
|
+
"client_id": GROK_OAUTH_CONFIG["client_id"],
|
|
54
|
+
"redirect_uri": self.redirect_uri,
|
|
55
|
+
"scope": GROK_OAUTH_CONFIG["scope"],
|
|
56
|
+
"code_challenge": self.code_challenge,
|
|
57
|
+
"code_challenge_method": "S256",
|
|
58
|
+
"state": self.state,
|
|
59
|
+
"nonce": self.nonce,
|
|
60
|
+
}
|
|
61
|
+
return f"{self.authorization_endpoint}?{urllib.parse.urlencode(params)}"
|
|
62
|
+
|
|
63
|
+
def exchange_code(self, code: str) -> bool:
|
|
64
|
+
payload = exchange_token(
|
|
65
|
+
self.token_endpoint,
|
|
66
|
+
{
|
|
67
|
+
"grant_type": "authorization_code",
|
|
68
|
+
"code": code,
|
|
69
|
+
"redirect_uri": self.redirect_uri,
|
|
70
|
+
"client_id": GROK_OAUTH_CONFIG["client_id"],
|
|
71
|
+
"code_verifier": self.code_verifier,
|
|
72
|
+
},
|
|
73
|
+
)
|
|
74
|
+
return save_tokens(tokens_from_payload(payload, self.token_endpoint))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class _CallbackHandler(BaseHTTPRequestHandler):
|
|
78
|
+
server: "_OAuthServer"
|
|
79
|
+
|
|
80
|
+
def do_OPTIONS(self) -> None: # noqa: N802
|
|
81
|
+
self.send_response(204)
|
|
82
|
+
self._send_cors_headers()
|
|
83
|
+
self.end_headers()
|
|
84
|
+
|
|
85
|
+
def do_GET(self) -> None: # noqa: N802
|
|
86
|
+
parsed = urllib.parse.urlparse(self.path)
|
|
87
|
+
if parsed.path != GROK_OAUTH_CONFIG["redirect_path"]:
|
|
88
|
+
self._send_failure(404, "Callback endpoint not found for the puppy parade.")
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
params = urllib.parse.parse_qs(parsed.query)
|
|
92
|
+
error = params.get("error", [None])[0]
|
|
93
|
+
if error:
|
|
94
|
+
description = params.get("error_description", [error])[0]
|
|
95
|
+
self._send_failure(400, f"xAI authorization failed: {description}")
|
|
96
|
+
self._shutdown_after_delay()
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
if params.get("state", [None])[0] != self.server.state:
|
|
100
|
+
self._send_failure(
|
|
101
|
+
400, "OAuth state mismatch — a squirrel may have tampered with it."
|
|
102
|
+
)
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
code = params.get("code", [None])[0]
|
|
106
|
+
if not code:
|
|
107
|
+
self._send_failure(400, "Missing auth code — the token treat rolled away.")
|
|
108
|
+
self._shutdown_after_delay()
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
saved = self.server.exchange_code(code)
|
|
113
|
+
except Exception as exc: # noqa: BLE001
|
|
114
|
+
self._send_failure(500, f"Token exchange failed: {exc}")
|
|
115
|
+
self._shutdown_after_delay()
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
if saved:
|
|
119
|
+
self.server.exit_code = 0
|
|
120
|
+
self._send_html(
|
|
121
|
+
oauth_success_html(
|
|
122
|
+
"Grok",
|
|
123
|
+
"You can now close this window and return to Code Puppy.",
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
else:
|
|
127
|
+
self._send_failure(
|
|
128
|
+
500, "Unable to persist auth file — a puppy probably chewed it."
|
|
129
|
+
)
|
|
130
|
+
self._shutdown_after_delay()
|
|
131
|
+
|
|
132
|
+
def log_message(self, fmt: str, *args: Any) -> None: # noqa: A003
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
def _send_cors_headers(self) -> None:
|
|
136
|
+
origin = self.headers.get("Origin")
|
|
137
|
+
if origin not in _ALLOWED_CORS_ORIGINS:
|
|
138
|
+
return
|
|
139
|
+
self.send_header("Access-Control-Allow-Origin", origin)
|
|
140
|
+
self.send_header("Access-Control-Allow-Methods", "GET, OPTIONS")
|
|
141
|
+
self.send_header("Access-Control-Allow-Headers", "Content-Type")
|
|
142
|
+
self.send_header("Access-Control-Allow-Private-Network", "true")
|
|
143
|
+
self.send_header("Vary", "Origin")
|
|
144
|
+
|
|
145
|
+
def _send_html(self, body: str, status: int = 200) -> None:
|
|
146
|
+
encoded = body.encode("utf-8")
|
|
147
|
+
self.send_response(status)
|
|
148
|
+
self._send_cors_headers()
|
|
149
|
+
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
150
|
+
self.send_header("Content-Length", str(len(encoded)))
|
|
151
|
+
self.end_headers()
|
|
152
|
+
self.wfile.write(encoded)
|
|
153
|
+
|
|
154
|
+
def _send_failure(self, status: int, reason: str) -> None:
|
|
155
|
+
self._send_html(oauth_failure_html("Grok", reason), status)
|
|
156
|
+
|
|
157
|
+
def _shutdown_after_delay(self, seconds: float = 2.0) -> None:
|
|
158
|
+
def _later() -> None:
|
|
159
|
+
time.sleep(seconds)
|
|
160
|
+
self.server.shutdown()
|
|
161
|
+
|
|
162
|
+
threading.Thread(target=_later, daemon=True).start()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def run_oauth_flow() -> bool:
|
|
166
|
+
"""Run the browser OAuth flow. Returns True when tokens were saved."""
|
|
167
|
+
existing_tokens = load_stored_tokens()
|
|
168
|
+
if existing_tokens and existing_tokens.get("access_token"):
|
|
169
|
+
emit_warning("Existing Grok tokens will be overwritten.")
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
server = _OAuthServer()
|
|
173
|
+
except Exception as exc: # noqa: BLE001
|
|
174
|
+
emit_error(f"Could not start Grok OAuth flow: {exc}")
|
|
175
|
+
return False
|
|
176
|
+
|
|
177
|
+
auth_url = server.auth_url()
|
|
178
|
+
emit_info(f"Open this URL in your browser: {auth_url}")
|
|
179
|
+
|
|
180
|
+
server_thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
181
|
+
server_thread.start()
|
|
182
|
+
|
|
183
|
+
from code_puppy.tools.common import should_suppress_browser
|
|
184
|
+
|
|
185
|
+
if should_suppress_browser():
|
|
186
|
+
emit_info(f"[HEADLESS MODE] Would normally open: {auth_url}")
|
|
187
|
+
else:
|
|
188
|
+
try:
|
|
189
|
+
import webbrowser
|
|
190
|
+
|
|
191
|
+
if not webbrowser.open(auth_url):
|
|
192
|
+
emit_warning("Please open the URL manually — the browser stayed home.")
|
|
193
|
+
except Exception as exc: # noqa: BLE001
|
|
194
|
+
emit_warning(f"Could not open browser automatically: {exc}")
|
|
195
|
+
|
|
196
|
+
emit_info("Waiting for xAI authentication callback…")
|
|
197
|
+
|
|
198
|
+
elapsed = 0.0
|
|
199
|
+
timeout = GROK_OAUTH_CONFIG["callback_timeout"]
|
|
200
|
+
interval = 0.25
|
|
201
|
+
while elapsed < timeout and server.exit_code != 0:
|
|
202
|
+
time.sleep(interval)
|
|
203
|
+
elapsed += interval
|
|
204
|
+
|
|
205
|
+
server.shutdown()
|
|
206
|
+
server_thread.join(timeout=5)
|
|
207
|
+
|
|
208
|
+
if server.exit_code != 0:
|
|
209
|
+
emit_error("Grok authentication failed or timed out.")
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
emit_success("Grok OAuth complete — access token saved.")
|
|
213
|
+
return True
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""Grok (x.ai) OAuth plugin callbacks.
|
|
2
|
+
|
|
3
|
+
Provides OAuth authentication for Grok models (SuperGrok / Grok CLI flow)
|
|
4
|
+
and registers the 'grok_oauth' model type handler plus the Grok model
|
|
5
|
+
catalogue (headlined by grok-4.5).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
11
|
+
|
|
12
|
+
from code_puppy.callbacks import register_callback
|
|
13
|
+
from code_puppy.messaging import emit_info, emit_success, emit_warning
|
|
14
|
+
from code_puppy.model_switching import set_model_and_reload_agent
|
|
15
|
+
|
|
16
|
+
from .config import GROK_MODELS, GROK_OAUTH_CONFIG, get_token_storage_path
|
|
17
|
+
from .oauth_flow import run_oauth_flow
|
|
18
|
+
from .utils import get_valid_access_token, load_stored_tokens
|
|
19
|
+
|
|
20
|
+
_PREFIX = GROK_OAUTH_CONFIG["prefix"]
|
|
21
|
+
_DEFAULT_MODEL = f"{_PREFIX}{GROK_OAUTH_CONFIG['default_model']}"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _custom_help() -> List[Tuple[str, str]]:
|
|
25
|
+
return [
|
|
26
|
+
(
|
|
27
|
+
"grok-auth",
|
|
28
|
+
"Authenticate with x.ai (Grok) via OAuth and register Grok models",
|
|
29
|
+
),
|
|
30
|
+
("grok-status", "Check Grok OAuth authentication status"),
|
|
31
|
+
("grok-logout", "Remove Grok OAuth tokens and registered models"),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _handle_grok_status() -> None:
|
|
36
|
+
tokens = load_stored_tokens()
|
|
37
|
+
if tokens and tokens.get("access_token"):
|
|
38
|
+
emit_success(" Grok OAuth: Authenticated")
|
|
39
|
+
emit_info(f" Available Grok models: {', '.join(_grok_model_names())}")
|
|
40
|
+
else:
|
|
41
|
+
emit_warning(" Grok OAuth: Not authenticated")
|
|
42
|
+
emit_info(" Run /grok-auth to launch the browser sign-in flow.")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _handle_grok_logout() -> None:
|
|
46
|
+
token_path = get_token_storage_path()
|
|
47
|
+
if token_path.exists():
|
|
48
|
+
token_path.unlink()
|
|
49
|
+
emit_info("Removed Grok OAuth tokens")
|
|
50
|
+
emit_success("Grok logout complete")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _handle_custom_command(command: str, name: str) -> Optional[bool]:
|
|
54
|
+
if not name:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
if name == "grok-auth":
|
|
58
|
+
if run_oauth_flow():
|
|
59
|
+
emit_info(f"Registering Grok models: {', '.join(_grok_model_names())}")
|
|
60
|
+
set_model_and_reload_agent(_DEFAULT_MODEL)
|
|
61
|
+
return True
|
|
62
|
+
|
|
63
|
+
if name == "grok-status":
|
|
64
|
+
_handle_grok_status()
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
if name == "grok-logout":
|
|
68
|
+
_handle_grok_logout()
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _grok_model_names() -> List[str]:
|
|
75
|
+
return [f"{_PREFIX}{model_id}" for model_id in GROK_MODELS]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _load_grok_models() -> Dict[str, Any]:
|
|
79
|
+
"""Inject Grok models into the catalogue once authenticated."""
|
|
80
|
+
tokens = load_stored_tokens()
|
|
81
|
+
if not (tokens and tokens.get("access_token")):
|
|
82
|
+
return {}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
f"{_PREFIX}{model_id}": {
|
|
86
|
+
"type": "grok_oauth",
|
|
87
|
+
"name": model_id,
|
|
88
|
+
"context_length": spec["context_length"],
|
|
89
|
+
"supported_settings": spec.get("supported_settings", []),
|
|
90
|
+
"oauth_source": "grok-oauth-plugin",
|
|
91
|
+
}
|
|
92
|
+
for model_id, spec in GROK_MODELS.items()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _create_grok_oauth_model(model_name: str, model_config: Dict, config: Dict) -> Any:
|
|
97
|
+
"""Create a Grok OAuth model instance.
|
|
98
|
+
|
|
99
|
+
This handler is registered via the 'register_model_type' callback to
|
|
100
|
+
handle models with type='grok_oauth'. Grok models speak the OpenAI
|
|
101
|
+
Responses API on api.x.ai with the OAuth access token as bearer key.
|
|
102
|
+
"""
|
|
103
|
+
from pydantic_ai.models.openai import OpenAIResponsesModel
|
|
104
|
+
from pydantic_ai.providers.openai import OpenAIProvider
|
|
105
|
+
|
|
106
|
+
from code_puppy.http_utils import create_async_client
|
|
107
|
+
|
|
108
|
+
access_token = get_valid_access_token()
|
|
109
|
+
if not access_token:
|
|
110
|
+
emit_warning(
|
|
111
|
+
f"Failed to get valid Grok OAuth token; skipping model "
|
|
112
|
+
f"'{model_config.get('name')}'. Run /grok-auth to authenticate."
|
|
113
|
+
)
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
provider = OpenAIProvider(
|
|
117
|
+
api_key=access_token,
|
|
118
|
+
base_url=GROK_OAUTH_CONFIG["api_base_url"],
|
|
119
|
+
http_client=create_async_client(model_name=model_name),
|
|
120
|
+
)
|
|
121
|
+
return OpenAIResponsesModel(model_name=model_config["name"], provider=provider)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _register_model_types() -> List[Dict[str, Any]]:
|
|
125
|
+
"""Register the grok_oauth model type handler."""
|
|
126
|
+
return [{"type": "grok_oauth", "handler": _create_grok_oauth_model}]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
register_callback("custom_command_help", _custom_help)
|
|
130
|
+
register_callback("custom_command", _handle_custom_command)
|
|
131
|
+
register_callback("register_model_type", _register_model_types)
|
|
132
|
+
register_callback("load_models_config", _load_grok_models)
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Tests for the Grok (x.ai) OAuth plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from unittest.mock import patch
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from code_puppy.plugins.grok_oauth import config as grok_config
|
|
11
|
+
from code_puppy.plugins.grok_oauth import register_callbacks as rc
|
|
12
|
+
from code_puppy.plugins.grok_oauth import utils
|
|
13
|
+
from code_puppy.plugins.grok_oauth.config import GROK_OAUTH_CONFIG
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def token_path(tmp_path):
|
|
18
|
+
path = tmp_path / "grok_oauth.json"
|
|
19
|
+
with (
|
|
20
|
+
patch.object(grok_config, "get_token_storage_path", return_value=path),
|
|
21
|
+
patch.object(utils, "get_token_storage_path", return_value=path),
|
|
22
|
+
patch.object(rc, "get_token_storage_path", return_value=path),
|
|
23
|
+
):
|
|
24
|
+
yield path
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _save_valid_tokens(**overrides):
|
|
28
|
+
tokens = {
|
|
29
|
+
"access_token": "fake-token",
|
|
30
|
+
"refresh_token": "fake-refresh",
|
|
31
|
+
"expires_at": time.time() + 3600,
|
|
32
|
+
"token_endpoint": "https://auth.x.ai/oauth2/token",
|
|
33
|
+
}
|
|
34
|
+
tokens.update(overrides)
|
|
35
|
+
assert utils.save_tokens(tokens)
|
|
36
|
+
return tokens
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_config_values():
|
|
40
|
+
assert GROK_OAUTH_CONFIG["issuer"] == "https://auth.x.ai"
|
|
41
|
+
assert "grok-cli:access" in GROK_OAUTH_CONFIG["scope"]
|
|
42
|
+
assert "api:access" in GROK_OAUTH_CONFIG["scope"]
|
|
43
|
+
assert GROK_OAUTH_CONFIG["api_base_url"] == "https://api.x.ai/v1"
|
|
44
|
+
assert GROK_OAUTH_CONFIG["default_model"] in grok_config.GROK_MODELS
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_pkce_pair_shape():
|
|
48
|
+
verifier, challenge = utils.generate_pkce_pair()
|
|
49
|
+
assert len(verifier) >= 43
|
|
50
|
+
assert "=" not in verifier and "=" not in challenge
|
|
51
|
+
# Deterministic: same verifier always hashes to the same challenge
|
|
52
|
+
import base64
|
|
53
|
+
import hashlib
|
|
54
|
+
|
|
55
|
+
digest = hashlib.sha256(verifier.encode()).digest()
|
|
56
|
+
expected = base64.urlsafe_b64encode(digest).decode().rstrip("=")
|
|
57
|
+
assert challenge == expected
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.mark.parametrize(
|
|
61
|
+
"url",
|
|
62
|
+
[
|
|
63
|
+
"http://auth.x.ai/oauth2/token", # not https
|
|
64
|
+
"https://evil.com/oauth2/token", # wrong host
|
|
65
|
+
"https://notx.ai/oauth2/token", # suffix trickery
|
|
66
|
+
"https://x.ai.evil.com/token", # prefix trickery
|
|
67
|
+
],
|
|
68
|
+
)
|
|
69
|
+
def test_validate_xai_endpoint_rejects(url):
|
|
70
|
+
with pytest.raises(ValueError):
|
|
71
|
+
utils.validate_xai_endpoint(url)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_validate_xai_endpoint_accepts():
|
|
75
|
+
assert utils.validate_xai_endpoint("https://auth.x.ai/oauth2/token")
|
|
76
|
+
assert utils.validate_xai_endpoint("https://x.ai/oauth2/token")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_save_and_load_tokens(token_path):
|
|
80
|
+
_save_valid_tokens()
|
|
81
|
+
loaded = utils.load_stored_tokens()
|
|
82
|
+
assert loaded["access_token"] == "fake-token"
|
|
83
|
+
assert oct(token_path.stat().st_mode & 0o777) == oct(0o600)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_get_valid_access_token_fresh(token_path):
|
|
87
|
+
_save_valid_tokens()
|
|
88
|
+
assert utils.get_valid_access_token() == "fake-token"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_get_valid_access_token_refreshes_expired(token_path):
|
|
92
|
+
_save_valid_tokens(access_token="stale", expires_at=time.time() - 10)
|
|
93
|
+
payload = {
|
|
94
|
+
"access_token": "fresh-token",
|
|
95
|
+
"refresh_token": "new-refresh",
|
|
96
|
+
"expires_in": 3600,
|
|
97
|
+
}
|
|
98
|
+
with patch.object(utils, "exchange_token", return_value=payload) as exchange:
|
|
99
|
+
assert utils.get_valid_access_token() == "fresh-token"
|
|
100
|
+
|
|
101
|
+
body = exchange.call_args[0][1]
|
|
102
|
+
assert body["grant_type"] == "refresh_token"
|
|
103
|
+
assert body["client_id"] == GROK_OAUTH_CONFIG["client_id"]
|
|
104
|
+
assert utils.load_stored_tokens()["refresh_token"] == "new-refresh"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_get_valid_access_token_unauthenticated(token_path):
|
|
108
|
+
assert utils.get_valid_access_token() is None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_load_grok_models_requires_auth(token_path):
|
|
112
|
+
assert rc._load_grok_models() == {}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_load_grok_models_when_authenticated(token_path):
|
|
116
|
+
_save_valid_tokens()
|
|
117
|
+
models = rc._load_grok_models()
|
|
118
|
+
assert "xai-grok-4.5" in models
|
|
119
|
+
entry = models["xai-grok-4.5"]
|
|
120
|
+
assert entry["type"] == "grok_oauth"
|
|
121
|
+
assert entry["name"] == "grok-4.5"
|
|
122
|
+
assert entry["context_length"] == 500_000
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_model_type_registration():
|
|
126
|
+
handlers = rc._register_model_types()
|
|
127
|
+
assert handlers == [{"type": "grok_oauth", "handler": rc._create_grok_oauth_model}]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_create_model_returns_none_without_auth(token_path):
|
|
131
|
+
model = rc._create_grok_oauth_model("xai-grok-4.5", {"name": "grok-4.5"}, {})
|
|
132
|
+
assert model is None
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_create_model_when_authenticated(token_path):
|
|
136
|
+
_save_valid_tokens()
|
|
137
|
+
model = rc._create_grok_oauth_model("xai-grok-4.5", {"name": "grok-4.5"}, {})
|
|
138
|
+
assert type(model).__name__ == "OpenAIResponsesModel"
|
|
139
|
+
assert model.model_name == "grok-4.5"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_custom_command_ignores_unknown():
|
|
143
|
+
assert rc._handle_custom_command("/other", "not-ours") is None
|
|
144
|
+
assert rc._handle_custom_command("/other", "") is None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_custom_command_handles_status(token_path):
|
|
148
|
+
assert rc._handle_custom_command("/grok-status", "grok-status") is True
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_custom_command_logout(token_path):
|
|
152
|
+
_save_valid_tokens()
|
|
153
|
+
assert rc._handle_custom_command("/grok-logout", "grok-logout") is True
|
|
154
|
+
assert not token_path.exists()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def test_custom_help_entries():
|
|
158
|
+
entries = rc._custom_help()
|
|
159
|
+
names = [name for name, _ in entries]
|
|
160
|
+
assert names == ["grok-auth", "grok-status", "grok-logout"]
|