codepp 0.0.437__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_puppy/__init__.py +10 -0
- code_puppy/__main__.py +10 -0
- code_puppy/agents/__init__.py +31 -0
- code_puppy/agents/agent_c_reviewer.py +155 -0
- code_puppy/agents/agent_code_puppy.py +117 -0
- code_puppy/agents/agent_code_reviewer.py +90 -0
- code_puppy/agents/agent_cpp_reviewer.py +132 -0
- code_puppy/agents/agent_creator_agent.py +638 -0
- code_puppy/agents/agent_golang_reviewer.py +151 -0
- code_puppy/agents/agent_helios.py +124 -0
- code_puppy/agents/agent_javascript_reviewer.py +160 -0
- code_puppy/agents/agent_manager.py +742 -0
- code_puppy/agents/agent_pack_leader.py +385 -0
- code_puppy/agents/agent_planning.py +165 -0
- code_puppy/agents/agent_python_programmer.py +169 -0
- code_puppy/agents/agent_python_reviewer.py +90 -0
- code_puppy/agents/agent_qa_expert.py +163 -0
- code_puppy/agents/agent_qa_kitten.py +208 -0
- code_puppy/agents/agent_scheduler.py +121 -0
- code_puppy/agents/agent_security_auditor.py +181 -0
- code_puppy/agents/agent_terminal_qa.py +323 -0
- code_puppy/agents/agent_typescript_reviewer.py +166 -0
- code_puppy/agents/base_agent.py +2156 -0
- code_puppy/agents/event_stream_handler.py +348 -0
- code_puppy/agents/json_agent.py +202 -0
- code_puppy/agents/pack/__init__.py +34 -0
- code_puppy/agents/pack/bloodhound.py +304 -0
- code_puppy/agents/pack/husky.py +327 -0
- code_puppy/agents/pack/retriever.py +393 -0
- code_puppy/agents/pack/shepherd.py +348 -0
- code_puppy/agents/pack/terrier.py +287 -0
- code_puppy/agents/pack/watchdog.py +367 -0
- code_puppy/agents/prompt_reviewer.py +145 -0
- code_puppy/agents/subagent_stream_handler.py +276 -0
- code_puppy/api/__init__.py +13 -0
- code_puppy/api/app.py +169 -0
- code_puppy/api/main.py +21 -0
- code_puppy/api/pty_manager.py +453 -0
- code_puppy/api/routers/__init__.py +12 -0
- code_puppy/api/routers/agents.py +36 -0
- code_puppy/api/routers/commands.py +217 -0
- code_puppy/api/routers/config.py +75 -0
- code_puppy/api/routers/sessions.py +234 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +692 -0
- code_puppy/chatgpt_codex_client.py +338 -0
- code_puppy/claude_cache_client.py +672 -0
- code_puppy/cli_runner.py +1073 -0
- code_puppy/command_line/__init__.py +1 -0
- code_puppy/command_line/add_model_menu.py +1092 -0
- code_puppy/command_line/agent_menu.py +662 -0
- code_puppy/command_line/attachments.py +395 -0
- code_puppy/command_line/autosave_menu.py +704 -0
- code_puppy/command_line/clipboard.py +527 -0
- code_puppy/command_line/colors_menu.py +532 -0
- code_puppy/command_line/command_handler.py +293 -0
- code_puppy/command_line/command_registry.py +150 -0
- code_puppy/command_line/config_commands.py +719 -0
- code_puppy/command_line/core_commands.py +867 -0
- code_puppy/command_line/diff_menu.py +865 -0
- code_puppy/command_line/file_path_completion.py +73 -0
- code_puppy/command_line/load_context_completion.py +52 -0
- code_puppy/command_line/mcp/__init__.py +10 -0
- code_puppy/command_line/mcp/base.py +32 -0
- code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
- code_puppy/command_line/mcp/custom_server_form.py +688 -0
- code_puppy/command_line/mcp/custom_server_installer.py +195 -0
- code_puppy/command_line/mcp/edit_command.py +148 -0
- code_puppy/command_line/mcp/handler.py +138 -0
- code_puppy/command_line/mcp/help_command.py +147 -0
- code_puppy/command_line/mcp/install_command.py +214 -0
- code_puppy/command_line/mcp/install_menu.py +705 -0
- code_puppy/command_line/mcp/list_command.py +94 -0
- code_puppy/command_line/mcp/logs_command.py +235 -0
- code_puppy/command_line/mcp/remove_command.py +82 -0
- code_puppy/command_line/mcp/restart_command.py +100 -0
- code_puppy/command_line/mcp/search_command.py +123 -0
- code_puppy/command_line/mcp/start_all_command.py +135 -0
- code_puppy/command_line/mcp/start_command.py +117 -0
- code_puppy/command_line/mcp/status_command.py +184 -0
- code_puppy/command_line/mcp/stop_all_command.py +112 -0
- code_puppy/command_line/mcp/stop_command.py +80 -0
- code_puppy/command_line/mcp/test_command.py +107 -0
- code_puppy/command_line/mcp/utils.py +129 -0
- code_puppy/command_line/mcp/wizard_utils.py +334 -0
- code_puppy/command_line/mcp_completion.py +174 -0
- code_puppy/command_line/model_picker_completion.py +197 -0
- code_puppy/command_line/model_settings_menu.py +932 -0
- code_puppy/command_line/motd.py +96 -0
- code_puppy/command_line/onboarding_slides.py +179 -0
- code_puppy/command_line/onboarding_wizard.py +342 -0
- code_puppy/command_line/pin_command_completion.py +329 -0
- code_puppy/command_line/prompt_toolkit_completion.py +846 -0
- code_puppy/command_line/session_commands.py +302 -0
- code_puppy/command_line/shell_passthrough.py +145 -0
- code_puppy/command_line/skills_completion.py +160 -0
- code_puppy/command_line/uc_menu.py +893 -0
- code_puppy/command_line/utils.py +93 -0
- code_puppy/command_line/wiggum_state.py +78 -0
- code_puppy/config.py +1770 -0
- code_puppy/error_logging.py +134 -0
- code_puppy/gemini_code_assist.py +385 -0
- code_puppy/gemini_model.py +754 -0
- code_puppy/hook_engine/README.md +105 -0
- code_puppy/hook_engine/__init__.py +21 -0
- code_puppy/hook_engine/aliases.py +155 -0
- code_puppy/hook_engine/engine.py +221 -0
- code_puppy/hook_engine/executor.py +296 -0
- code_puppy/hook_engine/matcher.py +156 -0
- code_puppy/hook_engine/models.py +240 -0
- code_puppy/hook_engine/registry.py +106 -0
- code_puppy/hook_engine/validator.py +144 -0
- code_puppy/http_utils.py +361 -0
- code_puppy/keymap.py +128 -0
- code_puppy/main.py +10 -0
- code_puppy/mcp_/__init__.py +66 -0
- code_puppy/mcp_/async_lifecycle.py +286 -0
- code_puppy/mcp_/blocking_startup.py +469 -0
- code_puppy/mcp_/captured_stdio_server.py +275 -0
- code_puppy/mcp_/circuit_breaker.py +290 -0
- code_puppy/mcp_/config_wizard.py +507 -0
- code_puppy/mcp_/dashboard.py +308 -0
- code_puppy/mcp_/error_isolation.py +407 -0
- code_puppy/mcp_/examples/retry_example.py +226 -0
- code_puppy/mcp_/health_monitor.py +589 -0
- code_puppy/mcp_/managed_server.py +428 -0
- code_puppy/mcp_/manager.py +807 -0
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/mcp_/registry.py +451 -0
- code_puppy/mcp_/retry_manager.py +337 -0
- code_puppy/mcp_/server_registry_catalog.py +1126 -0
- code_puppy/mcp_/status_tracker.py +355 -0
- code_puppy/mcp_/system_tools.py +209 -0
- code_puppy/mcp_prompts/__init__.py +1 -0
- code_puppy/mcp_prompts/hook_creator.py +103 -0
- code_puppy/messaging/__init__.py +255 -0
- code_puppy/messaging/bus.py +613 -0
- code_puppy/messaging/commands.py +167 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/message_queue.py +361 -0
- code_puppy/messaging/messages.py +569 -0
- code_puppy/messaging/queue_console.py +271 -0
- code_puppy/messaging/renderers.py +311 -0
- code_puppy/messaging/rich_renderer.py +1158 -0
- code_puppy/messaging/spinner/__init__.py +83 -0
- code_puppy/messaging/spinner/console_spinner.py +240 -0
- code_puppy/messaging/spinner/spinner_base.py +95 -0
- code_puppy/messaging/subagent_console.py +460 -0
- code_puppy/model_factory.py +848 -0
- code_puppy/model_switching.py +63 -0
- code_puppy/model_utils.py +168 -0
- code_puppy/models.json +174 -0
- code_puppy/models_dev_api.json +1 -0
- code_puppy/models_dev_parser.py +592 -0
- code_puppy/plugins/__init__.py +186 -0
- code_puppy/plugins/agent_skills/__init__.py +22 -0
- code_puppy/plugins/agent_skills/config.py +175 -0
- code_puppy/plugins/agent_skills/discovery.py +136 -0
- code_puppy/plugins/agent_skills/downloader.py +392 -0
- code_puppy/plugins/agent_skills/installer.py +22 -0
- code_puppy/plugins/agent_skills/metadata.py +219 -0
- code_puppy/plugins/agent_skills/prompt_builder.py +60 -0
- code_puppy/plugins/agent_skills/register_callbacks.py +241 -0
- code_puppy/plugins/agent_skills/remote_catalog.py +322 -0
- code_puppy/plugins/agent_skills/skill_catalog.py +257 -0
- code_puppy/plugins/agent_skills/skills_install_menu.py +664 -0
- code_puppy/plugins/agent_skills/skills_menu.py +781 -0
- code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
- code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +706 -0
- code_puppy/plugins/antigravity_oauth/config.py +42 -0
- code_puppy/plugins/antigravity_oauth/constants.py +133 -0
- code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
- code_puppy/plugins/antigravity_oauth/register_callbacks.py +518 -0
- code_puppy/plugins/antigravity_oauth/storage.py +288 -0
- code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
- code_puppy/plugins/antigravity_oauth/token.py +167 -0
- code_puppy/plugins/antigravity_oauth/transport.py +863 -0
- code_puppy/plugins/antigravity_oauth/utils.py +168 -0
- code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
- code_puppy/plugins/chatgpt_oauth/config.py +52 -0
- code_puppy/plugins/chatgpt_oauth/oauth_flow.py +329 -0
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +176 -0
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +301 -0
- code_puppy/plugins/chatgpt_oauth/utils.py +523 -0
- code_puppy/plugins/claude_code_hooks/__init__.py +1 -0
- code_puppy/plugins/claude_code_hooks/config.py +137 -0
- code_puppy/plugins/claude_code_hooks/register_callbacks.py +175 -0
- code_puppy/plugins/claude_code_oauth/README.md +167 -0
- code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
- code_puppy/plugins/claude_code_oauth/__init__.py +25 -0
- code_puppy/plugins/claude_code_oauth/config.py +52 -0
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +453 -0
- code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +241 -0
- code_puppy/plugins/claude_code_oauth/utils.py +640 -0
- code_puppy/plugins/customizable_commands/__init__.py +0 -0
- code_puppy/plugins/customizable_commands/register_callbacks.py +152 -0
- code_puppy/plugins/example_custom_command/README.md +280 -0
- code_puppy/plugins/example_custom_command/register_callbacks.py +51 -0
- code_puppy/plugins/file_permission_handler/__init__.py +4 -0
- code_puppy/plugins/file_permission_handler/register_callbacks.py +470 -0
- code_puppy/plugins/frontend_emitter/__init__.py +25 -0
- code_puppy/plugins/frontend_emitter/emitter.py +121 -0
- code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
- code_puppy/plugins/hook_creator/__init__.py +1 -0
- code_puppy/plugins/hook_creator/register_callbacks.py +33 -0
- code_puppy/plugins/hook_manager/__init__.py +1 -0
- code_puppy/plugins/hook_manager/config.py +290 -0
- code_puppy/plugins/hook_manager/hooks_menu.py +564 -0
- code_puppy/plugins/hook_manager/register_callbacks.py +227 -0
- code_puppy/plugins/oauth_puppy_html.py +228 -0
- code_puppy/plugins/scheduler/__init__.py +1 -0
- code_puppy/plugins/scheduler/register_callbacks.py +88 -0
- code_puppy/plugins/scheduler/scheduler_menu.py +522 -0
- code_puppy/plugins/scheduler/scheduler_wizard.py +341 -0
- code_puppy/plugins/shell_safety/__init__.py +6 -0
- code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_puppy/plugins/shell_safety/command_cache.py +156 -0
- code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
- code_puppy/plugins/synthetic_status/__init__.py +1 -0
- code_puppy/plugins/synthetic_status/register_callbacks.py +132 -0
- code_puppy/plugins/synthetic_status/status_api.py +147 -0
- code_puppy/plugins/universal_constructor/__init__.py +13 -0
- code_puppy/plugins/universal_constructor/models.py +138 -0
- code_puppy/plugins/universal_constructor/register_callbacks.py +47 -0
- code_puppy/plugins/universal_constructor/registry.py +302 -0
- code_puppy/plugins/universal_constructor/sandbox.py +584 -0
- code_puppy/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/pydantic_patches.py +356 -0
- code_puppy/reopenable_async_client.py +232 -0
- code_puppy/round_robin_model.py +150 -0
- code_puppy/scheduler/__init__.py +41 -0
- code_puppy/scheduler/__main__.py +9 -0
- code_puppy/scheduler/cli.py +118 -0
- code_puppy/scheduler/config.py +126 -0
- code_puppy/scheduler/daemon.py +280 -0
- code_puppy/scheduler/executor.py +155 -0
- code_puppy/scheduler/platform.py +19 -0
- code_puppy/scheduler/platform_unix.py +22 -0
- code_puppy/scheduler/platform_win.py +32 -0
- code_puppy/session_storage.py +338 -0
- code_puppy/status_display.py +257 -0
- code_puppy/summarization_agent.py +176 -0
- code_puppy/terminal_utils.py +418 -0
- code_puppy/tools/__init__.py +501 -0
- code_puppy/tools/agent_tools.py +603 -0
- code_puppy/tools/ask_user_question/__init__.py +26 -0
- code_puppy/tools/ask_user_question/constants.py +73 -0
- code_puppy/tools/ask_user_question/demo_tui.py +55 -0
- code_puppy/tools/ask_user_question/handler.py +232 -0
- code_puppy/tools/ask_user_question/models.py +304 -0
- code_puppy/tools/ask_user_question/registration.py +26 -0
- code_puppy/tools/ask_user_question/renderers.py +309 -0
- code_puppy/tools/ask_user_question/terminal_ui.py +329 -0
- code_puppy/tools/ask_user_question/theme.py +155 -0
- code_puppy/tools/ask_user_question/tui_loop.py +423 -0
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +289 -0
- code_puppy/tools/browser/browser_interactions.py +545 -0
- code_puppy/tools/browser/browser_locators.py +640 -0
- code_puppy/tools/browser/browser_manager.py +378 -0
- code_puppy/tools/browser/browser_navigation.py +251 -0
- code_puppy/tools/browser/browser_screenshot.py +179 -0
- code_puppy/tools/browser/browser_scripts.py +462 -0
- code_puppy/tools/browser/browser_workflows.py +221 -0
- code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
- code_puppy/tools/browser/terminal_command_tools.py +534 -0
- code_puppy/tools/browser/terminal_screenshot_tools.py +552 -0
- code_puppy/tools/browser/terminal_tools.py +525 -0
- code_puppy/tools/command_runner.py +1346 -0
- code_puppy/tools/common.py +1409 -0
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/file_modifications.py +886 -0
- code_puppy/tools/file_operations.py +802 -0
- code_puppy/tools/scheduler_tools.py +412 -0
- code_puppy/tools/skills_tools.py +244 -0
- code_puppy/tools/subagent_context.py +158 -0
- code_puppy/tools/tools_content.py +51 -0
- code_puppy/tools/universal_constructor.py +889 -0
- code_puppy/uvx_detection.py +242 -0
- code_puppy/version_checker.py +82 -0
- codepp-0.0.437.dist-info/METADATA +766 -0
- codepp-0.0.437.dist-info/RECORD +288 -0
- codepp-0.0.437.dist-info/WHEEL +4 -0
- codepp-0.0.437.dist-info/entry_points.txt +3 -0
- codepp-0.0.437.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
"""Antigravity OAuth Plugin callbacks for Code Puppy CLI.
|
|
2
|
+
|
|
3
|
+
Provides OAuth authentication for Antigravity models and registers
|
|
4
|
+
the 'antigravity' model type handler.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import threading
|
|
11
|
+
import time
|
|
12
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
13
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
14
|
+
from urllib.parse import parse_qs, urlparse
|
|
15
|
+
|
|
16
|
+
from code_puppy.callbacks import register_callback
|
|
17
|
+
from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
|
|
18
|
+
from code_puppy.model_switching import set_model_and_reload_agent
|
|
19
|
+
|
|
20
|
+
from ..oauth_puppy_html import oauth_failure_html, oauth_success_html
|
|
21
|
+
from .accounts import AccountManager
|
|
22
|
+
from .config import (
|
|
23
|
+
ANTIGRAVITY_OAUTH_CONFIG,
|
|
24
|
+
get_accounts_storage_path,
|
|
25
|
+
get_token_storage_path,
|
|
26
|
+
)
|
|
27
|
+
from .constants import ANTIGRAVITY_MODELS
|
|
28
|
+
from .oauth import (
|
|
29
|
+
TokenExchangeSuccess,
|
|
30
|
+
assign_redirect_uri,
|
|
31
|
+
build_authorization_url,
|
|
32
|
+
exchange_code_for_tokens,
|
|
33
|
+
fetch_antigravity_status,
|
|
34
|
+
prepare_oauth_context,
|
|
35
|
+
)
|
|
36
|
+
from .storage import clear_accounts
|
|
37
|
+
from .token import is_token_expired, refresh_access_token
|
|
38
|
+
from .transport import create_antigravity_client
|
|
39
|
+
from .utils import (
|
|
40
|
+
add_models_to_config,
|
|
41
|
+
load_antigravity_models,
|
|
42
|
+
load_stored_tokens,
|
|
43
|
+
reload_current_agent,
|
|
44
|
+
remove_antigravity_models,
|
|
45
|
+
save_tokens,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
logger = logging.getLogger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class _OAuthResult:
|
|
52
|
+
def __init__(self) -> None:
|
|
53
|
+
self.code: Optional[str] = None
|
|
54
|
+
self.state: Optional[str] = None
|
|
55
|
+
self.error: Optional[str] = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class _CallbackHandler(BaseHTTPRequestHandler):
|
|
59
|
+
result: _OAuthResult
|
|
60
|
+
received_event: threading.Event
|
|
61
|
+
redirect_uri: str
|
|
62
|
+
|
|
63
|
+
def do_GET(self) -> None: # noqa: N802
|
|
64
|
+
logger.info("Callback received: path=%s", self.path)
|
|
65
|
+
parsed = urlparse(self.path)
|
|
66
|
+
params: Dict[str, List[str]] = parse_qs(parsed.query)
|
|
67
|
+
|
|
68
|
+
code = params.get("code", [None])[0]
|
|
69
|
+
state = params.get("state", [None])[0]
|
|
70
|
+
|
|
71
|
+
if code and state:
|
|
72
|
+
self.result.code = code
|
|
73
|
+
self.result.state = state
|
|
74
|
+
success_html = oauth_success_html(
|
|
75
|
+
"Antigravity",
|
|
76
|
+
"You're connected to Antigravity! 🚀 Gemini & Claude models are now available.",
|
|
77
|
+
)
|
|
78
|
+
self._write_response(200, success_html)
|
|
79
|
+
else:
|
|
80
|
+
self.result.error = "Missing code or state"
|
|
81
|
+
failure_html = oauth_failure_html(
|
|
82
|
+
"Antigravity",
|
|
83
|
+
"Missing code or state parameter 🥺",
|
|
84
|
+
)
|
|
85
|
+
self._write_response(400, failure_html)
|
|
86
|
+
|
|
87
|
+
self.received_event.set()
|
|
88
|
+
|
|
89
|
+
def log_message(self, log_format: str, *args: Any) -> None:
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
def _write_response(self, status: int, body: str) -> None:
|
|
93
|
+
self.send_response(status)
|
|
94
|
+
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
95
|
+
self.end_headers()
|
|
96
|
+
self.wfile.write(body.encode("utf-8"))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _start_callback_server(
|
|
100
|
+
context: Any,
|
|
101
|
+
) -> Optional[Tuple[HTTPServer, _OAuthResult, threading.Event, str]]:
|
|
102
|
+
"""Start local HTTP server for OAuth callback."""
|
|
103
|
+
port_range = ANTIGRAVITY_OAUTH_CONFIG["callback_port_range"]
|
|
104
|
+
|
|
105
|
+
for port in range(port_range[0], port_range[1] + 1):
|
|
106
|
+
try:
|
|
107
|
+
server = HTTPServer(("localhost", port), _CallbackHandler)
|
|
108
|
+
redirect_uri = assign_redirect_uri(context, port)
|
|
109
|
+
result = _OAuthResult()
|
|
110
|
+
event = threading.Event()
|
|
111
|
+
_CallbackHandler.result = result
|
|
112
|
+
_CallbackHandler.received_event = event
|
|
113
|
+
_CallbackHandler.redirect_uri = redirect_uri
|
|
114
|
+
|
|
115
|
+
def run_server(server=server) -> None:
|
|
116
|
+
with server:
|
|
117
|
+
server.serve_forever()
|
|
118
|
+
|
|
119
|
+
threading.Thread(target=run_server, daemon=True).start()
|
|
120
|
+
return server, result, event, redirect_uri
|
|
121
|
+
except OSError:
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
emit_error("Could not start OAuth callback server; all candidate ports are in use")
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _await_callback(context: Any) -> Optional[Tuple[str, str, str]]:
|
|
129
|
+
"""Wait for OAuth callback and return (code, state, redirect_uri)."""
|
|
130
|
+
timeout = ANTIGRAVITY_OAUTH_CONFIG["callback_timeout"]
|
|
131
|
+
|
|
132
|
+
started = _start_callback_server(context)
|
|
133
|
+
if not started:
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
server, result, event, redirect_uri = started
|
|
137
|
+
|
|
138
|
+
auth_url = build_authorization_url(context)
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
import webbrowser
|
|
142
|
+
|
|
143
|
+
from code_puppy.tools.common import should_suppress_browser
|
|
144
|
+
|
|
145
|
+
if should_suppress_browser():
|
|
146
|
+
emit_info(
|
|
147
|
+
"[HEADLESS MODE] Would normally open browser for Antigravity OAuth…"
|
|
148
|
+
)
|
|
149
|
+
emit_info(f"In normal mode, would visit: {auth_url}")
|
|
150
|
+
else:
|
|
151
|
+
emit_info("🌐 Opening browser for Google OAuth…")
|
|
152
|
+
webbrowser.open(auth_url)
|
|
153
|
+
emit_info(f"If it doesn't open automatically, visit:\n{auth_url}")
|
|
154
|
+
except Exception as exc:
|
|
155
|
+
emit_warning(f"Failed to open browser: {exc}")
|
|
156
|
+
emit_info(f"Please open manually: {auth_url}")
|
|
157
|
+
|
|
158
|
+
emit_info(f"⏳ Waiting for callback on {redirect_uri}")
|
|
159
|
+
|
|
160
|
+
if not event.wait(timeout=timeout):
|
|
161
|
+
emit_error("OAuth callback timed out. Please try again.")
|
|
162
|
+
server.shutdown()
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
server.shutdown()
|
|
166
|
+
|
|
167
|
+
if result.error:
|
|
168
|
+
emit_error(f"OAuth callback error: {result.error}")
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
return result.code, result.state, redirect_uri
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _perform_authentication(
|
|
175
|
+
add_account: bool = False,
|
|
176
|
+
reload_agent: bool = True,
|
|
177
|
+
) -> bool:
|
|
178
|
+
"""Run the OAuth authentication flow.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
add_account: Whether to add a new account to the pool.
|
|
182
|
+
reload_agent: Whether to reload the current agent after auth.
|
|
183
|
+
"""
|
|
184
|
+
context = prepare_oauth_context()
|
|
185
|
+
callback_result = _await_callback(context)
|
|
186
|
+
|
|
187
|
+
if not callback_result:
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
code, state, redirect_uri = callback_result
|
|
191
|
+
|
|
192
|
+
emit_info("🔄 Exchanging authorization code for tokens…")
|
|
193
|
+
result = exchange_code_for_tokens(code, state, redirect_uri)
|
|
194
|
+
|
|
195
|
+
if not isinstance(result, TokenExchangeSuccess):
|
|
196
|
+
emit_error(f"Token exchange failed: {result.error}")
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
# Save tokens
|
|
200
|
+
tokens = {
|
|
201
|
+
"access_token": result.access_token,
|
|
202
|
+
"refresh_token": result.refresh_token,
|
|
203
|
+
"expires_at": result.expires_at,
|
|
204
|
+
"email": result.email,
|
|
205
|
+
"project_id": result.project_id,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if not save_tokens(tokens):
|
|
209
|
+
emit_error("Failed to save tokens locally. Check file permissions.")
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
# Handle multi-account
|
|
213
|
+
manager = AccountManager.load_from_disk(result.refresh_token)
|
|
214
|
+
|
|
215
|
+
if add_account or manager.account_count == 0:
|
|
216
|
+
manager.add_account(
|
|
217
|
+
refresh_token=result.refresh_token,
|
|
218
|
+
email=result.email,
|
|
219
|
+
project_id=result.project_id,
|
|
220
|
+
)
|
|
221
|
+
manager.save_to_disk()
|
|
222
|
+
|
|
223
|
+
if add_account:
|
|
224
|
+
emit_success(f"✅ Added account: {result.email or 'Unknown'}")
|
|
225
|
+
emit_info(f"📊 Total accounts: {manager.account_count}")
|
|
226
|
+
|
|
227
|
+
if result.email:
|
|
228
|
+
emit_success(f"🎉 Authenticated as {result.email}!")
|
|
229
|
+
else:
|
|
230
|
+
emit_success("🎉 Antigravity OAuth authentication successful!")
|
|
231
|
+
|
|
232
|
+
# Add models
|
|
233
|
+
emit_info("📦 Configuring available models…")
|
|
234
|
+
if add_models_to_config(result.access_token, result.project_id):
|
|
235
|
+
model_count = len(ANTIGRAVITY_MODELS)
|
|
236
|
+
emit_success(f"✅ {model_count} Antigravity models configured!")
|
|
237
|
+
emit_info(
|
|
238
|
+
" Use the `antigravity-` prefix (e.g., antigravity-gemini-3-pro-high)"
|
|
239
|
+
)
|
|
240
|
+
else:
|
|
241
|
+
emit_warning("Failed to configure models. Try running /antigravity-auth again.")
|
|
242
|
+
|
|
243
|
+
if reload_agent:
|
|
244
|
+
reload_current_agent()
|
|
245
|
+
return True
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _custom_help() -> List[Tuple[str, str]]:
|
|
249
|
+
"""Return help entries for Antigravity commands."""
|
|
250
|
+
return [
|
|
251
|
+
(
|
|
252
|
+
"antigravity-auth",
|
|
253
|
+
"Authenticate with Google/Antigravity for Gemini & Claude models",
|
|
254
|
+
),
|
|
255
|
+
(
|
|
256
|
+
"antigravity-add",
|
|
257
|
+
"Add another Google account for load balancing",
|
|
258
|
+
),
|
|
259
|
+
(
|
|
260
|
+
"antigravity-status",
|
|
261
|
+
"Check authentication status and account pool",
|
|
262
|
+
),
|
|
263
|
+
(
|
|
264
|
+
"antigravity-logout",
|
|
265
|
+
"Remove all Antigravity OAuth tokens and models",
|
|
266
|
+
),
|
|
267
|
+
]
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _handle_status() -> None:
|
|
271
|
+
"""Handle /antigravity-status command."""
|
|
272
|
+
tokens = load_stored_tokens()
|
|
273
|
+
|
|
274
|
+
if not tokens or not tokens.get("access_token"):
|
|
275
|
+
emit_warning("🔓 Antigravity: Not authenticated")
|
|
276
|
+
emit_info("Run /antigravity-auth to sign in with Google")
|
|
277
|
+
return
|
|
278
|
+
|
|
279
|
+
emit_success("🔐 Antigravity: Authenticated")
|
|
280
|
+
|
|
281
|
+
# Show email if available
|
|
282
|
+
if tokens.get("email"):
|
|
283
|
+
emit_info(f" Primary account: {tokens['email']}")
|
|
284
|
+
|
|
285
|
+
# Show token expiry
|
|
286
|
+
expires_at = tokens.get("expires_at")
|
|
287
|
+
if expires_at:
|
|
288
|
+
remaining = max(0, int(expires_at - time.time()))
|
|
289
|
+
hours, remainder = divmod(remaining, 3600)
|
|
290
|
+
minutes = remainder // 60
|
|
291
|
+
emit_info(f" Token expires in: ~{hours}h {minutes}m")
|
|
292
|
+
|
|
293
|
+
# Fetch tier/quota status from API
|
|
294
|
+
emit_info("\n📊 Fetching tier status...")
|
|
295
|
+
status = fetch_antigravity_status(tokens.get("access_token", ""))
|
|
296
|
+
|
|
297
|
+
if status.error:
|
|
298
|
+
emit_warning(f" Could not fetch status: {status.error}")
|
|
299
|
+
else:
|
|
300
|
+
# Show tier info
|
|
301
|
+
tier_display = {
|
|
302
|
+
"free-tier": "Free Tier (limited)",
|
|
303
|
+
"standard-tier": "Standard Tier (full access)",
|
|
304
|
+
}
|
|
305
|
+
current = tier_display.get(
|
|
306
|
+
status.current_tier, status.current_tier or "Unknown"
|
|
307
|
+
)
|
|
308
|
+
emit_info(f" Current tier: {current}")
|
|
309
|
+
|
|
310
|
+
if status.project_id:
|
|
311
|
+
emit_info(f" Project ID: {status.project_id}")
|
|
312
|
+
|
|
313
|
+
if status.allowed_tiers:
|
|
314
|
+
available = ", ".join(status.allowed_tiers)
|
|
315
|
+
emit_info(f" Available tiers: {available}")
|
|
316
|
+
|
|
317
|
+
# Show account pool
|
|
318
|
+
manager = AccountManager.load_from_disk()
|
|
319
|
+
if manager.account_count > 1:
|
|
320
|
+
emit_info(f"\n📊 Account Pool: {manager.account_count} accounts")
|
|
321
|
+
for acc in manager.get_accounts_snapshot():
|
|
322
|
+
email_str = acc.email or "Unknown"
|
|
323
|
+
limits = []
|
|
324
|
+
if acc.rate_limit_reset_times:
|
|
325
|
+
for key, reset_time in acc.rate_limit_reset_times.items():
|
|
326
|
+
if reset_time > time.time() * 1000:
|
|
327
|
+
wait_sec = int((reset_time - time.time() * 1000) / 1000)
|
|
328
|
+
limits.append(f"{key}: {wait_sec}s")
|
|
329
|
+
|
|
330
|
+
status = f" • {email_str}"
|
|
331
|
+
if limits:
|
|
332
|
+
status += f" (rate-limited: {', '.join(limits)})"
|
|
333
|
+
emit_info(status)
|
|
334
|
+
|
|
335
|
+
# Show configured models
|
|
336
|
+
models = load_antigravity_models()
|
|
337
|
+
antigravity_models = [
|
|
338
|
+
name
|
|
339
|
+
for name, cfg in models.items()
|
|
340
|
+
if cfg.get("oauth_source") == "antigravity-plugin"
|
|
341
|
+
]
|
|
342
|
+
|
|
343
|
+
if antigravity_models:
|
|
344
|
+
emit_info(f"\n🎯 Configured models: {len(antigravity_models)}")
|
|
345
|
+
# Group by family
|
|
346
|
+
gemini = [m for m in antigravity_models if "gemini" in m]
|
|
347
|
+
claude = [m for m in antigravity_models if "claude" in m]
|
|
348
|
+
other = [m for m in antigravity_models if m not in gemini and m not in claude]
|
|
349
|
+
|
|
350
|
+
if gemini:
|
|
351
|
+
emit_info(f" Gemini: {', '.join(sorted(gemini))}")
|
|
352
|
+
if claude:
|
|
353
|
+
emit_info(f" Claude: {', '.join(sorted(claude))}")
|
|
354
|
+
if other:
|
|
355
|
+
emit_info(f" Other: {', '.join(sorted(other))}")
|
|
356
|
+
else:
|
|
357
|
+
emit_warning("No Antigravity models configured")
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _handle_logout() -> None:
|
|
361
|
+
"""Handle /antigravity-logout command."""
|
|
362
|
+
# Remove tokens
|
|
363
|
+
token_path = get_token_storage_path()
|
|
364
|
+
if token_path.exists():
|
|
365
|
+
token_path.unlink()
|
|
366
|
+
emit_info("✓ Removed OAuth tokens")
|
|
367
|
+
|
|
368
|
+
# Remove accounts
|
|
369
|
+
accounts_path = get_accounts_storage_path()
|
|
370
|
+
if accounts_path.exists():
|
|
371
|
+
clear_accounts()
|
|
372
|
+
emit_info("✓ Removed account pool")
|
|
373
|
+
|
|
374
|
+
# Remove models
|
|
375
|
+
removed = remove_antigravity_models()
|
|
376
|
+
if removed:
|
|
377
|
+
emit_info(f"✓ Removed {removed} Antigravity models")
|
|
378
|
+
|
|
379
|
+
emit_success("👋 Antigravity logout complete")
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _handle_custom_command(command: str, name: str) -> Optional[bool]:
|
|
383
|
+
"""Handle Antigravity custom commands."""
|
|
384
|
+
if not name:
|
|
385
|
+
return None
|
|
386
|
+
|
|
387
|
+
if name == "antigravity-auth":
|
|
388
|
+
emit_info("🚀 Starting Antigravity OAuth authentication…")
|
|
389
|
+
tokens = load_stored_tokens()
|
|
390
|
+
if tokens and tokens.get("access_token"):
|
|
391
|
+
emit_warning(
|
|
392
|
+
"Existing tokens found. This will refresh your authentication."
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
if _perform_authentication(reload_agent=False):
|
|
396
|
+
set_model_and_reload_agent("antigravity-gemini-3-pro-high")
|
|
397
|
+
return True
|
|
398
|
+
|
|
399
|
+
if name == "antigravity-add":
|
|
400
|
+
emit_info("➕ Adding another Google account…")
|
|
401
|
+
manager = AccountManager.load_from_disk()
|
|
402
|
+
emit_info(f"Current accounts: {manager.account_count}")
|
|
403
|
+
_perform_authentication(add_account=True)
|
|
404
|
+
return True
|
|
405
|
+
|
|
406
|
+
if name == "antigravity-status":
|
|
407
|
+
_handle_status()
|
|
408
|
+
return True
|
|
409
|
+
|
|
410
|
+
if name == "antigravity-logout":
|
|
411
|
+
_handle_logout()
|
|
412
|
+
return True
|
|
413
|
+
|
|
414
|
+
return None
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def _create_antigravity_model(model_name: str, model_config: Dict, config: Dict) -> Any:
|
|
418
|
+
"""Create an Antigravity model instance.
|
|
419
|
+
|
|
420
|
+
This handler is registered via the 'register_model_type' callback to handle
|
|
421
|
+
models with type='antigravity'.
|
|
422
|
+
"""
|
|
423
|
+
from code_puppy.gemini_model import GeminiModel
|
|
424
|
+
from code_puppy.model_factory import get_custom_config
|
|
425
|
+
|
|
426
|
+
# Try to import custom model for thinking signatures
|
|
427
|
+
try:
|
|
428
|
+
from .antigravity_model import AntigravityModel
|
|
429
|
+
except ImportError:
|
|
430
|
+
AntigravityModel = None # type: ignore
|
|
431
|
+
|
|
432
|
+
url, headers, verify, api_key = get_custom_config(model_config)
|
|
433
|
+
if not api_key:
|
|
434
|
+
emit_warning(
|
|
435
|
+
f"API key is not set for Antigravity endpoint; skipping model '{model_config.get('name')}'."
|
|
436
|
+
)
|
|
437
|
+
return None
|
|
438
|
+
|
|
439
|
+
# Get fresh access token (refresh if needed)
|
|
440
|
+
tokens = load_stored_tokens()
|
|
441
|
+
if not tokens:
|
|
442
|
+
emit_warning("Antigravity tokens not found; run /antigravity-auth first.")
|
|
443
|
+
return None
|
|
444
|
+
|
|
445
|
+
access_token = tokens.get("access_token", "")
|
|
446
|
+
refresh_token = tokens.get("refresh_token", "")
|
|
447
|
+
expires_at = tokens.get("expires_at")
|
|
448
|
+
|
|
449
|
+
# Refresh if expired or about to expire (initial check)
|
|
450
|
+
if is_token_expired(expires_at):
|
|
451
|
+
new_tokens = refresh_access_token(refresh_token)
|
|
452
|
+
if new_tokens:
|
|
453
|
+
access_token = new_tokens.access_token
|
|
454
|
+
refresh_token = new_tokens.refresh_token
|
|
455
|
+
expires_at = new_tokens.expires_at
|
|
456
|
+
tokens["access_token"] = new_tokens.access_token
|
|
457
|
+
tokens["refresh_token"] = new_tokens.refresh_token
|
|
458
|
+
tokens["expires_at"] = new_tokens.expires_at
|
|
459
|
+
save_tokens(tokens)
|
|
460
|
+
else:
|
|
461
|
+
emit_warning(
|
|
462
|
+
"Failed to refresh Antigravity token; run /antigravity-auth again."
|
|
463
|
+
)
|
|
464
|
+
return None
|
|
465
|
+
|
|
466
|
+
# Callback to persist tokens when proactively refreshed during session
|
|
467
|
+
def on_token_refreshed(new_tokens: Any) -> None:
|
|
468
|
+
"""Persist new tokens when proactively refreshed."""
|
|
469
|
+
try:
|
|
470
|
+
updated_tokens = load_stored_tokens() or {}
|
|
471
|
+
updated_tokens["access_token"] = new_tokens.access_token
|
|
472
|
+
updated_tokens["refresh_token"] = new_tokens.refresh_token
|
|
473
|
+
updated_tokens["expires_at"] = new_tokens.expires_at
|
|
474
|
+
save_tokens(updated_tokens)
|
|
475
|
+
logger.debug("Persisted proactively refreshed Antigravity tokens")
|
|
476
|
+
except Exception as e:
|
|
477
|
+
logger.warning("Failed to persist refreshed tokens: %s", e)
|
|
478
|
+
|
|
479
|
+
project_id = tokens.get("project_id", model_config.get("project_id", ""))
|
|
480
|
+
client = create_antigravity_client(
|
|
481
|
+
access_token=access_token,
|
|
482
|
+
project_id=project_id,
|
|
483
|
+
model_name=model_config["name"],
|
|
484
|
+
base_url=url,
|
|
485
|
+
headers=headers,
|
|
486
|
+
refresh_token=refresh_token,
|
|
487
|
+
expires_at=expires_at,
|
|
488
|
+
on_token_refreshed=on_token_refreshed,
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
# Use custom model with direct httpx client
|
|
492
|
+
if AntigravityModel:
|
|
493
|
+
model = AntigravityModel(
|
|
494
|
+
model_name=model_config["name"],
|
|
495
|
+
api_key=api_key or "", # Antigravity uses OAuth, key may be empty
|
|
496
|
+
base_url=url,
|
|
497
|
+
http_client=client,
|
|
498
|
+
)
|
|
499
|
+
else:
|
|
500
|
+
model = GeminiModel(
|
|
501
|
+
model_name=model_config["name"],
|
|
502
|
+
api_key=api_key or "",
|
|
503
|
+
base_url=url,
|
|
504
|
+
http_client=client,
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
return model
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def _register_model_types() -> List[Dict[str, Any]]:
|
|
511
|
+
"""Register the antigravity model type handler."""
|
|
512
|
+
return [{"type": "antigravity", "handler": _create_antigravity_model}]
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
# Register callbacks
|
|
516
|
+
register_callback("custom_command_help", _custom_help)
|
|
517
|
+
register_callback("custom_command", _handle_custom_command)
|
|
518
|
+
register_callback("register_model_type", _register_model_types)
|