Full Code of router-for-me/CLIProxyAPI for AI

main db63f9b5d60e cached
451 files
3.6 MB
967.3k tokens
3936 symbols
1 requests
Download .txt
Showing preview only (3,858K chars total). Download the full file or copy to clipboard to get everything.
Repository: router-for-me/CLIProxyAPI
Branch: main
Commit: db63f9b5d60e
Files: 451
Total size: 3.6 MB

Directory structure:
gitextract_9oqd9is2/

├── .dockerignore
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.md
│   └── workflows/
│       ├── docker-image.yml
│       ├── pr-path-guard.yml
│       ├── pr-test-build.yml
│       └── release.yaml
├── .gitignore
├── .goreleaser.yml
├── Dockerfile
├── LICENSE
├── README.md
├── README_CN.md
├── auths/
│   └── .gitkeep
├── cmd/
│   ├── fetch_antigravity_models/
│   │   └── main.go
│   └── server/
│       └── main.go
├── config.example.yaml
├── docker-build.ps1
├── docker-build.sh
├── docker-compose.yml
├── examples/
│   ├── custom-provider/
│   │   └── main.go
│   ├── http-request/
│   │   └── main.go
│   └── translator/
│       └── main.go
├── go.mod
├── go.sum
├── internal/
│   ├── access/
│   │   ├── config_access/
│   │   │   └── provider.go
│   │   └── reconcile.go
│   ├── api/
│   │   ├── handlers/
│   │   │   └── management/
│   │   │       ├── api_tools.go
│   │   │       ├── api_tools_test.go
│   │   │       ├── auth_files.go
│   │   │       ├── auth_files_delete_test.go
│   │   │       ├── config_basic.go
│   │   │       ├── config_lists.go
│   │   │       ├── handler.go
│   │   │       ├── logs.go
│   │   │       ├── model_definitions.go
│   │   │       ├── oauth_callback.go
│   │   │       ├── oauth_sessions.go
│   │   │       ├── quota.go
│   │   │       ├── test_store_test.go
│   │   │       ├── usage.go
│   │   │       └── vertex_import.go
│   │   ├── middleware/
│   │   │   ├── request_logging.go
│   │   │   ├── request_logging_test.go
│   │   │   ├── response_writer.go
│   │   │   └── response_writer_test.go
│   │   ├── modules/
│   │   │   ├── amp/
│   │   │   │   ├── amp.go
│   │   │   │   ├── amp_test.go
│   │   │   │   ├── fallback_handlers.go
│   │   │   │   ├── fallback_handlers_test.go
│   │   │   │   ├── gemini_bridge.go
│   │   │   │   ├── gemini_bridge_test.go
│   │   │   │   ├── model_mapping.go
│   │   │   │   ├── model_mapping_test.go
│   │   │   │   ├── proxy.go
│   │   │   │   ├── proxy_test.go
│   │   │   │   ├── response_rewriter.go
│   │   │   │   ├── response_rewriter_test.go
│   │   │   │   ├── routes.go
│   │   │   │   ├── routes_test.go
│   │   │   │   ├── secret.go
│   │   │   │   └── secret_test.go
│   │   │   └── modules.go
│   │   ├── server.go
│   │   └── server_test.go
│   ├── auth/
│   │   ├── antigravity/
│   │   │   ├── auth.go
│   │   │   ├── constants.go
│   │   │   └── filename.go
│   │   ├── claude/
│   │   │   ├── anthropic.go
│   │   │   ├── anthropic_auth.go
│   │   │   ├── errors.go
│   │   │   ├── html_templates.go
│   │   │   ├── oauth_server.go
│   │   │   ├── pkce.go
│   │   │   ├── token.go
│   │   │   └── utls_transport.go
│   │   ├── codex/
│   │   │   ├── errors.go
│   │   │   ├── filename.go
│   │   │   ├── html_templates.go
│   │   │   ├── jwt_parser.go
│   │   │   ├── oauth_server.go
│   │   │   ├── openai.go
│   │   │   ├── openai_auth.go
│   │   │   ├── openai_auth_test.go
│   │   │   ├── pkce.go
│   │   │   └── token.go
│   │   ├── empty/
│   │   │   └── token.go
│   │   ├── gemini/
│   │   │   ├── gemini_auth.go
│   │   │   └── gemini_token.go
│   │   ├── iflow/
│   │   │   ├── cookie_helpers.go
│   │   │   ├── iflow_auth.go
│   │   │   ├── iflow_token.go
│   │   │   └── oauth_server.go
│   │   ├── kimi/
│   │   │   ├── kimi.go
│   │   │   └── token.go
│   │   ├── models.go
│   │   ├── qwen/
│   │   │   ├── qwen_auth.go
│   │   │   └── qwen_token.go
│   │   └── vertex/
│   │       ├── keyutil.go
│   │       └── vertex_credentials.go
│   ├── browser/
│   │   └── browser.go
│   ├── buildinfo/
│   │   └── buildinfo.go
│   ├── cache/
│   │   ├── signature_cache.go
│   │   └── signature_cache_test.go
│   ├── cmd/
│   │   ├── anthropic_login.go
│   │   ├── antigravity_login.go
│   │   ├── auth_manager.go
│   │   ├── iflow_cookie.go
│   │   ├── iflow_login.go
│   │   ├── kimi_login.go
│   │   ├── login.go
│   │   ├── openai_device_login.go
│   │   ├── openai_login.go
│   │   ├── qwen_login.go
│   │   ├── run.go
│   │   └── vertex_import.go
│   ├── config/
│   │   ├── codex_websocket_header_defaults_test.go
│   │   ├── config.go
│   │   ├── oauth_model_alias_test.go
│   │   ├── sdk_config.go
│   │   └── vertex_compat.go
│   ├── constant/
│   │   └── constant.go
│   ├── interfaces/
│   │   ├── api_handler.go
│   │   ├── client_models.go
│   │   ├── error_message.go
│   │   └── types.go
│   ├── logging/
│   │   ├── gin_logger.go
│   │   ├── gin_logger_test.go
│   │   ├── global_logger.go
│   │   ├── log_dir_cleaner.go
│   │   ├── log_dir_cleaner_test.go
│   │   ├── request_logger.go
│   │   └── requestid.go
│   ├── managementasset/
│   │   └── updater.go
│   ├── misc/
│   │   ├── claude_code_instructions.go
│   │   ├── claude_code_instructions.txt
│   │   ├── copy-example-config.go
│   │   ├── credentials.go
│   │   ├── header_utils.go
│   │   ├── mime-type.go
│   │   └── oauth.go
│   ├── registry/
│   │   ├── model_definitions.go
│   │   ├── model_registry.go
│   │   ├── model_registry_cache_test.go
│   │   ├── model_registry_hook_test.go
│   │   ├── model_registry_safety_test.go
│   │   ├── model_updater.go
│   │   └── models/
│   │       └── models.json
│   ├── runtime/
│   │   ├── executor/
│   │   │   ├── aistudio_executor.go
│   │   │   ├── antigravity_executor.go
│   │   │   ├── antigravity_executor_buildrequest_test.go
│   │   │   ├── cache_helpers.go
│   │   │   ├── caching_verify_test.go
│   │   │   ├── claude_executor.go
│   │   │   ├── claude_executor_test.go
│   │   │   ├── cloak_obfuscate.go
│   │   │   ├── cloak_utils.go
│   │   │   ├── codex_executor.go
│   │   │   ├── codex_executor_cache_test.go
│   │   │   ├── codex_executor_retry_test.go
│   │   │   ├── codex_websockets_executor.go
│   │   │   ├── codex_websockets_executor_test.go
│   │   │   ├── gemini_cli_executor.go
│   │   │   ├── gemini_executor.go
│   │   │   ├── gemini_vertex_executor.go
│   │   │   ├── iflow_executor.go
│   │   │   ├── iflow_executor_test.go
│   │   │   ├── kimi_executor.go
│   │   │   ├── kimi_executor_test.go
│   │   │   ├── logging_helpers.go
│   │   │   ├── openai_compat_executor.go
│   │   │   ├── openai_compat_executor_compact_test.go
│   │   │   ├── payload_helpers.go
│   │   │   ├── proxy_helpers.go
│   │   │   ├── proxy_helpers_test.go
│   │   │   ├── qwen_executor.go
│   │   │   ├── qwen_executor_test.go
│   │   │   ├── thinking_providers.go
│   │   │   ├── token_helpers.go
│   │   │   ├── usage_helpers.go
│   │   │   ├── usage_helpers_test.go
│   │   │   ├── user_id_cache.go
│   │   │   └── user_id_cache_test.go
│   │   └── geminicli/
│   │       └── state.go
│   ├── store/
│   │   ├── gitstore.go
│   │   ├── objectstore.go
│   │   └── postgresstore.go
│   ├── thinking/
│   │   ├── apply.go
│   │   ├── apply_user_defined_test.go
│   │   ├── convert.go
│   │   ├── errors.go
│   │   ├── provider/
│   │   │   ├── antigravity/
│   │   │   │   └── apply.go
│   │   │   ├── claude/
│   │   │   │   └── apply.go
│   │   │   ├── codex/
│   │   │   │   └── apply.go
│   │   │   ├── gemini/
│   │   │   │   └── apply.go
│   │   │   ├── geminicli/
│   │   │   │   └── apply.go
│   │   │   ├── iflow/
│   │   │   │   └── apply.go
│   │   │   ├── kimi/
│   │   │   │   ├── apply.go
│   │   │   │   └── apply_test.go
│   │   │   └── openai/
│   │   │       └── apply.go
│   │   ├── strip.go
│   │   ├── suffix.go
│   │   ├── text.go
│   │   ├── types.go
│   │   └── validate.go
│   ├── translator/
│   │   ├── antigravity/
│   │   │   ├── claude/
│   │   │   │   ├── antigravity_claude_request.go
│   │   │   │   ├── antigravity_claude_request_test.go
│   │   │   │   ├── antigravity_claude_response.go
│   │   │   │   ├── antigravity_claude_response_test.go
│   │   │   │   └── init.go
│   │   │   ├── gemini/
│   │   │   │   ├── antigravity_gemini_request.go
│   │   │   │   ├── antigravity_gemini_request_test.go
│   │   │   │   ├── antigravity_gemini_response.go
│   │   │   │   ├── antigravity_gemini_response_test.go
│   │   │   │   └── init.go
│   │   │   └── openai/
│   │   │       ├── chat-completions/
│   │   │       │   ├── antigravity_openai_request.go
│   │   │       │   ├── antigravity_openai_response.go
│   │   │       │   ├── antigravity_openai_response_test.go
│   │   │       │   └── init.go
│   │   │       └── responses/
│   │   │           ├── antigravity_openai-responses_request.go
│   │   │           ├── antigravity_openai-responses_response.go
│   │   │           └── init.go
│   │   ├── claude/
│   │   │   ├── gemini/
│   │   │   │   ├── claude_gemini_request.go
│   │   │   │   ├── claude_gemini_response.go
│   │   │   │   └── init.go
│   │   │   ├── gemini-cli/
│   │   │   │   ├── claude_gemini-cli_request.go
│   │   │   │   ├── claude_gemini-cli_response.go
│   │   │   │   └── init.go
│   │   │   └── openai/
│   │   │       ├── chat-completions/
│   │   │       │   ├── claude_openai_request.go
│   │   │       │   ├── claude_openai_request_test.go
│   │   │       │   ├── claude_openai_response.go
│   │   │       │   └── init.go
│   │   │       └── responses/
│   │   │           ├── claude_openai-responses_request.go
│   │   │           ├── claude_openai-responses_response.go
│   │   │           └── init.go
│   │   ├── codex/
│   │   │   ├── claude/
│   │   │   │   ├── codex_claude_request.go
│   │   │   │   ├── codex_claude_request_test.go
│   │   │   │   ├── codex_claude_response.go
│   │   │   │   └── init.go
│   │   │   ├── gemini/
│   │   │   │   ├── codex_gemini_request.go
│   │   │   │   ├── codex_gemini_response.go
│   │   │   │   └── init.go
│   │   │   ├── gemini-cli/
│   │   │   │   ├── codex_gemini-cli_request.go
│   │   │   │   ├── codex_gemini-cli_response.go
│   │   │   │   └── init.go
│   │   │   └── openai/
│   │   │       ├── chat-completions/
│   │   │       │   ├── codex_openai_request.go
│   │   │       │   ├── codex_openai_request_test.go
│   │   │       │   ├── codex_openai_response.go
│   │   │       │   ├── codex_openai_response_test.go
│   │   │       │   └── init.go
│   │   │       └── responses/
│   │   │           ├── codex_openai-responses_request.go
│   │   │           ├── codex_openai-responses_request_test.go
│   │   │           ├── codex_openai-responses_response.go
│   │   │           └── init.go
│   │   ├── gemini/
│   │   │   ├── claude/
│   │   │   │   ├── gemini_claude_request.go
│   │   │   │   ├── gemini_claude_request_test.go
│   │   │   │   ├── gemini_claude_response.go
│   │   │   │   └── init.go
│   │   │   ├── common/
│   │   │   │   └── safety.go
│   │   │   ├── gemini/
│   │   │   │   ├── gemini_gemini_request.go
│   │   │   │   ├── gemini_gemini_request_test.go
│   │   │   │   ├── gemini_gemini_response.go
│   │   │   │   └── init.go
│   │   │   ├── gemini-cli/
│   │   │   │   ├── gemini_gemini-cli_request.go
│   │   │   │   ├── gemini_gemini-cli_response.go
│   │   │   │   └── init.go
│   │   │   └── openai/
│   │   │       ├── chat-completions/
│   │   │       │   ├── gemini_openai_request.go
│   │   │       │   ├── gemini_openai_response.go
│   │   │       │   └── init.go
│   │   │       └── responses/
│   │   │           ├── gemini_openai-responses_request.go
│   │   │           ├── gemini_openai-responses_response.go
│   │   │           ├── gemini_openai-responses_response_test.go
│   │   │           └── init.go
│   │   ├── gemini-cli/
│   │   │   ├── claude/
│   │   │   │   ├── gemini-cli_claude_request.go
│   │   │   │   ├── gemini-cli_claude_request_test.go
│   │   │   │   ├── gemini-cli_claude_response.go
│   │   │   │   └── init.go
│   │   │   ├── gemini/
│   │   │   │   ├── gemini-cli_gemini_request.go
│   │   │   │   ├── gemini-cli_gemini_response.go
│   │   │   │   └── init.go
│   │   │   └── openai/
│   │   │       ├── chat-completions/
│   │   │       │   ├── gemini-cli_openai_request.go
│   │   │       │   ├── gemini-cli_openai_response.go
│   │   │       │   └── init.go
│   │   │       └── responses/
│   │   │           ├── gemini-cli_openai-responses_request.go
│   │   │           ├── gemini-cli_openai-responses_response.go
│   │   │           └── init.go
│   │   ├── init.go
│   │   ├── openai/
│   │   │   ├── claude/
│   │   │   │   ├── init.go
│   │   │   │   ├── openai_claude_request.go
│   │   │   │   ├── openai_claude_request_test.go
│   │   │   │   └── openai_claude_response.go
│   │   │   ├── gemini/
│   │   │   │   ├── init.go
│   │   │   │   ├── openai_gemini_request.go
│   │   │   │   └── openai_gemini_response.go
│   │   │   ├── gemini-cli/
│   │   │   │   ├── init.go
│   │   │   │   ├── openai_gemini_request.go
│   │   │   │   └── openai_gemini_response.go
│   │   │   └── openai/
│   │   │       ├── chat-completions/
│   │   │       │   ├── init.go
│   │   │       │   ├── openai_openai_request.go
│   │   │       │   └── openai_openai_response.go
│   │   │       └── responses/
│   │   │           ├── init.go
│   │   │           ├── openai_openai-responses_request.go
│   │   │           └── openai_openai-responses_response.go
│   │   └── translator/
│   │       └── translator.go
│   ├── tui/
│   │   ├── app.go
│   │   ├── auth_tab.go
│   │   ├── browser.go
│   │   ├── client.go
│   │   ├── config_tab.go
│   │   ├── dashboard.go
│   │   ├── i18n.go
│   │   ├── keys_tab.go
│   │   ├── loghook.go
│   │   ├── logs_tab.go
│   │   ├── oauth_tab.go
│   │   ├── styles.go
│   │   └── usage_tab.go
│   ├── usage/
│   │   └── logger_plugin.go
│   ├── util/
│   │   ├── claude_model.go
│   │   ├── claude_model_test.go
│   │   ├── claude_tool_id.go
│   │   ├── gemini_schema.go
│   │   ├── gemini_schema_test.go
│   │   ├── header_helpers.go
│   │   ├── image.go
│   │   ├── provider.go
│   │   ├── proxy.go
│   │   ├── sanitize_test.go
│   │   ├── ssh_helper.go
│   │   ├── translator.go
│   │   └── util.go
│   ├── watcher/
│   │   ├── clients.go
│   │   ├── config_reload.go
│   │   ├── diff/
│   │   │   ├── auth_diff.go
│   │   │   ├── config_diff.go
│   │   │   ├── config_diff_test.go
│   │   │   ├── model_hash.go
│   │   │   ├── model_hash_test.go
│   │   │   ├── models_summary.go
│   │   │   ├── oauth_excluded.go
│   │   │   ├── oauth_excluded_test.go
│   │   │   ├── oauth_model_alias.go
│   │   │   ├── openai_compat.go
│   │   │   └── openai_compat_test.go
│   │   ├── dispatcher.go
│   │   ├── events.go
│   │   ├── synthesizer/
│   │   │   ├── config.go
│   │   │   ├── config_test.go
│   │   │   ├── context.go
│   │   │   ├── file.go
│   │   │   ├── file_test.go
│   │   │   ├── helpers.go
│   │   │   ├── helpers_test.go
│   │   │   └── interface.go
│   │   ├── watcher.go
│   │   └── watcher_test.go
│   └── wsrelay/
│       ├── http.go
│       ├── manager.go
│       ├── message.go
│       └── session.go
├── sdk/
│   ├── access/
│   │   ├── errors.go
│   │   ├── manager.go
│   │   ├── registry.go
│   │   └── types.go
│   ├── api/
│   │   ├── handlers/
│   │   │   ├── claude/
│   │   │   │   └── code_handlers.go
│   │   │   ├── gemini/
│   │   │   │   ├── gemini-cli_handlers.go
│   │   │   │   └── gemini_handlers.go
│   │   │   ├── handlers.go
│   │   │   ├── handlers_error_response_test.go
│   │   │   ├── handlers_request_details_test.go
│   │   │   ├── handlers_stream_bootstrap_test.go
│   │   │   ├── header_filter.go
│   │   │   ├── header_filter_test.go
│   │   │   ├── openai/
│   │   │   │   ├── openai_handlers.go
│   │   │   │   ├── openai_responses_compact_test.go
│   │   │   │   ├── openai_responses_handlers.go
│   │   │   │   ├── openai_responses_handlers_stream_error_test.go
│   │   │   │   ├── openai_responses_websocket.go
│   │   │   │   └── openai_responses_websocket_test.go
│   │   │   ├── openai_responses_stream_error.go
│   │   │   ├── openai_responses_stream_error_test.go
│   │   │   └── stream_forwarder.go
│   │   ├── management.go
│   │   └── options.go
│   ├── auth/
│   │   ├── antigravity.go
│   │   ├── claude.go
│   │   ├── codex.go
│   │   ├── codex_device.go
│   │   ├── errors.go
│   │   ├── filestore.go
│   │   ├── filestore_test.go
│   │   ├── gemini.go
│   │   ├── iflow.go
│   │   ├── interfaces.go
│   │   ├── kimi.go
│   │   ├── manager.go
│   │   ├── qwen.go
│   │   ├── refresh_registry.go
│   │   └── store_registry.go
│   ├── cliproxy/
│   │   ├── auth/
│   │   │   ├── api_key_model_alias_test.go
│   │   │   ├── conductor.go
│   │   │   ├── conductor_availability_test.go
│   │   │   ├── conductor_executor_replace_test.go
│   │   │   ├── conductor_overrides_test.go
│   │   │   ├── conductor_scheduler_refresh_test.go
│   │   │   ├── conductor_update_test.go
│   │   │   ├── errors.go
│   │   │   ├── oauth_model_alias.go
│   │   │   ├── oauth_model_alias_test.go
│   │   │   ├── openai_compat_pool_test.go
│   │   │   ├── persist_policy.go
│   │   │   ├── persist_policy_test.go
│   │   │   ├── scheduler.go
│   │   │   ├── scheduler_benchmark_test.go
│   │   │   ├── scheduler_test.go
│   │   │   ├── selector.go
│   │   │   ├── selector_test.go
│   │   │   ├── status.go
│   │   │   ├── store.go
│   │   │   ├── types.go
│   │   │   └── types_test.go
│   │   ├── builder.go
│   │   ├── executor/
│   │   │   ├── context.go
│   │   │   └── types.go
│   │   ├── model_registry.go
│   │   ├── pipeline/
│   │   │   └── context.go
│   │   ├── pprof_server.go
│   │   ├── providers.go
│   │   ├── rtprovider.go
│   │   ├── rtprovider_test.go
│   │   ├── service.go
│   │   ├── service_codex_executor_binding_test.go
│   │   ├── service_excluded_models_test.go
│   │   ├── service_oauth_model_alias_test.go
│   │   ├── types.go
│   │   ├── usage/
│   │   │   └── manager.go
│   │   └── watcher.go
│   ├── config/
│   │   └── config.go
│   ├── logging/
│   │   └── request_logger.go
│   ├── proxyutil/
│   │   ├── proxy.go
│   │   └── proxy_test.go
│   └── translator/
│       ├── builtin/
│       │   └── builtin.go
│       ├── format.go
│       ├── formats.go
│       ├── helpers.go
│       ├── pipeline.go
│       ├── registry.go
│       └── types.go
└── test/
    ├── amp_management_test.go
    ├── builtin_tools_translation_test.go
    └── thinking_conversion_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
# Git and GitHub folders
.git/*
.github/*

# Docker and CI/CD related files
docker-compose.yml
.dockerignore
.gitignore
.goreleaser.yml
Dockerfile

# Documentation and license
docs/*
README.md
README_CN.md
LICENSE

# Runtime data folders (should be mounted as volumes)
auths/*
logs/*
conv/*
config.yaml

# Development/editor
bin/*
.vscode/*
.claude/*
.codex/*
.gemini/*
.serena/*
.agent/*
.agents/*
.opencode/*
.idea/*
.bmad/*
_bmad/*
_bmad-output/*


================================================
FILE: .github/FUNDING.yml
================================================
github: [router-for-me]


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Is it a request payload issue?**
[  ] Yes, this is a request payload issue. I am using a client/cURL to send a request payload, but I received an unexpected error.
[  ] No, it's another issue.

**If it's a request payload issue, you MUST know**
Our team doesn't have any GODs or ORACLEs or MIND READERs. Please make sure to attach the request log or curl payload.

**Describe the bug**
A clear and concise description of what the bug is.

**CLI Type**
What type of CLI account do you use?  (gemini-cli, gemini, codex, claude code or openai-compatibility)

**Model Name**
What model are you using? (example: gemini-2.5-pro, claude-sonnet-4-20250514, gpt-5, etc.)

**LLM Client**
What LLM Client are you using? (example: roo-code, cline, claude code, etc.)

**Request Information**
The best way is to paste the cURL command of the HTTP request here.
Alternatively, you can set `request-log: true` in the `config.yaml` file and then upload the detailed log file.

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**OS Type**
 - OS: [e.g. macOS]
 - Version [e.g. 15.6.0]

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/workflows/docker-image.yml
================================================
name: docker-image

on:
  push:
    tags:
      - v*

env:
  APP_NAME: CLIProxyAPI
  DOCKERHUB_REPO: eceasy/cli-proxy-api

jobs:
  docker_amd64:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Refresh models catalog
        run: |
          git fetch --depth 1 https://github.com/router-for-me/models.git main
          git show FETCH_HEAD:models.json > internal/registry/models/models.json
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      - name: Generate Build Metadata
        run: |
          echo "VERSION=${GITHUB_REF_NAME}" >> $GITHUB_ENV
          echo COMMIT=`git rev-parse --short HEAD` >> $GITHUB_ENV
          echo BUILD_DATE=`date -u +%Y-%m-%dT%H:%M:%SZ` >> $GITHUB_ENV
      - name: Build and push (amd64)
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64
          push: true
          build-args: |
            VERSION=${{ env.VERSION }}
            COMMIT=${{ env.COMMIT }}
            BUILD_DATE=${{ env.BUILD_DATE }}
          tags: |
            ${{ env.DOCKERHUB_REPO }}:latest-amd64
            ${{ env.DOCKERHUB_REPO }}:${{ env.VERSION }}-amd64

  docker_arm64:
    runs-on: ubuntu-24.04-arm
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Refresh models catalog
        run: |
          git fetch --depth 1 https://github.com/router-for-me/models.git main
          git show FETCH_HEAD:models.json > internal/registry/models/models.json
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      - name: Generate Build Metadata
        run: |
          echo "VERSION=${GITHUB_REF_NAME}" >> $GITHUB_ENV
          echo COMMIT=`git rev-parse --short HEAD` >> $GITHUB_ENV
          echo BUILD_DATE=`date -u +%Y-%m-%dT%H:%M:%SZ` >> $GITHUB_ENV
      - name: Build and push (arm64)
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/arm64
          push: true
          build-args: |
            VERSION=${{ env.VERSION }}
            COMMIT=${{ env.COMMIT }}
            BUILD_DATE=${{ env.BUILD_DATE }}
          tags: |
            ${{ env.DOCKERHUB_REPO }}:latest-arm64
            ${{ env.DOCKERHUB_REPO }}:${{ env.VERSION }}-arm64

  docker_manifest:
    runs-on: ubuntu-latest
    needs:
      - docker_amd64
      - docker_arm64
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      - name: Generate Build Metadata
        run: |
          echo "VERSION=${GITHUB_REF_NAME}" >> $GITHUB_ENV
          echo COMMIT=`git rev-parse --short HEAD` >> $GITHUB_ENV
          echo BUILD_DATE=`date -u +%Y-%m-%dT%H:%M:%SZ` >> $GITHUB_ENV
      - name: Create and push multi-arch manifests
        run: |
          docker buildx imagetools create \
            --tag "${DOCKERHUB_REPO}:latest" \
            "${DOCKERHUB_REPO}:latest-amd64" \
            "${DOCKERHUB_REPO}:latest-arm64"
          docker buildx imagetools create \
            --tag "${DOCKERHUB_REPO}:${VERSION}" \
            "${DOCKERHUB_REPO}:${VERSION}-amd64" \
            "${DOCKERHUB_REPO}:${VERSION}-arm64"
      - name: Cleanup temporary tags
        continue-on-error: true
        env:
          DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
          DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
        run: |
          set -euo pipefail
          namespace="${DOCKERHUB_REPO%%/*}"
          repo_name="${DOCKERHUB_REPO#*/}"

          token="$(
            curl -fsSL \
              -H 'Content-Type: application/json' \
              -d "{\"username\":\"${DOCKERHUB_USERNAME}\",\"password\":\"${DOCKERHUB_TOKEN}\"}" \
              'https://hub.docker.com/v2/users/login/' \
              | python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])'
          )"

          delete_tag() {
            local tag="$1"
            local url="https://hub.docker.com/v2/repositories/${namespace}/${repo_name}/tags/${tag}/"
            local http_code
            http_code="$(curl -sS -o /dev/null -w "%{http_code}" -X DELETE -H "Authorization: JWT ${token}" "${url}" || true)"
            if [ "${http_code}" = "204" ] || [ "${http_code}" = "404" ]; then
              echo "Docker Hub tag removed (or missing): ${DOCKERHUB_REPO}:${tag} (HTTP ${http_code})"
              return 0
            fi
            echo "Docker Hub tag delete failed: ${DOCKERHUB_REPO}:${tag} (HTTP ${http_code})"
            return 0
          }

          delete_tag "latest-amd64"
          delete_tag "latest-arm64"
          delete_tag "${VERSION}-amd64"
          delete_tag "${VERSION}-arm64"


================================================
FILE: .github/workflows/pr-path-guard.yml
================================================
name: translator-path-guard

on:
  pull_request:
    types:
      - opened
      - synchronize
      - reopened

jobs:
  ensure-no-translator-changes:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Detect internal/translator changes
        id: changed-files
        uses: tj-actions/changed-files@v45
        with:
          files: |
            internal/translator/**
      - name: Fail when restricted paths change
        if: steps.changed-files.outputs.any_changed == 'true'
        run: |
          echo "Changes under internal/translator are not allowed in pull requests."
          echo "You need to create an issue for our maintenance team to make the necessary changes."
          exit 1


================================================
FILE: .github/workflows/pr-test-build.yml
================================================
name: pr-test-build

on:
  pull_request:

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Refresh models catalog
        run: |
          git fetch --depth 1 https://github.com/router-for-me/models.git main
          git show FETCH_HEAD:models.json > internal/registry/models/models.json
      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version-file: go.mod
          cache: true
      - name: Build
        run: |
          go build -o test-output ./cmd/server
          rm -f test-output


================================================
FILE: .github/workflows/release.yaml
================================================
name: goreleaser

on:
  push:
    # run only against tags
    tags:
      - '*'

permissions:
  contents: write

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Refresh models catalog
        run: |
          git fetch --depth 1 https://github.com/router-for-me/models.git main
          git show FETCH_HEAD:models.json > internal/registry/models/models.json
      - run: git fetch --force --tags
      - uses: actions/setup-go@v4
        with:
          go-version: '>=1.26.0'
          cache: true
      - name: Generate Build Metadata
        run: |
          echo "VERSION=${GITHUB_REF_NAME}" >> $GITHUB_ENV
          echo COMMIT=`git rev-parse --short HEAD` >> $GITHUB_ENV
          echo BUILD_DATE=`date -u +%Y-%m-%dT%H:%M:%SZ` >> $GITHUB_ENV
      - uses: goreleaser/goreleaser-action@v4
        with:
          distribution: goreleaser
          version: latest
          args: release --clean --skip=validate
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          VERSION: ${{ env.VERSION }}
          COMMIT: ${{ env.COMMIT }}
          BUILD_DATE: ${{ env.BUILD_DATE }}


================================================
FILE: .gitignore
================================================
# Binaries
cli-proxy-api
*.exe

# Configuration
config.yaml
.env

# Generated content
bin/*
logs/*
conv/*
temp/*
refs/*

# Storage backends
pgstore/*
gitstore/*
objectstore/*

# Static assets
static/*

# Authentication data
auths/*
!auths/.gitkeep

# Documentation
docs/*
AGENTS.md
CLAUDE.md
GEMINI.md

# Tooling metadata
.vscode/*
.codex/*
.claude/*
.gemini/*
.serena/*
.agent/*
.agents/*
.agents/*
.opencode/*
.idea/*
.bmad/*
_bmad/*
_bmad-output/*

# macOS
.DS_Store
._*


================================================
FILE: .goreleaser.yml
================================================
version: 2

builds:
  - id: "cli-proxy-api"
    env:
      - CGO_ENABLED=0
    goos:
      - linux
      - windows
      - darwin
    goarch:
      - amd64
      - arm64
    main: ./cmd/server/
    binary: cli-proxy-api
    ldflags:
      - -s -w -X 'main.Version={{.Version}}' -X 'main.Commit={{.ShortCommit}}' -X 'main.BuildDate={{.Date}}'
archives:
  - id: "cli-proxy-api"
    format: tar.gz
    format_overrides:
      - goos: windows
        format: zip
    files:
      - LICENSE
      - README.md
      - README_CN.md
      - config.example.yaml

checksum:
  name_template: 'checksums.txt'

snapshot:
  name_template: "{{ incpatch .Version }}-next"

changelog:
  sort: asc
  filters:
    exclude:
      - '^docs:'
      - '^test:'


================================================
FILE: Dockerfile
================================================
FROM golang:1.26-alpine AS builder

WORKDIR /app

COPY go.mod go.sum ./

RUN go mod download

COPY . .

ARG VERSION=dev
ARG COMMIT=none
ARG BUILD_DATE=unknown

RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w -X 'main.Version=${VERSION}' -X 'main.Commit=${COMMIT}' -X 'main.BuildDate=${BUILD_DATE}'" -o ./CLIProxyAPI ./cmd/server/

FROM alpine:3.22.0

RUN apk add --no-cache tzdata

RUN mkdir /CLIProxyAPI

COPY --from=builder ./app/CLIProxyAPI /CLIProxyAPI/CLIProxyAPI

COPY config.example.yaml /CLIProxyAPI/config.example.yaml

WORKDIR /CLIProxyAPI

EXPOSE 8317

ENV TZ=Asia/Shanghai

RUN cp /usr/share/zoneinfo/${TZ} /etc/localtime && echo "${TZ}" > /etc/timezone

CMD ["./CLIProxyAPI"]

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2025-2005.9 Luis Pater
Copyright (c) 2025.9-present Router-For.ME

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================================================
FILE: README.md
================================================
# CLI Proxy API

English | [中文](README_CN.md)

A proxy server that provides OpenAI/Gemini/Claude/Codex compatible API interfaces for CLI.

It now also supports OpenAI Codex (GPT models) and Claude Code via OAuth.

So you can use local or multi-account CLI access with OpenAI(include Responses)/Gemini/Claude-compatible clients and SDKs.

## Sponsor

[![z.ai](https://assets.router-for.me/english-5-0.jpg)](https://z.ai/subscribe?ic=8JVLJQFSKB)

This project is sponsored by Z.ai, supporting us with their GLM CODING PLAN.

GLM CODING PLAN is a subscription service designed for AI coding, starting at just $10/month. It provides access to their flagship GLM-4.7 & (GLM-5 Only Available  for Pro Users)model across 10+ popular AI coding tools (Claude Code, Cline, Roo Code, etc.), offering developers top-tier, fast, and stable coding experiences.

Get 10% OFF GLM CODING PLAN:https://z.ai/subscribe?ic=8JVLJQFSKB

---

<table>
<tbody>
<tr>
<td width="180"><a href="https://www.packyapi.com/register?aff=cliproxyapi"><img src="./assets/packycode.png" alt="PackyCode" width="150"></a></td>
<td>Thanks to PackyCode for sponsoring this project! PackyCode is a reliable and efficient API relay service provider, offering relay services for Claude Code, Codex, Gemini, and more. PackyCode provides special discounts for our software users: register using <a href="https://www.packyapi.com/register?aff=cliproxyapi">this link</a> and enter the "cliproxyapi" promo code during recharge to get 10% off.</td>
</tr>
<tr>
<td width="180"><a href="https://www.aicodemirror.com/register?invitecode=TJNAIF"><img src="./assets/aicodemirror.png" alt="AICodeMirror" width="150"></a></td>
<td>Thanks to AICodeMirror for sponsoring this project! AICodeMirror provides official high-stability relay services for Claude Code / Codex / Gemini CLI, with enterprise-grade concurrency, fast invoicing, and 24/7 dedicated technical support. Claude Code / Codex / Gemini official channels at 38% / 2% / 9% of original price, with extra discounts on top-ups! AICodeMirror offers special benefits for CLIProxyAPI users: register via <a href="https://www.aicodemirror.com/register?invitecode=TJNAIF">this link</a> to enjoy 20% off your first top-up, and enterprise customers can get up to 25% off!</td>
</tr>
</tbody>
</table>

## Overview

- OpenAI/Gemini/Claude compatible API endpoints for CLI models
- OpenAI Codex support (GPT models) via OAuth login
- Claude Code support via OAuth login
- Qwen Code support via OAuth login
- iFlow support via OAuth login
- Amp CLI and IDE extensions support with provider routing
- Streaming and non-streaming responses
- Function calling/tools support
- Multimodal input support (text and images)
- Multiple accounts with round-robin load balancing (Gemini, OpenAI, Claude, Qwen and iFlow)
- Simple CLI authentication flows (Gemini, OpenAI, Claude, Qwen and iFlow)
- Generative Language API Key support
- AI Studio Build multi-account load balancing
- Gemini CLI multi-account load balancing
- Claude Code multi-account load balancing
- Qwen Code multi-account load balancing
- iFlow multi-account load balancing
- OpenAI Codex multi-account load balancing
- OpenAI-compatible upstream providers via config (e.g., OpenRouter)
- Reusable Go SDK for embedding the proxy (see `docs/sdk-usage.md`)

## Getting Started

CLIProxyAPI Guides: [https://help.router-for.me/](https://help.router-for.me/)

## Management API

see [MANAGEMENT_API.md](https://help.router-for.me/management/api)

## Amp CLI Support

CLIProxyAPI includes integrated support for [Amp CLI](https://ampcode.com) and Amp IDE extensions, enabling you to use your Google/ChatGPT/Claude OAuth subscriptions with Amp's coding tools:

- Provider route aliases for Amp's API patterns (`/api/provider/{provider}/v1...`)
- Management proxy for OAuth authentication and account features
- Smart model fallback with automatic routing
- **Model mapping** to route unavailable models to alternatives (e.g., `claude-opus-4.5` → `claude-sonnet-4`)
- Security-first design with localhost-only management endpoints

**→ [Complete Amp CLI Integration Guide](https://help.router-for.me/agent-client/amp-cli.html)**

## SDK Docs

- Usage: [docs/sdk-usage.md](docs/sdk-usage.md)
- Advanced (executors & translators): [docs/sdk-advanced.md](docs/sdk-advanced.md)
- Access: [docs/sdk-access.md](docs/sdk-access.md)
- Watcher: [docs/sdk-watcher.md](docs/sdk-watcher.md)
- Custom Provider Example: `examples/custom-provider`

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## Who is with us?

Those projects are based on CLIProxyAPI:

### [vibeproxy](https://github.com/automazeio/vibeproxy)

Native macOS menu bar app to use your Claude Code & ChatGPT subscriptions with AI coding tools - no API keys needed

### [Subtitle Translator](https://github.com/VjayC/SRT-Subtitle-Translator-Validator)

Browser-based tool to translate SRT subtitles using your Gemini subscription via CLIProxyAPI with automatic validation/error correction - no API keys needed

### [CCS (Claude Code Switch)](https://github.com/kaitranntt/ccs)

CLI wrapper for instant switching between multiple Claude accounts and alternative models (Gemini, Codex, Antigravity) via CLIProxyAPI OAuth - no API keys needed

### [ProxyPal](https://github.com/heyhuynhgiabuu/proxypal)

Native macOS GUI for managing CLIProxyAPI: configure providers, model mappings, and endpoints via OAuth - no API keys needed.

### [Quotio](https://github.com/nguyenphutrong/quotio)

Native macOS menu bar app that unifies Claude, Gemini, OpenAI, Qwen, and Antigravity subscriptions with real-time quota tracking and smart auto-failover for AI coding tools like Claude Code, OpenCode, and Droid - no API keys needed.

### [CodMate](https://github.com/loocor/CodMate)

Native macOS SwiftUI app for managing CLI AI sessions (Codex, Claude Code, Gemini CLI) with unified provider management, Git review, project organization, global search, and terminal integration. Integrates CLIProxyAPI to provide OAuth authentication for Codex, Claude, Gemini, Antigravity, and Qwen Code, with built-in and third-party provider rerouting through a single proxy endpoint - no API keys needed for OAuth providers.

### [ProxyPilot](https://github.com/Finesssee/ProxyPilot)

Windows-native CLIProxyAPI fork with TUI, system tray, and multi-provider OAuth for AI coding tools - no API keys needed.

### [Claude Proxy VSCode](https://github.com/uzhao/claude-proxy-vscode)

VSCode extension for quick switching between Claude Code models, featuring integrated CLIProxyAPI as its backend with automatic background lifecycle management.

### [ZeroLimit](https://github.com/0xtbug/zero-limit)

Windows desktop app built with Tauri + React for monitoring AI coding assistant quotas via CLIProxyAPI. Track usage across Gemini, Claude, OpenAI Codex, and Antigravity accounts with real-time dashboard, system tray integration, and one-click proxy control - no API keys needed.

### [CPA-XXX Panel](https://github.com/ferretgeek/CPA-X)

A lightweight web admin panel for CLIProxyAPI with health checks, resource monitoring, real-time logs, auto-update, request statistics and pricing display. Supports one-click installation and systemd service.

### [CLIProxyAPI Tray](https://github.com/kitephp/CLIProxyAPI_Tray)

A Windows tray application implemented using PowerShell scripts, without relying on any third-party libraries. The main features include: automatic creation of shortcuts, silent running, password management, channel switching (Main / Plus), and automatic downloading and updating.

### [霖君](https://github.com/wangdabaoqq/LinJun)

霖君 is a cross-platform desktop application for managing AI programming assistants, supporting macOS, Windows, and Linux systems. Unified management of Claude Code, Gemini CLI, OpenAI Codex, Qwen Code, and other AI coding tools, with local proxy for multi-account quota tracking and one-click configuration.

### [CLIProxyAPI Dashboard](https://github.com/itsmylife44/cliproxyapi-dashboard)

A modern web-based management dashboard for CLIProxyAPI built with Next.js, React, and PostgreSQL. Features real-time log streaming, structured configuration editing, API key management, OAuth provider integration for Claude/Gemini/Codex, usage analytics, container management, and config sync with OpenCode via companion plugin - no manual YAML editing needed.

### [All API Hub](https://github.com/qixing-jk/all-api-hub)

Browser extension for one-stop management of New API-compatible relay site accounts, featuring balance and usage dashboards, auto check-in, one-click key export to common apps, in-page API availability testing, and channel/model sync and redirection. It integrates with CLIProxyAPI through the Management API for one-click provider import and config sync.

### [Shadow AI](https://github.com/HEUDavid/shadow-ai)

Shadow AI is an AI assistant tool designed specifically for restricted environments. It provides a stealthy operation
mode without windows or traces, and enables cross-device AI Q&A interaction and control via the local area network (
LAN). Essentially, it is an automated collaboration layer of "screen/audio capture + AI inference + low-friction delivery",
helping users to immersively use AI assistants across applications on controlled devices or in restricted environments.

> [!NOTE]  
> If you developed a project based on CLIProxyAPI, please open a PR to add it to this list.

## More choices

Those projects are ports of CLIProxyAPI or inspired by it:

### [9Router](https://github.com/decolua/9router)

A Next.js implementation inspired by CLIProxyAPI, easy to install and use, built from scratch with format translation (OpenAI/Claude/Gemini/Ollama), combo system with auto-fallback, multi-account management with exponential backoff, a Next.js web dashboard, and support for CLI tools (Cursor, Claude Code, Cline, RooCode) - no API keys needed.

### [OmniRoute](https://github.com/diegosouzapw/OmniRoute)

Never stop coding. Smart routing to FREE & low-cost AI models with automatic fallback.

OmniRoute is an AI gateway for multi-provider LLMs: an OpenAI-compatible endpoint with smart routing, load balancing, retries, and fallbacks. Add policies, rate limits, caching, and observability for reliable, cost-aware inference.

> [!NOTE]  
> If you have developed a port of CLIProxyAPI or a project inspired by it, please open a PR to add it to this list.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.


================================================
FILE: README_CN.md
================================================
# CLI 代理 API

[English](README.md) | 中文

一个为 CLI 提供 OpenAI/Gemini/Claude/Codex 兼容 API 接口的代理服务器。

现已支持通过 OAuth 登录接入 OpenAI Codex(GPT 系列)和 Claude Code。

您可以使用本地或多账户的CLI方式,通过任何与 OpenAI(包括Responses)/Gemini/Claude 兼容的客户端和SDK进行访问。

## 赞助商

[![bigmodel.cn](https://assets.router-for.me/chinese-5-0.jpg)](https://www.bigmodel.cn/claude-code?ic=RRVJPB5SII)

本项目由 Z智谱 提供赞助, 他们通过 GLM CODING PLAN 对本项目提供技术支持。

GLM CODING PLAN 是专为AI编码打造的订阅套餐,每月最低仅需20元,即可在十余款主流AI编码工具如 Claude Code、Cline、Roo Code 中畅享智谱旗舰模型GLM-4.7(受限于算力,目前仅限Pro用户开放),为开发者提供顶尖的编码体验。

智谱AI为本产品提供了特别优惠,使用以下链接购买可以享受九折优惠:https://www.bigmodel.cn/claude-code?ic=RRVJPB5SII

---

<table>
<tbody>
<tr>
<td width="180"><a href="https://www.packyapi.com/register?aff=cliproxyapi"><img src="./assets/packycode.png" alt="PackyCode" width="150"></a></td>
<td>感谢 PackyCode 对本项目的赞助!PackyCode 是一家可靠高效的 API 中转服务商,提供 Claude Code、Codex、Gemini 等多种服务的中转。PackyCode 为本软件用户提供了特别优惠:使用<a href="https://www.packyapi.com/register?aff=cliproxyapi">此链接</a>注册,并在充值时输入 "cliproxyapi" 优惠码即可享受九折优惠。</td>
</tr>
<tr>
<td width="180"><a href="https://www.aicodemirror.com/register?invitecode=TJNAIF"><img src="./assets/aicodemirror.png" alt="AICodeMirror" width="150"></a></td>
<td>感谢 AICodeMirror 赞助了本项目!AICodeMirror 提供 Claude Code / Codex / Gemini CLI 官方高稳定中转服务,支持企业级高并发、极速开票、7×24 专属技术支持。 Claude Code / Codex / Gemini 官方渠道低至 3.8 / 0.2 / 0.9 折,充值更有折上折!AICodeMirror 为 CLIProxyAPI 的用户提供了特别福利,通过<a href="https://www.aicodemirror.com/register?invitecode=TJNAIF">此链接</a>注册的用户,可享受首充8折,企业客户最高可享 7.5 折!</td>
</tr>
</tbody>
</table>


## 功能特性

- 为 CLI 模型提供 OpenAI/Gemini/Claude/Codex 兼容的 API 端点
- 新增 OpenAI Codex(GPT 系列)支持(OAuth 登录)
- 新增 Claude Code 支持(OAuth 登录)
- 新增 Qwen Code 支持(OAuth 登录)
- 新增 iFlow 支持(OAuth 登录)
- 支持流式与非流式响应
- 函数调用/工具支持
- 多模态输入(文本、图片)
- 多账户支持与轮询负载均衡(Gemini、OpenAI、Claude、Qwen 与 iFlow)
- 简单的 CLI 身份验证流程(Gemini、OpenAI、Claude、Qwen 与 iFlow)
- 支持 Gemini AIStudio API 密钥
- 支持 AI Studio Build 多账户轮询
- 支持 Gemini CLI 多账户轮询
- 支持 Claude Code 多账户轮询
- 支持 Qwen Code 多账户轮询
- 支持 iFlow 多账户轮询
- 支持 OpenAI Codex 多账户轮询
- 通过配置接入上游 OpenAI 兼容提供商(例如 OpenRouter)
- 可复用的 Go SDK(见 `docs/sdk-usage_CN.md`)

## 新手入门

CLIProxyAPI 用户手册: [https://help.router-for.me/](https://help.router-for.me/cn/)

## 管理 API 文档

请参见 [MANAGEMENT_API_CN.md](https://help.router-for.me/cn/management/api)

## Amp CLI 支持

CLIProxyAPI 已内置对 [Amp CLI](https://ampcode.com) 和 Amp IDE 扩展的支持,可让你使用自己的 Google/ChatGPT/Claude OAuth 订阅来配合 Amp 编码工具:

- 提供商路由别名,兼容 Amp 的 API 路径模式(`/api/provider/{provider}/v1...`)
- 管理代理,处理 OAuth 认证和账号功能
- 智能模型回退与自动路由
- 以安全为先的设计,管理端点仅限 localhost

**→ [Amp CLI 完整集成指南](https://help.router-for.me/cn/agent-client/amp-cli.html)**

## SDK 文档

- 使用文档:[docs/sdk-usage_CN.md](docs/sdk-usage_CN.md)
- 高级(执行器与翻译器):[docs/sdk-advanced_CN.md](docs/sdk-advanced_CN.md)
- 认证: [docs/sdk-access_CN.md](docs/sdk-access_CN.md)
- 凭据加载/更新: [docs/sdk-watcher_CN.md](docs/sdk-watcher_CN.md)
- 自定义 Provider 示例:`examples/custom-provider`

## 贡献

欢迎贡献!请随时提交 Pull Request。

1. Fork 仓库
2. 创建您的功能分支(`git checkout -b feature/amazing-feature`)
3. 提交您的更改(`git commit -m 'Add some amazing feature'`)
4. 推送到分支(`git push origin feature/amazing-feature`)
5. 打开 Pull Request

## 谁与我们在一起?

这些项目基于 CLIProxyAPI:

### [vibeproxy](https://github.com/automazeio/vibeproxy)

一个原生 macOS 菜单栏应用,让您可以使用 Claude Code & ChatGPT 订阅服务和 AI 编程工具,无需 API 密钥。

### [Subtitle Translator](https://github.com/VjayC/SRT-Subtitle-Translator-Validator)

一款基于浏览器的 SRT 字幕翻译工具,可通过 CLI 代理 API 使用您的 Gemini 订阅。内置自动验证与错误修正功能,无需 API 密钥。

### [CCS (Claude Code Switch)](https://github.com/kaitranntt/ccs)

CLI 封装器,用于通过 CLIProxyAPI OAuth 即时切换多个 Claude 账户和替代模型(Gemini, Codex, Antigravity),无需 API 密钥。

### [ProxyPal](https://github.com/heyhuynhgiabuu/proxypal)

基于 macOS 平台的原生 CLIProxyAPI GUI:配置供应商、模型映射以及OAuth端点,无需 API 密钥。

### [Quotio](https://github.com/nguyenphutrong/quotio)

原生 macOS 菜单栏应用,统一管理 Claude、Gemini、OpenAI、Qwen 和 Antigravity 订阅,提供实时配额追踪和智能自动故障转移,支持 Claude Code、OpenCode 和 Droid 等 AI 编程工具,无需 API 密钥。

### [CodMate](https://github.com/loocor/CodMate)

原生 macOS SwiftUI 应用,用于管理 CLI AI 会话(Claude Code、Codex、Gemini CLI),提供统一的提供商管理、Git 审查、项目组织、全局搜索和终端集成。集成 CLIProxyAPI 为 Codex、Claude、Gemini、Antigravity 和 Qwen Code 提供统一的 OAuth 认证,支持内置和第三方提供商通过单一代理端点重路由 - OAuth 提供商无需 API 密钥。

### [ProxyPilot](https://github.com/Finesssee/ProxyPilot)

原生 Windows CLIProxyAPI 分支,集成 TUI、系统托盘及多服务商 OAuth 认证,专为 AI 编程工具打造,无需 API 密钥。

### [Claude Proxy VSCode](https://github.com/uzhao/claude-proxy-vscode)

一款 VSCode 扩展,提供了在 VSCode 中快速切换 Claude Code 模型的功能,内置 CLIProxyAPI 作为其后端,支持后台自动启动和关闭。

### [ZeroLimit](https://github.com/0xtbug/zero-limit)

Windows 桌面应用,基于 Tauri + React 构建,用于通过 CLIProxyAPI 监控 AI 编程助手配额。支持跨 Gemini、Claude、OpenAI Codex 和 Antigravity 账户的使用量追踪,提供实时仪表盘、系统托盘集成和一键代理控制,无需 API 密钥。

### [CPA-XXX Panel](https://github.com/ferretgeek/CPA-X)

面向 CLIProxyAPI 的 Web 管理面板,提供健康检查、资源监控、日志查看、自动更新、请求统计与定价展示,支持一键安装与 systemd 服务。

### [CLIProxyAPI Tray](https://github.com/kitephp/CLIProxyAPI_Tray)

Windows 托盘应用,基于 PowerShell 脚本实现,不依赖任何第三方库。主要功能包括:自动创建快捷方式、静默运行、密码管理、通道切换(Main / Plus)以及自动下载与更新。

### [霖君](https://github.com/wangdabaoqq/LinJun)

霖君是一款用于管理AI编程助手的跨平台桌面应用,支持macOS、Windows、Linux系统。统一管理Claude Code、Gemini CLI、OpenAI Codex、Qwen Code等AI编程工具,本地代理实现多账户配额跟踪和一键配置。

### [CLIProxyAPI Dashboard](https://github.com/itsmylife44/cliproxyapi-dashboard)

一个面向 CLIProxyAPI 的现代化 Web 管理仪表盘,基于 Next.js、React 和 PostgreSQL 构建。支持实时日志流、结构化配置编辑、API Key 管理、Claude/Gemini/Codex 的 OAuth 提供方集成、使用量分析、容器管理,并可通过配套插件与 OpenCode 同步配置,无需手动编辑 YAML。

### [All API Hub](https://github.com/qixing-jk/all-api-hub)

用于一站式管理 New API 兼容中转站账号的浏览器扩展,提供余额与用量看板、自动签到、密钥一键导出到常用应用、网页内 API 可用性测试,以及渠道与模型同步和重定向。支持通过 CLIProxyAPI Management API 一键导入 Provider 与同步配置。

### [Shadow AI](https://github.com/HEUDavid/shadow-ai)

Shadow AI 是一款专为受限环境设计的 AI 辅助工具。提供无窗口、无痕迹的隐蔽运行方式,并通过局域网实现跨设备的 AI 问答交互与控制。本质上是一个「屏幕/音频采集 + AI 推理 + 低摩擦投送」的自动化协作层,帮助用户在受控设备/受限环境下沉浸式跨应用地使用 AI 助手。

> [!NOTE]  
> 如果你开发了基于 CLIProxyAPI 的项目,请提交一个 PR(拉取请求)将其添加到此列表中。

## 更多选择

以下项目是 CLIProxyAPI 的移植版或受其启发:

### [9Router](https://github.com/decolua/9router)

基于 Next.js 的实现,灵感来自 CLIProxyAPI,易于安装使用;自研格式转换(OpenAI/Claude/Gemini/Ollama)、组合系统与自动回退、多账户管理(指数退避)、Next.js Web 控制台,并支持 Cursor、Claude Code、Cline、RooCode 等 CLI 工具,无需 API 密钥。

### [OmniRoute](https://github.com/diegosouzapw/OmniRoute)

代码不止,创新不停。智能路由至免费及低成本 AI 模型,并支持自动故障转移。

OmniRoute 是一个面向多供应商大语言模型的 AI 网关:它提供兼容 OpenAI 的端点,具备智能路由、负载均衡、重试及回退机制。通过添加策略、速率限制、缓存和可观测性,确保推理过程既可靠又具备成本意识。

> [!NOTE]  
> 如果你开发了 CLIProxyAPI 的移植或衍生项目,请提交 PR 将其添加到此列表中。

## 许可证

此项目根据 MIT 许可证授权 - 有关详细信息,请参阅 [LICENSE](LICENSE) 文件。

## 写给所有中国网友的

QQ 群:188637136

或

Telegram 群:https://t.me/CLIProxyAPI


================================================
FILE: auths/.gitkeep
================================================


================================================
FILE: cmd/fetch_antigravity_models/main.go
================================================
// Command fetch_antigravity_models connects to the Antigravity API using the
// stored auth credentials and saves the dynamically fetched model list to a
// JSON file for inspection or offline use.
//
// Usage:
//
//	go run ./cmd/fetch_antigravity_models [flags]
//
// Flags:
//
//	--auths-dir <path>  Directory containing auth JSON files (default: "auths")
//	--output    <path>  Output JSON file path             (default: "antigravity_models.json")
//	--pretty            Pretty-print the output JSON      (default: true)
package main

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/router-for-me/CLIProxyAPI/v6/internal/logging"
	sdkauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/auth"
	coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
	"github.com/router-for-me/CLIProxyAPI/v6/sdk/proxyutil"
	log "github.com/sirupsen/logrus"
	"github.com/tidwall/gjson"
)

const (
	antigravityBaseURLDaily        = "https://daily-cloudcode-pa.googleapis.com"
	antigravitySandboxBaseURLDaily = "https://daily-cloudcode-pa.sandbox.googleapis.com"
	antigravityBaseURLProd         = "https://cloudcode-pa.googleapis.com"
	antigravityModelsPath          = "/v1internal:fetchAvailableModels"
)

func init() {
	logging.SetupBaseLogger()
	log.SetLevel(log.InfoLevel)
}

// modelOutput wraps the fetched model list with fetch metadata.
type modelOutput struct {
	Models []modelEntry `json:"models"`
}

// modelEntry contains only the fields we want to keep for static model definitions.
type modelEntry struct {
	ID                  string `json:"id"`
	Object              string `json:"object"`
	OwnedBy             string `json:"owned_by"`
	Type                string `json:"type"`
	DisplayName         string `json:"display_name"`
	Name                string `json:"name"`
	Description         string `json:"description"`
	ContextLength       int    `json:"context_length,omitempty"`
	MaxCompletionTokens int    `json:"max_completion_tokens,omitempty"`
}

func main() {
	var authsDir string
	var outputPath string
	var pretty bool

	flag.StringVar(&authsDir, "auths-dir", "auths", "Directory containing auth JSON files")
	flag.StringVar(&outputPath, "output", "antigravity_models.json", "Output JSON file path")
	flag.BoolVar(&pretty, "pretty", true, "Pretty-print the output JSON")
	flag.Parse()

	// Resolve relative paths against the working directory.
	wd, err := os.Getwd()
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: cannot get working directory: %v\n", err)
		os.Exit(1)
	}
	if !filepath.IsAbs(authsDir) {
		authsDir = filepath.Join(wd, authsDir)
	}
	if !filepath.IsAbs(outputPath) {
		outputPath = filepath.Join(wd, outputPath)
	}

	fmt.Printf("Scanning auth files in: %s\n", authsDir)

	// Load all auth records from the directory.
	fileStore := sdkauth.NewFileTokenStore()
	fileStore.SetBaseDir(authsDir)

	ctx := context.Background()
	auths, err := fileStore.List(ctx)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: failed to list auth files: %v\n", err)
		os.Exit(1)
	}
	if len(auths) == 0 {
		fmt.Fprintf(os.Stderr, "error: no auth files found in %s\n", authsDir)
		os.Exit(1)
	}

	// Find the first enabled antigravity auth.
	var chosen *coreauth.Auth
	for _, a := range auths {
		if a == nil || a.Disabled {
			continue
		}
		if strings.EqualFold(strings.TrimSpace(a.Provider), "antigravity") {
			chosen = a
			break
		}
	}
	if chosen == nil {
		fmt.Fprintf(os.Stderr, "error: no enabled antigravity auth found in %s\n", authsDir)
		os.Exit(1)
	}

	fmt.Printf("Using auth: id=%s label=%s\n", chosen.ID, chosen.Label)

	// Fetch models from the upstream Antigravity API.
	fmt.Println("Fetching Antigravity model list from upstream...")

	fetchCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()

	models := fetchModels(fetchCtx, chosen)
	if len(models) == 0 {
		fmt.Fprintln(os.Stderr, "warning: no models returned (API may be unavailable or token expired)")
	} else {
		fmt.Printf("Fetched %d models.\n", len(models))
	}

	// Build the output payload.
	out := modelOutput{
		Models: models,
	}

	// Marshal to JSON.
	var raw []byte
	if pretty {
		raw, err = json.MarshalIndent(out, "", "  ")
	} else {
		raw, err = json.Marshal(out)
	}
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: failed to marshal JSON: %v\n", err)
		os.Exit(1)
	}

	if err = os.WriteFile(outputPath, raw, 0o644); err != nil {
		fmt.Fprintf(os.Stderr, "error: failed to write output file %s: %v\n", outputPath, err)
		os.Exit(1)
	}

	fmt.Printf("Model list saved to: %s\n", outputPath)
}

func fetchModels(ctx context.Context, auth *coreauth.Auth) []modelEntry {
	accessToken := metaStringValue(auth.Metadata, "access_token")
	if accessToken == "" {
		fmt.Fprintln(os.Stderr, "error: no access token found in auth")
		return nil
	}

	baseURLs := []string{antigravityBaseURLProd, antigravityBaseURLDaily, antigravitySandboxBaseURLDaily}

	for _, baseURL := range baseURLs {
		modelsURL := baseURL + antigravityModelsPath

		var payload []byte
		if auth != nil && auth.Metadata != nil {
			if pid, ok := auth.Metadata["project_id"].(string); ok && strings.TrimSpace(pid) != "" {
				payload = []byte(fmt.Sprintf(`{"project": "%s"}`, strings.TrimSpace(pid)))
			}
		}
		if len(payload) == 0 {
			payload = []byte(`{}`)
		}

		httpReq, errReq := http.NewRequestWithContext(ctx, http.MethodPost, modelsURL, strings.NewReader(string(payload)))
		if errReq != nil {
			continue
		}
		httpReq.Close = true
		httpReq.Header.Set("Content-Type", "application/json")
		httpReq.Header.Set("Authorization", "Bearer "+accessToken)
		httpReq.Header.Set("User-Agent", "antigravity/1.19.6 darwin/arm64")

		httpClient := &http.Client{Timeout: 30 * time.Second}
		if transport, _, errProxy := proxyutil.BuildHTTPTransport(auth.ProxyURL); errProxy == nil && transport != nil {
			httpClient.Transport = transport
		}
		httpResp, errDo := httpClient.Do(httpReq)
		if errDo != nil {
			continue
		}

		bodyBytes, errRead := io.ReadAll(httpResp.Body)
		httpResp.Body.Close()
		if errRead != nil {
			continue
		}

		if httpResp.StatusCode < http.StatusOK || httpResp.StatusCode >= http.StatusMultipleChoices {
			continue
		}

		result := gjson.GetBytes(bodyBytes, "models")
		if !result.Exists() {
			continue
		}

		var models []modelEntry

		for originalName, modelData := range result.Map() {
			modelID := strings.TrimSpace(originalName)
			if modelID == "" {
				continue
			}
			// Skip internal/experimental models
			switch modelID {
			case "chat_20706", "chat_23310", "tab_flash_lite_preview", "tab_jump_flash_lite_preview", "gemini-2.5-flash-thinking", "gemini-2.5-pro":
				continue
			}

			displayName := modelData.Get("displayName").String()
			if displayName == "" {
				displayName = modelID
			}

			entry := modelEntry{
				ID:          modelID,
				Object:      "model",
				OwnedBy:     "antigravity",
				Type:        "antigravity",
				DisplayName: displayName,
				Name:        modelID,
				Description: displayName,
			}

			if maxTok := modelData.Get("maxTokens").Int(); maxTok > 0 {
				entry.ContextLength = int(maxTok)
			}
			if maxOut := modelData.Get("maxOutputTokens").Int(); maxOut > 0 {
				entry.MaxCompletionTokens = int(maxOut)
			}

			models = append(models, entry)
		}

		return models
	}

	return nil
}

func metaStringValue(m map[string]interface{}, key string) string {
	if m == nil {
		return ""
	}
	v, ok := m[key]
	if !ok {
		return ""
	}
	switch val := v.(type) {
	case string:
		return val
	default:
		return ""
	}
}


================================================
FILE: cmd/server/main.go
================================================
// Package main provides the entry point for the CLI Proxy API server.
// This server acts as a proxy that provides OpenAI/Gemini/Claude compatible API interfaces
// for CLI models, allowing CLI models to be used with tools and libraries designed for standard AI APIs.
package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/fs"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/joho/godotenv"
	configaccess "github.com/router-for-me/CLIProxyAPI/v6/internal/access/config_access"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/buildinfo"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/cmd"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/logging"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/managementasset"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/store"
	_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/tui"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/usage"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
	sdkAuth "github.com/router-for-me/CLIProxyAPI/v6/sdk/auth"
	coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
	log "github.com/sirupsen/logrus"
)

var (
	Version           = "dev"
	Commit            = "none"
	BuildDate         = "unknown"
	DefaultConfigPath = ""
)

// init initializes the shared logger setup.
func init() {
	logging.SetupBaseLogger()
	buildinfo.Version = Version
	buildinfo.Commit = Commit
	buildinfo.BuildDate = BuildDate
}

// main is the entry point of the application.
// It parses command-line flags, loads configuration, and starts the appropriate
// service based on the provided flags (login, codex-login, or server mode).
func main() {
	fmt.Printf("CLIProxyAPI Version: %s, Commit: %s, BuiltAt: %s\n", buildinfo.Version, buildinfo.Commit, buildinfo.BuildDate)

	// Command-line flags to control the application's behavior.
	var login bool
	var codexLogin bool
	var codexDeviceLogin bool
	var claudeLogin bool
	var qwenLogin bool
	var iflowLogin bool
	var iflowCookie bool
	var noBrowser bool
	var oauthCallbackPort int
	var antigravityLogin bool
	var kimiLogin bool
	var projectID string
	var vertexImport string
	var configPath string
	var password string
	var tuiMode bool
	var standalone bool

	// Define command-line flags for different operation modes.
	flag.BoolVar(&login, "login", false, "Login Google Account")
	flag.BoolVar(&codexLogin, "codex-login", false, "Login to Codex using OAuth")
	flag.BoolVar(&codexDeviceLogin, "codex-device-login", false, "Login to Codex using device code flow")
	flag.BoolVar(&claudeLogin, "claude-login", false, "Login to Claude using OAuth")
	flag.BoolVar(&qwenLogin, "qwen-login", false, "Login to Qwen using OAuth")
	flag.BoolVar(&iflowLogin, "iflow-login", false, "Login to iFlow using OAuth")
	flag.BoolVar(&iflowCookie, "iflow-cookie", false, "Login to iFlow using Cookie")
	flag.BoolVar(&noBrowser, "no-browser", false, "Don't open browser automatically for OAuth")
	flag.IntVar(&oauthCallbackPort, "oauth-callback-port", 0, "Override OAuth callback port (defaults to provider-specific port)")
	flag.BoolVar(&antigravityLogin, "antigravity-login", false, "Login to Antigravity using OAuth")
	flag.BoolVar(&kimiLogin, "kimi-login", false, "Login to Kimi using OAuth")
	flag.StringVar(&projectID, "project_id", "", "Project ID (Gemini only, not required)")
	flag.StringVar(&configPath, "config", DefaultConfigPath, "Configure File Path")
	flag.StringVar(&vertexImport, "vertex-import", "", "Import Vertex service account key JSON file")
	flag.StringVar(&password, "password", "", "")
	flag.BoolVar(&tuiMode, "tui", false, "Start with terminal management UI")
	flag.BoolVar(&standalone, "standalone", false, "In TUI mode, start an embedded local server")

	flag.CommandLine.Usage = func() {
		out := flag.CommandLine.Output()
		_, _ = fmt.Fprintf(out, "Usage of %s\n", os.Args[0])
		flag.CommandLine.VisitAll(func(f *flag.Flag) {
			if f.Name == "password" {
				return
			}
			s := fmt.Sprintf("  -%s", f.Name)
			name, unquoteUsage := flag.UnquoteUsage(f)
			if name != "" {
				s += " " + name
			}
			if len(s) <= 4 {
				s += "	"
			} else {
				s += "\n    "
			}
			if unquoteUsage != "" {
				s += unquoteUsage
			}
			if f.DefValue != "" && f.DefValue != "false" && f.DefValue != "0" {
				s += fmt.Sprintf(" (default %s)", f.DefValue)
			}
			_, _ = fmt.Fprint(out, s+"\n")
		})
	}

	// Parse the command-line flags.
	flag.Parse()

	// Core application variables.
	var err error
	var cfg *config.Config
	var isCloudDeploy bool
	var (
		usePostgresStore     bool
		pgStoreDSN           string
		pgStoreSchema        string
		pgStoreLocalPath     string
		pgStoreInst          *store.PostgresStore
		useGitStore          bool
		gitStoreRemoteURL    string
		gitStoreUser         string
		gitStorePassword     string
		gitStoreLocalPath    string
		gitStoreInst         *store.GitTokenStore
		gitStoreRoot         string
		useObjectStore       bool
		objectStoreEndpoint  string
		objectStoreAccess    string
		objectStoreSecret    string
		objectStoreBucket    string
		objectStoreLocalPath string
		objectStoreInst      *store.ObjectTokenStore
	)

	wd, err := os.Getwd()
	if err != nil {
		log.Errorf("failed to get working directory: %v", err)
		return
	}

	// Load environment variables from .env if present.
	if errLoad := godotenv.Load(filepath.Join(wd, ".env")); errLoad != nil {
		if !errors.Is(errLoad, os.ErrNotExist) {
			log.WithError(errLoad).Warn("failed to load .env file")
		}
	}

	lookupEnv := func(keys ...string) (string, bool) {
		for _, key := range keys {
			if value, ok := os.LookupEnv(key); ok {
				if trimmed := strings.TrimSpace(value); trimmed != "" {
					return trimmed, true
				}
			}
		}
		return "", false
	}
	writableBase := util.WritablePath()
	if value, ok := lookupEnv("PGSTORE_DSN", "pgstore_dsn"); ok {
		usePostgresStore = true
		pgStoreDSN = value
	}
	if usePostgresStore {
		if value, ok := lookupEnv("PGSTORE_SCHEMA", "pgstore_schema"); ok {
			pgStoreSchema = value
		}
		if value, ok := lookupEnv("PGSTORE_LOCAL_PATH", "pgstore_local_path"); ok {
			pgStoreLocalPath = value
		}
		if pgStoreLocalPath == "" {
			if writableBase != "" {
				pgStoreLocalPath = writableBase
			} else {
				pgStoreLocalPath = wd
			}
		}
		useGitStore = false
	}
	if value, ok := lookupEnv("GITSTORE_GIT_URL", "gitstore_git_url"); ok {
		useGitStore = true
		gitStoreRemoteURL = value
	}
	if value, ok := lookupEnv("GITSTORE_GIT_USERNAME", "gitstore_git_username"); ok {
		gitStoreUser = value
	}
	if value, ok := lookupEnv("GITSTORE_GIT_TOKEN", "gitstore_git_token"); ok {
		gitStorePassword = value
	}
	if value, ok := lookupEnv("GITSTORE_LOCAL_PATH", "gitstore_local_path"); ok {
		gitStoreLocalPath = value
	}
	if value, ok := lookupEnv("OBJECTSTORE_ENDPOINT", "objectstore_endpoint"); ok {
		useObjectStore = true
		objectStoreEndpoint = value
	}
	if value, ok := lookupEnv("OBJECTSTORE_ACCESS_KEY", "objectstore_access_key"); ok {
		objectStoreAccess = value
	}
	if value, ok := lookupEnv("OBJECTSTORE_SECRET_KEY", "objectstore_secret_key"); ok {
		objectStoreSecret = value
	}
	if value, ok := lookupEnv("OBJECTSTORE_BUCKET", "objectstore_bucket"); ok {
		objectStoreBucket = value
	}
	if value, ok := lookupEnv("OBJECTSTORE_LOCAL_PATH", "objectstore_local_path"); ok {
		objectStoreLocalPath = value
	}

	// Check for cloud deploy mode only on first execution
	// Read env var name in uppercase: DEPLOY
	deployEnv := os.Getenv("DEPLOY")
	if deployEnv == "cloud" {
		isCloudDeploy = true
	}

	// Determine and load the configuration file.
	// Prefer the Postgres store when configured, otherwise fallback to git or local files.
	var configFilePath string
	if usePostgresStore {
		if pgStoreLocalPath == "" {
			pgStoreLocalPath = wd
		}
		pgStoreLocalPath = filepath.Join(pgStoreLocalPath, "pgstore")
		ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
		pgStoreInst, err = store.NewPostgresStore(ctx, store.PostgresStoreConfig{
			DSN:      pgStoreDSN,
			Schema:   pgStoreSchema,
			SpoolDir: pgStoreLocalPath,
		})
		cancel()
		if err != nil {
			log.Errorf("failed to initialize postgres token store: %v", err)
			return
		}
		examplePath := filepath.Join(wd, "config.example.yaml")
		ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
		if errBootstrap := pgStoreInst.Bootstrap(ctx, examplePath); errBootstrap != nil {
			cancel()
			log.Errorf("failed to bootstrap postgres-backed config: %v", errBootstrap)
			return
		}
		cancel()
		configFilePath = pgStoreInst.ConfigPath()
		cfg, err = config.LoadConfigOptional(configFilePath, isCloudDeploy)
		if err == nil {
			cfg.AuthDir = pgStoreInst.AuthDir()
			log.Infof("postgres-backed token store enabled, workspace path: %s", pgStoreInst.WorkDir())
		}
	} else if useObjectStore {
		if objectStoreLocalPath == "" {
			if writableBase != "" {
				objectStoreLocalPath = writableBase
			} else {
				objectStoreLocalPath = wd
			}
		}
		objectStoreRoot := filepath.Join(objectStoreLocalPath, "objectstore")
		resolvedEndpoint := strings.TrimSpace(objectStoreEndpoint)
		useSSL := true
		if strings.Contains(resolvedEndpoint, "://") {
			parsed, errParse := url.Parse(resolvedEndpoint)
			if errParse != nil {
				log.Errorf("failed to parse object store endpoint %q: %v", objectStoreEndpoint, errParse)
				return
			}
			switch strings.ToLower(parsed.Scheme) {
			case "http":
				useSSL = false
			case "https":
				useSSL = true
			default:
				log.Errorf("unsupported object store scheme %q (only http and https are allowed)", parsed.Scheme)
				return
			}
			if parsed.Host == "" {
				log.Errorf("object store endpoint %q is missing host information", objectStoreEndpoint)
				return
			}
			resolvedEndpoint = parsed.Host
			if parsed.Path != "" && parsed.Path != "/" {
				resolvedEndpoint = strings.TrimSuffix(parsed.Host+parsed.Path, "/")
			}
		}
		resolvedEndpoint = strings.TrimRight(resolvedEndpoint, "/")
		objCfg := store.ObjectStoreConfig{
			Endpoint:  resolvedEndpoint,
			Bucket:    objectStoreBucket,
			AccessKey: objectStoreAccess,
			SecretKey: objectStoreSecret,
			LocalRoot: objectStoreRoot,
			UseSSL:    useSSL,
			PathStyle: true,
		}
		objectStoreInst, err = store.NewObjectTokenStore(objCfg)
		if err != nil {
			log.Errorf("failed to initialize object token store: %v", err)
			return
		}
		examplePath := filepath.Join(wd, "config.example.yaml")
		ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
		if errBootstrap := objectStoreInst.Bootstrap(ctx, examplePath); errBootstrap != nil {
			cancel()
			log.Errorf("failed to bootstrap object-backed config: %v", errBootstrap)
			return
		}
		cancel()
		configFilePath = objectStoreInst.ConfigPath()
		cfg, err = config.LoadConfigOptional(configFilePath, isCloudDeploy)
		if err == nil {
			if cfg == nil {
				cfg = &config.Config{}
			}
			cfg.AuthDir = objectStoreInst.AuthDir()
			log.Infof("object-backed token store enabled, bucket: %s", objectStoreBucket)
		}
	} else if useGitStore {
		if gitStoreLocalPath == "" {
			if writableBase != "" {
				gitStoreLocalPath = writableBase
			} else {
				gitStoreLocalPath = wd
			}
		}
		gitStoreRoot = filepath.Join(gitStoreLocalPath, "gitstore")
		authDir := filepath.Join(gitStoreRoot, "auths")
		gitStoreInst = store.NewGitTokenStore(gitStoreRemoteURL, gitStoreUser, gitStorePassword)
		gitStoreInst.SetBaseDir(authDir)
		if errRepo := gitStoreInst.EnsureRepository(); errRepo != nil {
			log.Errorf("failed to prepare git token store: %v", errRepo)
			return
		}
		configFilePath = gitStoreInst.ConfigPath()
		if configFilePath == "" {
			configFilePath = filepath.Join(gitStoreRoot, "config", "config.yaml")
		}
		if _, statErr := os.Stat(configFilePath); errors.Is(statErr, fs.ErrNotExist) {
			examplePath := filepath.Join(wd, "config.example.yaml")
			if _, errExample := os.Stat(examplePath); errExample != nil {
				log.Errorf("failed to find template config file: %v", errExample)
				return
			}
			if errCopy := misc.CopyConfigTemplate(examplePath, configFilePath); errCopy != nil {
				log.Errorf("failed to bootstrap git-backed config: %v", errCopy)
				return
			}
			if errCommit := gitStoreInst.PersistConfig(context.Background()); errCommit != nil {
				log.Errorf("failed to commit initial git-backed config: %v", errCommit)
				return
			}
			log.Infof("git-backed config initialized from template: %s", configFilePath)
		} else if statErr != nil {
			log.Errorf("failed to inspect git-backed config: %v", statErr)
			return
		}
		cfg, err = config.LoadConfigOptional(configFilePath, isCloudDeploy)
		if err == nil {
			cfg.AuthDir = gitStoreInst.AuthDir()
			log.Infof("git-backed token store enabled, repository path: %s", gitStoreRoot)
		}
	} else if configPath != "" {
		configFilePath = configPath
		cfg, err = config.LoadConfigOptional(configPath, isCloudDeploy)
	} else {
		wd, err = os.Getwd()
		if err != nil {
			log.Errorf("failed to get working directory: %v", err)
			return
		}
		configFilePath = filepath.Join(wd, "config.yaml")
		cfg, err = config.LoadConfigOptional(configFilePath, isCloudDeploy)
	}
	if err != nil {
		log.Errorf("failed to load config: %v", err)
		return
	}
	if cfg == nil {
		cfg = &config.Config{}
	}

	// In cloud deploy mode, check if we have a valid configuration
	var configFileExists bool
	if isCloudDeploy {
		if info, errStat := os.Stat(configFilePath); errStat != nil {
			// Don't mislead: API server will not start until configuration is provided.
			log.Info("Cloud deploy mode: No configuration file detected; standing by for configuration")
			configFileExists = false
		} else if info.IsDir() {
			log.Info("Cloud deploy mode: Config path is a directory; standing by for configuration")
			configFileExists = false
		} else if cfg.Port == 0 {
			// LoadConfigOptional returns empty config when file is empty or invalid.
			// Config file exists but is empty or invalid; treat as missing config
			log.Info("Cloud deploy mode: Configuration file is empty or invalid; standing by for valid configuration")
			configFileExists = false
		} else {
			log.Info("Cloud deploy mode: Configuration file detected; starting service")
			configFileExists = true
		}
	}
	usage.SetStatisticsEnabled(cfg.UsageStatisticsEnabled)
	coreauth.SetQuotaCooldownDisabled(cfg.DisableCooling)

	if err = logging.ConfigureLogOutput(cfg); err != nil {
		log.Errorf("failed to configure log output: %v", err)
		return
	}

	log.Infof("CLIProxyAPI Version: %s, Commit: %s, BuiltAt: %s", buildinfo.Version, buildinfo.Commit, buildinfo.BuildDate)

	// Set the log level based on the configuration.
	util.SetLogLevel(cfg)

	if resolvedAuthDir, errResolveAuthDir := util.ResolveAuthDir(cfg.AuthDir); errResolveAuthDir != nil {
		log.Errorf("failed to resolve auth directory: %v", errResolveAuthDir)
		return
	} else {
		cfg.AuthDir = resolvedAuthDir
	}
	managementasset.SetCurrentConfig(cfg)

	// Create login options to be used in authentication flows.
	options := &cmd.LoginOptions{
		NoBrowser:    noBrowser,
		CallbackPort: oauthCallbackPort,
	}

	// Register the shared token store once so all components use the same persistence backend.
	if usePostgresStore {
		sdkAuth.RegisterTokenStore(pgStoreInst)
	} else if useObjectStore {
		sdkAuth.RegisterTokenStore(objectStoreInst)
	} else if useGitStore {
		sdkAuth.RegisterTokenStore(gitStoreInst)
	} else {
		sdkAuth.RegisterTokenStore(sdkAuth.NewFileTokenStore())
	}

	// Register built-in access providers before constructing services.
	configaccess.Register(&cfg.SDKConfig)

	// Handle different command modes based on the provided flags.

	if vertexImport != "" {
		// Handle Vertex service account import
		cmd.DoVertexImport(cfg, vertexImport)
	} else if login {
		// Handle Google/Gemini login
		cmd.DoLogin(cfg, projectID, options)
	} else if antigravityLogin {
		// Handle Antigravity login
		cmd.DoAntigravityLogin(cfg, options)
	} else if codexLogin {
		// Handle Codex login
		cmd.DoCodexLogin(cfg, options)
	} else if codexDeviceLogin {
		// Handle Codex device-code login
		cmd.DoCodexDeviceLogin(cfg, options)
	} else if claudeLogin {
		// Handle Claude login
		cmd.DoClaudeLogin(cfg, options)
	} else if qwenLogin {
		cmd.DoQwenLogin(cfg, options)
	} else if iflowLogin {
		cmd.DoIFlowLogin(cfg, options)
	} else if iflowCookie {
		cmd.DoIFlowCookieAuth(cfg, options)
	} else if kimiLogin {
		cmd.DoKimiLogin(cfg, options)
	} else {
		// In cloud deploy mode without config file, just wait for shutdown signals
		if isCloudDeploy && !configFileExists {
			// No config file available, just wait for shutdown
			cmd.WaitForCloudDeploy()
			return
		}
		if tuiMode {
			if standalone {
				// Standalone mode: start an embedded local server and connect TUI client to it.
				managementasset.StartAutoUpdater(context.Background(), configFilePath)
				registry.StartModelsUpdater(context.Background())
				hook := tui.NewLogHook(2000)
				hook.SetFormatter(&logging.LogFormatter{})
				log.AddHook(hook)

				origStdout := os.Stdout
				origStderr := os.Stderr
				origLogOutput := log.StandardLogger().Out
				log.SetOutput(io.Discard)

				devNull, errOpenDevNull := os.Open(os.DevNull)
				if errOpenDevNull == nil {
					os.Stdout = devNull
					os.Stderr = devNull
				}

				restoreIO := func() {
					os.Stdout = origStdout
					os.Stderr = origStderr
					log.SetOutput(origLogOutput)
					if devNull != nil {
						_ = devNull.Close()
					}
				}

				localMgmtPassword := fmt.Sprintf("tui-%d-%d", os.Getpid(), time.Now().UnixNano())
				if password == "" {
					password = localMgmtPassword
				}

				cancel, done := cmd.StartServiceBackground(cfg, configFilePath, password)

				client := tui.NewClient(cfg.Port, password)
				ready := false
				backoff := 100 * time.Millisecond
				for i := 0; i < 30; i++ {
					if _, errGetConfig := client.GetConfig(); errGetConfig == nil {
						ready = true
						break
					}
					time.Sleep(backoff)
					if backoff < time.Second {
						backoff = time.Duration(float64(backoff) * 1.5)
					}
				}

				if !ready {
					restoreIO()
					cancel()
					<-done
					fmt.Fprintf(os.Stderr, "TUI error: embedded server is not ready\n")
					return
				}

				if errRun := tui.Run(cfg.Port, password, hook, origStdout); errRun != nil {
					restoreIO()
					fmt.Fprintf(os.Stderr, "TUI error: %v\n", errRun)
				} else {
					restoreIO()
				}

				cancel()
				<-done
			} else {
				// Default TUI mode: pure management client.
				// The proxy server must already be running.
				if errRun := tui.Run(cfg.Port, password, nil, os.Stdout); errRun != nil {
					fmt.Fprintf(os.Stderr, "TUI error: %v\n", errRun)
				}
			}
		} else {
			// Start the main proxy service
			managementasset.StartAutoUpdater(context.Background(), configFilePath)
			registry.StartModelsUpdater(context.Background())
			cmd.StartService(cfg, configFilePath, password)
		}
	}
}


================================================
FILE: config.example.yaml
================================================
# Server host/interface to bind to. Default is empty ("") to bind all interfaces (IPv4 + IPv6).
# Use "127.0.0.1" or "localhost" to restrict access to local machine only.
host: ""

# Server port
port: 8317

# TLS settings for HTTPS. When enabled, the server listens with the provided certificate and key.
tls:
  enable: false
  cert: ""
  key: ""

# Management API settings
remote-management:
  # Whether to allow remote (non-localhost) management access.
  # When false, only localhost can access management endpoints (a key is still required).
  allow-remote: false

  # Management key. If a plaintext value is provided here, it will be hashed on startup.
  # All management requests (even from localhost) require this key.
  # Leave empty to disable the Management API entirely (404 for all /v0/management routes).
  secret-key: ""

  # Disable the bundled management control panel asset download and HTTP route when true.
  disable-control-panel: false

  # GitHub repository for the management control panel. Accepts a repository URL or releases API URL.
  panel-github-repository: "https://github.com/router-for-me/Cli-Proxy-API-Management-Center"

# Authentication directory (supports ~ for home directory)
auth-dir: "~/.cli-proxy-api"

# API keys for authentication
api-keys:
  - "your-api-key-1"
  - "your-api-key-2"
  - "your-api-key-3"

# Enable debug logging
debug: false

# Enable pprof HTTP debug server (host:port). Keep it bound to localhost for safety.
pprof:
  enable: false
  addr: "127.0.0.1:8316"

# When true, disable high-overhead HTTP middleware features to reduce per-request memory usage under high concurrency.
commercial-mode: false

# When true, write application logs to rotating files instead of stdout
logging-to-file: false

# Maximum total size (MB) of log files under the logs directory. When exceeded, the oldest log
# files are deleted until within the limit. Set to 0 to disable.
logs-max-total-size-mb: 0

# Maximum number of error log files retained when request logging is disabled.
# When exceeded, the oldest error log files are deleted. Default is 10. Set to 0 to disable cleanup.
error-logs-max-files: 10

# When false, disable in-memory usage statistics aggregation
usage-statistics-enabled: false

# Proxy URL. Supports socks5/http/https protocols. Example: socks5://user:pass@192.168.1.1:1080/
# Per-entry proxy-url also supports "direct" or "none" to bypass both the global proxy-url and environment proxies explicitly.
proxy-url: ""

# When true, unprefixed model requests only use credentials without a prefix (except when prefix == model name).
force-model-prefix: false

# When true, forward filtered upstream response headers to downstream clients.
# Default is false (disabled).
passthrough-headers: false

# Number of times to retry a request. Retries will occur if the HTTP response code is 403, 408, 500, 502, 503, or 504.
request-retry: 3

# Maximum number of different credentials to try for one failed request.
# Set to 0 to keep legacy behavior (try all available credentials).
max-retry-credentials: 0

# Maximum wait time in seconds for a cooled-down credential before triggering a retry.
max-retry-interval: 30

# Quota exceeded behavior
quota-exceeded:
  switch-project: true # Whether to automatically switch to another project when a quota is exceeded
  switch-preview-model: true # Whether to automatically switch to a preview model when a quota is exceeded

# Routing strategy for selecting credentials when multiple match.
routing:
  strategy: "round-robin" # round-robin (default), fill-first

# When true, enable authentication for the WebSocket API (/v1/ws).
ws-auth: false

# When > 0, emit blank lines every N seconds for non-streaming responses to prevent idle timeouts.
nonstream-keepalive-interval: 0

# Streaming behavior (SSE keep-alives + safe bootstrap retries).
# streaming:
#   keepalive-seconds: 15   # Default: 0 (disabled). <= 0 disables keep-alives.
#   bootstrap-retries: 1    # Default: 0 (disabled). Retries before first byte is sent.

# Gemini API keys
# gemini-api-key:
#   - api-key: "AIzaSy...01"
#     prefix: "test" # optional: require calls like "test/gemini-3-pro-preview" to target this credential
#     base-url: "https://generativelanguage.googleapis.com"
#     headers:
#       X-Custom-Header: "custom-value"
#     proxy-url: "socks5://proxy.example.com:1080"
#     # proxy-url: "direct" # optional: explicit direct connect for this credential
#     models:
#       - name: "gemini-2.5-flash" # upstream model name
#         alias: "gemini-flash"    # client alias mapped to the upstream model
#     excluded-models:
#       - "gemini-2.5-pro"     # exclude specific models from this provider (exact match)
#       - "gemini-2.5-*"       # wildcard matching prefix (e.g. gemini-2.5-flash, gemini-2.5-pro)
#       - "*-preview"          # wildcard matching suffix (e.g. gemini-3-pro-preview)
#       - "*flash*"            # wildcard matching substring (e.g. gemini-2.5-flash-lite)
#   - api-key: "AIzaSy...02"

# Codex API keys
# codex-api-key:
#   - api-key: "sk-atSM..."
#     prefix: "test" # optional: require calls like "test/gpt-5-codex" to target this credential
#     base-url: "https://www.example.com" # use the custom codex API endpoint
#     headers:
#       X-Custom-Header: "custom-value"
#     proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override
#     # proxy-url: "direct" # optional: explicit direct connect for this credential
#     models:
#       - name: "gpt-5-codex"   # upstream model name
#         alias: "codex-latest" # client alias mapped to the upstream model
#     excluded-models:
#       - "gpt-5.1"         # exclude specific models (exact match)
#       - "gpt-5-*"         # wildcard matching prefix (e.g. gpt-5-medium, gpt-5-codex)
#       - "*-mini"          # wildcard matching suffix (e.g. gpt-5-codex-mini)
#       - "*codex*"         # wildcard matching substring (e.g. gpt-5-codex-low)

# Claude API keys
# claude-api-key:
#   - api-key: "sk-atSM..." # use the official claude API key, no need to set the base url
#   - api-key: "sk-atSM..."
#     prefix: "test" # optional: require calls like "test/claude-sonnet-latest" to target this credential
#     base-url: "https://www.example.com" # use the custom claude API endpoint
#     headers:
#       X-Custom-Header: "custom-value"
#     proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override
#     # proxy-url: "direct" # optional: explicit direct connect for this credential
#     models:
#       - name: "claude-3-5-sonnet-20241022" # upstream model name
#         alias: "claude-sonnet-latest"      # client alias mapped to the upstream model
#     excluded-models:
#       - "claude-opus-4-5-20251101" # exclude specific models (exact match)
#       - "claude-3-*"               # wildcard matching prefix (e.g. claude-3-7-sonnet-20250219)
#       - "*-thinking"               # wildcard matching suffix (e.g. claude-opus-4-5-thinking)
#       - "*haiku*"                  # wildcard matching substring (e.g. claude-3-5-haiku-20241022)
#     cloak:                         # optional: request cloaking for non-Claude-Code clients
#       mode: "auto"                 # "auto" (default): cloak only when client is not Claude Code
#                                    # "always": always apply cloaking
#                                    # "never": never apply cloaking
#       strict-mode: false           # false (default): prepend Claude Code prompt to user system messages
#                                    # true: strip all user system messages, keep only Claude Code prompt
#       sensitive-words:             # optional: words to obfuscate with zero-width characters
#         - "API"
#         - "proxy"
#       cache-user-id: true          # optional: default is false; set true to reuse cached user_id per API key instead of generating a random one each request

# Default headers for Claude API requests. Update when Claude Code releases new versions.
# These are used as fallbacks when the client does not send its own headers.
# claude-header-defaults:
#   user-agent: "claude-cli/2.1.44 (external, sdk-cli)"
#   package-version: "0.74.0"
#   runtime-version: "v24.3.0"
#   timeout: "600"

# Default headers for Codex OAuth model requests.
# These are used only for file-backed/OAuth Codex requests when the client
# does not send the header. `user-agent` applies to HTTP and websocket requests;
# `beta-features` only applies to websocket requests. They do not apply to codex-api-key entries.
# codex-header-defaults:
#   user-agent: "codex_cli_rs/0.114.0 (Mac OS 14.2.0; x86_64) vscode/1.111.0"
#   beta-features: "multi_agent"

# OpenAI compatibility providers
# openai-compatibility:
#   - name: "openrouter" # The name of the provider; it will be used in the user agent and other places.
#     prefix: "test" # optional: require calls like "test/kimi-k2" to target this provider's credentials
#     base-url: "https://openrouter.ai/api/v1" # The base URL of the provider.
#     headers:
#       X-Custom-Header: "custom-value"
#     api-key-entries:
#       - api-key: "sk-or-v1-...b780"
#         proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override
#         # proxy-url: "direct" # optional: explicit direct connect for this credential
#       - api-key: "sk-or-v1-...b781" # without proxy-url
#     models: # The models supported by the provider.
#       - name: "moonshotai/kimi-k2:free" # The actual model name.
#         alias: "kimi-k2" # The alias used in the API.
#       # You may repeat the same alias to build an internal model pool.
#       # The client still sees only one alias in the model list.
#       # Requests to that alias will round-robin across the upstream names below,
#       # and if the chosen upstream fails before producing output, the request will
#       # continue with the next upstream model in the same alias pool.
#       - name: "qwen3.5-plus"
#         alias: "claude-opus-4.66"
#       - name: "glm-5"
#         alias: "claude-opus-4.66"
#       - name: "kimi-k2.5"
#         alias: "claude-opus-4.66"

# Vertex API keys (Vertex-compatible endpoints, base-url is optional)
# vertex-api-key:
#   - api-key: "vk-123..."                        # x-goog-api-key header
#     prefix: "test"                              # optional: require calls like "test/vertex-pro" to target this credential
#     base-url: "https://example.com/api"         # optional, e.g. https://zenmux.ai/api; falls back to Google Vertex when omitted
#     proxy-url: "socks5://proxy.example.com:1080" # optional per-key proxy override
#     # proxy-url: "direct" # optional: explicit direct connect for this credential
#     headers:
#       X-Custom-Header: "custom-value"
#     models:                                     # optional: map aliases to upstream model names
#       - name: "gemini-2.5-flash"                # upstream model name
#         alias: "vertex-flash"                   # client-visible alias
#       - name: "gemini-2.5-pro"
#         alias: "vertex-pro"
#     excluded-models:                            # optional: models to exclude from listing
#       - "imagen-3.0-generate-002"
#       - "imagen-*"

# Amp Integration
# ampcode:
#   # Configure upstream URL for Amp CLI OAuth and management features
#   upstream-url: "https://ampcode.com"
#   # Optional: Override API key for Amp upstream (otherwise uses env or file)
#   upstream-api-key: ""
#   # Per-client upstream API key mapping
#   # Maps client API keys (from top-level api-keys) to different Amp upstream API keys.
#   # Useful when different clients need to use different Amp accounts/quotas.
#   # If a client key isn't mapped, falls back to upstream-api-key (default behavior).
#   upstream-api-keys:
#     - upstream-api-key: "amp_key_for_team_a"    # Upstream key to use for these clients
#       api-keys:                                 # Client keys that use this upstream key
#         - "your-api-key-1"
#         - "your-api-key-2"
#     - upstream-api-key: "amp_key_for_team_b"
#       api-keys:
#         - "your-api-key-3"
#   # Restrict Amp management routes (/api/auth, /api/user, etc.) to localhost only (default: false)
#   restrict-management-to-localhost: false
#   # Force model mappings to run before checking local API keys (default: false)
#   force-model-mappings: false
#   # Amp Model Mappings
#   # Route unavailable Amp models to alternative models available in your local proxy.
#   # Useful when Amp CLI requests models you don't have access to (e.g., Claude Opus 4.5)
#   # but you have a similar model available (e.g., Claude Sonnet 4).
#   model-mappings:
#     - from: "claude-opus-4-5-20251101"          # Model requested by Amp CLI
#       to: "gemini-claude-opus-4-5-thinking"     # Route to this available model instead
#     - from: "claude-sonnet-4-5-20250929"
#       to: "gemini-claude-sonnet-4-5-thinking"
#     - from: "claude-haiku-4-5-20251001"
#       to: "gemini-2.5-flash"

# Global OAuth model name aliases (per channel)
# These aliases rename model IDs for both model listing and request routing.
# Supported channels: gemini-cli, vertex, aistudio, antigravity, claude, codex, qwen, iflow, kimi.
# NOTE: Aliases do not apply to gemini-api-key, codex-api-key, claude-api-key, openai-compatibility, vertex-api-key, or ampcode.
# You can repeat the same name with different aliases to expose multiple client model names.
# oauth-model-alias:
#   gemini-cli:
#     - name: "gemini-2.5-pro"          # original model name under this channel
#       alias: "g2.5p"                  # client-visible alias
#       fork: true                      # when true, keep original and also add the alias as an extra model (default: false)
#   vertex:
#     - name: "gemini-2.5-pro"
#       alias: "g2.5p"
#   aistudio:
#     - name: "gemini-2.5-pro"
#       alias: "g2.5p"
#   antigravity:
#     - name: "gemini-3-pro-high"
#       alias: "gemini-3-pro-preview"
#   claude:
#     - name: "claude-sonnet-4-5-20250929"
#       alias: "cs4.5"
#   codex:
#     - name: "gpt-5"
#       alias: "g5"
#   qwen:
#     - name: "qwen3-coder-plus"
#       alias: "qwen-plus"
#   iflow:
#     - name: "glm-4.7"
#       alias: "glm-god"
#   kimi:
#     - name: "kimi-k2.5"
#       alias: "k2.5"

# OAuth provider excluded models
# oauth-excluded-models:
#   gemini-cli:
#     - "gemini-2.5-pro"     # exclude specific models (exact match)
#     - "gemini-2.5-*"       # wildcard matching prefix (e.g. gemini-2.5-flash, gemini-2.5-pro)
#     - "*-preview"          # wildcard matching suffix (e.g. gemini-3-pro-preview)
#     - "*flash*"            # wildcard matching substring (e.g. gemini-2.5-flash-lite)
#   vertex:
#     - "gemini-3-pro-preview"
#   aistudio:
#     - "gemini-3-pro-preview"
#   antigravity:
#     - "gemini-3-pro-preview"
#   claude:
#     - "claude-3-5-haiku-20241022"
#   codex:
#     - "gpt-5-codex-mini"
#   qwen:
#     - "vision-model"
#   iflow:
#     - "tstars2.0"
#   kimi:
#     - "kimi-k2-thinking"

# Optional payload configuration
# payload:
#   default: # Default rules only set parameters when they are missing in the payload.
#     - models:
#         - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*")
#           protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity
#       params: # JSON path (gjson/sjson syntax) -> value
#         "generationConfig.thinkingConfig.thinkingBudget": 32768
#   default-raw: # Default raw rules set parameters using raw JSON when missing (must be valid JSON).
#     - models:
#         - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*")
#           protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity
#       params: # JSON path (gjson/sjson syntax) -> raw JSON value (strings are used as-is, must be valid JSON)
#         "generationConfig.responseJsonSchema": "{\"type\":\"object\",\"properties\":{\"answer\":{\"type\":\"string\"}}}"
#   override: # Override rules always set parameters, overwriting any existing values.
#     - models:
#         - name: "gpt-*" # Supports wildcards (e.g., "gpt-*")
#           protocol: "codex" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity
#       params: # JSON path (gjson/sjson syntax) -> value
#         "reasoning.effort": "high"
#   override-raw: # Override raw rules always set parameters using raw JSON (must be valid JSON).
#     - models:
#         - name: "gpt-*" # Supports wildcards (e.g., "gpt-*")
#           protocol: "codex" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity
#       params: # JSON path (gjson/sjson syntax) -> raw JSON value (strings are used as-is, must be valid JSON)
#         "response_format": "{\"type\":\"json_schema\",\"json_schema\":{\"name\":\"answer\",\"schema\":{\"type\":\"object\"}}}"
#   filter: # Filter rules remove specified parameters from the payload.
#     - models:
#         - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*")
#           protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity
#       params: # JSON paths (gjson/sjson syntax) to remove from the payload
#         - "generationConfig.thinkingConfig.thinkingBudget"
#         - "generationConfig.responseJsonSchema"


================================================
FILE: docker-build.ps1
================================================
# build.ps1 - Windows PowerShell Build Script
#
# This script automates the process of building and running the Docker container
# with version information dynamically injected at build time.

# Stop script execution on any error
$ErrorActionPreference = "Stop"

# --- Step 1: Choose Environment ---
Write-Host "Please select an option:"
Write-Host "1) Run using Pre-built Image (Recommended)"
Write-Host "2) Build from Source and Run (For Developers)"
$choice = Read-Host -Prompt "Enter choice [1-2]"

# --- Step 2: Execute based on choice ---
switch ($choice) {
    "1" {
        Write-Host "--- Running with Pre-built Image ---"
        docker compose up -d --remove-orphans --no-build
        Write-Host "Services are starting from remote image."
        Write-Host "Run 'docker compose logs -f' to see the logs."
    }
    "2" {
        Write-Host "--- Building from Source and Running ---"

        # Get Version Information
        $VERSION = (git describe --tags --always --dirty)
        $COMMIT  = (git rev-parse --short HEAD)
        $BUILD_DATE = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")

        Write-Host "Building with the following info:"
        Write-Host "  Version: $VERSION"
        Write-Host "  Commit: $COMMIT"
        Write-Host "  Build Date: $BUILD_DATE"
        Write-Host "----------------------------------------"

        # Build and start the services with a local-only image tag
        $env:CLI_PROXY_IMAGE = "cli-proxy-api:local"
        
        Write-Host "Building the Docker image..."
        docker compose build --build-arg VERSION=$VERSION --build-arg COMMIT=$COMMIT --build-arg BUILD_DATE=$BUILD_DATE

        Write-Host "Starting the services..."
        docker compose up -d --remove-orphans --pull never

        Write-Host "Build complete. Services are starting."
        Write-Host "Run 'docker compose logs -f' to see the logs."
    }
    default {
        Write-Host "Invalid choice. Please enter 1 or 2."
        exit 1
    }
}

================================================
FILE: docker-build.sh
================================================
#!/usr/bin/env bash
#
# build.sh - Linux/macOS Build Script
#
# This script automates the process of building and running the Docker container
# with version information dynamically injected at build time.

# Hidden feature: Preserve usage statistics across rebuilds
# Usage: ./docker-build.sh --with-usage
# First run prompts for management API key, saved to temp/stats/.api_secret

set -euo pipefail

STATS_DIR="temp/stats"
STATS_FILE="${STATS_DIR}/.usage_backup.json"
SECRET_FILE="${STATS_DIR}/.api_secret"
WITH_USAGE=false

get_port() {
  if [[ -f "config.yaml" ]]; then
    grep -E "^port:" config.yaml | sed -E 's/^port: *["'"'"']?([0-9]+)["'"'"']?.*$/\1/'
  else
    echo "8317"
  fi
}

export_stats_api_secret() {
  if [[ -f "${SECRET_FILE}" ]]; then
    API_SECRET=$(cat "${SECRET_FILE}")
  else
    if [[ ! -d "${STATS_DIR}" ]]; then
      mkdir -p "${STATS_DIR}"
    fi
    echo "First time using --with-usage. Management API key required."
    read -r -p "Enter management key: " -s API_SECRET
    echo
    echo "${API_SECRET}" > "${SECRET_FILE}"
    chmod 600 "${SECRET_FILE}"
  fi
}

check_container_running() {
  local port
  port=$(get_port)

  if ! curl -s -o /dev/null -w "%{http_code}" "http://localhost:${port}/" | grep -q "200"; then
    echo "Error: cli-proxy-api service is not responding at localhost:${port}"
    echo "Please start the container first or use without --with-usage flag."
    exit 1
  fi
}

export_stats() {
  local port
  port=$(get_port)

  if [[ ! -d "${STATS_DIR}" ]]; then
    mkdir -p "${STATS_DIR}"
  fi
  check_container_running
  echo "Exporting usage statistics..."
  EXPORT_RESPONSE=$(curl -s -w "\n%{http_code}" -H "X-Management-Key: ${API_SECRET}" \
    "http://localhost:${port}/v0/management/usage/export")
  HTTP_CODE=$(echo "${EXPORT_RESPONSE}" | tail -n1)
  RESPONSE_BODY=$(echo "${EXPORT_RESPONSE}" | sed '$d')

  if [[ "${HTTP_CODE}" != "200" ]]; then
    echo "Export failed (HTTP ${HTTP_CODE}): ${RESPONSE_BODY}"
    exit 1
  fi

  echo "${RESPONSE_BODY}" > "${STATS_FILE}"
  echo "Statistics exported to ${STATS_FILE}"
}

import_stats() {
  local port
  port=$(get_port)

  echo "Importing usage statistics..."
  IMPORT_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
    -H "X-Management-Key: ${API_SECRET}" \
    -H "Content-Type: application/json" \
    -d @"${STATS_FILE}" \
    "http://localhost:${port}/v0/management/usage/import")
  IMPORT_CODE=$(echo "${IMPORT_RESPONSE}" | tail -n1)
  IMPORT_BODY=$(echo "${IMPORT_RESPONSE}" | sed '$d')

  if [[ "${IMPORT_CODE}" == "200" ]]; then
    echo "Statistics imported successfully"
  else
    echo "Import failed (HTTP ${IMPORT_CODE}): ${IMPORT_BODY}"
  fi

  rm -f "${STATS_FILE}"
}

wait_for_service() {
  local port
  port=$(get_port)

  echo "Waiting for service to be ready..."
  for i in {1..30}; do
    if curl -s -o /dev/null -w "%{http_code}" "http://localhost:${port}/" | grep -q "200"; then
      break
    fi
    sleep 1
  done
  sleep 2
}

if [[ "${1:-}" == "--with-usage" ]]; then
  WITH_USAGE=true
  export_stats_api_secret
fi

# --- Step 1: Choose Environment ---
echo "Please select an option:"
echo "1) Run using Pre-built Image (Recommended)"
echo "2) Build from Source and Run (For Developers)"
read -r -p "Enter choice [1-2]: " choice

# --- Step 2: Execute based on choice ---
case "$choice" in
  1)
    echo "--- Running with Pre-built Image ---"
    if [[ "${WITH_USAGE}" == "true" ]]; then
      export_stats
    fi
    docker compose up -d --remove-orphans --no-build
    if [[ "${WITH_USAGE}" == "true" ]]; then
      wait_for_service
      import_stats
    fi
    echo "Services are starting from remote image."
    echo "Run 'docker compose logs -f' to see the logs."
    ;;
  2)
    echo "--- Building from Source and Running ---"

    # Get Version Information
    VERSION="$(git describe --tags --always --dirty)"
    COMMIT="$(git rev-parse --short HEAD)"
    BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

    echo "Building with the following info:"
    echo "  Version: ${VERSION}"
    echo "  Commit: ${COMMIT}"
    echo "  Build Date: ${BUILD_DATE}"
    echo "----------------------------------------"

    # Build and start the services with a local-only image tag
    export CLI_PROXY_IMAGE="cli-proxy-api:local"

    echo "Building the Docker image..."
    docker compose build \
      --build-arg VERSION="${VERSION}" \
      --build-arg COMMIT="${COMMIT}" \
      --build-arg BUILD_DATE="${BUILD_DATE}"

    if [[ "${WITH_USAGE}" == "true" ]]; then
      export_stats
    fi

    echo "Starting the services..."
    docker compose up -d --remove-orphans --pull never

    if [[ "${WITH_USAGE}" == "true" ]]; then
      wait_for_service
      import_stats
    fi

    echo "Build complete. Services are starting."
    echo "Run 'docker compose logs -f' to see the logs."
    ;;
  *)
    echo "Invalid choice. Please enter 1 or 2."
    exit 1
    ;;
esac


================================================
FILE: docker-compose.yml
================================================
services:
  cli-proxy-api:
    image: ${CLI_PROXY_IMAGE:-eceasy/cli-proxy-api:latest}
    pull_policy: always
    build:
      context: .
      dockerfile: Dockerfile
      args:
        VERSION: ${VERSION:-dev}
        COMMIT: ${COMMIT:-none}
        BUILD_DATE: ${BUILD_DATE:-unknown}
    container_name: cli-proxy-api
    # env_file:
    #   - .env
    environment:
      DEPLOY: ${DEPLOY:-}
    ports:
      - "8317:8317"
      - "8085:8085"
      - "1455:1455"
      - "54545:54545"
      - "51121:51121"
      - "11451:11451"
    volumes:
      - ${CLI_PROXY_CONFIG_PATH:-./config.yaml}:/CLIProxyAPI/config.yaml
      - ${CLI_PROXY_AUTH_PATH:-./auths}:/root/.cli-proxy-api
      - ${CLI_PROXY_LOG_PATH:-./logs}:/CLIProxyAPI/logs
    restart: unless-stopped


================================================
FILE: examples/custom-provider/main.go
================================================
// Package main demonstrates how to create a custom AI provider executor
// and integrate it with the CLI Proxy API server. This example shows how to:
// - Create a custom executor that implements the Executor interface
// - Register custom translators for request/response transformation
// - Integrate the custom provider with the SDK server
// - Register custom models in the model registry
//
// This example uses a simple echo service (httpbin.org) as the upstream API
// for demonstration purposes. In a real implementation, you would replace
// this with your actual AI service provider.
package main

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/router-for-me/CLIProxyAPI/v6/sdk/api"
	sdkAuth "github.com/router-for-me/CLIProxyAPI/v6/sdk/auth"
	"github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy"
	coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
	clipexec "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
	"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
	"github.com/router-for-me/CLIProxyAPI/v6/sdk/logging"
	sdktr "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
)

const (
	// providerKey is the identifier for our custom provider.
	providerKey = "myprov"

	// fOpenAI represents the OpenAI chat format.
	fOpenAI = sdktr.Format("openai.chat")

	// fMyProv represents our custom provider's chat format.
	fMyProv = sdktr.Format("myprov.chat")
)

// init registers trivial translators for demonstration purposes.
// In a real implementation, you would implement proper request/response
// transformation logic between OpenAI format and your provider's format.
func init() {
	sdktr.Register(fOpenAI, fMyProv,
		func(model string, raw []byte, stream bool) []byte { return raw },
		sdktr.ResponseTransform{
			Stream: func(ctx context.Context, model string, originalReq, translatedReq, raw []byte, param *any) []string {
				return []string{string(raw)}
			},
			NonStream: func(ctx context.Context, model string, originalReq, translatedReq, raw []byte, param *any) string {
				return string(raw)
			},
		},
	)
}

// MyExecutor is a minimal provider implementation for demonstration purposes.
// It implements the Executor interface to handle requests to a custom AI provider.
type MyExecutor struct{}

// Identifier returns the unique identifier for this executor.
func (MyExecutor) Identifier() string { return providerKey }

// PrepareRequest optionally injects credentials to raw HTTP requests.
// This method is called before each request to allow the executor to modify
// the HTTP request with authentication headers or other necessary modifications.
//
// Parameters:
//   - req: The HTTP request to prepare
//   - a: The authentication information
//
// Returns:
//   - error: An error if request preparation fails
func (MyExecutor) PrepareRequest(req *http.Request, a *coreauth.Auth) error {
	if req == nil || a == nil {
		return nil
	}
	if a.Attributes != nil {
		if ak := strings.TrimSpace(a.Attributes["api_key"]); ak != "" {
			req.Header.Set("Authorization", "Bearer "+ak)
		}
	}
	return nil
}

func buildHTTPClient(a *coreauth.Auth) *http.Client {
	if a == nil || strings.TrimSpace(a.ProxyURL) == "" {
		return http.DefaultClient
	}
	u, err := url.Parse(a.ProxyURL)
	if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
		return http.DefaultClient
	}
	return &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(u)}}
}

func upstreamEndpoint(a *coreauth.Auth) string {
	if a != nil && a.Attributes != nil {
		if ep := strings.TrimSpace(a.Attributes["endpoint"]); ep != "" {
			return ep
		}
	}
	// Demo echo endpoint; replace with your upstream.
	return "https://httpbin.org/post"
}

func (MyExecutor) Execute(ctx context.Context, a *coreauth.Auth, req clipexec.Request, opts clipexec.Options) (clipexec.Response, error) {
	client := buildHTTPClient(a)
	endpoint := upstreamEndpoint(a)

	httpReq, errNew := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(req.Payload))
	if errNew != nil {
		return clipexec.Response{}, errNew
	}
	httpReq.Header.Set("Content-Type", "application/json")

	// Inject credentials via PrepareRequest hook.
	if errPrep := (MyExecutor{}).PrepareRequest(httpReq, a); errPrep != nil {
		return clipexec.Response{}, errPrep
	}

	resp, errDo := client.Do(httpReq)
	if errDo != nil {
		return clipexec.Response{}, errDo
	}
	defer func() {
		if errClose := resp.Body.Close(); errClose != nil {
			fmt.Fprintf(os.Stderr, "close response body error: %v\n", errClose)
		}
	}()
	body, _ := io.ReadAll(resp.Body)
	return clipexec.Response{Payload: body}, nil
}

func (MyExecutor) HttpRequest(ctx context.Context, a *coreauth.Auth, req *http.Request) (*http.Response, error) {
	if req == nil {
		return nil, fmt.Errorf("myprov executor: request is nil")
	}
	if ctx == nil {
		ctx = req.Context()
	}
	httpReq := req.WithContext(ctx)
	if errPrep := (MyExecutor{}).PrepareRequest(httpReq, a); errPrep != nil {
		return nil, errPrep
	}
	client := buildHTTPClient(a)
	return client.Do(httpReq)
}

func (MyExecutor) CountTokens(context.Context, *coreauth.Auth, clipexec.Request, clipexec.Options) (clipexec.Response, error) {
	return clipexec.Response{}, errors.New("count tokens not implemented")
}

func (MyExecutor) ExecuteStream(ctx context.Context, a *coreauth.Auth, req clipexec.Request, opts clipexec.Options) (*clipexec.StreamResult, error) {
	ch := make(chan clipexec.StreamChunk, 1)
	go func() {
		defer close(ch)
		ch <- clipexec.StreamChunk{Payload: []byte("data: {\"ok\":true}\n\n")}
	}()
	return &clipexec.StreamResult{Chunks: ch}, nil
}

func (MyExecutor) Refresh(ctx context.Context, a *coreauth.Auth) (*coreauth.Auth, error) {
	return a, nil
}

func main() {
	cfg, err := config.LoadConfig("config.yaml")
	if err != nil {
		panic(err)
	}

	tokenStore := sdkAuth.GetTokenStore()
	if dirSetter, ok := tokenStore.(interface{ SetBaseDir(string) }); ok {
		dirSetter.SetBaseDir(cfg.AuthDir)
	}
	core := coreauth.NewManager(tokenStore, nil, nil)
	core.RegisterExecutor(MyExecutor{})

	hooks := cliproxy.Hooks{
		OnAfterStart: func(s *cliproxy.Service) {
			// Register demo models for the custom provider so they appear in /v1/models.
			models := []*cliproxy.ModelInfo{{ID: "myprov-pro-1", Object: "model", Type: providerKey, DisplayName: "MyProv Pro 1"}}
			for _, a := range core.List() {
				if strings.EqualFold(a.Provider, providerKey) {
					cliproxy.GlobalModelRegistry().RegisterClient(a.ID, providerKey, models)
				}
			}
		},
	}

	svc, err := cliproxy.NewBuilder().
		WithConfig(cfg).
		WithConfigPath("config.yaml").
		WithCoreAuthManager(core).
		WithServerOptions(
			// Optional: add a simple middleware + custom request logger
			api.WithMiddleware(func(c *gin.Context) { c.Header("X-Example", "custom-provider"); c.Next() }),
			api.WithRequestLoggerFactory(func(cfg *config.Config, cfgPath string) logging.RequestLogger {
				return logging.NewFileRequestLoggerWithOptions(true, "logs", filepath.Dir(cfgPath), cfg.ErrorLogsMaxFiles)
			}),
		).
		WithHooks(hooks).
		Build()
	if err != nil {
		panic(err)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	if errRun := svc.Run(ctx); errRun != nil && !errors.Is(errRun, context.Canceled) {
		panic(errRun)
	}
	_ = os.Stderr // keep os import used (demo only)
	_ = time.Second
}


================================================
FILE: examples/http-request/main.go
================================================
// Package main demonstrates how to use coreauth.Manager.HttpRequest/NewHttpRequest
// to execute arbitrary HTTP requests with provider credentials injected.
//
// This example registers a minimal custom executor that injects an Authorization
// header from auth.Attributes["api_key"], then performs two requests against
// httpbin.org to show the injected headers.
package main

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
	clipexec "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
	log "github.com/sirupsen/logrus"
)

const providerKey = "echo"

// EchoExecutor is a minimal provider implementation for demonstration purposes.
type EchoExecutor struct{}

func (EchoExecutor) Identifier() string { return providerKey }

func (EchoExecutor) PrepareRequest(req *http.Request, auth *coreauth.Auth) error {
	if req == nil || auth == nil {
		return nil
	}
	if auth.Attributes != nil {
		if apiKey := strings.TrimSpace(auth.Attributes["api_key"]); apiKey != "" {
			req.Header.Set("Authorization", "Bearer "+apiKey)
		}
	}
	return nil
}

func (EchoExecutor) HttpRequest(ctx context.Context, auth *coreauth.Auth, req *http.Request) (*http.Response, error) {
	if req == nil {
		return nil, fmt.Errorf("echo executor: request is nil")
	}
	if ctx == nil {
		ctx = req.Context()
	}
	httpReq := req.WithContext(ctx)
	if errPrep := (EchoExecutor{}).PrepareRequest(httpReq, auth); errPrep != nil {
		return nil, errPrep
	}
	return http.DefaultClient.Do(httpReq)
}

func (EchoExecutor) Execute(context.Context, *coreauth.Auth, clipexec.Request, clipexec.Options) (clipexec.Response, error) {
	return clipexec.Response{}, errors.New("echo executor: Execute not implemented")
}

func (EchoExecutor) ExecuteStream(context.Context, *coreauth.Auth, clipexec.Request, clipexec.Options) (*clipexec.StreamResult, error) {
	return nil, errors.New("echo executor: ExecuteStream not implemented")
}

func (EchoExecutor) Refresh(context.Context, *coreauth.Auth) (*coreauth.Auth, error) {
	return nil, errors.New("echo executor: Refresh not implemented")
}

func (EchoExecutor) CountTokens(context.Context, *coreauth.Auth, clipexec.Request, clipexec.Options) (clipexec.Response, error) {
	return clipexec.Response{}, errors.New("echo executor: CountTokens not implemented")
}

func main() {
	log.SetLevel(log.InfoLevel)

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	core := coreauth.NewManager(nil, nil, nil)
	core.RegisterExecutor(EchoExecutor{})

	auth := &coreauth.Auth{
		ID:       "demo-echo",
		Provider: providerKey,
		Attributes: map[string]string{
			"api_key": "demo-api-key",
		},
	}

	// Example 1: Build a prepared request and execute it using your own http.Client.
	reqPrepared, errReqPrepared := core.NewHttpRequest(
		ctx,
		auth,
		http.MethodGet,
		"https://httpbin.org/anything",
		nil,
		http.Header{"X-Example": []string{"prepared"}},
	)
	if errReqPrepared != nil {
		panic(errReqPrepared)
	}
	respPrepared, errDoPrepared := http.DefaultClient.Do(reqPrepared)
	if errDoPrepared != nil {
		panic(errDoPrepared)
	}
	defer func() {
		if errClose := respPrepared.Body.Close(); errClose != nil {
			log.Errorf("close response body error: %v", errClose)
		}
	}()
	bodyPrepared, errReadPrepared := io.ReadAll(respPrepared.Body)
	if errReadPrepared != nil {
		panic(errReadPrepared)
	}
	fmt.Printf("Prepared request status: %d\n%s\n\n", respPrepared.StatusCode, bodyPrepared)

	// Example 2: Execute a raw request via core.HttpRequest (auto inject + do).
	rawBody := []byte(`{"hello":"world"}`)
	rawReq, errRawReq := http.NewRequestWithContext(ctx, http.MethodPost, "https://httpbin.org/anything", bytes.NewReader(rawBody))
	if errRawReq != nil {
		panic(errRawReq)
	}
	rawReq.Header.Set("Content-Type", "application/json")
	rawReq.Header.Set("X-Example", "executed")

	respExec, errDoExec := core.HttpRequest(ctx, auth, rawReq)
	if errDoExec != nil {
		panic(errDoExec)
	}
	defer func() {
		if errClose := respExec.Body.Close(); errClose != nil {
			log.Errorf("close response body error: %v", errClose)
		}
	}()
	bodyExec, errReadExec := io.ReadAll(respExec.Body)
	if errReadExec != nil {
		panic(errReadExec)
	}
	fmt.Printf("Manager HttpRequest status: %d\n%s\n", respExec.StatusCode, bodyExec)
}


================================================
FILE: examples/translator/main.go
================================================
package main

import (
	"context"
	"fmt"

	"github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
	_ "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator/builtin"
)

func main() {
	rawRequest := []byte(`{"messages":[{"content":[{"text":"Hello! Gemini","type":"text"}],"role":"user"}],"model":"gemini-2.5-pro","stream":false}`)
	fmt.Println("Has gemini->openai response translator:", translator.HasResponseTransformerByFormatName(
		translator.FormatGemini,
		translator.FormatOpenAI,
	))

	translatedRequest := translator.TranslateRequestByFormatName(
		translator.FormatOpenAI,
		translator.FormatGemini,
		"gemini-2.5-pro",
		rawRequest,
		false,
	)

	fmt.Printf("Translated request to Gemini format:\n%s\n\n", translatedRequest)

	claudeResponse := []byte(`{"candidates":[{"content":{"role":"model","parts":[{"thought":true,"text":"Okay, here's what's going through my mind. I need to schedule a meeting"},{"thoughtSignature":"","functionCall":{"name":"schedule_meeting","args":{"topic":"Q3 planning","attendees":["Bob","Alice"],"time":"10:00","date":"2025-03-27"}}}]},"finishReason":"STOP","avgLogprobs":-0.50018133435930523}],"usageMetadata":{"promptTokenCount":117,"candidatesTokenCount":28,"totalTokenCount":474,"trafficType":"PROVISIONED_THROUGHPUT","promptTokensDetails":[{"modality":"TEXT","tokenCount":117}],"candidatesTokensDetails":[{"modality":"TEXT","tokenCount":28}],"thoughtsTokenCount":329},"modelVersion":"gemini-2.5-pro","createTime":"2025-08-15T04:12:55.249090Z","responseId":"x7OeaIKaD6CU48APvNXDyA4"}`)

	convertedResponse := translator.TranslateNonStreamByFormatName(
		context.Background(),
		translator.FormatGemini,
		translator.FormatOpenAI,
		"gemini-2.5-pro",
		rawRequest,
		translatedRequest,
		claudeResponse,
		nil,
	)

	fmt.Printf("Converted response for OpenAI clients:\n%s\n", convertedResponse)
}


================================================
FILE: go.mod
================================================
module github.com/router-for-me/CLIProxyAPI/v6

go 1.26.0

require (
	github.com/andybalholm/brotli v1.0.6
	github.com/atotto/clipboard v0.1.4
	github.com/charmbracelet/bubbles v1.0.0
	github.com/charmbracelet/bubbletea v1.3.10
	github.com/charmbracelet/lipgloss v1.1.0
	github.com/fsnotify/fsnotify v1.9.0
	github.com/gin-gonic/gin v1.10.1
	github.com/go-git/go-git/v6 v6.0.0-20251009132922-75a182125145
	github.com/google/uuid v1.6.0
	github.com/gorilla/websocket v1.5.3
	github.com/jackc/pgx/v5 v5.7.6
	github.com/joho/godotenv v1.5.1
	github.com/klauspost/compress v1.17.4
	github.com/minio/minio-go/v7 v7.0.66
	github.com/refraction-networking/utls v1.8.2
	github.com/sirupsen/logrus v1.9.3
	github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
	github.com/tidwall/gjson v1.18.0
	github.com/tidwall/sjson v1.2.5
	github.com/tiktoken-go/tokenizer v0.7.0
	golang.org/x/crypto v0.45.0
	golang.org/x/net v0.47.0
	golang.org/x/oauth2 v0.30.0
	golang.org/x/sync v0.18.0
	gopkg.in/natefinch/lumberjack.v2 v2.2.1
	gopkg.in/yaml.v3 v3.0.1
)

require (
	cloud.google.com/go/compute/metadata v0.3.0 // indirect
	github.com/Microsoft/go-winio v0.6.2 // indirect
	github.com/ProtonMail/go-crypto v1.3.0 // indirect
	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
	github.com/bytedance/sonic v1.11.6 // indirect
	github.com/bytedance/sonic/loader v0.1.1 // indirect
	github.com/charmbracelet/colorprofile v0.4.1 // indirect
	github.com/charmbracelet/x/ansi v0.11.6 // indirect
	github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
	github.com/charmbracelet/x/term v0.2.2 // indirect
	github.com/clipperhouse/displaywidth v0.9.0 // indirect
	github.com/clipperhouse/stringish v0.1.1 // indirect
	github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
	github.com/cloudflare/circl v1.6.1 // indirect
	github.com/cloudwego/base64x v0.1.4 // indirect
	github.com/cloudwego/iasm v0.2.0 // indirect
	github.com/cyphar/filepath-securejoin v0.4.1 // indirect
	github.com/dlclark/regexp2 v1.11.5 // indirect
	github.com/dustin/go-humanize v1.0.1 // indirect
	github.com/emirpasic/gods v1.18.1 // indirect
	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
	github.com/gabriel-vasile/mimetype v1.4.3 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/go-git/gcfg/v2 v2.0.2 // indirect
	github.com/go-git/go-billy/v6 v6.0.0-20250627091229-31e2a16eef30 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.20.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
	github.com/jackc/pgpassfile v1.0.0 // indirect
	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
	github.com/jackc/puddle/v2 v2.2.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/kevinburke/ssh_config v1.4.0 // indirect
	github.com/klauspost/cpuid/v2 v2.3.0 // indirect
	github.com/leodido/go-urn v1.4.0 // indirect
	github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
	github.com/mattn/go-isatty v0.0.20 // indirect
	github.com/mattn/go-localereader v0.0.1 // indirect
	github.com/mattn/go-runewidth v0.0.19 // indirect
	github.com/minio/md5-simd v1.1.2 // indirect
	github.com/minio/sha256-simd v1.0.1 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
	github.com/muesli/cancelreader v0.2.2 // indirect
	github.com/muesli/termenv v0.16.0 // indirect
	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
	github.com/pjbgf/sha1cd v0.5.0 // indirect
	github.com/rivo/uniseg v0.4.7 // indirect
	github.com/rs/xid v1.5.0 // indirect
	github.com/sergi/go-diff v1.4.0 // indirect
	github.com/tidwall/match v1.1.1 // indirect
	github.com/tidwall/pretty v1.2.0 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.12 // indirect
	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
	golang.org/x/arch v0.8.0 // indirect
	golang.org/x/sys v0.38.0 // indirect
	golang.org/x/text v0.31.0 // indirect
	google.golang.org/protobuf v1.34.1 // indirect
	gopkg.in/ini.v1 v1.67.0 // indirect
)


================================================
FILE: go.sum
================================================
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA=
github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA=
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo=
github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs=
github.com/go-git/go-billy/v6 v6.0.0-20250627091229-31e2a16eef30 h1:4KqVJTL5eanN8Sgg3BV6f2/QzfZEFbCd+rTak1fGRRA=
github.com/go-git/go-billy/v6 v6.0.0-20250627091229-31e2a16eef30/go.mod h1:snwvGrbywVFy2d6KJdQ132zapq4aLyzLMgpo79XdEfM=
github.com/go-git/go-git-fixtures/v5 v5.1.1 h1:OH8i1ojV9bWfr0ZfasfpgtUXQHQyVS8HXik/V1C099w=
github.com/go-git/go-git-fixtures/v5 v5.1.1/go.mod h1:Altk43lx3b1ks+dVoAG2300o5WWUnktvfY3VI6bcaXU=
github.com/go-git/go-git/v6 v6.0.0-20251009132922-75a182125145 h1:C/oVxHd6KkkuvthQ/StZfHzZK07gl6xjfCfT3derko0=
github.com/go-git/go-git/v6 v6.0.0-20251009132922-75a182125145/go.mod h1:gR+xpbL+o1wuJJDwRN4pOkpNwDS0D24Eo4AD5Aau2DY=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0Dzw=
github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo=
github.com/refraction-networking/utls v1.8.2/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tiktoken-go/tokenizer v0.7.0 h1:VMu6MPT0bXFDHr7UPh9uii7CNItVt3X9K90omxL54vw=
github.com/tiktoken-go/tokenizer v0.7.0/go.mod h1:6UCYI/DtOallbmL7sSy30p6YQv60qNyU/4aVigPOx6w=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=


================================================
FILE: internal/access/config_access/provider.go
================================================
package configaccess

import (
	"context"
	"net/http"
	"strings"

	sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access"
	sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
)

// Register ensures the config-access provider is available to the access manager.
func Register(cfg *sdkconfig.SDKConfig) {
	if cfg == nil {
		sdkaccess.UnregisterProvider(sdkaccess.AccessProviderTypeConfigAPIKey)
		return
	}

	keys := normalizeKeys(cfg.APIKeys)
	if len(keys) == 0 {
		sdkaccess.UnregisterProvider(sdkaccess.AccessProviderTypeConfigAPIKey)
		return
	}

	sdkaccess.RegisterProvider(
		sdkaccess.AccessProviderTypeConfigAPIKey,
		newProvider(sdkaccess.DefaultAccessProviderName, keys),
	)
}

type provider struct {
	name string
	keys map[string]struct{}
}

func newProvider(name string, keys []string) *provider {
	providerName := strings.TrimSpace(name)
	if providerName == "" {
		providerName = sdkaccess.DefaultAccessProviderName
	}
	keySet := make(map[string]struct{}, len(keys))
	for _, key := range keys {
		keySet[key] = struct{}{}
	}
	return &provider{name: providerName, keys: keySet}
}

func (p *provider) Identifier() string {
	if p == nil || p.name == "" {
		return sdkaccess.DefaultAccessProviderName
	}
	return p.name
}

func (p *provider) Authenticate(_ context.Context, r *http.Request) (*sdkaccess.Result, *sdkaccess.AuthError) {
	if p == nil {
		return nil, sdkaccess.NewNotHandledError()
	}
	if len(p.keys) == 0 {
		return nil, sdkaccess.NewNotHandledError()
	}
	authHeader := r.Header.Get("Authorization")
	authHeaderGoogle := r.Header.Get("X-Goog-Api-Key")
	authHeaderAnthropic := r.Header.Get("X-Api-Key")
	queryKey := ""
	queryAuthToken := ""
	if r.URL != nil {
		queryKey = r.URL.Query().Get("key")
		queryAuthToken = r.URL.Query().Get("auth_token")
	}
	if authHeader == "" && authHeaderGoogle == "" && authHeaderAnthropic == "" && queryKey == "" && queryAuthToken == "" {
		return nil, sdkaccess.NewNoCredentialsError()
	}

	apiKey := extractBearerToken(authHeader)

	candidates := []struct {
		value  string
		source string
	}{
		{apiKey, "authorization"},
		{authHeaderGoogle, "x-goog-api-key"},
		{authHeaderAnthropic, "x-api-key"},
		{queryKey, "query-key"},
		{queryAuthToken, "query-auth-token"},
	}

	for _, candidate := range candidates {
		if candidate.value == "" {
			continue
		}
		if _, ok := p.keys[candidate.value]; ok {
			return &sdkaccess.Result{
				Provider:  p.Identifier(),
				Principal: candidate.value,
				Metadata: map[string]string{
					"source": candidate.source,
				},
			}, nil
		}
	}

	return nil, sdkaccess.NewInvalidCredentialError()
}

func extractBearerToken(header string) string {
	if header == "" {
		return ""
	}
	parts := strings.SplitN(header, " ", 2)
	if len(parts) != 2 {
		return header
	}
	if strings.ToLower(parts[0]) != "bearer" {
		return header
	}
	return strings.TrimSpace(parts[1])
}

func normalizeKeys(keys []string) []string {
	if len(keys) == 0 {
		return nil
	}
	normalized := make([]string, 0, len(keys))
	seen := make(map[string]struct{}, len(keys))
	for _, key := range keys {
		trimmedKey := strings.TrimSpace(key)
		if trimmedKey == "" {
			continue
		}
		if _, exists := seen[trimmedKey]; exists {
			continue
		}
		seen[trimmedKey] = struct{}{}
		normalized = append(normalized, trimmedKey)
	}
	if len(normalized) == 0 {
		return nil
	}
	return normalized
}


================================================
FILE: internal/access/reconcile.go
================================================
package access

import (
	"fmt"
	"reflect"
	"sort"
	"strings"

	configaccess "github.com/router-for-me/CLIProxyAPI/v6/internal/access/config_access"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
	sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access"
	log "github.com/sirupsen/logrus"
)

// ReconcileProviders builds the desired provider list by reusing existing providers when possible
// and creating or removing providers only when their configuration changed. It returns the final
// ordered provider slice along with the identifiers of providers that were added, updated, or
// removed compared to the previous configuration.
func ReconcileProviders(oldCfg, newCfg *config.Config, existing []sdkaccess.Provider) (result []sdkaccess.Provider, added, updated, removed []string, err error) {
	_ = oldCfg
	if newCfg == nil {
		return nil, nil, nil, nil, nil
	}

	result = sdkaccess.RegisteredProviders()

	existingMap := make(map[string]sdkaccess.Provider, len(existing))
	for _, provider := range existing {
		providerID := identifierFromProvider(provider)
		if providerID == "" {
			continue
		}
		existingMap[providerID] = provider
	}

	finalIDs := make(map[string]struct{}, len(result))

	isInlineProvider := func(id string) bool {
		return strings.EqualFold(id, sdkaccess.DefaultAccessProviderName)
	}
	appendChange := func(list *[]string, id string) {
		if isInlineProvider(id) {
			return
		}
		*list = append(*list, id)
	}

	for _, provider := range result {
		providerID := identifierFromProvider(provider)
		if providerID == "" {
			continue
		}
		finalIDs[providerID] = struct{}{}

		existingProvider, exists := existingMap[providerID]
		if !exists {
			appendChange(&added, providerID)
			continue
		}
		if !providerInstanceEqual(existingProvider, provider) {
			appendChange(&updated, providerID)
		}
	}

	for providerID := range existingMap {
		if _, exists := finalIDs[providerID]; exists {
			continue
		}
		appendChange(&removed, providerID)
	}

	sort.Strings(added)
	sort.Strings(updated)
	sort.Strings(removed)

	return result, added, updated, removed, nil
}

// ApplyAccessProviders reconciles the configured access providers against the
// currently registered providers and updates the manager. It logs a concise
// summary of the detected changes and returns whether any provider changed.
func ApplyAccessProviders(manager *sdkaccess.Manager, oldCfg, newCfg *config.Config) (bool, error) {
	if manager == nil || newCfg == nil {
		return false, nil
	}

	existing := manager.Providers()
	configaccess.Register(&newCfg.SDKConfig)
	providers, added, updated, removed, err := ReconcileProviders(oldCfg, newCfg, existing)
	if err != nil {
		log.Errorf("failed to reconcile request auth providers: %v", err)
		return false, fmt.Errorf("reconciling access providers: %w", err)
	}

	manager.SetProviders(providers)

	if len(added)+len(updated)+len(removed) > 0 {
		log.Debugf("auth providers reconciled (added=%d updated=%d removed=%d)", len(added), len(updated), len(removed))
		log.Debugf("auth providers changes details - added=%v updated=%v removed=%v", added, updated, removed)
		return true, nil
	}

	log.Debug("auth providers unchanged after config update")
	return false, nil
}

func identifierFromProvider(provider sdkaccess.Provider) string {
	if provider == nil {
		return ""
	}
	return strings.TrimSpace(provider.Identifier())
}

func providerInstanceEqual(a, b sdkaccess.Provider) bool {
	if a == nil || b == nil {
		return a == nil && b == nil
	}
	if reflect.TypeOf(a) != reflect.TypeOf(b) {
		return false
	}
	valueA := reflect.ValueOf(a)
	valueB := reflect.ValueOf(b)
	if valueA.Kind() == reflect.Pointer && valueB.Kind() == reflect.Pointer {
		return valueA.Pointer() == valueB.Pointer()
	}
	return reflect.DeepEqual(a, b)
}


================================================
FILE: internal/api/handlers/management/api_tools.go
================================================
package management

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/runtime/geminicli"
	coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
	"github.com/router-for-me/CLIProxyAPI/v6/sdk/proxyutil"
	log "github.com/sirupsen/logrus"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

const defaultAPICallTimeout = 60 * time.Second

const (
	geminiOAuthClientID     = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com"
	geminiOAuthClientSecret = "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"
)

var geminiOAuthScopes = []string{
	"https://www.googleapis.com/auth/cloud-platform",
	"https://www.googleapis.com/auth/userinfo.email",
	"https://www.googleapis.com/auth/userinfo.profile",
}

const (
	antigravityOAuthClientID     = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com"
	antigravityOAuthClientSecret = "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"
)

var antigravityOAuthTokenURL = "https://oauth2.googleapis.com/token"

type apiCallRequest struct {
	AuthIndexSnake  *string           `json:"auth_index"`
	AuthIndexCamel  *string           `json:"authIndex"`
	AuthIndexPascal *string           `json:"AuthIndex"`
	Method          string            `json:"method"`
	URL             string            `json:"url"`
	Header          map[string]string `json:"header"`
	Data            string            `json:"data"`
}

type apiCallResponse struct {
	StatusCode int                 `json:"status_code"`
	Header     map[string][]string `json:"header"`
	Body       string              `json:"body"`
}

// APICall makes a generic HTTP request on behalf of the management API caller.
// It is protected by the management middleware.
//
// Endpoint:
//
//	POST /v0/management/api-call
//
// Authentication:
//
//	Same as other management APIs (requires a management key and remote-management rules).
//	You can provide the key via:
//	- Authorization: Bearer <key>
//	- X-Management-Key: <key>
//
// Request JSON:
//   - auth_index / authIndex / AuthIndex (optional):
//     The credential "auth_index" from GET /v0/management/auth-files (or other endpoints returning it).
//     If omitted or not found, credential-specific proxy/token substitution is skipped.
//   - method (required): HTTP method, e.g. GET, POST, PUT, PATCH, DELETE.
//   - url (required): Absolute URL including scheme and host, e.g. "https://api.example.com/v1/ping".
//   - header (optional): Request headers map.
//     Supports magic variable "$TOKEN$" which is replaced using the selected credential:
//     1) metadata.access_token
//     2) attributes.api_key
//     3) metadata.token / metadata.id_token / metadata.cookie
//     Example: {"Authorization":"Bearer $TOKEN$"}.
//     Note: if you need to override the HTTP Host header, set header["Host"].
//   - data (optional): Raw request body as string (useful for POST/PUT/PATCH).
//
// Proxy selection (highest priority first):
//  1. Selected credential proxy_url
//  2. Global config proxy-url
//  3. Direct connect (environment proxies are not used)
//
// Response JSON (returned with HTTP 200 when the APICall itself succeeds):
//   - status_code: Upstream HTTP status code.
//   - header: Upstream response headers.
//   - body: Upstream response body as string.
//
// Example:
//
//	curl -sS -X POST "http://127.0.0.1:8317/v0/management/api-call" \
//	  -H "Authorization: Bearer <MANAGEMENT_KEY>" \
//	  -H "Content-Type: application/json" \
//	  -d '{"auth_index":"<AUTH_INDEX>","method":"GET","url":"https://api.example.com/v1/ping","header":{"Authorization":"Bearer $TOKEN$"}}'
//
//	curl -sS -X POST "http://127.0.0.1:8317/v0/management/api-call" \
//	  -H "Authorization: Bearer 831227" \
//	  -H "Content-Type: application/json" \
//	  -d '{"auth_index":"<AUTH_INDEX>","method":"POST","url":"https://api.example.com/v1/fetchAvailableModels","header":{"Authorization":"Bearer $TOKEN$","Content-Type":"application/json","User-Agent":"cliproxyapi"},"data":"{}"}'
func (h *Handler) APICall(c *gin.Context) {
	var body apiCallRequest
	if errBindJSON := c.ShouldBindJSON(&body); errBindJSON != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid body"})
		return
	}

	method := strings.ToUpper(strings.TrimSpace(body.Method))
	if method == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "missing method"})
		return
	}

	urlStr := strings.TrimSpace(body.URL)
	if urlStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "missing url"})
		return
	}
	parsedURL, errParseURL := url.Parse(urlStr)
	if errParseURL != nil || parsedURL.Scheme == "" || parsedURL.Host == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid url"})
		return
	}

	authIndex := firstNonEmptyString(body.AuthIndexSnake, body.AuthIndexCamel, body.AuthIndexPascal)
	auth := h.authByIndex(authIndex)

	reqHeaders := body.Header
	if reqHeaders == nil {
		reqHeaders = map[string]string{}
	}

	var hostOverride string
	var token string
	var tokenResolved bool
	var tokenErr error
	for key, value := range reqHeaders {
		if !strings.Contains(value, "$TOKEN$") {
			continue
		}
		if !tokenResolved {
			token, tokenErr = h.resolveTokenForAuth(c.Request.Context(), auth)
			tokenResolved = true
		}
		if auth != nil && token == "" {
			if tokenErr != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error": "auth token refresh failed"})
				return
			}
			c.JSON(http.StatusBadRequest, gin.H{"error": "auth token not found"})
			return
		}
		if token == "" {
			continue
		}
		reqHeaders[key] = strings.ReplaceAll(value, "$TOKEN$", token)
	}

	var requestBody io.Reader
	if body.Data != "" {
		requestBody = strings.NewReader(body.Data)
	}

	req, errNewRequest := http.NewRequestWithContext(c.Request.Context(), method, urlStr, requestBody)
	if errNewRequest != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "failed to build request"})
		return
	}

	for key, value := range reqHeaders {
		if strings.EqualFold(key, "host") {
			hostOverride = strings.TrimSpace(value)
			continue
		}
		req.Header.Set(key, value)
	}
	if hostOverride != "" {
		req.Host = hostOverride
	}

	httpClient := &http.Client{
		Timeout: defaultAPICallTimeout,
	}
	httpClient.Transport = h.apiCallTransport(auth)

	resp, errDo := httpClient.Do(req)
	if errDo != nil {
		log.WithError(errDo).Debug("management APICall request failed")
		c.JSON(http.StatusBadGateway, gin.H{"error": "request failed"})
		return
	}
	defer func() {
		if errClose := resp.Body.Close(); errClose != nil {
			log.Errorf("response body close error: %v", errClose)
		}
	}()

	respBody, errReadAll := io.ReadAll(resp.Body)
	if errReadAll != nil {
		c.JSON(http.StatusBadGateway, gin.H{"error": "failed to read response"})
		return
	}

	c.JSON(http.StatusOK, apiCallResponse{
		StatusCode: resp.StatusCode,
		Header:     resp.Header,
		Body:       string(respBody),
	})
}

func firstNonEmptyString(values ...*string) string {
	for _, v := range values {
		if v == nil {
			continue
		}
		if out := strings.TrimSpace(*v); out != "" {
			return out
		}
	}
	return ""
}

func tokenValueForAuth(auth *coreauth.Auth) string {
	if auth == nil {
		return ""
	}
	if v := tokenValueFromMetadata(auth.Metadata); v != "" {
		return v
	}
	if auth.Attributes != nil {
		if v := strings.TrimSpace(auth.Attributes["api_key"]); v != "" {
			return v
		}
	}
	if shared := geminicli.ResolveSharedCredential(auth.Runtime); shared != nil {
		if v := tokenValueFromMetadata(shared.MetadataSnapshot()); v != "" {
			return v
		}
	}
	return ""
}

func (h *Handler) resolveTokenForAuth(ctx context.Context, auth *coreauth.Auth) (string, error) {
	if auth == nil {
		return "", nil
	}

	provider := strings.ToLower(strings.TrimSpace(auth.Provider))
	if provider == "gemini-cli" {
		token, errToken := h.refreshGeminiOAuthAccessToken(ctx, auth)
		return token, errToken
	}
	if provider == "antigravity" {
		token, errToken := h.refreshAntigravityOAuthAccessToken(ctx, auth)
		return token, errToken
	}

	return tokenValueForAuth(auth), nil
}

func (h *Handler) refreshGeminiOAuthAccessToken(ctx context.Context, auth *coreauth.Auth) (string, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	if auth == nil {
		return "", nil
	}

	metadata, updater := geminiOAuthMetadata(auth)
	if len(metadata) == 0 {
		return "", fmt.Errorf("gemini oauth metadata missing")
	}

	base := make(map[string]any)
	if tokenRaw, ok := metadata["token"].(map[string]any); ok && tokenRaw != nil {
		base = cloneMap(tokenRaw)
	}

	var token oauth2.Token
	if len(base) > 0 {
		if raw, errMarshal := json.Marshal(base); errMarshal == nil {
			_ = json.Unmarshal(raw, &token)
		}
	}

	if token.AccessToken == "" {
		token.AccessToken = stringValue(metadata, "access_token")
	}
	if token.RefreshToken == "" {
		token.RefreshToken = stringValue(metadata, "refresh_token")
	}
	if token.TokenType == "" {
		token.TokenType = stringValue(metadata, "token_type")
	}
	if token.Expiry.IsZero() {
		if expiry := stringValue(metadata, "expiry"); expiry != "" {
			if ts, errParseTime := time.Parse(time.RFC3339, expiry); errParseTime == nil {
				token.Expiry = ts
			}
		}
	}

	conf := &oauth2.Config{
		ClientID:     geminiOAuthClientID,
		ClientSecret: geminiOAuthClientSecret,
		Scopes:       geminiOAuthScopes,
		Endpoint:     google.Endpoint,
	}

	ctxToken := ctx
	httpClient := &http.Client{
		Timeout:   defaultAPICallTimeout,
		Transport: h.apiCallTransport(auth),
	}
	ctxToken = context.WithValue(ctxToken, oauth2.HTTPClient, httpClient)

	src := conf.TokenSource(ctxToken, &token)
	currentToken, errToken := src.Token()
	if errToken != nil {
		return "", errToken
	}

	merged := buildOAuthTokenMap(base, currentToken)
	fields := buildOAuthTokenFields(currentToken, merged)
	if updater != nil {
		updater(fields)
	}
	return strings.TrimSpace(currentToken.AccessToken), nil
}

func (h *Handler) refreshAntigravityOAuthAccessToken(ctx context.Context, auth *coreauth.Auth) (string, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	if auth == nil {
		return "", nil
	}

	metadata := auth.Metadata
	if len(metadata) == 0 {
		return "", fmt.Errorf("antigravity oauth metadata missing")
	}

	current := strings.TrimSpace(tokenValueFromMetadata(metadata))
	if current != "" && !antigravityTokenNeedsRefresh(metadata) {
		return current, nil
	}

	refreshToken := stringValue(metadata, "refresh_token")
	if refreshToken == "" {
		return "", fmt.Errorf("antigravity refresh token missing")
	}

	tokenURL := strings.TrimSpace(antigravityOAuthTokenURL)
	if tokenURL == "" {
		tokenURL = "https://oauth2.googleapis.com/token"
	}
	form := url.Values{}
	form.Set("client_id", antigravityOAuthClientID)
	form.Set("client_secret", antigravityOAuthClientSecret)
	form.Set("grant_type", "refresh_token")
	form.Set("refresh_token", refreshToken)

	req, errReq := http.NewRequestWithContext(ctx, http.MethodPost, tokenURL, strings.NewReader(form.Encode()))
	if errReq != nil {
		return "", errReq
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	httpClient := &http.Client{
		Timeout:   defaultAPICallTimeout,
		Transport: h.apiCallTransport(auth),
	}
	resp, errDo := httpClient.Do(req)
	if errDo != nil {
		return "", errDo
	}
	defer func() {
		if errClose := resp.Body.Close(); errClose != nil {
			log.Errorf("response body close error: %v", errClose)
		}
	}()

	bodyBytes, errRead := io.ReadAll(resp.Body)
	if errRead != nil {
		return "", errRead
	}
	if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
		return "", fmt.Errorf("antigravity oauth token refresh failed: status %d: %s", resp.StatusCode, strings.TrimSpace(string(bodyBytes)))
	}

	var tokenResp struct {
		AccessToken  string `json:"access_token"`
		RefreshToken string `json:"refresh_token"`
		ExpiresIn    int64  `json:"expires_in"`
		TokenType    string `json:"token_type"`
	}
	if errUnmarshal := json.Unmarshal(bodyBytes, &tokenResp); errUnmarshal != nil {
		return "", errUnmarshal
	}

	if strings.TrimSpace(tokenResp.AccessToken) == "" {
		return "", fmt.Errorf("antigravity oauth token refresh returned empty access_token")
	}

	if auth.Metadata == nil {
		auth.Metadata = make(map[string]any)
	}
	now := time.Now()
	auth.Metadata["access_token"] = strings.TrimSpace(tokenResp.AccessToken)
	if strings.TrimSpace(tokenResp.RefreshToken) != "" {
		auth.Metadata["refresh_token"] = strings.TrimSpace(tokenResp.RefreshToken)
	}
	if tokenResp.ExpiresIn > 0 {
		auth.Metadata["expires_in"] = tokenResp.ExpiresIn
		auth.Metadata["timestamp"] = now.UnixMilli()
		auth.Metadata["expired"] = now.Add(time.Duration(tokenResp.ExpiresIn) * time.Second).Format(time.RFC3339)
	}
	auth.Metadata["type"] = "antigravity"

	if h != nil && h.authManager != nil {
		auth.LastRefreshedAt = now
		auth.UpdatedAt = now
		_, _ = h.authManager.Update(ctx, auth)
	}

	return strings.TrimSpace(tokenResp.AccessToken), nil
}

func antigravityTokenNeedsRefresh(metadata map[string]any) bool {
	// Refresh a bit early to avoid requests racing token expiry.
	const skew = 30 * time.Second

	if metadata == nil {
		return true
	}
	if expStr, ok := metadata["expired"].(string); ok {
		if ts, errParse := time.Parse(time.RFC3339, strings.TrimSpace(expStr)); errParse == nil {
			return !ts.After(time.Now().Add(skew))
		}
	}
	expiresIn := int64Value(metadata["expires_in"])
	timestampMs := int64Value(metadata["timestamp"])
	if expiresIn > 0 && timestampMs > 0 {
		exp := time.UnixMilli(timestampMs).Add(time.Duration(expiresIn) * time.Second)
		return !exp.After(time.Now().Add(skew))
	}
	return true
}

func int64Value(raw any) int64 {
	switch typed := raw.(type) {
	case int:
		return int64(typed)
	case int32:
		return int64(typed)
	case int64:
		return typed
	case uint:
		return int64(typed)
	case uint32:
		return int64(typed)
	case uint64:
		if typed > uint64(^uint64(0)>>1) {
			return 0
		}
		return int64(typed)
	case float32:
		return int64(typed)
	case float64:
		return int64(typed)
	case json.Number:
		if i, errParse := typed.Int64(); errParse == nil {
			return i
		}
	case string:
		if s := strings.TrimSpace(typed); s != "" {
			if i, errParse := json.Number(s).Int64(); errParse == nil {
				return i
			}
		}
	}
	return 0
}

func geminiOAuthMetadata(auth *coreauth.Auth) (map[string]any, func(map[string]any)) {
	if auth == nil {
		return nil, nil
	}
	if shared := geminicli.ResolveSharedCredential(auth.Runtime); shared != nil {
		snapshot := shared.MetadataSnapshot()
		return snapshot, func(fields map[string]any) { shared.MergeMetadata(fields) }
	}
	return auth.Metadata, func(fields map[string]any) {
		if auth.Metadata == nil {
			auth.Metadata = make(map[string]any)
		}
		for k, v := range fields {
			auth.Metadata[k] = v
		}
	}
}

func stringValue(metadata map[string]any, key string) string {
	if len(metadata) == 0 || key == "" {
		return ""
	}
	if v, ok := metadata[key].(string); ok {
		return strings.TrimSpace(v)
	}
	return ""
}

func cloneMap(in map[string]any) map[string]any {
	if len(in) == 0 {
		return nil
	}
	out := make(map[string]any, len(in))
	for k, v := range in {
		out[k] = v
	}
	return out
}

func buildOAuthTokenMap(base map[string]any, tok *oauth2.Token) map[string]any {
	merged := cloneMap(base)
	if merged == nil {
		merged = make(map[string]any)
	}
	if tok == nil {
		return merged
	}
	if raw, errMarshal := json.Marshal(tok); errMarshal == nil {
		var tokenMap map[string]any
		if errUnmarshal := json.Unmarshal(raw, &tokenMap); errUnmarshal == nil {
			for k, v := range tokenMap {
				merged[k] = v
			}
		}
	}
	return merged
}

func buildOAuthTokenFields(tok *oauth2.Token, merged map[string]any) map[string]any {
	fields := make(map[string]any, 5)
	if tok != nil && tok.AccessToken != "" {
		fields["access_token"] = tok.AccessToken
	}
	if tok != nil && tok.TokenType != "" {
		fields["token_type"] = tok.TokenType
	}
	if tok != nil && tok.RefreshToken != "" {
		fields["refresh_token"] = tok.RefreshToken
	}
	if tok != nil && !tok.Expiry.IsZero() {
		fields["expiry"] = tok.Expiry.Format(time.RFC3339)
	}
	if len(merged) > 0 {
		fields["token"] = cloneMap(merged)
	}
	return fields
}

func tokenValueFromMetadata(metadata map[string]any) string {
	if len(metadata) == 0 {
		return ""
	}
	if v, ok := metadata["accessToken"].(string); ok && strings.TrimSpace(v) != "" {
		return strings.TrimSpace(v)
	}
	if v, ok := metadata["access_token"].(string); ok && strings.TrimSpace(v) != "" {
		return strings.TrimSpace(v)
	}
	if tokenRaw, ok := metadata["token"]; ok && tokenRaw != nil {
		switch typed := tokenRaw.(type) {
		case string:
			if v := strings.TrimSpace(typed); v != "" {
				return v
			}
		case map[string]any:
			if v, ok := typed["access_token"].(string); ok && strings.TrimSpace(v) != "" {
				return strings.TrimSpace(v)
			}
			if v, ok := typed["accessToken"].(string); ok && strings.TrimSpace(v) != "" {
				return strings.TrimSpace(v)
			}
		case map[string]string:
			if v := strings.TrimSpace(typed["access_token"]); v != "" {
				return v
			}
			if v := strings.TrimSpace(typed["accessToken"]); v != "" {
				return v
			}
		}
	}
	if v, ok := metadata["token"].(string); ok && strings.TrimSpace(v) != "" {
		return strings.TrimSpace(v)
	}
	if v, ok := metadata["id_token"].(string); ok && strings.TrimSpace(v) != "" {
		return strings.TrimSpace(v)
	}
	if v, ok := metadata["cookie"].(string); ok && strings.TrimSpace(v) != "" {
		return strings.TrimSpace(v)
	}
	return ""
}

func (h *Handler) authByIndex(authIndex string) *coreauth.Auth {
	authIndex = strings.TrimSpace(authIndex)
	if authIndex == "" || h == nil || h.authManager == nil {
		return nil
	}
	auths := h.authManager.List()
	for _, auth := range auths {
		if auth == nil {
			continue
		}
		auth.EnsureIndex()
		if auth.Index == authIndex {
			return auth
		}
	}
	return nil
}

func (h *Handler) apiCallTransport(auth *coreauth.Auth) http.RoundTripper {
	var proxyCandidates []string
	if auth != nil {
		if proxyStr := strings.TrimSpace(auth.ProxyURL); proxyStr != "" {
			proxyCandidates = append(proxyCandidates, proxyStr)
		}
	}
	if h != nil && h.cfg != nil {
		if proxyStr := strings.TrimSpace(h.cfg.ProxyURL); proxyStr != "" {
			proxyCandidates = append(proxyCandidates, proxyStr)
		}
	}

	for _, proxyStr := range proxyCandidates {
		if transport := buildProxyTransport(proxyStr); transport != nil {
			return transport
		}
	}

	transport, ok := http.DefaultTransport.(*http.Transport)
	if !ok || transport == nil {
		return &http.Transport{Proxy: nil}
	}
	clone := transport.Clone()
	clone.Proxy = nil
	return clone
}

func buildProxyTransport(proxyStr string) *http.Transport {
	transport, _, errBuild := proxyutil.BuildHTTPTransport(proxyStr)
	if errBuild != nil {
		log.WithError(errBuild).Debug("build proxy transport failed")
		return nil
	}
	return transport
}


================================================
FILE: internal/api/handlers/management/api_tools_test.go
================================================
package management

import (
	"context"
	"net/http"
	"testing"

	"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
	coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
	sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
)

func TestAPICallTransportDirectBypassesGlobalProxy(t *testing.T) {
	t.Parallel()

	h := &Handler{
		cfg: &config.Config{
			SDKConfig: sdkconfig.SDKConfig{ProxyURL: "http://global-proxy.example.com:8080"},
		},
	}

	transport := h.apiCallTransport(&coreauth.Auth{ProxyURL: "direct"})
	httpTransport, ok := transport.(*http.Transport)
	if !ok {
		t.Fatalf("transport type = %T, want *http.Transport", transport)
	}
	if httpTransport.Proxy != nil {
		t.Fatal("expected direct transport to disable proxy function")
	}
}

func TestAPICallTransportInvalidAuthFallsBackToGlobalProxy(t *testing.T) {
	t.Parallel()

	h := &Handler{
		cfg: &config.Config{
			SDKConfig: sdkconfig.SDKConfig{ProxyURL: "http://global-proxy.example.com:8080"},
		},
	}

	transport := h.apiCallTransport(&coreauth.Auth{ProxyURL: "bad-value"})
	httpTransport, ok := transport.(*http.Transport)
	if !ok {
		t.Fatalf("transport type = %T, want *http.Transport", transport)
	}

	req, errRequest := http.NewRequest(http.MethodGet, "https://example.com", nil)
	if errRequest != nil {
		t.Fatalf("http.NewRequest returned error: %v", errRequest)
	}

	proxyURL, errProxy := httpTransport.Proxy(req)
	if errProxy != nil {
		t.Fatalf("httpTransport.Proxy returned error: %v", errProxy)
	}
	if proxyURL == nil || proxyURL.String() != "http://global-proxy.example.com:8080" {
		t.Fatalf("proxy URL = %v, want http://global-proxy.example.com:8080", proxyURL)
	}
}

func TestAuthByIndexDistinguishesSharedAPIKeysAcrossProviders(t *testing.T) {
	t.Parallel()

	manager := coreauth.NewManager(nil, nil, nil)
	geminiAuth := &coreauth.Auth{
		ID:       "gemini:apikey:123",
		Provider: "gemini",
		Attributes: map[string]string{
			"api_key": "shared-key",
		},
	}
	compatAuth := &coreauth.Auth{
		ID:       "openai-compatibility:bohe:456",
		Provider: "bohe",
		Label:    "bohe",
		Attributes: map[string]string{
			"api_key":      "shared-key",
			"compat_name":  "bohe",
			"provider_key": "bohe",
		},
	}

	if _, errRegister := manager.Register(context.Background(), geminiAuth); errRegister != nil {
		t.Fatalf("register gemini auth: %v", errRegister)
	}
	if _, errRegister := manager.Register(context.Background(), compatAuth); errRegister != nil {
		t.Fatalf("register compat auth: %v", errRegister)
	}

	geminiIndex := geminiAuth.EnsureIndex()
	compatIndex := compatAuth.EnsureIndex()
	if geminiIndex == compatIndex {
		t.Fatalf("shared api key produced duplicate auth_index %q", geminiIndex)
	}

	h := &Handler{authManager: manager}

	gotGemini := h.authByIndex(geminiIndex)
	if gotGemini == nil {
		t.Fatal("expected gemini auth by index")
	}
	if gotGemini.ID != geminiAuth.ID {
		t.Fatalf("authByIndex(gemini) returned %q, want %q", gotGemini.ID, geminiAuth.ID)
	}

	gotCompat := h.authByIndex(compatIndex)
	if gotCompat == nil {
		t.Fatal("expected compat auth by index")
	}
	if gotCompat.ID != compatAuth.ID {
		t.Fatalf("authByIndex(compat) returned %q, want %q", gotCompat.ID, compatAuth.ID)
	}
}


================================================
FILE: internal/api/handlers/management/auth_files.go
================================================
package management

import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/antigravity"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/claude"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/codex"
	geminiAuth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/gemini"
	iflowauth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/iflow"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/kimi"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/qwen"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
	"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
	sdkAuth "github.com/router-for-me/CLIProxyAPI/v6/sdk/auth"
	coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
	log "github.com/sirupsen/logrus"
	"github.com/tidwall/gjson"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

var lastRefreshKeys = []string{"last_refresh", "lastRefresh", "last_refreshed_at", "lastRefreshedAt"}

const (
	anthropicCallbackPort = 54545
	geminiCallbackPort    = 8085
	codexCallbackPort     = 1455
	geminiCLIEndpoint     = "https://cloudcode-pa.googleapis.com"
	geminiCLIVersion      = "v1internal"
)

type callbackForwarder struct {
	provider string
	server   *http.Server
	done     chan struct{}
}

var (
	callbackForwardersMu sync.Mutex
	callbackForwarders   = make(map[int]*callbackForwarder)
)

func extractLastRefreshTimestamp(meta map[string]any) (time.Time, bool) {
	if len(meta) == 0 {
		return time.Time{}, false
	}
	for _, key := range lastRefreshKeys {
		if val, ok := meta[key]; ok {
			if ts, ok1 := parseLastRefreshValue(val); ok1 {
				return ts, true
			}
		}
	}
	return time.Time{}, false
}

func parseLastRefreshValue(v any) (time.Time, bool) {
	switch val := v.(type) {
	case string:
		s := strings.TrimSpace(val)
		if s == "" {
			return time.Time{}, false
		}
		layouts := []string{time.RFC3339, time.RFC3339Nano, "2006-01-02 15:04:05", "2006-01-02T15:04:05Z07:00"}
		for _, layout := range layouts {
			if ts, err := time.Parse(layout, s); err == nil {
				return ts.UTC(), true
			}
		}
		if unix, err := strconv.ParseInt(s, 10, 64); err == nil && unix > 0 {
			return time.Unix(unix, 0).UTC(), true
		}
	case float64:
		if val <= 0 {
			return time.Time{}, false
		}
		return time.Unix(int64(val), 0).UTC(), true
	case int64:
		if val <= 0 {
			return time.Time{}, false
		}
		return time.Unix(val, 0).UTC(), true
	case int:
		if val <= 0 {
			return time.Time{}, false
		}
		return time.Unix(int64(val), 0).UTC(), true
	case json.Number:
		if i, err := val.Int64(); err == nil && i > 0 {
			return time.Unix(i, 0).UTC(), true
		}
	}
	return time.Time{}, false
}

func isWebUIRequest(c *gin.Context) bool {
	raw := strings.TrimSpace(c.Query("is_webui"))
	if raw == "" {
		return false
	}
	switch strings.ToLower(raw) {
	case "1", "true", "yes", "on":
		return true
	default:
		return false
	}
}

func startCallbackForwarder(port int, provider, targetBase string) (*callbackForwarder, error) {
	callbackForwardersMu.Lock()
	prev := callbackForwarders[port]
	if prev != nil {
		delete(callbackForwarders, port)
	}
	callbackForwardersMu.Unlock()

	if prev != nil {
		stopForwarderInstance(port, prev)
	}

	addr := fmt.Sprintf("127.0.0.1:%d", port)
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return nil, fmt.Errorf("failed to listen on %s: %w", addr, err)
	}

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		target := targetBase
		if raw := r.URL.RawQuery; raw != "" {
			if strings.Contains(target, "?") {
				target = target + "&" + raw
			} else {
				target = target + "?" + raw
			}
		}
		w.Header().Set("Cache-Control", "no-store")
		http.Redirect(w, r, target, http.StatusFound)
	})

	srv := &http.Server{
		Handler:           handler,
		ReadHeaderTimeout: 5 * time.Second,
		WriteTimeout:      5 * time.Second,
	}
	done := make(chan struct{})

	go func() {
		if errServe := srv.Serve(ln); errServe != nil && !errors.Is(errServe, http.ErrServerClosed) {
			log.WithError(errServe).Warnf("callback forwarder for %s stopped unexpectedly", provider)
		}
		close(done)
	}()

	forwarder := &callbackForwarder{
		provider: provider,
		server:   srv,
		done:     done,
	}

	callbackForwardersMu.Lock()
	callbackForwarders[port] = forwarder
	callbackForwardersMu.Unlock()

	log.Infof("callback forwarder for %s listening on %s", provider, addr)

	return forwarder, nil
}

func stopCallbackForwarderInstance(port int, forwarder *callbackForwarder) {
	if forwarder == nil {
		return
	}
	callbackForwardersMu.Lock()
	if current := callbackForwarders[port]; current == forwarder {
		delete(callbackForwarders, port)
	}
	callbackForwardersMu.Unlock()

	stopForwarderInstance(port, forwarder)
}

func stopForwarderInstance(port int, forwarder *callbackForwarder) {
	if forwarder == nil || forwarder.server == nil {
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()

	if err := forwarder.server.Shutdown(ctx); err != nil && !errors.Is(err, http.ErrServerClosed) {
		log.WithError(err).Warnf("failed to shut down callback forwarder on port %d", port)
	}

	select {
	case <-forwarder.done:
	case <-time.After(2 * time.Second):
	}

	log.Infof("callback forwarder on port %d stopped", port)
}

func (h *Handler) managementCallbackURL(path string) (string, error) {
	if h == nil || h.cfg == nil || h.cfg.Port <= 0 {
		return "", fmt.Errorf("server port is not configured")
	}
	if !strings.HasPrefix(path, "/") {
		path = "/" + path
	}
	scheme := "http"
	if h.cfg.TLS.Enable {
		scheme = "https"
	}
	return fmt.Sprintf("%s://127.0.0.1:%d%s", scheme, h.cfg.Port, path), nil
}

func (h *Handler) ListAuthFiles(c *gin.Context) {
	if h == nil {
		c.JSON(500, gin.H{"error": "handler not initialized"})
		return
	}
	if h.authManager == nil {
		h.listAuthFilesFromDisk(c)
		return
	}
	auths := h.authManager.List()
	files := make([]gin.H, 0, len(auths))
	for _, auth := range auths {
		if entry := h.buildAuthFileEntry(auth); entry != nil {
			files = append(files, entry)
		}
	}
	sort.Slice(files, func(i, j int) bool {
		nameI, _ := files[i]["name"].(string)
		nameJ, _ := files[j]["name"].(string)
		return strings.ToLower(nameI) < strings.ToLower(nameJ)
	})
	c.JSON(200, gin.H{"files": files})
}

// GetAuthFileModels returns the models supported by a specific auth file
func (h *Handler) GetAuthFileModels(c *gin.Context) {
	name := c.Query("name")
	if name == "" {
		c.JSON(400, gin.H{"error": "name is required"})
		return
	}

	// Try to find auth ID via authManager
	var authID string
	if h.authManager != nil {
		auths := h.authManager.List()
		for _, auth := range auths {
			if auth.FileName == name || auth.ID == name {
				authID = auth.ID
				break
			}
		}
	}

	if authID == "" {
		authID = name // fallback to filename as ID
	}

	// Get models from registry
	reg := registry.GetGlobalRegistry()
	models := reg.GetModelsForClient(authID)

	result := make([]gin.H, 0, len(models))
	for _, m := range models {
		entry := gin.H{
			"id": m.ID,
		}
		if m.DisplayName != "" {
			entry["display_name"] = m.DisplayName
		}
		if m.Type != "" {
			entry["type"] = m.Type
		}
		if m.OwnedBy != "" {
			entry["owned_by"] = m.OwnedBy
		}
		result = append(result, entry)
	}

	c.JSON(200, gin.H{"models": result})
}

// List auth files from disk when the auth manager is unavailable.
func (h *Handler) listAuthFilesFromDisk(c *gin.Context) {
	entries, err := os.ReadDir(h.cfg.AuthDir)
	if err != nil {
		c.JSON(500, gin.H{"error": fmt.Sprintf("failed to read auth dir: %v", err)})
		return
	}
	files := make([]gin.H, 0)
	for _, e := range entries {
		if e.IsDir() {
			continue
		}
		name := e.Name()
		if !strings.HasSuffix(strings.ToLower(name), ".json") {
			continue
		}
		if info, errInfo := e.Info(); errInfo == nil {
			fileData := gin.H{"name": name, "size": info.Size(), "modtime": info.ModTime()}

			// Read file to get type field
			full := filepath.Join(h.cfg.AuthDir, name)
			if data, errRead := os.ReadFile(full); errRead == nil {
				typeValue := gjson.GetBytes(data, "type").String()
				emailValue := gjson.GetBytes(data, "email").String()
				fileData["type"] = typeValue
				fileData["email"] = emailValue
				if pv := gjson.GetBytes(data, "priority"); pv.Exists() {
					switch pv.Type {
					case gjson.Number:
						fileData["priority"] = int(pv.Int())
					case gjson.String:
						if parsed, errAtoi := strconv.Atoi(strings.TrimSpace(pv.String())); errAtoi == nil {
							fileData["priority"] = parsed
						}
					}
				}
				if nv := gjson.GetBytes(data, "note"); nv.Exists() && nv.Type == gjson.String {
					if trimmed := strings.TrimSpace(nv.String()); trimmed != "" {
						fileData["note"] = trimmed
					}
				}
			}

			files = append(files, fileData)
		}
	}
	c.JSON(200, gin.H{"files": files})
}

func (h *Handler) buildAuthFileEntry(auth *coreauth.Auth) gin.H {
	if auth == nil {
		return nil
	}
	auth.EnsureIndex()
	runtimeOnly := isRuntimeOnlyAuth(auth)
	if runtimeOnly && (auth.Disabled || auth.Status == coreauth.StatusDisabled) {
		return nil
	}
	path := strings.TrimSpace(authAttribute(auth, "path"))
	if path == "" && !runtimeOnly {
		return nil
	}
	name := strings.TrimSpace(auth.FileName)
	if name == "" {
		name = auth.ID
	}
	entry := gin.H{
		"id":             auth.ID,
		"auth_index":     auth.Index,
		"name":           name,
		"type":           strings.TrimSpace(auth.Provider),
		"provider":       strings.TrimSpace(auth.Provider),
		"label":          auth.Label,
		"status":         auth.Status,
		"status_message": auth.StatusMessage,
		"disabled":       auth.Disabled,
		"unavailable":    auth.Unavailable,
		"runtime_only":   runtimeOnly,
		"source":         "memory",
		"size":           int64(0),
	}
	if email := authEmail(auth); email != "" {
		entry["email"] = email
	}
	if accountType, account := auth.AccountInfo(); accountType != "" || account != "" {
		if accountType != "" {
			entry["account_type"] = accountType
		}
		if account != "" {
			entry["account"] = account
		}
	}
	if !auth.CreatedAt.IsZero() {
		entry["created_at"] = auth.CreatedAt
	}
	if !auth.UpdatedAt.IsZero() {
		entry["modtime"] = auth.UpdatedAt
		entry["updated_at"] = auth.UpdatedAt
	}
	if !auth.LastRefreshedAt.IsZero() {
		entry["last_refresh"] = auth.LastRefreshedAt
	}
	if !auth.NextRetryAfter.IsZero() {
		entry["next_retry_after"] = auth.NextRetryAfter
	}
	if path != "" {
		entry["path"] = path
		entry["source"] = "file"
		if info, err := os.Stat(path); err == nil {
			entry["size"] = info.Size()
			entry["modtime"] = info.ModTime()
		} else if os.IsNotExist(err) {
			// Hide credentials removed from disk but still lingering in memory.
			if !runtimeOnly && (auth.Disabled || auth.Status == coreauth.StatusDisabled || strings.EqualFold(strings.TrimSpace(auth.StatusMessage), "removed via management api")) {
				return nil
			}
			entry["source"] = "memory"
		} else {
			log.WithError(err).Warnf("failed to stat auth file %s", path)
		}
	}
	if claims := extractCodexIDTokenClaims(auth); claims != nil {
		entry["id_token"] = claims
	}
	// Expose priority from Attributes (set by synthesizer from JSON "priority" field).
	// Fall back to Metadata for auths registered via UploadAuthFile (no synthesizer).
	if p := strings.TrimSpace(authAttribute(auth, "priority")); p != "" {
		if parsed, err := strconv.Atoi(p); err == nil {
			entry["priority"] = parsed
		}
	} else if auth.Metadata != nil {
		if rawPriority, ok := auth.Metadata["priority"]; ok {
			switch v := rawPriority.(type) {
			case float64:
				entry["priority"] = int(v)
			case int:
				entry["priority"] = v
			case string:
				if parsed, err := strconv.Atoi(strings.TrimSpace(v)); err == nil {
					entry["priority"] = parsed
				}
			}
		}
	}
	// Expose note from Attributes (set by synthesizer from JSON "note" field).
	// Fall back to Metadata for auths registered via UploadAuthFile (no synthesizer).
	if note := strings.TrimSpace(authAttribute(auth, "note")); note != "" {
		entry["note"] = note
	} else if auth.Metadata != nil {
		if rawNote, ok := auth.Metadata["note"].(string); ok {
			if trimmed := strings.TrimSpace(rawNote); trimmed != "" {
				entry["note"] = trimmed
			}
		}
	}
	return entry
}

func extractCodexIDTokenClaims(auth *coreauth.Auth) gin.H {
	if auth == nil || auth.Metadata == nil {
		return nil
	}
	if !strings.EqualFold(strings.TrimSpace(auth.Provider), "codex") {
		return nil
	}
	idTokenRaw, ok := auth.Metadata["id_token"].(string)
	if !ok {
		return nil
	}
	idToken := strings.TrimSpace(idTokenRaw)
	if idToken == "" {
		return nil
	}
	claims, err := codex.ParseJWTToken(idToken)
	if err != nil || claims == nil {
		return nil
	}

	result := gin.H{}
	if v := strings.TrimSpace(claims.CodexAuthInfo.ChatgptAccountID); v != "" {
		result["chatgpt_account_id"] = v
	}
	if v := strings.TrimSpace(claims.CodexAuthInfo.ChatgptPlanType); v != "" {
		result["plan_type"] = v
	}
	if v := claims.CodexAuthInfo.ChatgptSubscriptionActiveStart; v != nil {
		result["chatgpt_subscription_active_start"] = v
	}
	if v := claims.CodexAuthInfo.ChatgptSubscriptionActiveUntil; v != nil {
		result["chatgpt_subscription_active_until"] = v
	}

	if len(result) == 0 {
		return nil
	}
	return result
}

func authEmail(auth *coreauth.Auth) string {
	if auth == nil {
		return ""
	}
	if auth.Metadata != nil {
		if v, ok := auth.Metadata["email"].(string); ok {
			return strings.TrimSpace(v)
		}
	}
	if auth.Attributes != nil {
		if v := strings.TrimSpace(auth.Attributes["email"]); v != "" {
			return v
		}
		if v := strings.TrimSpace(auth.Attributes["account_email"]); v != "" {
			return v
		}
	}
	return ""
}

func authAttribute(auth *coreauth.Auth, key string) string {
	if auth == nil || len(auth.Attributes) == 0 {
		return ""
	}
	return auth.Attributes[key]
}

func isRuntimeOnlyAuth(auth *coreauth.Auth) bool {
	if auth == nil || len(auth.Attributes) == 0 {
		return false
	}
	return strings.EqualFold(strings.TrimSpace(auth.Attributes["runtime_only"]), "true")
}

// Download single auth file by name
func (h *Handler) DownloadAuthFile(c *gin.Context) {
	name := c.Query("name")
	if name == "" || strings.Contains(name, string(os.PathSeparator)) {
		c.JSON(400, gin.H{"error": "invalid name"})
		return
	}
	if !strings.HasSuffix(strings.ToLower(name), ".json") {
		c.JSON(400, gin.H{"error": "name must end with .json"})
		return
	}
	full := filepath.Join(h.cfg.AuthDir, name)
	data, err := os.ReadFile(full)
	if err != nil {
		if os.IsNotExist(err) {
			c.JSON(404, gin.H{"error": "file not found"})
		} else {
			c.JSON(500, gin.H{"error": fmt.Sprintf("failed to read file: %v", err)})
		}
		return
	}
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", name))
	c.Data(200, "application/json", data)
}

// Upload auth file: multipart or raw JSON with ?name=
func (h *Handler) UploadAuthFile(c *gin.Context) {
	if h.authManager == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "core auth manager unavailable"})
		return
	}
	ctx := c.Request.Context()
	if file, err := c.FormFile("file"); err == nil && file != nil {
		name := filepath.Base(file.Filename)
		if !strings.HasSuffix(strings.ToLower(name), ".json") {
			c.JSON(400, gin.H{"error": "file must be .json"})
			return
		}
		dst := filepath.Join(h.cfg.AuthDir, name)
		if !filepath.IsAbs(dst) {
			if abs, errAbs := filepath.Abs(dst); errAbs == nil {
				dst = abs
			}
		}
		if errSave := c.SaveUploadedFile(file, dst); errSave != nil {
			c.JSON(500, gin.H{"error": fmt.Sprintf("failed to save file: %v", errSave)})
			return
		}
		data, errRead := os.ReadFile(dst)
		if errRead != nil {
			c.JSON(500, gin.H{"error": fmt.Sprintf("failed to read saved file: %v", errRead)})
			return
		}
		if errReg := h.registerAuthFromFile(ctx, dst, data); errReg != nil {
			c.JSON(500, gin.H{"error": errReg.Error()})
			return
		}
		c.JSON(200, gin.H{"status": "ok"})
		return
	}
	name := c.Query("name")
	if name == "" || strings.Contains(name, string(os.PathSeparator)) {
		c.JSON(400, gin.H{"error": "invalid name"})
		return
	}
	if !strings.HasSuffix(strings.ToLower(name), ".json") {
		c.JSON(400, gin.H{"error": "name must end with .json"})
		return
	}
	data, err := io.ReadAll(c.Request.Body)
	if err != nil {
		c.JSON(400, gin.H{"error": "failed to read body"})
		return
	}
	dst := filepath.Join(h.cfg.AuthDir, filepath.Base(name))
	if !filepath.IsAbs(dst) {
		if abs, errAbs := filepath.Abs(dst); errAbs == nil {
			dst = abs
		}
	}
	if errWrite := os.WriteFile(dst, data, 0o600); errWrite != nil {
		c.JSON(500, gin.H{"error": fmt.Sprintf("failed to write file: %v", errWrite)})
		return
	}
	if err = h.registerAuthFromFile(ctx, dst, data); err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}
	c.JSON(200, gin.H{"status": "ok"})
}

// Delete auth files: single by name or all
func (h *Handler) DeleteAuthFile(c *gin.Context) {
	if h.authManager == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "core auth manager unavailable"})
		return
	}
	ctx := c.Request.Context()
	if all := c.Query("all"); all == "true" || all == "1" || all == "*" {
		entries, err := os.ReadDir(h.cfg.AuthDir)
		if err != nil {
			c.JSON(500, gin.H{"error": fmt.Sprintf("failed to read auth dir: %v", err)})
			return
		}
		deleted := 0
		for _, e := range entries {
			if e.IsDir() {
				continue
			}
			name := e.Name()
			if !strings.HasSuffix(strings.ToLower(name), ".json") {
				continue
			}
			full := filepath.Join(h.cfg.AuthDir, name)
			if !filepath.IsAbs(full) {
				if abs, errAbs := filepath.Abs(full); errAbs == nil {
					full = abs
				}
			}
			if err = os.Remove(full); err == nil {
				if errDel := h.deleteTokenRecord(ctx, full); errDel != nil {
					c.JSON(500, gin.H{"error": errDel.Error()})
					return
				}
				deleted++
				h.disableAuth(ctx, full)
			}
		}
		c.JSON(200, gin.H{"status": "ok", "deleted": deleted})
		return
	}
	name := c.Query("name")
	if name == "" || strings.Contains(name, string(os.PathSeparator)) {
		c.JSON(400, gin.H{"error": "invalid name"})
		return
	}

	targetPath := filepath.Join(h.cfg.AuthDir, filepath.Base(name))
	targetID := ""
	if targetAuth := h.findAuthForDelete(name); targetAuth != nil {
		targetID = strings.TrimSpace(targetAuth.ID)
		if path := strings.TrimSpace(authAttribute(targetAuth, "path")); path != "" {
			targetPath = path
		}
	}
	if !filepath.IsAbs(targetPath) {
		if abs, errAbs := filepath.Abs(targetPath); errAbs == nil {
			targetPath = abs
		}
	}
	if errRemove := os.Remove(targetPath); errRemove != nil {
		if os.IsNotExist(errRemove) {
			c.JSON(404, gin.H{"error": "file not found"})
		} else {
			c.JSON(500, gin.H{"error": fmt.Sprintf("failed to remove file: %v", errRemove)})
		}
		return
	}
	if errDeleteRecord := h.deleteTokenRecord(ctx, targetPath); errDeleteRecord != nil {
		c.JSON(500, gin.H{"error": errDeleteRecord.Error()})
		return
	}
	if targetID != "" {
		h.disableAuth(ctx, targetID)
	} else {
		h.disableAuth(ctx, targetPath)
	}
	c.JSON(200, gin.H{"status": "ok"})
}

func (h *Handler) findAuthForDelete(name string) *coreauth.Auth {
	if h == nil || h.authManager == nil {
		return nil
	}
	name = strings.TrimSpace(name)
	if name == "" {
		return nil
	}
	if auth, ok := h.authManager.GetByID(name); ok {
		return auth
	}
	auths := h.authManager.List()
	for _, auth := range auths {
		if auth == nil {
			continue
		}
		if strings.TrimSpace(auth.FileName) == name {
			return auth
		}
		if filepath.Base(strings.TrimSpace(authAttribute(auth, "path"))) == name {
			return auth
		}
	}
	return nil
}

func (h *Handler) authIDForPath(path string) string {
	path = strings.TrimSpace(path)
	if path == "" {
		return ""
	}
	id := path
	if h != nil && h.cfg != nil {
		authDir := strings.TrimSpace(h.cfg.AuthDir)
		if authDir != "" {
			if rel, errRel := filepath.Rel(authDir, path); errRel == nil && rel != "" {
				id = rel
			}
		}
	}
	// On Windows, normalize ID casing to avoid duplicate auth entries caused by case-insensitive paths.
	if runtime.GOOS == "windows" {
		id = strings.ToLower(id)
	}
	return id
}

func (h *Handler) registerAuthFromFile(ctx context.Context, path string, data []byte) error {
	if h.authManager == nil {
		return nil
	}
	if path == "" {
		return fmt.Errorf("auth path is empty")
	}
	if data == nil {
		var err error
		data, err = os.ReadFile(path)
		if err != nil {
			return fmt.Errorf("failed to read auth file: %w", err)
		}
	}
	metadata := make(map[string]any)
	if err := json.Unmarshal(data, &metadata); err != nil {
		return fmt.Errorf("invalid auth file: %w", err)
	}
	provider, _ := metadata["type"].(string)
	if provider == "" {
		provider = "unknown"
	}
	label := provider
	if email, ok := metadata["email"].(string); ok && email != "" {
		label = email
	}
	lastRefresh, hasLastRefresh := extractLastRefreshTimestamp(metadata)

	authID := h.authIDForPath(path)
	if authID == "" {
		authID = path
	}
	attr := map[string]string{
		"path":   path,
		"source": path,
	}
	auth := &coreauth.Auth{
		ID:         authID,
		Provider:   provider,
		FileName:   filepath.Base(path),
		Label:      label,
		Status:     coreauth.StatusActive,
		Attributes: attr,
		Metadata:   metadata,
		CreatedAt:  time.Now(),
		UpdatedAt:  time.Now(),
	}
	if hasLastRefresh {
		auth.LastRefreshedAt = lastRefresh
	}
	if existing, ok := h.authManager.GetByID(authID); ok {
		auth.CreatedAt = existing.CreatedAt
		if !hasLastRefresh {
			auth.LastRefreshedAt = existing.LastRefreshedAt
		}
		auth.NextRefreshAfter = existing.NextRefreshAfter
		auth.Runtime = existing.Runtime
		_, err := h.authManager.Update(ctx, auth)
		return err
	}
	_, err := h.authManager.Register(ctx, auth)
	return err
}

// PatchAuthFileStatus toggles the disabled state of an auth file
func (h *Handler) PatchAuthFileStatus(c *gin.Context) {
	if h.authManager == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "core auth manager unavailable"})
		return
	}

	var req struct {
		Name     string `json:"name"`
		Disabled *bool  `json:"disabled"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
		return
	}

	name := strings.TrimSpace(req.Name)
	if name == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"})
		return
	}
	if req.Disabled == nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "disabled is required"})
		return
	}

	ctx := c.Request.Context()

	// Find auth by name or ID
	var targetAuth *coreauth.Auth
	if auth, ok := h.authManager.GetByID(name); ok {
		targetAuth = auth
	} else {
		auths := h.authManager.List()
		for _, auth := range auths {
			if auth.FileName == name {
				targetAuth = auth
				break
			}
		}
	}

	if targetAuth == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "auth file not found"})
		return
	}

	// Update disabled state
	targetAuth.Disabled = *req.Disabled
	if *req.Disabled {
		targetAuth.Status = coreauth.StatusDisabled
		targetAuth.StatusMessage = "disabled via management API"
	} else {
		targetAuth.Status = coreauth.StatusActive
		targetAuth.StatusMessage = ""
	}
	targetAuth.UpdatedAt = time.Now()

	if _, err := h.authManager.Update(ctx, targetAuth); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to update auth: %v", err)})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": "ok", "disabled": *req.Disabled})
}

// PatchAuthFileFields updates editable fields (prefix, proxy_url, priority, note) of an auth file.
func (h *Handler) PatchAuthFileFields(c *gin.Context) {
	if h.authManager == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "core auth manager unavailable"})
		return
	}

	var req struct {
		Name     string  `json:"name"`
		Prefix   *string `json:"prefix"`
		ProxyURL *string `json:"proxy_url"`
		Priority *int    `json:"priority"`
		Note     *string `json:"note"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
		return
	}

	name := strings.TrimSpace(req.Name)
	if name == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"})
		return
	}

	ctx := c.Request.Context()

	// Find auth by name or ID
	var targetAuth *coreauth.Auth
	if auth, ok := h.authManager.GetByID(name); ok {
		targetAuth = auth
	} else {
		auths := h.authManager.List()
		for _, auth := range auths {
			if auth.FileName == name {
				targetAuth = auth
				break
			}
		}
	}

	if targetAuth == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "auth file not found"})
		return
	}

	changed := false
	if req.Prefix != nil {
		targetAuth.Prefix = *req.Prefix
		changed = true
	}
	if req.ProxyURL != nil {
		targetAuth.ProxyURL = *req.ProxyURL
		changed = true
	}
	if req.Priority != nil || req.Note != nil {
		if targetAuth.Metadata == nil {
			targetAuth.Metadata = make(map[string]any)
		}
		if targetAuth.Attributes == nil {
			targetAuth.Attributes = make(map[string]string)
		}

		if req.Priority != nil {
			if *req.Priority == 0 {
				delete(targetAuth.Metadata, "priority")
				delete(targetAuth.Attributes, "priority")
			} else {
				targetAuth.Metadata["priority"] = *req.Priority
				targetAuth.Attributes["priority"] = strconv.Itoa(*req.Priority)
			}
		}
		if req.Note != nil {
			trimmedNote := strings.TrimSpace(*req.Note)
			if trimmedNote == "" {
				delete(targetAuth.Metadata, "note")
				delete(targetAuth.Attributes, "note")
			} else {
				targetAuth.Metadata["note"] = trimmedNote
				targetAuth.Attributes["note"] = trimmedNote
			}
		}
		changed = true
	}

	if !changed {
		c.JSON(http.StatusBadRequest, gin.H{"error": "no fields to update"})
		return
	}

	targetAuth.UpdatedAt = time.Now()

	if _, err := h.authManager.Update(ctx, targetAuth); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to update auth: %v", err)})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": "ok"})
}

func (h *Handler) disableAuth(ctx context.Context, id string) {
	if h == nil || h.authManager == nil {
		return
	}
	id = strings.TrimSpace(id)
	if id == "" {
		return
	}
	if auth, ok := h.authManager.GetByID(id); ok {
		auth.Disabled = true
		auth.Status = coreauth.StatusDisabled
		auth.StatusMessage = "removed via management API"
		auth.UpdatedAt = time.Now()
		_, _ = h.authManager.Update(ctx, auth)
		return
	}
	authID := h.authIDForPath(id)
	if authID == "" {
		return
	}
	if auth, ok := h.authManager.GetByID(authID); ok {
		auth.Disabled = true
		auth.Status = coreauth.StatusDisabled
		auth.StatusMessage = "removed via management API"
		auth.UpdatedAt = time.Now()
		_, _ = h.authManager.Update(ctx, auth)
	}
}

func (h *Handler) deleteTokenRecord(ctx context.Context, path string) error {
	if strings.TrimSpace(path) == "" {
		return fmt.Errorf("auth path is empty")
	}
	store := h.tokenStoreWithBaseDir()
	if store == nil {
		return fmt.Errorf("token store unavailable")
	}
	return store.Delete(ctx, path)
}

func (h *Handler) tokenStoreWithBaseDir() coreauth.Store {
	if h == nil {
		return nil
	}
	store := h.tokenStore
	if store == nil {
		store = sdkAuth.GetTokenStore()
		h.tokenStore = store
	}
	if h.cfg != nil {
		if dirSetter, ok := store.(interface{ SetBaseDir(string) }); ok {
			dirSetter.SetBaseDir(h.cfg.AuthDir)
		}
	}
	return store
}

func (h *Handler) saveTokenRecord(ctx context.Context, record *coreauth.Auth) (string, error) {
	if record == nil {
		return "", fmt.Errorf("token record is nil")
	}
	store := h.tokenStoreWithBaseDir()
	if store == nil {
		return "", fmt.Errorf("token store unavailable")
	}
	if h.postAuthHook != nil {
		if err := h.postAuthHook(ctx, record); err != nil {
			return "", fmt.Errorf("post-auth hook failed: %w", err)
		}
	}
	return store.Save(ctx, record)
}

func (h *Handler) RequestAnthropicToken(c *gin.Context) {
	ctx := context.Background()
	ctx = PopulateAuthContext(ctx, c)

	fmt.Println("Initializing Claude authentication...")

	// Generate PKCE codes
	pkceCodes, err := claude.GeneratePKCECodes()
	if err != nil {
		log.Errorf("Failed to generate PKCE codes: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate PKCE codes"})
		return
	}

	// Generate random state parameter
	state, err := misc.GenerateRandomState()
	if err != nil {
		log.Errorf("Failed to generate state parameter: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate state parameter"})
		return
	}

	// Initialize Claude auth service
	anthropicAuth := claude.NewClaudeAuth(h.cfg)

	// Generate authorization URL (then override redirect_uri to reuse server port)
	authURL, state, err := anthropicAuth.GenerateAuthURL(state, pkceCodes)
	if err != nil {
		log.Errorf("Failed to generate authorization URL: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate authorization url"})
		return
	}

	RegisterOAuthSession(state, "anthropic")

	isWebUI := isWebUIRequest(c)
	var forwarder *callbackForwarder
	if isWebUI {
		targetURL, errTarget := h.managementCallbackURL("/anthropic/callback")
		if errTarget != nil {
			log.WithError(errTarget).Error("failed to compute anthropic callback target")
			c.JSON(http.StatusInternalServerError, gin.H{"error": "callback server unavailable"})
			return
		}
		var errStart error
		if forwarder, errStart = startCallbackForwarder(anthropicCallbackPort, "anthropic", targetURL); errStart != nil {
			log.WithError(errStart).Error("failed to start anthropic callback forwarder")
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to start callback server"})
			return
		}
	}

	go func() {
		if isWebUI {
			defer stopCallbackForwarderInstance(anthropicCallbackPort, forwarder)
		}

		// Helper: wait for callback file
		waitFile := filepath.Join(h.cfg.AuthDir, fmt.Sprintf(".oauth-anthropic-%s.oauth", state))
		waitForFile := func(path string, timeout time.Duration) (map[string]string, error) {
			deadline := time.Now().Add(timeout)
			for {
				if !IsOAuthSessionPending(state, "anthropic") {
					return nil, errOAuthSessionNotPending
				}
				if time.Now().After(deadline) {
					SetOAuthSessionError(state, "Timeout waiting for OAuth callback")
					return nil, fmt.Errorf("timeout waiting for OAuth callback")
				}
				data, errRead := os.ReadFile(path)
				if errRead == nil {
					var m map[string]string
					_ = json.Unmarshal(data, &m)
					_ = os.Remove(path)
					return m, nil
				}
				time.Sleep(500 * time.Millisecond)
			}
		}

		fmt.Println("Waiting for authentication callback...")
		// Wait up to 5 minutes
		resultMap, errWait := waitForFile(waitFile, 5*time.Minute)
		if errWait != nil {
			if errors.Is(errWait, errOAuthSessionNotPending) {
				return
			}
			authErr := claude.NewAuthenticationError(claude.ErrCallbackTimeout, errWait)
			log.Error(claude.GetUserFriendlyMessage(authErr))
			return
		}
		if errStr := resultMap["error"]; errStr != "" {
			oauthErr := claude.NewOAuthError(errStr, "", http.StatusBadRequest)
			log.Error(claude.GetUserFriendlyMessage(oauthErr))
			SetOAuthSessionError(state, "Bad request")
			return
		}
		if resultMap["state"] != state {
			authErr := claude.NewAuthenticationError(claude.ErrInvalidState, fmt.Errorf("expected %s, got %s", state, resultMap["state"]))
			log.Error(claude.GetUserFriendlyMessage(authErr))
			SetOAuthSessionError(state, "State code error")
			return
		}

		// Parse code (Claude may append state after '#')
		rawCode := resultMap["code"]
		code := strings.Split(rawCode, "#")[0]

		// Exchange code for tokens using internal auth service
		bundle, errExchange := anthropicAuth.ExchangeCodeForTokens(ctx, code, state, pkceCodes)
		if errExchange != nil {
			authErr := claude.NewAuthenticationError(claude.ErrCodeExchangeFailed, errExchange)
			log.Errorf("Failed to exchange authorization code for tokens: %v", authErr)
			SetOAuthSessionError(state, "Failed to exchange authorization code for tokens")
			return
		}

		// Create token storage
		tokenStorage := anthropicAuth.CreateTokenStorage(bundle)
		record := &coreauth.Auth{
			ID:       fmt.Sprintf("claude-%s.json", tokenStorage.Email),
			Provider: "claude",
			FileName: fmt.Sprintf("claude-%s.json", tokenStorage.Email),
			Storage:  tokenStorage,
			Metadata: map[string]any{"email": tokenStorage.Email},
		}
		savedPath, errSave := h.saveTokenRecord(ctx, record)
		if errSave != nil {
			log.Errorf("Failed to save authentication tokens: %v", errSave)
			SetOAuthSessionError(state, "Failed to save authentication tokens")
			return
		}

		fmt.Printf("Authentication successful! Token saved to %s\n", savedPath)
		if bundle.APIKey != "" {
			fmt.Println("API key obtained and saved")
		}
		fmt.Println("You can now use Claude services through this CLI")
		CompleteOAuthSession(state)
		CompleteOAuthSessionsByProvider("anthropic")
	}()

	c.JSON(200, gin.H{"status": "ok", "url": authURL, "state": state})
}

func (h *Handler) RequestGeminiCLIToken(c *gin.Context) {
	ctx := context.Background()
	ctx = PopulateAuthContext(ctx, c)
	proxyHTTPClient := util.SetProxy(&h.cfg.SDKConfig, &http.Client{})
	ctx = context.WithValue(ctx, oauth2.HTTPClient, proxyHTTPClient)

	// Optional project ID from query
	projectID := c.Query("project_id")

	fmt.Println("Initializing Google authentication...")

	// OAuth2 configuration using exported constants from internal/auth/gemini
	conf := &oauth2.Config{
		ClientID:     geminiAuth.ClientID,
		ClientSecret: geminiAuth.ClientSecret,
		RedirectURL:  fmt.Sprintf("http://localhost:%d/oauth2callback", geminiAuth.DefaultCallbackPort),
		Scopes:       geminiAuth.Scopes,
		Endpoint:     google.Endpoint,
	}

	// Build authorization URL and return it immediately
	state := fmt.Sprintf("gem-%d", time.Now().UnixNano())
	authURL := conf.AuthCodeURL(state, oauth2.AccessTypeOffline, oauth2.SetAuthURLParam("prompt", "consent"))

	RegisterOAuthSession(state, "gemini")

	isWebUI := isWebUIRequest(c)
	var forwarder *callbackForwarder
	if isWebUI {
		targetURL, errTarget := h.managementCallbackURL("/google/callback")
		if errTarget != nil {
			log.WithError(errTarget).Error("failed to compute gemini callback target")
			c.JSON(http.StatusInternalServerError, gin.H{"error": "callback server unavailable"})
			return
		}
		var errStart error
		if forwarder, errStart = startCallbackForwarder(geminiCallbackPort, "gemini", targetURL); errStart != nil {
			log.WithError(errStart).Error("failed to start gemini callback forwarder")
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to start callback server"})
			return
		}
	}

	go func() {
		if isWebUI {
			defer stopCallbackForwarderInstance(geminiCallbackPort, forwarder)
		}

		// Wait for callback file written by server route
		waitFile := filepath.Join(h.cfg.AuthDir, fmt.Sprintf(".oauth-gemini-%s.oauth", state))
		fmt.Println("Waiting for authentication callback...")
		deadline := time.Now().Add(5 * time.Minute)
		var authCode string
		for {
			if !IsOAuthSessionPending(state, "gemini") {
				return
			}
			if time.Now().After(deadline) {
				log.Error("oauth flow timed out")
				SetOAuthSessionError(state, "OAuth flow timed out")
				return
			}
			if data, errR := os.ReadFile(waitFile); errR == nil {
				var m map[string]string
				_ = json.Unmarshal(data, &m)
				_ = os.Remove(waitFile)
				if errStr := m["error"]; errStr != "" {
					log.Errorf("Authentication failed: %s", errStr)
					SetOAuthSessionError(state, "Authentication failed")
					return
				}
				authCode = m["code"]
				if authCode == "" {
					log.Errorf("Authentication failed: code not found")
					SetOAuthSessionError(state, "Authentication failed: code not found")
					return
				}
				break
			}
			time.Sleep(500 * time.Millisecond)
		}

		// Exchange authorization code for token
		token, err := conf.Exchange(ctx, authCode)
		if err != nil {
			log.Errorf("Failed to exchange token: %v", err)
			SetOAuthSessionError(state, "Failed to exchange token")
			return
		}

		requestedProjectID := strings.TrimSpace(projectID)

		// Create token storage (mirrors internal/auth/gemini createTokenStorage)
		authHTTPClient := conf.Client(ctx, token)
		req, errNewRequest := http.NewRequestWithContext(ctx, "GET", "https://www.googleapis.com/oauth2/v1/userinfo?alt=json", nil)
		if errNewRequest != nil {
			log.Errorf("Could not get user info: %v", errNewRequest)
			SetOAuthSessionError(state, "Could not get user info")
			return
		}
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.AccessToken))

		resp, errDo := authHTTPClient.Do(req)
		if errDo != nil {
			log.Errorf("Failed to execute request: %v", errDo)
			SetOAuthSessionError(state, "Failed to execute request")
			return
		}
		defer func() {
			if errClose := resp.Body.Close(); errClose != nil {
				log.Printf("warn: failed to close response body: %v", errClose)
			}
		}()

		bodyBytes, _ := io.ReadAll(resp.Body)
		if resp.StatusCode < 200 || resp.StatusCode >= 300 {
			log.Errorf("Get user info request failed with status %d: %s", resp.StatusCode, string(bodyBytes))
			SetOAuthSessionError(state, fmt.Sprintf("Get user info request failed with status %d", resp.StatusCode))
			return
		}

		email := gjson.GetBytes(bodyBytes, "email").String()
		if email != "" {
			fmt.Printf("Authenticated user email: %s\n", email)
		} else {
			fmt.Println("Failed to get user email from token")
		}

		// Marshal/unmarshal oauth2.Token to generic map and enrich fields
		var ifToken map[string]any
		jsonData, _ := json.Marshal(token)
		if errUnmarshal := json.Unmarshal(jsonData, &ifToken); errUnmarshal != nil {
			log.Errorf("Failed to unmarshal token: %v", errUnmarshal)
			SetOAuthSessionError(state, "Failed to unmarshal token")
			return
		}

		ifToken["token_uri"] = "https://oauth2.googleapis.com/token"
		ifToken["client_id"] = geminiAuth.ClientID
		ifToken["client_secret"] = geminiAuth.ClientSecret
		ifToken["scopes"] = geminiAuth.Scopes
		ifToken["universe_domain"] = "googleapis.com"

		ts := geminiAuth.GeminiTokenStorage{
			Token:     ifToken,
			ProjectID: requestedProjectID,
			Email:     email,
			Auto:      requestedProjectID == "",
		}

		// Initialize authenticated HTTP client via GeminiAuth to honor proxy settings
		gemAuth := geminiAuth.NewGeminiAuth()
		gemClient, errGetClient := gemAuth.GetAuthenticatedClient(ctx, &ts, h.cfg, &geminiAuth.WebLoginOptions{
			NoBrowser: true,
		})
		if errGetClient != nil {
			log.Errorf("failed to get authenticated client: %v", errGetClient)
			SetOAuthSessionError(state, "Failed to get authenticated client")
			return
		}
		fmt.Println("Authentication successful.")

		if strings.EqualFold(requestedProjectID, "ALL") {
			ts.Auto = false
			projects, errAll := onboardAllGeminiProjects(ctx, gemClient, &ts)
			if errAll != nil {
				log.Errorf("Failed to complete Gemini CLI onboarding: %v", errAll)
				SetOAuthSessionError(state, fmt.Sprintf("Failed to complete Gemini CLI onboarding: %v", errAll))
				return
			}
			if errVerify := ensureGeminiProjectsEnabled(ctx, gemClient, projects); errVerify != nil {
				log.Errorf("Failed to verify Cloud AI API status: %v", errVerify)
				SetOAuthSessionError(state, fmt.Sprintf("Failed to verify Cloud AI API status: %v", errVerify))
				return
			}
			ts.ProjectID = strings.Join(projects, ",")
			ts.Checked = true
		} else if strings.EqualFold(requestedProjectID, "GOOGLE_ONE") {
			ts.Auto = false
			if errSetup := performGeminiCLISetup(ctx, gemClient, &ts, ""); errSetup != nil {
				log.Errorf("Google One auto-discovery failed: %v", errSetup)
				SetOAuthSessionError(state, fmt.Sprintf("Google One auto-discovery failed: %v", errSetup))
				return
			}
			if strings.TrimSpace(ts.ProjectID) == "" {
				log.Error("Google One auto-discovery returned empty project ID")
				SetOAuthSessionError(state, "Google One auto-discovery returned empty project ID")
				return
			}
			isChecked, errCheck := checkCloudAPIIsEnabled(ctx, gemClient, ts.ProjectID)
			if errCheck != nil {
				log.Errorf("Failed to verify Cloud AI API status: %v", errCheck)
				SetOAuthSessionError(state, fmt.Sprintf("Failed to verify Cloud AI API status: %v", errCheck))
				return
			}
			ts.Checked = isChecked
			if !isChecked {
				log.Error("Cloud AI API is not enabled for the auto-discovered project")
				SetOAuthSessionError(state, fmt.Sprintf("Cloud AI API not enabled for project %s", ts.ProjectID))
				return
			}
		} else {
			if errEnsure := ensureGeminiProjectAndOnboard(ctx, gemClient, &ts, requestedProjectID); errEnsure != nil {
				log.Errorf("Failed to complete Gemini CLI onboarding: %v", errEnsure)
				SetOAuthSessionError(state, fmt.Sprintf("Failed to complete Gemini CLI onboarding: %v", errEnsure))
				return
			}

			if strings.TrimSpace(ts.ProjectID) == "" {
				log.Error("Onboarding did not return a project ID")
				SetOAuthSessionError(state, "Failed to resolve project ID")
				return
			}

			isChecked, errCheck := checkCloudAPIIsEnabled(ctx, gemClient, ts.ProjectID)
			if errCheck != nil {
				log.Errorf("Failed to verify Cloud AI API status: %v", errCheck)
				SetOAuthSessionError(state, fmt.Sprintf("Failed to verify Cloud AI API status: %v", errCheck))
				return
			}
			ts.Checked = isChecked
			if !isChecked {
				log.Error("Cloud AI API is not enabled for the selected project")
				SetOAuthSessionError(state, fmt.Sprintf("Cloud AI API not enabled for project %s", ts.ProjectID))
				return
			}
		}

		recordMetadata := map[string]any{
			"email":      ts.Email,
			"project_id": ts.ProjectID,
			"auto":       ts.Auto,
			"checked":    ts.Checked,
		}

		fileName := geminiAuth.CredentialFileName(ts.Email, ts.ProjectID, true)
		record := &coreauth.Auth{
			ID:       fileName,
			Provider: "gemini",
			FileName: fileName,
			Storage:  &ts,
			Metadata: recordMetadata,
		}
		savedPath, errSave := h.saveTokenRecord(ctx, record)
		if errSave != nil {
			log.Errorf("Failed to save token to file: %v", errSave)
			SetOAuthSessionError(state, "Failed to save token to file")
			return
		}

		CompleteOAuthSession(state)
		CompleteOAuthSessionsByProvider("gemini")
		fmt.Printf("You can now use Gemini CLI services through this CLI; token saved to %s\n", savedPath)
	}()

	c.JSON(200, gin.H{"status": "ok", "url": authURL, "state": state})
}

func (h *Handler) RequestCodexToken(c *gin.Context) {
	ctx := context.Background()
	ctx = PopulateAuthContext(ctx, c)

	fmt.Println("Initializing Codex authentication...")

	// Generate PKCE codes
	pkceCodes, err := codex.GeneratePKCECodes()
	if err != nil {
		log.Errorf("Failed to generate PKCE codes: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate PKCE codes"})
		return
	}

	// Generate random state parameter
	state, err := misc.GenerateRandomState()
	if err != nil {
		log.Errorf("Failed to generate state parameter: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate state parameter"})
		return
	}

	// Initialize Codex auth service
	openaiAuth := codex.NewCodexAuth(h.cfg)

	// Generate authorization URL
	authURL, err := openaiAuth.GenerateAuthURL(state, pkceCodes)
	if err != nil {
		log.Errorf("Failed to generate authorization URL: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate authorization url"})
		return
	}

	RegisterOAuthSession(state, "codex")

	isWebUI := isWebUIRequest(c)
	var forwarder *callbackForwarder
	if isWebUI {
		targetURL, errTarget := h.managementCallbackURL("/codex/callback")
		if errTarget != nil {
			log.WithError(errTarget).Error("failed to compute codex callback target")
			c.JSON(http.StatusInternalServerError, gin.H{"error": "callback server unavailable"})
			return
		}
		var errStart error
		if forwarder, errStart = startCallbackForwarder(codexCallbackPort, "codex", targetURL); errStart != nil {
			log.WithError(errStart).Error("failed to start codex callback forwarder")
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to start callback server"})
			return
		}
	}

	go func() {
		if isWebUI {
			defer stopCallbackForwarderInstance(codexCallbackPort, forwarder)
		}

		// Wait for callback file
		waitFile := filepath.Join(h.cfg.AuthDir, fmt.Sprintf(".oauth-codex-%s.oauth", state))
		deadline := time.Now().Add(5 * time.Minute)
		var code string
		for {
			if !IsOAuthSessionPending(state, "codex") {
				return
			}
			if time.Now().After(deadline) {
				authErr := codex.NewAuthenticationEr
Download .txt
gitextract_9oqd9is2/

├── .dockerignore
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.md
│   └── workflows/
│       ├── docker-image.yml
│       ├── pr-path-guard.yml
│       ├── pr-test-build.yml
│       └── release.yaml
├── .gitignore
├── .goreleaser.yml
├── Dockerfile
├── LICENSE
├── README.md
├── README_CN.md
├── auths/
│   └── .gitkeep
├── cmd/
│   ├── fetch_antigravity_models/
│   │   └── main.go
│   └── server/
│       └── main.go
├── config.example.yaml
├── docker-build.ps1
├── docker-build.sh
├── docker-compose.yml
├── examples/
│   ├── custom-provider/
│   │   └── main.go
│   ├── http-request/
│   │   └── main.go
│   └── translator/
│       └── main.go
├── go.mod
├── go.sum
├── internal/
│   ├── access/
│   │   ├── config_access/
│   │   │   └── provider.go
│   │   └── reconcile.go
│   ├── api/
│   │   ├── handlers/
│   │   │   └── management/
│   │   │       ├── api_tools.go
│   │   │       ├── api_tools_test.go
│   │   │       ├── auth_files.go
│   │   │       ├── auth_files_delete_test.go
│   │   │       ├── config_basic.go
│   │   │       ├── config_lists.go
│   │   │       ├── handler.go
│   │   │       ├── logs.go
│   │   │       ├── model_definitions.go
│   │   │       ├── oauth_callback.go
│   │   │       ├── oauth_sessions.go
│   │   │       ├── quota.go
│   │   │       ├── test_store_test.go
│   │   │       ├── usage.go
│   │   │       └── vertex_import.go
│   │   ├── middleware/
│   │   │   ├── request_logging.go
│   │   │   ├── request_logging_test.go
│   │   │   ├── response_writer.go
│   │   │   └── response_writer_test.go
│   │   ├── modules/
│   │   │   ├── amp/
│   │   │   │   ├── amp.go
│   │   │   │   ├── amp_test.go
│   │   │   │   ├── fallback_handlers.go
│   │   │   │   ├── fallback_handlers_test.go
│   │   │   │   ├── gemini_bridge.go
│   │   │   │   ├── gemini_bridge_test.go
│   │   │   │   ├── model_mapping.go
│   │   │   │   ├── model_mapping_test.go
│   │   │   │   ├── proxy.go
│   │   │   │   ├── proxy_test.go
│   │   │   │   ├── response_rewriter.go
│   │   │   │   ├── response_rewriter_test.go
│   │   │   │   ├── routes.go
│   │   │   │   ├── routes_test.go
│   │   │   │   ├── secret.go
│   │   │   │   └── secret_test.go
│   │   │   └── modules.go
│   │   ├── server.go
│   │   └── server_test.go
│   ├── auth/
│   │   ├── antigravity/
│   │   │   ├── auth.go
│   │   │   ├── constants.go
│   │   │   └── filename.go
│   │   ├── claude/
│   │   │   ├── anthropic.go
│   │   │   ├── anthropic_auth.go
│   │   │   ├── errors.go
│   │   │   ├── html_templates.go
│   │   │   ├── oauth_server.go
│   │   │   ├── pkce.go
│   │   │   ├── token.go
│   │   │   └── utls_transport.go
│   │   ├── codex/
│   │   │   ├── errors.go
│   │   │   ├── filename.go
│   │   │   ├── html_templates.go
│   │   │   ├── jwt_parser.go
│   │   │   ├── oauth_server.go
│   │   │   ├── openai.go
│   │   │   ├── openai_auth.go
│   │   │   ├── openai_auth_test.go
│   │   │   ├── pkce.go
│   │   │   └── token.go
│   │   ├── empty/
│   │   │   └── token.go
│   │   ├── gemini/
│   │   │   ├── gemini_auth.go
│   │   │   └── gemini_token.go
│   │   ├── iflow/
│   │   │   ├── cookie_helpers.go
│   │   │   ├── iflow_auth.go
│   │   │   ├── iflow_token.go
│   │   │   └── oauth_server.go
│   │   ├── kimi/
│   │   │   ├── kimi.go
│   │   │   └── token.go
│   │   ├── models.go
│   │   ├── qwen/
│   │   │   ├── qwen_auth.go
│   │   │   └── qwen_token.go
│   │   └── vertex/
│   │       ├── keyutil.go
│   │       └── vertex_credentials.go
│   ├── browser/
│   │   └── browser.go
│   ├── buildinfo/
│   │   └── buildinfo.go
│   ├── cache/
│   │   ├── signature_cache.go
│   │   └── signature_cache_test.go
│   ├── cmd/
│   │   ├── anthropic_login.go
│   │   ├── antigravity_login.go
│   │   ├── auth_manager.go
│   │   ├── iflow_cookie.go
│   │   ├── iflow_login.go
│   │   ├── kimi_login.go
│   │   ├── login.go
│   │   ├── openai_device_login.go
│   │   ├── openai_login.go
│   │   ├── qwen_login.go
│   │   ├── run.go
│   │   └── vertex_import.go
│   ├── config/
│   │   ├── codex_websocket_header_defaults_test.go
│   │   ├── config.go
│   │   ├── oauth_model_alias_test.go
│   │   ├── sdk_config.go
│   │   └── vertex_compat.go
│   ├── constant/
│   │   └── constant.go
│   ├── interfaces/
│   │   ├── api_handler.go
│   │   ├── client_models.go
│   │   ├── error_message.go
│   │   └── types.go
│   ├── logging/
│   │   ├── gin_logger.go
│   │   ├── gin_logger_test.go
│   │   ├── global_logger.go
│   │   ├── log_dir_cleaner.go
│   │   ├── log_dir_cleaner_test.go
│   │   ├── request_logger.go
│   │   └── requestid.go
│   ├── managementasset/
│   │   └── updater.go
│   ├── misc/
│   │   ├── claude_code_instructions.go
│   │   ├── claude_code_instructions.txt
│   │   ├── copy-example-config.go
│   │   ├── credentials.go
│   │   ├── header_utils.go
│   │   ├── mime-type.go
│   │   └── oauth.go
│   ├── registry/
│   │   ├── model_definitions.go
│   │   ├── model_registry.go
│   │   ├── model_registry_cache_test.go
│   │   ├── model_registry_hook_test.go
│   │   ├── model_registry_safety_test.go
│   │   ├── model_updater.go
│   │   └── models/
│   │       └── models.json
│   ├── runtime/
│   │   ├── executor/
│   │   │   ├── aistudio_executor.go
│   │   │   ├── antigravity_executor.go
│   │   │   ├── antigravity_executor_buildrequest_test.go
│   │   │   ├── cache_helpers.go
│   │   │   ├── caching_verify_test.go
│   │   │   ├── claude_executor.go
│   │   │   ├── claude_executor_test.go
│   │   │   ├── cloak_obfuscate.go
│   │   │   ├── cloak_utils.go
│   │   │   ├── codex_executor.go
│   │   │   ├── codex_executor_cache_test.go
│   │   │   ├── codex_executor_retry_test.go
│   │   │   ├── codex_websockets_executor.go
│   │   │   ├── codex_websockets_executor_test.go
│   │   │   ├── gemini_cli_executor.go
│   │   │   ├── gemini_executor.go
│   │   │   ├── gemini_vertex_executor.go
│   │   │   ├── iflow_executor.go
│   │   │   ├── iflow_executor_test.go
│   │   │   ├── kimi_executor.go
│   │   │   ├── kimi_executor_test.go
│   │   │   ├── logging_helpers.go
│   │   │   ├── openai_compat_executor.go
│   │   │   ├── openai_compat_executor_compact_test.go
│   │   │   ├── payload_helpers.go
│   │   │   ├── proxy_helpers.go
│   │   │   ├── proxy_helpers_test.go
│   │   │   ├── qwen_executor.go
│   │   │   ├── qwen_executor_test.go
│   │   │   ├── thinking_providers.go
│   │   │   ├── token_helpers.go
│   │   │   ├── usage_helpers.go
│   │   │   ├── usage_helpers_test.go
│   │   │   ├── user_id_cache.go
│   │   │   └── user_id_cache_test.go
│   │   └── geminicli/
│   │       └── state.go
│   ├── store/
│   │   ├── gitstore.go
│   │   ├── objectstore.go
│   │   └── postgresstore.go
│   ├── thinking/
│   │   ├── apply.go
│   │   ├── apply_user_defined_test.go
│   │   ├── convert.go
│   │   ├── errors.go
│   │   ├── provider/
│   │   │   ├── antigravity/
│   │   │   │   └── apply.go
│   │   │   ├── claude/
│   │   │   │   └── apply.go
│   │   │   ├── codex/
│   │   │   │   └── apply.go
│   │   │   ├── gemini/
│   │   │   │   └── apply.go
│   │   │   ├── geminicli/
│   │   │   │   └── apply.go
│   │   │   ├── iflow/
│   │   │   │   └── apply.go
│   │   │   ├── kimi/
│   │   │   │   ├── apply.go
│   │   │   │   └── apply_test.go
│   │   │   └── openai/
│   │   │       └── apply.go
│   │   ├── strip.go
│   │   ├── suffix.go
│   │   ├── text.go
│   │   ├── types.go
│   │   └── validate.go
│   ├── translator/
│   │   ├── antigravity/
│   │   │   ├── claude/
│   │   │   │   ├── antigravity_claude_request.go
│   │   │   │   ├── antigravity_claude_request_test.go
│   │   │   │   ├── antigravity_claude_response.go
│   │   │   │   ├── antigravity_claude_response_test.go
│   │   │   │   └── init.go
│   │   │   ├── gemini/
│   │   │   │   ├── antigravity_gemini_request.go
│   │   │   │   ├── antigravity_gemini_request_test.go
│   │   │   │   ├── antigravity_gemini_response.go
│   │   │   │   ├── antigravity_gemini_response_test.go
│   │   │   │   └── init.go
│   │   │   └── openai/
│   │   │       ├── chat-completions/
│   │   │       │   ├── antigravity_openai_request.go
│   │   │       │   ├── antigravity_openai_response.go
│   │   │       │   ├── antigravity_openai_response_test.go
│   │   │       │   └── init.go
│   │   │       └── responses/
│   │   │           ├── antigravity_openai-responses_request.go
│   │   │           ├── antigravity_openai-responses_response.go
│   │   │           └── init.go
│   │   ├── claude/
│   │   │   ├── gemini/
│   │   │   │   ├── claude_gemini_request.go
│   │   │   │   ├── claude_gemini_response.go
│   │   │   │   └── init.go
│   │   │   ├── gemini-cli/
│   │   │   │   ├── claude_gemini-cli_request.go
│   │   │   │   ├── claude_gemini-cli_response.go
│   │   │   │   └── init.go
│   │   │   └── openai/
│   │   │       ├── chat-completions/
│   │   │       │   ├── claude_openai_request.go
│   │   │       │   ├── claude_openai_request_test.go
│   │   │       │   ├── claude_openai_response.go
│   │   │       │   └── init.go
│   │   │       └── responses/
│   │   │           ├── claude_openai-responses_request.go
│   │   │           ├── claude_openai-responses_response.go
│   │   │           └── init.go
│   │   ├── codex/
│   │   │   ├── claude/
│   │   │   │   ├── codex_claude_request.go
│   │   │   │   ├── codex_claude_request_test.go
│   │   │   │   ├── codex_claude_response.go
│   │   │   │   └── init.go
│   │   │   ├── gemini/
│   │   │   │   ├── codex_gemini_request.go
│   │   │   │   ├── codex_gemini_response.go
│   │   │   │   └── init.go
│   │   │   ├── gemini-cli/
│   │   │   │   ├── codex_gemini-cli_request.go
│   │   │   │   ├── codex_gemini-cli_response.go
│   │   │   │   └── init.go
│   │   │   └── openai/
│   │   │       ├── chat-completions/
│   │   │       │   ├── codex_openai_request.go
│   │   │       │   ├── codex_openai_request_test.go
│   │   │       │   ├── codex_openai_response.go
│   │   │       │   ├── codex_openai_response_test.go
│   │   │       │   └── init.go
│   │   │       └── responses/
│   │   │           ├── codex_openai-responses_request.go
│   │   │           ├── codex_openai-responses_request_test.go
│   │   │           ├── codex_openai-responses_response.go
│   │   │           └── init.go
│   │   ├── gemini/
│   │   │   ├── claude/
│   │   │   │   ├── gemini_claude_request.go
│   │   │   │   ├── gemini_claude_request_test.go
│   │   │   │   ├── gemini_claude_response.go
│   │   │   │   └── init.go
│   │   │   ├── common/
│   │   │   │   └── safety.go
│   │   │   ├── gemini/
│   │   │   │   ├── gemini_gemini_request.go
│   │   │   │   ├── gemini_gemini_request_test.go
│   │   │   │   ├── gemini_gemini_response.go
│   │   │   │   └── init.go
│   │   │   ├── gemini-cli/
│   │   │   │   ├── gemini_gemini-cli_request.go
│   │   │   │   ├── gemini_gemini-cli_response.go
│   │   │   │   └── init.go
│   │   │   └── openai/
│   │   │       ├── chat-completions/
│   │   │       │   ├── gemini_openai_request.go
│   │   │       │   ├── gemini_openai_response.go
│   │   │       │   └── init.go
│   │   │       └── responses/
│   │   │           ├── gemini_openai-responses_request.go
│   │   │           ├── gemini_openai-responses_response.go
│   │   │           ├── gemini_openai-responses_response_test.go
│   │   │           └── init.go
│   │   ├── gemini-cli/
│   │   │   ├── claude/
│   │   │   │   ├── gemini-cli_claude_request.go
│   │   │   │   ├── gemini-cli_claude_request_test.go
│   │   │   │   ├── gemini-cli_claude_response.go
│   │   │   │   └── init.go
│   │   │   ├── gemini/
│   │   │   │   ├── gemini-cli_gemini_request.go
│   │   │   │   ├── gemini-cli_gemini_response.go
│   │   │   │   └── init.go
│   │   │   └── openai/
│   │   │       ├── chat-completions/
│   │   │       │   ├── gemini-cli_openai_request.go
│   │   │       │   ├── gemini-cli_openai_response.go
│   │   │       │   └── init.go
│   │   │       └── responses/
│   │   │           ├── gemini-cli_openai-responses_request.go
│   │   │           ├── gemini-cli_openai-responses_response.go
│   │   │           └── init.go
│   │   ├── init.go
│   │   ├── openai/
│   │   │   ├── claude/
│   │   │   │   ├── init.go
│   │   │   │   ├── openai_claude_request.go
│   │   │   │   ├── openai_claude_request_test.go
│   │   │   │   └── openai_claude_response.go
│   │   │   ├── gemini/
│   │   │   │   ├── init.go
│   │   │   │   ├── openai_gemini_request.go
│   │   │   │   └── openai_gemini_response.go
│   │   │   ├── gemini-cli/
│   │   │   │   ├── init.go
│   │   │   │   ├── openai_gemini_request.go
│   │   │   │   └── openai_gemini_response.go
│   │   │   └── openai/
│   │   │       ├── chat-completions/
│   │   │       │   ├── init.go
│   │   │       │   ├── openai_openai_request.go
│   │   │       │   └── openai_openai_response.go
│   │   │       └── responses/
│   │   │           ├── init.go
│   │   │           ├── openai_openai-responses_request.go
│   │   │           └── openai_openai-responses_response.go
│   │   └── translator/
│   │       └── translator.go
│   ├── tui/
│   │   ├── app.go
│   │   ├── auth_tab.go
│   │   ├── browser.go
│   │   ├── client.go
│   │   ├── config_tab.go
│   │   ├── dashboard.go
│   │   ├── i18n.go
│   │   ├── keys_tab.go
│   │   ├── loghook.go
│   │   ├── logs_tab.go
│   │   ├── oauth_tab.go
│   │   ├── styles.go
│   │   └── usage_tab.go
│   ├── usage/
│   │   └── logger_plugin.go
│   ├── util/
│   │   ├── claude_model.go
│   │   ├── claude_model_test.go
│   │   ├── claude_tool_id.go
│   │   ├── gemini_schema.go
│   │   ├── gemini_schema_test.go
│   │   ├── header_helpers.go
│   │   ├── image.go
│   │   ├── provider.go
│   │   ├── proxy.go
│   │   ├── sanitize_test.go
│   │   ├── ssh_helper.go
│   │   ├── translator.go
│   │   └── util.go
│   ├── watcher/
│   │   ├── clients.go
│   │   ├── config_reload.go
│   │   ├── diff/
│   │   │   ├── auth_diff.go
│   │   │   ├── config_diff.go
│   │   │   ├── config_diff_test.go
│   │   │   ├── model_hash.go
│   │   │   ├── model_hash_test.go
│   │   │   ├── models_summary.go
│   │   │   ├── oauth_excluded.go
│   │   │   ├── oauth_excluded_test.go
│   │   │   ├── oauth_model_alias.go
│   │   │   ├── openai_compat.go
│   │   │   └── openai_compat_test.go
│   │   ├── dispatcher.go
│   │   ├── events.go
│   │   ├── synthesizer/
│   │   │   ├── config.go
│   │   │   ├── config_test.go
│   │   │   ├── context.go
│   │   │   ├── file.go
│   │   │   ├── file_test.go
│   │   │   ├── helpers.go
│   │   │   ├── helpers_test.go
│   │   │   └── interface.go
│   │   ├── watcher.go
│   │   └── watcher_test.go
│   └── wsrelay/
│       ├── http.go
│       ├── manager.go
│       ├── message.go
│       └── session.go
├── sdk/
│   ├── access/
│   │   ├── errors.go
│   │   ├── manager.go
│   │   ├── registry.go
│   │   └── types.go
│   ├── api/
│   │   ├── handlers/
│   │   │   ├── claude/
│   │   │   │   └── code_handlers.go
│   │   │   ├── gemini/
│   │   │   │   ├── gemini-cli_handlers.go
│   │   │   │   └── gemini_handlers.go
│   │   │   ├── handlers.go
│   │   │   ├── handlers_error_response_test.go
│   │   │   ├── handlers_request_details_test.go
│   │   │   ├── handlers_stream_bootstrap_test.go
│   │   │   ├── header_filter.go
│   │   │   ├── header_filter_test.go
│   │   │   ├── openai/
│   │   │   │   ├── openai_handlers.go
│   │   │   │   ├── openai_responses_compact_test.go
│   │   │   │   ├── openai_responses_handlers.go
│   │   │   │   ├── openai_responses_handlers_stream_error_test.go
│   │   │   │   ├── openai_responses_websocket.go
│   │   │   │   └── openai_responses_websocket_test.go
│   │   │   ├── openai_responses_stream_error.go
│   │   │   ├── openai_responses_stream_error_test.go
│   │   │   └── stream_forwarder.go
│   │   ├── management.go
│   │   └── options.go
│   ├── auth/
│   │   ├── antigravity.go
│   │   ├── claude.go
│   │   ├── codex.go
│   │   ├── codex_device.go
│   │   ├── errors.go
│   │   ├── filestore.go
│   │   ├── filestore_test.go
│   │   ├── gemini.go
│   │   ├── iflow.go
│   │   ├── interfaces.go
│   │   ├── kimi.go
│   │   ├── manager.go
│   │   ├── qwen.go
│   │   ├── refresh_registry.go
│   │   └── store_registry.go
│   ├── cliproxy/
│   │   ├── auth/
│   │   │   ├── api_key_model_alias_test.go
│   │   │   ├── conductor.go
│   │   │   ├── conductor_availability_test.go
│   │   │   ├── conductor_executor_replace_test.go
│   │   │   ├── conductor_overrides_test.go
│   │   │   ├── conductor_scheduler_refresh_test.go
│   │   │   ├── conductor_update_test.go
│   │   │   ├── errors.go
│   │   │   ├── oauth_model_alias.go
│   │   │   ├── oauth_model_alias_test.go
│   │   │   ├── openai_compat_pool_test.go
│   │   │   ├── persist_policy.go
│   │   │   ├── persist_policy_test.go
│   │   │   ├── scheduler.go
│   │   │   ├── scheduler_benchmark_test.go
│   │   │   ├── scheduler_test.go
│   │   │   ├── selector.go
│   │   │   ├── selector_test.go
│   │   │   ├── status.go
│   │   │   ├── store.go
│   │   │   ├── types.go
│   │   │   └── types_test.go
│   │   ├── builder.go
│   │   ├── executor/
│   │   │   ├── context.go
│   │   │   └── types.go
│   │   ├── model_registry.go
│   │   ├── pipeline/
│   │   │   └── context.go
│   │   ├── pprof_server.go
│   │   ├── providers.go
│   │   ├── rtprovider.go
│   │   ├── rtprovider_test.go
│   │   ├── service.go
│   │   ├── service_codex_executor_binding_test.go
│   │   ├── service_excluded_models_test.go
│   │   ├── service_oauth_model_alias_test.go
│   │   ├── types.go
│   │   ├── usage/
│   │   │   └── manager.go
│   │   └── watcher.go
│   ├── config/
│   │   └── config.go
│   ├── logging/
│   │   └── request_logger.go
│   ├── proxyutil/
│   │   ├── proxy.go
│   │   └── proxy_test.go
│   └── translator/
│       ├── builtin/
│       │   └── builtin.go
│       ├── format.go
│       ├── formats.go
│       ├── helpers.go
│       ├── pipeline.go
│       ├── registry.go
│       └── types.go
└── test/
    ├── amp_management_test.go
    ├── builtin_tools_translation_test.go
    └── thinking_conversion_test.go
Download .txt
Showing preview only (423K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (3936 symbols across 423 files)

FILE: cmd/fetch_antigravity_models/main.go
  constant antigravityBaseURLDaily (line 37) | antigravityBaseURLDaily        = "https://daily-cloudcode-pa.googleapis....
  constant antigravitySandboxBaseURLDaily (line 38) | antigravitySandboxBaseURLDaily = "https://daily-cloudcode-pa.sandbox.goo...
  constant antigravityBaseURLProd (line 39) | antigravityBaseURLProd         = "https://cloudcode-pa.googleapis.com"
  constant antigravityModelsPath (line 40) | antigravityModelsPath          = "/v1internal:fetchAvailableModels"
  function init (line 43) | func init() {
  type modelOutput (line 49) | type modelOutput struct
  type modelEntry (line 54) | type modelEntry struct
  function main (line 66) | func main() {
  function fetchModels (line 162) | func fetchModels(ctx context.Context, auth *coreauth.Auth) []modelEntry {
  function metaStringValue (line 261) | func metaStringValue(m map[string]interface{}, key string) string {

FILE: cmd/server/main.go
  function init (line 46) | func init() {
  function main (line 56) | func main() {

FILE: examples/custom-provider/main.go
  constant providerKey (line 39) | providerKey = "myprov"
  constant fOpenAI (line 42) | fOpenAI = sdktr.Format("openai.chat")
  constant fMyProv (line 45) | fMyProv = sdktr.Format("myprov.chat")
  function init (line 51) | func init() {
  type MyExecutor (line 67) | type MyExecutor struct
    method Identifier (line 70) | func (MyExecutor) Identifier() string { return providerKey }
    method PrepareRequest (line 82) | func (MyExecutor) PrepareRequest(req *http.Request, a *coreauth.Auth) ...
    method Execute (line 115) | func (MyExecutor) Execute(ctx context.Context, a *coreauth.Auth, req c...
    method HttpRequest (line 143) | func (MyExecutor) HttpRequest(ctx context.Context, a *coreauth.Auth, r...
    method CountTokens (line 158) | func (MyExecutor) CountTokens(context.Context, *coreauth.Auth, clipexe...
    method ExecuteStream (line 162) | func (MyExecutor) ExecuteStream(ctx context.Context, a *coreauth.Auth,...
    method Refresh (line 171) | func (MyExecutor) Refresh(ctx context.Context, a *coreauth.Auth) (*cor...
  function buildHTTPClient (line 94) | func buildHTTPClient(a *coreauth.Auth) *http.Client {
  function upstreamEndpoint (line 105) | func upstreamEndpoint(a *coreauth.Auth) string {
  function main (line 175) | func main() {

FILE: examples/http-request/main.go
  constant providerKey (line 24) | providerKey = "echo"
  type EchoExecutor (line 27) | type EchoExecutor struct
    method Identifier (line 29) | func (EchoExecutor) Identifier() string { return providerKey }
    method PrepareRequest (line 31) | func (EchoExecutor) PrepareRequest(req *http.Request, auth *coreauth.A...
    method HttpRequest (line 43) | func (EchoExecutor) HttpRequest(ctx context.Context, auth *coreauth.Au...
    method Execute (line 57) | func (EchoExecutor) Execute(context.Context, *coreauth.Auth, clipexec....
    method ExecuteStream (line 61) | func (EchoExecutor) ExecuteStream(context.Context, *coreauth.Auth, cli...
    method Refresh (line 65) | func (EchoExecutor) Refresh(context.Context, *coreauth.Auth) (*coreaut...
    method CountTokens (line 69) | func (EchoExecutor) CountTokens(context.Context, *coreauth.Auth, clipe...
  function main (line 73) | func main() {

FILE: examples/translator/main.go
  function main (line 11) | func main() {

FILE: internal/access/config_access/provider.go
  function Register (line 13) | func Register(cfg *sdkconfig.SDKConfig) {
  type provider (line 31) | type provider struct
    method Identifier (line 48) | func (p *provider) Identifier() string {
    method Authenticate (line 55) | func (p *provider) Authenticate(_ context.Context, r *http.Request) (*...
  function newProvider (line 36) | func newProvider(name string, keys []string) *provider {
  function extractBearerToken (line 106) | func extractBearerToken(header string) string {
  function normalizeKeys (line 120) | func normalizeKeys(keys []string) []string {

FILE: internal/access/reconcile.go
  function ReconcileProviders (line 19) | func ReconcileProviders(oldCfg, newCfg *config.Config, existing []sdkacc...
  function ApplyAccessProviders (line 82) | func ApplyAccessProviders(manager *sdkaccess.Manager, oldCfg, newCfg *co...
  function identifierFromProvider (line 107) | func identifierFromProvider(provider sdkaccess.Provider) string {
  function providerInstanceEqual (line 114) | func providerInstanceEqual(a, b sdkaccess.Provider) bool {

FILE: internal/api/handlers/management/api_tools.go
  constant defaultAPICallTimeout (line 22) | defaultAPICallTimeout = 60 * time.Second
  constant geminiOAuthClientID (line 25) | geminiOAuthClientID     = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j...
  constant geminiOAuthClientSecret (line 26) | geminiOAuthClientSecret = "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"
  constant antigravityOAuthClientID (line 36) | antigravityOAuthClientID     = "1071006060591-tmhssin2h21lcre235vtolojh4...
  constant antigravityOAuthClientSecret (line 37) | antigravityOAuthClientSecret = "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"
  type apiCallRequest (line 42) | type apiCallRequest struct
  type apiCallResponse (line 52) | type apiCallResponse struct
  method APICall (line 108) | func (h *Handler) APICall(c *gin.Context) {
  function firstNonEmptyString (line 218) | func firstNonEmptyString(values ...*string) string {
  function tokenValueForAuth (line 230) | func tokenValueForAuth(auth *coreauth.Auth) string {
  method resolveTokenForAuth (line 250) | func (h *Handler) resolveTokenForAuth(ctx context.Context, auth *coreaut...
  method refreshGeminiOAuthAccessToken (line 268) | func (h *Handler) refreshGeminiOAuthAccessToken(ctx context.Context, aut...
  method refreshAntigravityOAuthAccessToken (line 338) | func (h *Handler) refreshAntigravityOAuthAccessToken(ctx context.Context...
  function antigravityTokenNeedsRefresh (line 437) | func antigravityTokenNeedsRefresh(metadata map[string]any) bool {
  function int64Value (line 458) | func int64Value(raw any) int64 {
  function geminiOAuthMetadata (line 493) | func geminiOAuthMetadata(auth *coreauth.Auth) (map[string]any, func(map[...
  function stringValue (line 511) | func stringValue(metadata map[string]any, key string) string {
  function cloneMap (line 521) | func cloneMap(in map[string]any) map[string]any {
  function buildOAuthTokenMap (line 532) | func buildOAuthTokenMap(base map[string]any, tok *oauth2.Token) map[stri...
  function buildOAuthTokenFields (line 551) | func buildOAuthTokenFields(tok *oauth2.Token, merged map[string]any) map...
  function tokenValueFromMetadata (line 571) | func tokenValueFromMetadata(metadata map[string]any) string {
  method authByIndex (line 615) | func (h *Handler) authByIndex(authIndex string) *coreauth.Auth {
  method apiCallTransport (line 633) | func (h *Handler) apiCallTransport(auth *coreauth.Auth) http.RoundTripper {
  function buildProxyTransport (line 661) | func buildProxyTransport(proxyStr string) *http.Transport {

FILE: internal/api/handlers/management/api_tools_test.go
  function TestAPICallTransportDirectBypassesGlobalProxy (line 13) | func TestAPICallTransportDirectBypassesGlobalProxy(t *testing.T) {
  function TestAPICallTransportInvalidAuthFallsBackToGlobalProxy (line 32) | func TestAPICallTransportInvalidAuthFallsBackToGlobalProxy(t *testing.T) {
  function TestAuthByIndexDistinguishesSharedAPIKeysAcrossProviders (line 61) | func TestAuthByIndexDistinguishesSharedAPIKeysAcrossProviders(t *testing...

FILE: internal/api/handlers/management/auth_files.go
  constant anthropicCallbackPort (line 46) | anthropicCallbackPort = 54545
  constant geminiCallbackPort (line 47) | geminiCallbackPort    = 8085
  constant codexCallbackPort (line 48) | codexCallbackPort     = 1455
  constant geminiCLIEndpoint (line 49) | geminiCLIEndpoint     = "https://cloudcode-pa.googleapis.com"
  constant geminiCLIVersion (line 50) | geminiCLIVersion      = "v1internal"
  type callbackForwarder (line 53) | type callbackForwarder struct
  function extractLastRefreshTimestamp (line 64) | func extractLastRefreshTimestamp(meta map[string]any) (time.Time, bool) {
  function parseLastRefreshValue (line 78) | func parseLastRefreshValue(v any) (time.Time, bool) {
  function isWebUIRequest (line 117) | func isWebUIRequest(c *gin.Context) bool {
  function startCallbackForwarder (line 130) | func startCallbackForwarder(port int, provider, targetBase string) (*cal...
  function stopCallbackForwarderInstance (line 190) | func stopCallbackForwarderInstance(port int, forwarder *callbackForwarde...
  function stopForwarderInstance (line 203) | func stopForwarderInstance(port int, forwarder *callbackForwarder) {
  method managementCallbackURL (line 223) | func (h *Handler) managementCallbackURL(path string) (string, error) {
  method ListAuthFiles (line 237) | func (h *Handler) ListAuthFiles(c *gin.Context) {
  method GetAuthFileModels (line 262) | func (h *Handler) GetAuthFileModels(c *gin.Context) {
  method listAuthFilesFromDisk (line 310) | func (h *Handler) listAuthFilesFromDisk(c *gin.Context) {
  method buildAuthFileEntry (line 358) | func (h *Handler) buildAuthFileEntry(auth *coreauth.Auth) gin.H {
  function extractCodexIDTokenClaims (line 467) | func extractCodexIDTokenClaims(auth *coreauth.Auth) gin.H {
  function authEmail (line 507) | func authEmail(auth *coreauth.Auth) string {
  function authAttribute (line 527) | func authAttribute(auth *coreauth.Auth, key string) string {
  function isRuntimeOnlyAuth (line 534) | func isRuntimeOnlyAuth(auth *coreauth.Auth) bool {
  method DownloadAuthFile (line 542) | func (h *Handler) DownloadAuthFile(c *gin.Context) {
  method UploadAuthFile (line 567) | func (h *Handler) UploadAuthFile(c *gin.Context) {
  method DeleteAuthFile (line 633) | func (h *Handler) DeleteAuthFile(c *gin.Context) {
  method findAuthForDelete (line 711) | func (h *Handler) findAuthForDelete(name string) *coreauth.Auth {
  method authIDForPath (line 737) | func (h *Handler) authIDForPath(path string) string {
  method registerAuthFromFile (line 758) | func (h *Handler) registerAuthFromFile(ctx context.Context, path string,...
  method PatchAuthFileStatus (line 823) | func (h *Handler) PatchAuthFileStatus(c *gin.Context) {
  method PatchAuthFileFields (line 889) | func (h *Handler) PatchAuthFileFields(c *gin.Context) {
  method disableAuth (line 988) | func (h *Handler) disableAuth(ctx context.Context, id string) {
  method deleteTokenRecord (line 1017) | func (h *Handler) deleteTokenRecord(ctx context.Context, path string) er...
  method tokenStoreWithBaseDir (line 1028) | func (h *Handler) tokenStoreWithBaseDir() coreauth.Store {
  method saveTokenRecord (line 1045) | func (h *Handler) saveTokenRecord(ctx context.Context, record *coreauth....
  method RequestAnthropicToken (line 1061) | func (h *Handler) RequestAnthropicToken(c *gin.Context) {
  method RequestGeminiCLIToken (line 1206) | func (h *Handler) RequestGeminiCLIToken(c *gin.Context) {
  method RequestCodexToken (line 1465) | func (h *Handler) RequestCodexToken(c *gin.Context) {
  method RequestAntigravityToken (line 1611) | func (h *Handler) RequestAntigravityToken(c *gin.Context) {
  method RequestQwenToken (line 1776) | func (h *Handler) RequestQwenToken(c *gin.Context) {
  method RequestKimiToken (line 1832) | func (h *Handler) RequestKimiToken(c *gin.Context) {
  method RequestIFlowToken (line 1909) | func (h *Handler) RequestIFlowToken(c *gin.Context) {
  method RequestIFlowCookieToken (line 2023) | func (h *Handler) RequestIFlowCookieToken(c *gin.Context) {
  type projectSelectionRequiredError (line 2118) | type projectSelectionRequiredError struct
    method Error (line 2120) | func (e *projectSelectionRequiredError) Error() string {
  function ensureGeminiProjectAndOnboard (line 2124) | func ensureGeminiProjectAndOnboard(ctx context.Context, httpClient *http...
  function onboardAllGeminiProjects (line 2158) | func onboardAllGeminiProjects(ctx context.Context, httpClient *http.Clie...
  function ensureGeminiProjectsEnabled (line 2192) | func ensureGeminiProjectsEnabled(ctx context.Context, httpClient *http.C...
  function performGeminiCLISetup (line 2209) | func performGeminiCLISetup(ctx context.Context, httpClient *http.Client,...
  function callGeminiCLI (line 2370) | func callGeminiCLI(ctx context.Context, httpClient *http.Client, endpoin...
  function fetchGCPProjects (line 2419) | func fetchGCPProjects(ctx context.Context, httpClient *http.Client) ([]i...
  function checkCloudAPIIsEnabled (line 2448) | func checkCloudAPIIsEnabled(ctx context.Context, httpClient *http.Client...
  method GetAuthStatus (line 2508) | func (h *Handler) GetAuthStatus(c *gin.Context) {
  function PopulateAuthContext (line 2532) | func PopulateAuthContext(ctx context.Context, c *gin.Context) context.Co...

FILE: internal/api/handlers/management/auth_files_delete_test.go
  function TestDeleteAuthFile_UsesAuthPathFromManager (line 18) | func TestDeleteAuthFile_UsesAuthPathFromManager(t *testing.T) {
  function TestDeleteAuthFile_FallbackToAuthDirPath (line 102) | func TestDeleteAuthFile_FallbackToAuthDirPath(t *testing.T) {

FILE: internal/api/handlers/management/config_basic.go
  constant latestReleaseURL (line 22) | latestReleaseURL       = "https://api.github.com/repos/router-for-me/CLI...
  constant latestReleaseUserAgent (line 23) | latestReleaseUserAgent = "CLIProxyAPI"
  method GetConfig (line 26) | func (h *Handler) GetConfig(c *gin.Context) {
  type releaseInfo (line 34) | type releaseInfo struct
  method GetLatestVersion (line 40) | func (h *Handler) GetLatestVersion(c *gin.Context) {
  function WriteConfig (line 94) | func WriteConfig(path string, data []byte) error {
  method PutConfigYAML (line 111) | func (h *Handler) PutConfigYAML(c *gin.Context) {
  method GetConfigYAML (line 167) | func (h *Handler) GetConfigYAML(c *gin.Context) {
  method GetDebug (line 185) | func (h *Handler) GetDebug(c *gin.Context) { c.JSON(200, gin.H{"debug": ...
  method PutDebug (line 186) | func (h *Handler) PutDebug(c *gin.Context) { h.updateBoolField(c, func(v...
  method GetUsageStatisticsEnabled (line 189) | func (h *Handler) GetUsageStatisticsEnabled(c *gin.Context) {
  method PutUsageStatisticsEnabled (line 192) | func (h *Handler) PutUsageStatisticsEnabled(c *gin.Context) {
  method GetLoggingToFile (line 197) | func (h *Handler) GetLoggingToFile(c *gin.Context) {
  method PutLoggingToFile (line 200) | func (h *Handler) PutLoggingToFile(c *gin.Context) {
  method GetLogsMaxTotalSizeMB (line 205) | func (h *Handler) GetLogsMaxTotalSizeMB(c *gin.Context) {
  method PutLogsMaxTotalSizeMB (line 208) | func (h *Handler) PutLogsMaxTotalSizeMB(c *gin.Context) {
  method GetErrorLogsMaxFiles (line 225) | func (h *Handler) GetErrorLogsMaxFiles(c *gin.Context) {
  method PutErrorLogsMaxFiles (line 228) | func (h *Handler) PutErrorLogsMaxFiles(c *gin.Context) {
  method GetRequestLog (line 245) | func (h *Handler) GetRequestLog(c *gin.Context) { c.JSON(200, gin.H{"req...
  method PutRequestLog (line 246) | func (h *Handler) PutRequestLog(c *gin.Context) {
  method GetWebsocketAuth (line 251) | func (h *Handler) GetWebsocketAuth(c *gin.Context) {
  method PutWebsocketAuth (line 254) | func (h *Handler) PutWebsocketAuth(c *gin.Context) {
  method GetRequestRetry (line 259) | func (h *Handler) GetRequestRetry(c *gin.Context) {
  method PutRequestRetry (line 262) | func (h *Handler) PutRequestRetry(c *gin.Context) {
  method GetMaxRetryInterval (line 267) | func (h *Handler) GetMaxRetryInterval(c *gin.Context) {
  method PutMaxRetryInterval (line 270) | func (h *Handler) PutMaxRetryInterval(c *gin.Context) {
  method GetForceModelPrefix (line 275) | func (h *Handler) GetForceModelPrefix(c *gin.Context) {
  method PutForceModelPrefix (line 278) | func (h *Handler) PutForceModelPrefix(c *gin.Context) {
  function normalizeRoutingStrategy (line 282) | func normalizeRoutingStrategy(strategy string) (string, bool) {
  method GetRoutingStrategy (line 295) | func (h *Handler) GetRoutingStrategy(c *gin.Context) {
  method PutRoutingStrategy (line 303) | func (h *Handler) PutRoutingStrategy(c *gin.Context) {
  method GetProxyURL (line 321) | func (h *Handler) GetProxyURL(c *gin.Context) { c.JSON(200, gin.H{"proxy...
  method PutProxyURL (line 322) | func (h *Handler) PutProxyURL(c *gin.Context) {
  method DeleteProxyURL (line 325) | func (h *Handler) DeleteProxyURL(c *gin.Context) {

FILE: internal/api/handlers/management/config_lists.go
  method putStringList (line 13) | func (h *Handler) putStringList(c *gin.Context, set func([]string), afte...
  method patchStringList (line 37) | func (h *Handler) patchStringList(c *gin.Context, target *[]string, afte...
  method deleteFromStringList (line 77) | func (h *Handler) deleteFromStringList(c *gin.Context, target *[]string,...
  method GetAPIKeys (line 108) | func (h *Handler) GetAPIKeys(c *gin.Context) { c.JSON(200, gin.H{"api-ke...
  method PutAPIKeys (line 109) | func (h *Handler) PutAPIKeys(c *gin.Context) {
  method PatchAPIKeys (line 114) | func (h *Handler) PatchAPIKeys(c *gin.Context) {
  method DeleteAPIKeys (line 117) | func (h *Handler) DeleteAPIKeys(c *gin.Context) {
  method GetGeminiKeys (line 122) | func (h *Handler) GetGeminiKeys(c *gin.Context) {
  method PutGeminiKeys (line 125) | func (h *Handler) PutGeminiKeys(c *gin.Context) {
  method PatchGeminiKey (line 146) | func (h *Handler) PatchGeminiKey(c *gin.Context) {
  method DeleteGeminiKey (line 215) | func (h *Handler) DeleteGeminiKey(c *gin.Context) {
  method GetClaudeKeys (line 245) | func (h *Handler) GetClaudeKeys(c *gin.Context) {
  method PutClaudeKeys (line 248) | func (h *Handler) PutClaudeKeys(c *gin.Context) {
  method PatchClaudeKey (line 272) | func (h *Handler) PatchClaudeKey(c *gin.Context) {
  method DeleteClaudeKey (line 337) | func (h *Handler) DeleteClaudeKey(c *gin.Context) {
  method GetOpenAICompat (line 364) | func (h *Handler) GetOpenAICompat(c *gin.Context) {
  method PutOpenAICompat (line 367) | func (h *Handler) PutOpenAICompat(c *gin.Context) {
  method PatchOpenAICompat (line 395) | func (h *Handler) PatchOpenAICompat(c *gin.Context) {
  method DeleteOpenAICompat (line 463) | func (h *Handler) DeleteOpenAICompat(c *gin.Context) {
  method GetVertexCompatKeys (line 490) | func (h *Handler) GetVertexCompatKeys(c *gin.Context) {
  method PutVertexCompatKeys (line 493) | func (h *Handler) PutVertexCompatKeys(c *gin.Context) {
  method PatchVertexCompatKey (line 521) | func (h *Handler) PatchVertexCompatKey(c *gin.Context) {
  method DeleteVertexCompatKey (line 602) | func (h *Handler) DeleteVertexCompatKey(c *gin.Context) {
  method GetOAuthExcludedModels (line 629) | func (h *Handler) GetOAuthExcludedModels(c *gin.Context) {
  method PutOAuthExcludedModels (line 633) | func (h *Handler) PutOAuthExcludedModels(c *gin.Context) {
  method PatchOAuthExcludedModels (line 654) | func (h *Handler) PatchOAuthExcludedModels(c *gin.Context) {
  method DeleteOAuthExcludedModels (line 692) | func (h *Handler) DeleteOAuthExcludedModels(c *gin.Context) {
  method GetOAuthModelAlias (line 714) | func (h *Handler) GetOAuthModelAlias(c *gin.Context) {
  method PutOAuthModelAlias (line 718) | func (h *Handler) PutOAuthModelAlias(c *gin.Context) {
  method PatchOAuthModelAlias (line 739) | func (h *Handler) PatchOAuthModelAlias(c *gin.Context) {
  method DeleteOAuthModelAlias (line 786) | func (h *Handler) DeleteOAuthModelAlias(c *gin.Context) {
  method GetCodexKeys (line 811) | func (h *Handler) GetCodexKeys(c *gin.Context) {
  method PutCodexKeys (line 814) | func (h *Handler) PutCodexKeys(c *gin.Context) {
  method PatchCodexKey (line 845) | func (h *Handler) PatchCodexKey(c *gin.Context) {
  method DeleteCodexKey (line 917) | func (h *Handler) DeleteCodexKey(c *gin.Context) {
  function normalizeOpenAICompatibilityEntry (line 943) | func normalizeOpenAICompatibilityEntry(entry *config.OpenAICompatibility) {
  function normalizedOpenAICompatibilityEntries (line 960) | func normalizedOpenAICompatibilityEntries(entries []config.OpenAICompati...
  function normalizeClaudeKey (line 976) | func normalizeClaudeKey(entry *config.ClaudeKey) {
  function normalizeCodexKey (line 1001) | func normalizeCodexKey(entry *config.CodexKey) {
  function normalizeVertexCompatKey (line 1027) | func normalizeVertexCompatKey(entry *config.VertexCompatKey) {
  function sanitizedOAuthModelAlias (line 1053) | func sanitizedOAuthModelAlias(entries map[string][]config.OAuthModelAlia...
  method GetAmpCode (line 1076) | func (h *Handler) GetAmpCode(c *gin.Context) {
  method GetAmpUpstreamURL (line 1085) | func (h *Handler) GetAmpUpstreamURL(c *gin.Context) {
  method PutAmpUpstreamURL (line 1094) | func (h *Handler) PutAmpUpstreamURL(c *gin.Context) {
  method DeleteAmpUpstreamURL (line 1099) | func (h *Handler) DeleteAmpUpstreamURL(c *gin.Context) {
  method GetAmpUpstreamAPIKey (line 1105) | func (h *Handler) GetAmpUpstreamAPIKey(c *gin.Context) {
  method PutAmpUpstreamAPIKey (line 1114) | func (h *Handler) PutAmpUpstreamAPIKey(c *gin.Context) {
  method DeleteAmpUpstreamAPIKey (line 1119) | func (h *Handler) DeleteAmpUpstreamAPIKey(c *gin.Context) {
  method GetAmpRestrictManagementToLocalhost (line 1125) | func (h *Handler) GetAmpRestrictManagementToLocalhost(c *gin.Context) {
  method PutAmpRestrictManagementToLocalhost (line 1134) | func (h *Handler) PutAmpRestrictManagementToLocalhost(c *gin.Context) {
  method GetAmpModelMappings (line 1139) | func (h *Handler) GetAmpModelMappings(c *gin.Context) {
  method PutAmpModelMappings (line 1148) | func (h *Handler) PutAmpModelMappings(c *gin.Context) {
  method PatchAmpModelMappings (line 1161) | func (h *Handler) PatchAmpModelMappings(c *gin.Context) {
  method DeleteAmpModelMappings (line 1188) | func (h *Handler) DeleteAmpModelMappings(c *gin.Context) {
  method GetAmpForceModelMappings (line 1214) | func (h *Handler) GetAmpForceModelMappings(c *gin.Context) {
  method PutAmpForceModelMappings (line 1223) | func (h *Handler) PutAmpForceModelMappings(c *gin.Context) {
  method GetAmpUpstreamAPIKeys (line 1228) | func (h *Handler) GetAmpUpstreamAPIKeys(c *gin.Context) {
  method PutAmpUpstreamAPIKeys (line 1237) | func (h *Handler) PutAmpUpstreamAPIKeys(c *gin.Context) {
  method PatchAmpUpstreamAPIKeys (line 1253) | func (h *Handler) PatchAmpUpstreamAPIKeys(c *gin.Context) {
  method DeleteAmpUpstreamAPIKeys (line 1290) | func (h *Handler) DeleteAmpUpstreamAPIKeys(c *gin.Context) {
  function normalizeAmpUpstreamAPIKeyEntries (line 1335) | func normalizeAmpUpstreamAPIKeyEntries(entries []config.AmpUpstreamAPIKe...
  function normalizeAPIKeysList (line 1358) | func normalizeAPIKeysList(keys []string) []string {

FILE: internal/api/handlers/management/handler.go
  type attemptInfo (line 24) | type attemptInfo struct
  constant attemptCleanupInterval (line 31) | attemptCleanupInterval = 1 * time.Hour
  constant attemptMaxIdleTime (line 34) | attemptMaxIdleTime = 2 * time.Hour
  type Handler (line 37) | type Handler struct
    method startAttemptCleanup (line 74) | func (h *Handler) startAttemptCleanup() {
    method purgeStaleAttempts (line 86) | func (h *Handler) purgeStaleAttempts() {
    method SetConfig (line 108) | func (h *Handler) SetConfig(cfg *config.Config) { h.cfg = cfg }
    method SetAuthManager (line 111) | func (h *Handler) SetAuthManager(manager *coreauth.Manager) { h.authMa...
    method SetUsageStatistics (line 114) | func (h *Handler) SetUsageStatistics(stats *usage.RequestStatistics) {...
    method SetLocalPassword (line 117) | func (h *Handler) SetLocalPassword(password string) { h.localPassword ...
    method SetLogDirectory (line 120) | func (h *Handler) SetLogDirectory(dir string) {
    method SetPostAuthHook (line 133) | func (h *Handler) SetPostAuthHook(hook coreauth.PostAuthHook) {
    method Middleware (line 140) | func (h *Handler) Middleware() gin.HandlerFunc {
    method persist (line 276) | func (h *Handler) persist(c *gin.Context) bool {
    method updateBoolField (line 289) | func (h *Handler) updateBoolField(c *gin.Context, set func(bool)) {
    method updateIntField (line 301) | func (h *Handler) updateIntField(c *gin.Context, set func(int)) {
    method updateStringField (line 313) | func (h *Handler) updateStringField(c *gin.Context, set func(string)) {
  function NewHandler (line 54) | func NewHandler(cfg *config.Config, configFilePath string, manager *core...
  function NewHandlerWithoutConfigFilePath (line 103) | func NewHandlerWithoutConfigFilePath(cfg *config.Config, manager *coreau...

FILE: internal/api/handlers/management/logs.go
  constant defaultLogFileName (line 20) | defaultLogFileName      = "main.log"
  constant logScannerInitialBuffer (line 21) | logScannerInitialBuffer = 64 * 1024
  constant logScannerMaxBuffer (line 22) | logScannerMaxBuffer     = 8 * 1024 * 1024
  method GetLogs (line 26) | func (h *Handler) GetLogs(c *gin.Context) {
  method DeleteLogs (line 88) | func (h *Handler) DeleteLogs(c *gin.Context) {
  method GetRequestErrorLogs (line 150) | func (h *Handler) GetRequestErrorLogs(c *gin.Context) {
  method GetRequestLogByID (line 214) | func (h *Handler) GetRequestLogByID(c *gin.Context) {
  method DownloadRequestErrorLog (line 301) | func (h *Handler) DownloadRequestErrorLog(c *gin.Context) {
  method logDirectory (line 356) | func (h *Handler) logDirectory() string {
  method collectLogFiles (line 366) | func (h *Handler) collectLogFiles(dir string) ([]string, error) {
  type logAccumulator (line 400) | type logAccumulator struct
    method consumeFile (line 421) | func (acc *logAccumulator) consumeFile(path string) error {
    method addLine (line 445) | func (acc *logAccumulator) addLine(raw string) {
    method append (line 464) | func (acc *logAccumulator) append(line string) {
    method result (line 471) | func (acc *logAccumulator) result() ([]string, int, int64) {
  function newLogAccumulator (line 409) | func newLogAccumulator(cutoff int64, limit int) *logAccumulator {
  function parseCutoff (line 478) | func parseCutoff(raw string) int64 {
  function parseLimit (line 490) | func parseLimit(raw string) (int, error) {
  function parseTimestamp (line 505) | func parseTimestamp(line string) int64 {
  function isRotatedLogFile (line 520) | func isRotatedLogFile(name string) bool {
  function rotationOrder (line 527) | func rotationOrder(name string) (int64, bool) {
  function numericRotationOrder (line 537) | func numericRotationOrder(name string) (int64, bool) {
  function timestampRotationOrder (line 552) | func timestampRotationOrder(name string) (int64, bool) {

FILE: internal/api/handlers/management/model_definitions.go
  method GetStaticModelDefinitions (line 13) | func (h *Handler) GetStaticModelDefinitions(c *gin.Context) {

FILE: internal/api/handlers/management/oauth_callback.go
  type oauthCallbackRequest (line 12) | type oauthCallbackRequest struct
  method PostOAuthCallback (line 20) | func (h *Handler) PostOAuthCallback(c *gin.Context) {

FILE: internal/api/handlers/management/oauth_sessions.go
  constant oauthSessionTTL (line 15) | oauthSessionTTL     = 10 * time.Minute
  constant maxOAuthStateLength (line 16) | maxOAuthStateLength = 128
  type oauthSession (line 25) | type oauthSession struct
  type oauthSessionStore (line 32) | type oauthSessionStore struct
    method purgeExpiredLocked (line 48) | func (s *oauthSessionStore) purgeExpiredLocked(now time.Time) {
    method Register (line 56) | func (s *oauthSessionStore) Register(state, provider string) {
    method SetError (line 76) | func (s *oauthSessionStore) SetError(state, message string) {
    method Complete (line 100) | func (s *oauthSessionStore) Complete(state string) {
    method CompleteProvider (line 114) | func (s *oauthSessionStore) CompleteProvider(provider string) int {
    method Get (line 135) | func (s *oauthSessionStore) Get(state string) (oauthSession, bool) {
    method IsPending (line 147) | func (s *oauthSessionStore) IsPending(state, provider string) bool {
  function newOAuthSessionStore (line 38) | func newOAuthSessionStore(ttl time.Duration) *oauthSessionStore {
  function RegisterOAuthSession (line 171) | func RegisterOAuthSession(state, provider string) { oauthSessions.Regist...
  function SetOAuthSessionError (line 173) | func SetOAuthSessionError(state, message string) { oauthSessions.SetErro...
  function CompleteOAuthSession (line 175) | func CompleteOAuthSession(state string) { oauthSessions.Complete(state) }
  function CompleteOAuthSessionsByProvider (line 177) | func CompleteOAuthSessionsByProvider(provider string) int {
  function GetOAuthSession (line 181) | func GetOAuthSession(state string) (provider string, status string, ok b...
  function IsOAuthSessionPending (line 189) | func IsOAuthSessionPending(state, provider string) bool {
  function ValidateOAuthState (line 193) | func ValidateOAuthState(state string) error {
  function NormalizeOAuthProvider (line 220) | func NormalizeOAuthProvider(provider string) (string, error) {
  type oauthCallbackFilePayload (line 239) | type oauthCallbackFilePayload struct
  function WriteOAuthCallbackFile (line 245) | func WriteOAuthCallbackFile(authDir, provider, state, code, errorMessage...
  function WriteOAuthCallbackFileForPendingSession (line 274) | func WriteOAuthCallbackFileForPendingSession(authDir, provider, state, c...

FILE: internal/api/handlers/management/quota.go
  method GetSwitchProject (line 6) | func (h *Handler) GetSwitchProject(c *gin.Context) {
  method PutSwitchProject (line 9) | func (h *Handler) PutSwitchProject(c *gin.Context) {
  method GetSwitchPreviewModel (line 13) | func (h *Handler) GetSwitchPreviewModel(c *gin.Context) {
  method PutSwitchPreviewModel (line 16) | func (h *Handler) PutSwitchPreviewModel(c *gin.Context) {

FILE: internal/api/handlers/management/test_store_test.go
  type memoryAuthStore (line 10) | type memoryAuthStore struct
    method List (line 15) | func (s *memoryAuthStore) List(_ context.Context) ([]*coreauth.Auth, e...
    method Save (line 26) | func (s *memoryAuthStore) Save(_ context.Context, auth *coreauth.Auth)...
    method Delete (line 41) | func (s *memoryAuthStore) Delete(_ context.Context, id string) error {
    method SetBaseDir (line 49) | func (s *memoryAuthStore) SetBaseDir(string) {}

FILE: internal/api/handlers/management/usage.go
  type usageExportPayload (line 12) | type usageExportPayload struct
  type usageImportPayload (line 18) | type usageImportPayload struct
  method GetUsageStatistics (line 24) | func (h *Handler) GetUsageStatistics(c *gin.Context) {
  method ExportUsageStatistics (line 36) | func (h *Handler) ExportUsageStatistics(c *gin.Context) {
  method ImportUsageStatistics (line 49) | func (h *Handler) ImportUsageStatistics(c *gin.Context) {

FILE: internal/api/handlers/management/vertex_import.go
  method ImportVertexCredential (line 17) | func (h *Handler) ImportVertexCredential(c *gin.Context) {
  function valueAsString (line 119) | func valueAsString(v any) string {
  function sanitizeVertexFilePart (line 131) | func sanitizeVertexFilePart(s string) string {
  function labelForVertex (line 143) | func labelForVertex(projectID, email string) string {

FILE: internal/api/middleware/request_logging.go
  constant maxErrorOnlyCapturedRequestBodyBytes (line 18) | maxErrorOnlyCapturedRequestBodyBytes int64 = 1 << 20
  function RequestLoggingMiddleware (line 24) | func RequestLoggingMiddleware(logger logging.RequestLogger) gin.HandlerF...
  function shouldSkipMethodForRequestLogging (line 71) | func shouldSkipMethodForRequestLogging(req *http.Request) bool {
  function isResponsesWebsocketUpgrade (line 81) | func isResponsesWebsocketUpgrade(req *http.Request) bool {
  function shouldCaptureRequestBody (line 91) | func shouldCaptureRequestBody(loggerEnabled bool, req *http.Request) bool {
  function captureRequestInfo (line 111) | func captureRequestInfo(c *gin.Context, captureBody bool) (*RequestInfo,...
  function shouldLogRequest (line 155) | func shouldLogRequest(path string) bool {

FILE: internal/api/middleware/request_logging_test.go
  function TestShouldSkipMethodForRequestLogging (line 11) | func TestShouldSkipMethodForRequestLogging(t *testing.T) {
  function TestShouldCaptureRequestBody (line 67) | func TestShouldCaptureRequestBody(t *testing.T) {

FILE: internal/api/middleware/response_writer.go
  constant requestBodyOverrideContextKey (line 17) | requestBodyOverrideContextKey = "REQUEST_BODY_OVERRIDE"
  type RequestInfo (line 20) | type RequestInfo struct
  type ResponseWriterWrapper (line 31) | type ResponseWriterWrapper struct
    method Write (line 71) | func (w *ResponseWriterWrapper) Write(data []byte) (int, error) {
    method shouldBufferResponseBody (line 100) | func (w *ResponseWriterWrapper) shouldBufferResponseBody() bool {
    method WriteString (line 121) | func (w *ResponseWriterWrapper) WriteString(data string) (int, error) {
    method WriteHeader (line 149) | func (w *ResponseWriterWrapper) WriteHeader(statusCode int) {
    method ensureHeadersCaptured (line 189) | func (w *ResponseWriterWrapper) ensureHeadersCaptured() {
    method captureCurrentHeaders (line 196) | func (w *ResponseWriterWrapper) captureCurrentHeaders() {
    method detectStreaming (line 214) | func (w *ResponseWriterWrapper) detectStreaming(contentType string) bo...
    method processStreamingChunks (line 237) | func (w *ResponseWriterWrapper) processStreamingChunks(done chan struc...
    method Finalize (line 257) | func (w *ResponseWriterWrapper) Finalize(c *gin.Context) error {
    method cloneHeaders (line 318) | func (w *ResponseWriterWrapper) cloneHeaders() map[string][]string {
    method extractAPIRequest (line 331) | func (w *ResponseWriterWrapper) extractAPIRequest(c *gin.Context) []by...
    method extractAPIResponse (line 343) | func (w *ResponseWriterWrapper) extractAPIResponse(c *gin.Context) []b...
    method extractAPIResponseTimestamp (line 355) | func (w *ResponseWriterWrapper) extractAPIResponseTimestamp(c *gin.Con...
    method extractRequestBody (line 366) | func (w *ResponseWriterWrapper) extractRequestBody(c *gin.Context) []b...
    method logRequest (line 387) | func (w *ResponseWriterWrapper) logRequest(requestBody []byte, statusC...
  function NewResponseWriterWrapper (line 56) | func NewResponseWriterWrapper(w gin.ResponseWriter, logger logging.Reque...

FILE: internal/api/middleware/response_writer_test.go
  function TestExtractRequestBodyPrefersOverride (line 10) | func TestExtractRequestBodyPrefersOverride(t *testing.T) {
  function TestExtractRequestBodySupportsStringOverride (line 31) | func TestExtractRequestBodySupportsStringOverride(t *testing.T) {

FILE: internal/api/modules/amp/amp.go
  type Option (line 19) | type Option
  type AmpModule (line 27) | type AmpModule struct
    method Name (line 99) | func (m *AmpModule) Name() string {
    method forceModelMappings (line 104) | func (m *AmpModule) forceModelMappings() bool {
    method Register (line 116) | func (m *AmpModule) Register(ctx modules.Context) error {
    method getAuthMiddleware (line 163) | func (m *AmpModule) getAuthMiddleware(ctx modules.Context) gin.Handler...
    method OnConfigUpdated (line 180) | func (m *AmpModule) OnConfigUpdated(cfg *config.Config) error {
    method enableUpstreamProxy (line 260) | func (m *AmpModule) enableUpstreamProxy(upstreamURL string, settings *...
    method hasModelMappingsChanged (line 293) | func (m *AmpModule) hasModelMappingsChanged(old *config.AmpCode, new *...
    method hasAPIKeyChanged (line 327) | func (m *AmpModule) hasAPIKeyChanged(old *config.AmpCode, new *config....
    method hasUpstreamAPIKeysChanged (line 337) | func (m *AmpModule) hasUpstreamAPIKeysChanged(old *config.AmpCode, new...
    method GetModelMapper (line 397) | func (m *AmpModule) GetModelMapper() *DefaultModelMapper {
    method getProxy (line 402) | func (m *AmpModule) getProxy() *httputil.ReverseProxy {
    method setProxy (line 409) | func (m *AmpModule) setProxy(proxy *httputil.ReverseProxy) {
    method IsRestrictedToLocalhost (line 416) | func (m *AmpModule) IsRestrictedToLocalhost() bool {
    method setRestrictToLocalhost (line 423) | func (m *AmpModule) setRestrictToLocalhost(restrict bool) {
  function New (line 56) | func New(opts ...Option) *AmpModule {
  function NewLegacy (line 70) | func NewLegacy(accessManager *sdkaccess.Manager, authMiddleware gin.Hand...
  function WithSecretSource (line 78) | func WithSecretSource(source SecretSource) Option {
  function WithAccessManager (line 85) | func WithAccessManager(am *sdkaccess.Manager) Option {
  function WithAuthMiddleware (line 92) | func WithAuthMiddleware(middleware gin.HandlerFunc) Option {

FILE: internal/api/modules/amp/amp_test.go
  function TestAmpModule_Name (line 18) | func TestAmpModule_Name(t *testing.T) {
  function TestAmpModule_New (line 25) | func TestAmpModule_New(t *testing.T) {
  function TestAmpModule_Register_WithUpstream (line 45) | func TestAmpModule_Register_WithUpstream(t *testing.T) {
  function TestAmpModule_Register_WithoutUpstream (line 81) | func TestAmpModule_Register_WithoutUpstream(t *testing.T) {
  function TestAmpModule_Register_InvalidUpstream (line 118) | func TestAmpModule_Register_InvalidUpstream(t *testing.T) {
  function TestAmpModule_OnConfigUpdated_CacheInvalidation (line 139) | func TestAmpModule_OnConfigUpdated_CacheInvalidation(t *testing.T) {
  function TestAmpModule_OnConfigUpdated_NotEnabled (line 172) | func TestAmpModule_OnConfigUpdated_NotEnabled(t *testing.T) {
  function TestAmpModule_OnConfigUpdated_URLRemoved (line 181) | func TestAmpModule_OnConfigUpdated_URLRemoved(t *testing.T) {
  function TestAmpModule_OnConfigUpdated_NonMultiSourceSecret (line 194) | func TestAmpModule_OnConfigUpdated_NonMultiSourceSecret(t *testing.T) {
  function TestAmpModule_AuthMiddleware_Fallback (line 207) | func TestAmpModule_AuthMiddleware_Fallback(t *testing.T) {
  function TestAmpModule_SecretSource_FromConfig (line 238) | func TestAmpModule_SecretSource_FromConfig(t *testing.T) {
  function TestAmpModule_ProviderAliasesAlwaysRegistered (line 278) | func TestAmpModule_ProviderAliasesAlwaysRegistered(t *testing.T) {
  function TestAmpModule_hasUpstreamAPIKeysChanged_DetectsRemovedKeyWithDuplicateInput (line 316) | func TestAmpModule_hasUpstreamAPIKeysChanged_DetectsRemovedKeyWithDuplic...
  function TestAmpModule_hasUpstreamAPIKeysChanged_IgnoresEmptyAndWhitespaceKeys (line 335) | func TestAmpModule_hasUpstreamAPIKeysChanged_IgnoresEmptyAndWhitespaceKe...

FILE: internal/api/modules/amp/fallback_handlers.go
  type AmpRouteType (line 19) | type AmpRouteType
  constant RouteTypeLocalProvider (line 23) | RouteTypeLocalProvider AmpRouteType = "LOCAL_PROVIDER"
  constant RouteTypeModelMapping (line 25) | RouteTypeModelMapping AmpRouteType = "MODEL_MAPPING"
  constant RouteTypeAmpCredits (line 27) | RouteTypeAmpCredits AmpRouteType = "AMP_CREDITS"
  constant RouteTypeNoProvider (line 29) | RouteTypeNoProvider AmpRouteType = "NO_PROVIDER"
  constant MappedModelContextKey (line 33) | MappedModelContextKey = "mapped_model"
  function logAmpRouting (line 36) | func logAmpRouting(routeType AmpRouteType, requestedModel, resolvedModel...
  type FallbackHandler (line 80) | type FallbackHandler struct
    method SetModelMapper (line 108) | func (fh *FallbackHandler) SetModelMapper(mapper ModelMapper) {
    method WrapHandler (line 114) | func (fh *FallbackHandler) WrapHandler(handler gin.HandlerFunc) gin.Ha...
  function NewFallbackHandler (line 88) | func NewFallbackHandler(getProxy func() *httputil.ReverseProxy) *Fallbac...
  function NewFallbackHandlerWithMapper (line 96) | func NewFallbackHandlerWithMapper(getProxy func() *httputil.ReverseProxy...
  function filterAntropicBetaHeader (line 276) | func filterAntropicBetaHeader(c *gin.Context) {
  function rewriteModelInRequest (line 287) | func rewriteModelInRequest(body []byte, newModel string) []byte {
  function extractModelFromRequest (line 300) | func extractModelFromRequest(body []byte, c *gin.Context) string {

FILE: internal/api/modules/amp/fallback_handlers_test.go
  function TestFallbackHandler_ModelMapping_PreservesThinkingSuffixAndRewritesResponse (line 16) | func TestFallbackHandler_ModelMapping_PreservesThinkingSuffixAndRewrites...

FILE: internal/api/modules/amp/gemini_bridge.go
  function createGeminiBridgeHandler (line 19) | func createGeminiBridgeHandler(handler gin.HandlerFunc) gin.HandlerFunc {

FILE: internal/api/modules/amp/gemini_bridge_test.go
  function TestCreateGeminiBridgeHandler_ActionParameterExtraction (line 11) | func TestCreateGeminiBridgeHandler_ActionParameterExtraction(t *testing....
  function TestCreateGeminiBridgeHandler_InvalidPath (line 75) | func TestCreateGeminiBridgeHandler_InvalidPath(t *testing.T) {

FILE: internal/api/modules/amp/model_mapping.go
  type ModelMapper (line 19) | type ModelMapper interface
  type DefaultModelMapper (line 29) | type DefaultModelMapper struct
    method MapModel (line 53) | func (m *DefaultModelMapper) MapModel(requestedModel string) string {
    method UpdateMappings (line 113) | func (m *DefaultModelMapper) UpdateMappings(mappings []config.AmpModel...
    method GetMappings (line 157) | func (m *DefaultModelMapper) GetMappings() map[string]string {
  function NewModelMapper (line 36) | func NewModelMapper(mappings []config.AmpModelMapping) *DefaultModelMapp...
  type regexMapping (line 168) | type regexMapping struct

FILE: internal/api/modules/amp/model_mapping_test.go
  function TestNewModelMapper (line 10) | func TestNewModelMapper(t *testing.T) {
  function TestNewModelMapper_Empty (line 27) | func TestNewModelMapper_Empty(t *testing.T) {
  function TestModelMapper_MapModel_NoProvider (line 39) | func TestModelMapper_MapModel_NoProvider(t *testing.T) {
  function TestModelMapper_MapModel_WithProvider (line 53) | func TestModelMapper_MapModel_WithProvider(t *testing.T) {
  function TestModelMapper_MapModel_TargetWithThinkingSuffix (line 74) | func TestModelMapper_MapModel_TargetWithThinkingSuffix(t *testing.T) {
  function TestModelMapper_MapModel_CaseInsensitive (line 93) | func TestModelMapper_MapModel_CaseInsensitive(t *testing.T) {
  function TestModelMapper_MapModel_NotFound (line 113) | func TestModelMapper_MapModel_NotFound(t *testing.T) {
  function TestModelMapper_MapModel_EmptyInput (line 127) | func TestModelMapper_MapModel_EmptyInput(t *testing.T) {
  function TestModelMapper_UpdateMappings (line 140) | func TestModelMapper_UpdateMappings(t *testing.T) {
  function TestModelMapper_UpdateMappings_SkipsInvalid (line 170) | func TestModelMapper_UpdateMappings_SkipsInvalid(t *testing.T) {
  function TestModelMapper_GetMappings_ReturnsCopy (line 186) | func TestModelMapper_GetMappings_ReturnsCopy(t *testing.T) {
  function TestModelMapper_Regex_MatchBaseWithoutParens (line 207) | func TestModelMapper_Regex_MatchBaseWithoutParens(t *testing.T) {
  function TestModelMapper_Regex_ExactPrecedence (line 227) | func TestModelMapper_Regex_ExactPrecedence(t *testing.T) {
  function TestModelMapper_Regex_InvalidPattern_Skipped (line 252) | func TestModelMapper_Regex_InvalidPattern_Skipped(t *testing.T) {
  function TestModelMapper_Regex_CaseInsensitive (line 266) | func TestModelMapper_Regex_CaseInsensitive(t *testing.T) {
  function TestModelMapper_SuffixPreservation (line 285) | func TestModelMapper_SuffixPreservation(t *testing.T) {

FILE: internal/api/modules/amp/proxy.go
  function removeQueryValuesMatching (line 21) | func removeQueryValuesMatching(req *http.Request, key string, match stri...
  type readCloser (line 50) | type readCloser struct
    method Read (line 55) | func (rc *readCloser) Read(p []byte) (int, error) { return rc.r.Read(p) }
    method Close (line 56) | func (rc *readCloser) Close() error               { return rc.c.Close() }
  function createReverseProxy (line 60) | func createReverseProxy(upstreamURL string, secretSource SecretSource) (...
  function isStreamingResponse (line 214) | func isStreamingResponse(resp *http.Response) bool {
  function proxyHandler (line 226) | func proxyHandler(proxy *httputil.ReverseProxy) gin.HandlerFunc {
  function filterBetaFeatures (line 233) | func filterBetaFeatures(header, featureToRemove string) string {

FILE: internal/api/modules/amp/proxy_test.go
  function gzipBytes (line 18) | func gzipBytes(b []byte) []byte {
  function mkResp (line 27) | func mkResp(status int, hdr http.Header, body []byte) *http.Response {
  function TestCreateReverseProxy_ValidURL (line 39) | func TestCreateReverseProxy_ValidURL(t *testing.T) {
  function TestCreateReverseProxy_InvalidURL (line 49) | func TestCreateReverseProxy_InvalidURL(t *testing.T) {
  function TestModifyResponse_GzipScenarios (line 56) | func TestModifyResponse_GzipScenarios(t *testing.T) {
  function TestModifyResponse_UpdatesContentLengthHeader (line 161) | func TestModifyResponse_UpdatesContentLengthHeader(t *testing.T) {
  function TestModifyResponse_SkipsStreamingResponses (line 199) | func TestModifyResponse_SkipsStreamingResponses(t *testing.T) {
  function TestModifyResponse_DecompressesChunkedJSON (line 221) | func TestModifyResponse_DecompressesChunkedJSON(t *testing.T) {
  function TestReverseProxy_InjectsHeaders (line 244) | func TestReverseProxy_InjectsHeaders(t *testing.T) {
  function TestReverseProxy_EmptySecret (line 278) | func TestReverseProxy_EmptySecret(t *testing.T) {
  function TestReverseProxy_StripsClientCredentialsFromHeadersAndQuery (line 313) | func TestReverseProxy_StripsClientCredentialsFromHeadersAndQuery(t *test...
  function TestReverseProxy_InjectsMappedSecret_FromRequestContext (line 377) | func TestReverseProxy_InjectsMappedSecret_FromRequestContext(t *testing....
  function TestReverseProxy_MappedSecret_FallsBackToDefault (line 422) | func TestReverseProxy_MappedSecret_FallsBackToDefault(t *testing.T) {
  function TestReverseProxy_ErrorHandler (line 466) | func TestReverseProxy_ErrorHandler(t *testing.T) {
  function TestReverseProxy_ErrorHandler_ContextCanceled (line 496) | func TestReverseProxy_ErrorHandler_ContextCanceled(t *testing.T) {
  function TestReverseProxy_FullRoundTrip_Gzip (line 520) | func TestReverseProxy_FullRoundTrip_Gzip(t *testing.T) {
  function TestReverseProxy_FullRoundTrip_PlainJSON (line 551) | func TestReverseProxy_FullRoundTrip_PlainJSON(t *testing.T) {
  function TestIsStreamingResponse (line 583) | func TestIsStreamingResponse(t *testing.T) {
  function TestFilterBetaFeatures (line 622) | func TestFilterBetaFeatures(t *testing.T) {

FILE: internal/api/modules/amp/response_rewriter.go
  type ResponseRewriter (line 16) | type ResponseRewriter struct
    method Write (line 33) | func (rw *ResponseRewriter) Write(data []byte) (int, error) {
    method Flush (line 54) | func (rw *ResponseRewriter) Flush() {
    method rewriteModelInResponse (line 73) | func (rw *ResponseRewriter) rewriteModelInResponse(data []byte) []byte {
    method rewriteStreamChunk (line 108) | func (rw *ResponseRewriter) rewriteStreamChunk(chunk []byte) []byte {
  function NewResponseRewriter (line 24) | func NewResponseRewriter(w gin.ResponseWriter, originalModel string) *Re...

FILE: internal/api/modules/amp/response_rewriter_test.go
  function TestRewriteModelInResponse_TopLevel (line 7) | func TestRewriteModelInResponse_TopLevel(t *testing.T) {
  function TestRewriteModelInResponse_ResponseModel (line 19) | func TestRewriteModelInResponse_ResponseModel(t *testing.T) {
  function TestRewriteModelInResponse_ResponseCreated (line 31) | func TestRewriteModelInResponse_ResponseCreated(t *testing.T) {
  function TestRewriteModelInResponse_NoModelField (line 43) | func TestRewriteModelInResponse_NoModelField(t *testing.T) {
  function TestRewriteModelInResponse_EmptyOriginalModel (line 54) | func TestRewriteModelInResponse_EmptyOriginalModel(t *testing.T) {
  function TestRewriteStreamChunk_SSEWithResponseModel (line 65) | func TestRewriteStreamChunk_SSEWithResponseModel(t *testing.T) {
  function TestRewriteStreamChunk_MultipleEvents (line 77) | func TestRewriteStreamChunk_MultipleEvents(t *testing.T) {
  function TestRewriteStreamChunk_MessageModel (line 91) | func TestRewriteStreamChunk_MessageModel(t *testing.T) {
  function contains (line 103) | func contains(data, substr []byte) bool {

FILE: internal/api/modules/amp/routes.go
  type clientAPIKeyContextKey (line 22) | type clientAPIKeyContextKey struct
  function clientAPIKeyMiddleware (line 26) | func clientAPIKeyMiddleware() gin.HandlerFunc {
  function getClientAPIKeyFromContext (line 42) | func getClientAPIKeyFromContext(ctx context.Context) string {
  method localhostOnlyMiddleware (line 53) | func (m *AmpModule) localhostOnlyMiddleware() gin.HandlerFunc {
  function noCORSMiddleware (line 97) | func noCORSMiddleware() gin.HandlerFunc {
  method managementAvailabilityMiddleware (line 117) | func (m *AmpModule) managementAvailabilityMiddleware() gin.HandlerFunc {
  function wrapManagementAuth (line 131) | func wrapManagementAuth(auth gin.HandlerFunc, prefixes ...string) gin.Ha...
  method registerManagementRoutes (line 148) | func (m *AmpModule) registerManagementRoutes(engine *gin.Engine, baseHan...
  method registerProviderAliases (line 265) | func (m *AmpModule) registerProviderAliases(engine *gin.Engine, baseHand...

FILE: internal/api/modules/amp/routes_test.go
  function TestRegisterManagementRoutes (line 12) | func TestRegisterManagementRoutes(t *testing.T) {
  function TestRegisterProviderAliases_AllProvidersRegistered (line 88) | func TestRegisterProviderAliases_AllProvidersRegistered(t *testing.T) {
  function TestRegisterProviderAliases_DynamicModelsHandler (line 140) | func TestRegisterProviderAliases_DynamicModelsHandler(t *testing.T) {
  function TestRegisterProviderAliases_V1Routes (line 166) | func TestRegisterProviderAliases_V1Routes(t *testing.T) {
  function TestRegisterProviderAliases_V1BetaRoutes (line 199) | func TestRegisterProviderAliases_V1BetaRoutes(t *testing.T) {
  function TestRegisterProviderAliases_NoAuthMiddleware (line 229) | func TestRegisterProviderAliases_NoAuthMiddleware(t *testing.T) {
  function TestLocalhostOnlyMiddleware_PreventsSpoofing (line 249) | func TestLocalhostOnlyMiddleware_PreventsSpoofing(t *testing.T) {
  function TestLocalhostOnlyMiddleware_HotReload (line 333) | func TestLocalhostOnlyMiddleware_HotReload(t *testing.T) {

FILE: internal/api/modules/amp/secret.go
  type SecretSource (line 18) | type SecretSource interface
  type cachedSecret (line 23) | type cachedSecret struct
  type MultiSourceSecret (line 32) | type MultiSourceSecret struct
    method Get (line 75) | func (s *MultiSourceSecret) Get(ctx context.Context) (string, error) {
    method readFromFile (line 110) | func (s *MultiSourceSecret) readFromFile() (string, error) {
    method updateCache (line 129) | func (s *MultiSourceSecret) updateCache(value string) {
    method InvalidateCache (line 139) | func (s *MultiSourceSecret) InvalidateCache() {
    method UpdateExplicitKey (line 146) | func (s *MultiSourceSecret) UpdateExplicitKey(key string) {
  function NewMultiSourceSecret (line 43) | func NewMultiSourceSecret(explicitKey string, cacheTTL time.Duration) *M...
  function NewMultiSourceSecretWithPath (line 60) | func NewMultiSourceSecretWithPath(explicitKey string, filePath string, c...
  type StaticSecretSource (line 157) | type StaticSecretSource struct
    method Get (line 167) | func (s *StaticSecretSource) Get(ctx context.Context) (string, error) {
  function NewStaticSecretSource (line 162) | func NewStaticSecretSource(key string) *StaticSecretSource {
  type MappedSecretSource (line 174) | type MappedSecretSource struct
    method Get (line 191) | func (s *MappedSecretSource) Get(ctx context.Context) (string, error) {
    method UpdateMappings (line 209) | func (s *MappedSecretSource) UpdateMappings(entries []config.AmpUpstre...
    method UpdateDefaultExplicitKey (line 237) | func (s *MappedSecretSource) UpdateDefaultExplicitKey(key string) {
    method InvalidateCache (line 244) | func (s *MappedSecretSource) InvalidateCache() {
  function NewMappedSecretSource (line 181) | func NewMappedSecretSource(defaultSource SecretSource) *MappedSecretSour...

FILE: internal/api/modules/amp/secret_test.go
  function TestMultiSourceSecret_PrecedenceOrder (line 17) | func TestMultiSourceSecret_PrecedenceOrder(t *testing.T) {
  function TestMultiSourceSecret_CacheBehavior (line 62) | func TestMultiSourceSecret_CacheBehavior(t *testing.T) {
  function TestMultiSourceSecret_FileHandling (line 110) | func TestMultiSourceSecret_FileHandling(t *testing.T) {
  function TestMultiSourceSecret_Concurrency (line 170) | func TestMultiSourceSecret_Concurrency(t *testing.T) {
  function TestStaticSecretSource (line 213) | func TestStaticSecretSource(t *testing.T) {
  function TestMultiSourceSecret_CacheEmptyResult (line 250) | func TestMultiSourceSecret_CacheEmptyResult(t *testing.T) {
  function TestMappedSecretSource_UsesMappingFromContext (line 286) | func TestMappedSecretSource_UsesMappingFromContext(t *testing.T) {
  function TestMappedSecretSource_DuplicateClientKey_FirstWins (line 315) | func TestMappedSecretSource_DuplicateClientKey_FirstWins(t *testing.T) {
  function TestMappedSecretSource_DuplicateClientKey_LogsWarning (line 339) | func TestMappedSecretSource_DuplicateClientKey_LogsWarning(t *testing.T) {

FILE: internal/api/modules/modules.go
  type Context (line 17) | type Context struct
  type RouteModule (line 29) | type RouteModule interface
  type RouteModuleV2 (line 51) | type RouteModuleV2 interface
  function RegisterModule (line 80) | func RegisterModule(ctx Context, mod interface{}) error {

FILE: internal/api/server.go
  constant oauthCallbackSuccessHTML (line 43) | oauthCallbackSuccessHTML = `<html><head><meta charset="utf-8"><title>Aut...
  type serverOptionConfig (line 45) | type serverOptionConfig struct
  type ServerOption (line 58) | type ServerOption
  function defaultRequestLoggerFactory (line 60) | func defaultRequestLoggerFactory(cfg *config.Config, configPath string) ...
  function WithMiddleware (line 67) | func WithMiddleware(mw ...gin.HandlerFunc) ServerOption {
  function WithEngineConfigurator (line 74) | func WithEngineConfigurator(fn func(*gin.Engine)) ServerOption {
  function WithRouterConfigurator (line 81) | func WithRouterConfigurator(fn func(*gin.Engine, *handlers.BaseAPIHandle...
  function WithLocalManagementPassword (line 88) | func WithLocalManagementPassword(password string) ServerOption {
  function WithKeepAliveEndpoint (line 95) | func WithKeepAliveEndpoint(timeout time.Duration, onTimeout func()) Serv...
  function WithRequestLoggerFactory (line 107) | func WithRequestLoggerFactory(factory func(*config.Config, string) loggi...
  function WithPostAuthHook (line 114) | func WithPostAuthHook(hook auth.PostAuthHook) ServerOption {
  type Server (line 122) | type Server struct
    method setupRoutes (line 319) | func (s *Server) setupRoutes() {
    method AttachWebsocketRoute (line 441) | func (s *Server) AttachWebsocketRoute(path string, handler http.Handle...
    method registerManagementRoutes (line 476) | func (s *Server) registerManagementRoutes() {
    method managementAvailabilityMiddleware (line 646) | func (s *Server) managementAvailabilityMiddleware() gin.HandlerFunc {
    method serveManagementControlPanel (line 656) | func (s *Server) serveManagementControlPanel(c *gin.Context) {
    method enableKeepAlive (line 686) | func (s *Server) enableKeepAlive(timeout time.Duration, onTimeout func...
    method handleKeepAlive (line 702) | func (s *Server) handleKeepAlive(c *gin.Context) {
    method signalKeepAlive (line 724) | func (s *Server) signalKeepAlive() {
    method watchKeepAlive (line 734) | func (s *Server) watchKeepAlive() {
    method unifiedModelsHandler (line 768) | func (s *Server) unifiedModelsHandler(openaiHandler *openai.OpenAIAPIH...
    method Start (line 788) | func (s *Server) Start() error {
    method Stop (line 823) | func (s *Server) Stop(ctx context.Context) error {
    method applyAccessConfig (line 862) | func (s *Server) applyAccessConfig(oldCfg, newCfg *config.Config) {
    method UpdateClients (line 877) | func (s *Server) UpdateClients(cfg *config.Config) {
    method SetWebsocketAuthChangeHandler (line 1016) | func (s *Server) SetWebsocketAuthChangeHandler(fn func(bool, bool)) {
  function NewServer (line 191) | func NewServer(cfg *config.Config, authManager *auth.Manager, accessMana...
  function corsMiddleware (line 847) | func corsMiddleware() gin.HandlerFunc {
  function AuthMiddleware (line 1028) | func AuthMiddleware(manager *sdkaccess.Manager) gin.HandlerFunc {

FILE: internal/api/server_test.go
  function newTestServer (line 20) | func newTestServer(t *testing.T) *Server {
  function TestAmpProviderModelRoutes (line 49) | func TestAmpProviderModelRoutes(t *testing.T) {
  function TestDefaultRequestLoggerFactory_UsesResolvedLogDirectory (line 115) | func TestDefaultRequestLoggerFactory_UsesResolvedLogDirectory(t *testing...

FILE: internal/auth/antigravity/auth.go
  type TokenResponse (line 20) | type TokenResponse struct
  type userInfo (line 28) | type userInfo struct
  type AntigravityAuth (line 33) | type AntigravityAuth struct
    method BuildAuthURL (line 51) | func (o *AntigravityAuth) BuildAuthURL(state, redirectURI string) stri...
    method ExchangeCodeForTokens (line 67) | func (o *AntigravityAuth) ExchangeCodeForTokens(ctx context.Context, c...
    method FetchUserInfo (line 111) | func (o *AntigravityAuth) FetchUserInfo(ctx context.Context, accessTok...
    method FetchProjectID (line 155) | func (o *AntigravityAuth) FetchProjectID(ctx context.Context, accessTo...
    method OnboardUser (line 245) | func (o *AntigravityAuth) OnboardUser(ctx context.Context, accessToken...
  function NewAntigravityAuth (line 38) | func NewAntigravityAuth(cfg *config.Config, httpClient *http.Client) *An...

FILE: internal/auth/antigravity/constants.go
  constant ClientID (line 6) | ClientID     = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.goog...
  constant ClientSecret (line 7) | ClientSecret = "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"
  constant CallbackPort (line 8) | CallbackPort = 51121
  constant TokenEndpoint (line 22) | TokenEndpoint    = "https://oauth2.googleapis.com/token"
  constant AuthEndpoint (line 23) | AuthEndpoint     = "https://accounts.google.com/o/oauth2/v2/auth"
  constant UserInfoEndpoint (line 24) | UserInfoEndpoint = "https://www.googleapis.com/oauth2/v1/userinfo?alt=json"
  constant APIEndpoint (line 29) | APIEndpoint    = "https://cloudcode-pa.googleapis.com"
  constant APIVersion (line 30) | APIVersion     = "v1internal"
  constant APIUserAgent (line 31) | APIUserAgent   = "google-api-nodejs-client/9.15.1"
  constant APIClient (line 32) | APIClient      = "google-cloud-sdk vscode_cloudshelleditor/0.1"
  constant ClientMetadata (line 33) | ClientMetadata = `{"ideType":"IDE_UNSPECIFIED","platform":"PLATFORM_UNSP...

FILE: internal/auth/antigravity/filename.go
  function CredentialFileName (line 10) | func CredentialFileName(email string) string {

FILE: internal/auth/claude/anthropic.go
  type PKCECodes (line 4) | type PKCECodes struct
  type ClaudeTokenData (line 13) | type ClaudeTokenData struct
  type ClaudeAuthBundle (line 25) | type ClaudeAuthBundle struct

FILE: internal/auth/claude/anthropic_auth.go
  constant AuthURL (line 22) | AuthURL     = "https://claude.ai/oauth/authorize"
  constant TokenURL (line 23) | TokenURL    = "https://api.anthropic.com/v1/oauth/token"
  constant ClientID (line 24) | ClientID    = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
  constant RedirectURI (line 25) | RedirectURI = "http://localhost:54545/callback"
  type tokenResponse (line 30) | type tokenResponse struct
  type ClaudeAuth (line 48) | type ClaudeAuth struct
    method GenerateAuthURL (line 81) | func (o *ClaudeAuth) GenerateAuthURL(state string, pkceCodes *PKCECode...
    method parseCodeAndState (line 110) | func (c *ClaudeAuth) parseCodeAndState(code string) (parsedCode, parse...
    method ExchangeCodeForTokens (line 132) | func (o *ClaudeAuth) ExchangeCodeForTokens(ctx context.Context, code, ...
    method RefreshTokens (line 221) | func (o *ClaudeAuth) RefreshTokens(ctx context.Context, refreshToken s...
    method CreateTokenStorage (line 287) | func (o *ClaudeAuth) CreateTokenStorage(bundle *ClaudeAuthBundle) *Cla...
    method RefreshTokensWithRetry (line 311) | func (o *ClaudeAuth) RefreshTokensWithRetry(ctx context.Context, refre...
    method UpdateTokenStorage (line 343) | func (o *ClaudeAuth) UpdateTokenStorage(storage *ClaudeTokenStorage, t...
  function NewClaudeAuth (line 61) | func NewClaudeAuth(cfg *config.Config) *ClaudeAuth {

FILE: internal/auth/claude/errors.go
  type OAuthError (line 13) | type OAuthError struct
    method Error (line 25) | func (e *OAuthError) Error() string {
  function NewOAuthError (line 33) | func NewOAuthError(code, description string, statusCode int) *OAuthError {
  type AuthenticationError (line 42) | type AuthenticationError struct
    method Error (line 54) | func (e *AuthenticationError) Error() string {
  function NewAuthenticationError (line 106) | func NewAuthenticationError(baseErr *AuthenticationError, cause error) *...
  function IsAuthenticationError (line 116) | func IsAuthenticationError(err error) bool {
  function IsOAuthError (line 123) | func IsOAuthError(err error) bool {
  function GetUserFriendlyMessage (line 130) | func GetUserFriendlyMessage(err error) string {

FILE: internal/auth/claude/html_templates.go
  constant LoginSuccessHtml (line 10) | LoginSuccessHtml = `<!DOCTYPE html>
  constant SetupNoticeHtml (line 214) | SetupNoticeHtml = `

FILE: internal/auth/claude/oauth_server.go
  type OAuthServer (line 22) | type OAuthServer struct
    method Start (line 72) | func (s *OAuthServer) Start() error {
    method Stop (line 119) | func (s *OAuthServer) Stop(ctx context.Context) error {
    method WaitForCallback (line 150) | func (s *OAuthServer) WaitForCallback(timeout time.Duration) (*OAuthRe...
    method handleCallback (line 168) | func (s *OAuthServer) handleCallback(w http.ResponseWriter, r *http.Re...
    method handleSuccess (line 231) | func (s *OAuthServer) handleSuccess(w http.ResponseWriter, r *http.Req...
    method generateSuccessHTML (line 264) | func (s *OAuthServer) generateSuccessHTML(setupRequired bool, platform...
    method sendResult (line 286) | func (s *OAuthServer) sendResult(result *OAuthResult) {
    method isPortAvailable (line 300) | func (s *OAuthServer) isPortAvailable() bool {
    method IsRunning (line 316) | func (s *OAuthServer) IsRunning() bool {
  type OAuthResult (line 40) | type OAuthResult struct
  function NewOAuthServer (line 58) | func NewOAuthServer(port int) *OAuthServer {

FILE: internal/auth/claude/pkce.go
  function GeneratePKCECodes (line 21) | func GeneratePKCECodes() (*PKCECodes, error) {
  function generateCodeVerifier (line 39) | func generateCodeVerifier() (string, error) {
  function generateCodeChallenge (line 53) | func generateCodeChallenge(codeVerifier string) string {

FILE: internal/auth/claude/token.go
  type ClaudeTokenStorage (line 18) | type ClaudeTokenStorage struct
    method SetMetadata (line 46) | func (ts *ClaudeTokenStorage) SetMetadata(meta map[string]any) {
    method SaveTokenToFile (line 60) | func (ts *ClaudeTokenStorage) SaveTokenToFile(authFilePath string) err...

FILE: internal/auth/claude/utls_transport.go
  type utlsRoundTripper (line 20) | type utlsRoundTripper struct
    method getOrCreateConnection (line 53) | func (t *utlsRoundTripper) getOrCreateConnection(host, addr string) (*...
    method createConnection (line 101) | func (t *utlsRoundTripper) createConnection(host, addr string) (*http2...
    method RoundTrip (line 126) | func (t *utlsRoundTripper) RoundTrip(req *http.Request) (*http.Respons...
  function newUtlsRoundTripper (line 32) | func newUtlsRoundTripper(cfg *config.SDKConfig) *utlsRoundTripper {
  function NewAnthropicHttpClient (line 158) | func NewAnthropicHttpClient(cfg *config.SDKConfig) *http.Client {

FILE: internal/auth/codex/errors.go
  type OAuthError (line 10) | type OAuthError struct
    method Error (line 22) | func (e *OAuthError) Error() string {
  function NewOAuthError (line 30) | func NewOAuthError(code, description string, statusCode int) *OAuthError {
  type AuthenticationError (line 39) | type AuthenticationError struct
    method Error (line 51) | func (e *AuthenticationError) Error() string {
  function NewAuthenticationError (line 110) | func NewAuthenticationError(baseErr *AuthenticationError, cause error) *...
  function IsAuthenticationError (line 120) | func IsAuthenticationError(err error) bool {
  function IsOAuthError (line 127) | func IsOAuthError(err error) bool {
  function GetUserFriendlyMessage (line 134) | func GetUserFriendlyMessage(err error) string {

FILE: internal/auth/codex/filename.go
  function CredentialFileName (line 12) | func CredentialFileName(email, planType, hashAccountID string, includePr...
  function normalizePlanTypeForFilename (line 29) | func normalizePlanTypeForFilename(planType string) string {

FILE: internal/auth/codex/html_templates.go
  constant LoginSuccessHtml (line 6) | LoginSuccessHtml = `<!DOCTYPE html>
  constant SetupNoticeHtml (line 210) | SetupNoticeHtml = `

FILE: internal/auth/codex/jwt_parser.go
  type JWTClaims (line 14) | type JWTClaims struct
    method GetUserEmail (line 94) | func (c *JWTClaims) GetUserEmail() string {
    method GetAccountID (line 100) | func (c *JWTClaims) GetAccountID() string {
  type Organizations (line 33) | type Organizations struct
  type CodexAuthInfo (line 42) | type CodexAuthInfo struct
  function ParseJWTToken (line 58) | func ParseJWTToken(token string) (*JWTClaims, error) {
  function base64URLDecode (line 81) | func base64URLDecode(data string) ([]byte, error) {

FILE: internal/auth/codex/oauth_server.go
  type OAuthServer (line 19) | type OAuthServer struct
    method Start (line 69) | func (s *OAuthServer) Start() error {
    method Stop (line 116) | func (s *OAuthServer) Stop(ctx context.Context) error {
    method WaitForCallback (line 147) | func (s *OAuthServer) WaitForCallback(timeout time.Duration) (*OAuthRe...
    method handleCallback (line 165) | func (s *OAuthServer) handleCallback(w http.ResponseWriter, r *http.Re...
    method handleSuccess (line 228) | func (s *OAuthServer) handleSuccess(w http.ResponseWriter, r *http.Req...
    method generateSuccessHTML (line 261) | func (s *OAuthServer) generateSuccessHTML(setupRequired bool, platform...
    method sendResult (line 283) | func (s *OAuthServer) sendResult(result *OAuthResult) {
    method isPortAvailable (line 297) | func (s *OAuthServer) isPortAvailable() bool {
    method IsRunning (line 313) | func (s *OAuthServer) IsRunning() bool {
  type OAuthResult (line 37) | type OAuthResult struct
  function NewOAuthServer (line 55) | func NewOAuthServer(port int) *OAuthServer {

FILE: internal/auth/codex/openai.go
  type PKCECodes (line 5) | type PKCECodes struct
  type CodexTokenData (line 15) | type CodexTokenData struct
  type CodexAuthBundle (line 32) | type CodexAuthBundle struct

FILE: internal/auth/codex/openai_auth.go
  constant AuthURL (line 24) | AuthURL     = "https://auth.openai.com/oauth/authorize"
  constant TokenURL (line 25) | TokenURL    = "https://auth.openai.com/oauth/token"
  constant ClientID (line 26) | ClientID    = "app_EMoamEEZ73f0CkXaXp7hrann"
  constant RedirectURI (line 27) | RedirectURI = "http://localhost:1455/auth/callback"
  type CodexAuth (line 33) | type CodexAuth struct
    method GenerateAuthURL (line 48) | func (o *CodexAuth) GenerateAuthURL(state string, pkceCodes *PKCECodes...
    method ExchangeCodeForTokens (line 73) | func (o *CodexAuth) ExchangeCodeForTokens(ctx context.Context, code st...
    method ExchangeCodeForTokensWithRedirect (line 80) | func (o *CodexAuth) ExchangeCodeForTokensWithRedirect(ctx context.Cont...
    method RefreshTokens (line 171) | func (o *CodexAuth) RefreshTokens(ctx context.Context, refreshToken st...
    method CreateTokenStorage (line 245) | func (o *CodexAuth) CreateTokenStorage(bundle *CodexAuthBundle) *Codex...
    method RefreshTokensWithRetry (line 262) | func (o *CodexAuth) RefreshTokensWithRetry(ctx context.Context, refres...
    method UpdateTokenStorage (line 301) | func (o *CodexAuth) UpdateTokenStorage(storage *CodexTokenStorage, tok...
  function NewCodexAuth (line 39) | func NewCodexAuth(cfg *config.Config) *CodexAuth {
  function isNonRetryableRefreshErr (line 291) | func isNonRetryableRefreshErr(err error) bool {

FILE: internal/auth/codex/openai_auth_test.go
  type roundTripFunc (line 12) | type roundTripFunc
    method RoundTrip (line 14) | func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, e...
  function TestRefreshTokensWithRetry_NonRetryableOnlyAttemptsOnce (line 18) | func TestRefreshTokensWithRetry_NonRetryableOnlyAttemptsOnce(t *testing....

FILE: internal/auth/codex/pkce.go
  function GeneratePKCECodes (line 17) | func GeneratePKCECodes() (*PKCECodes, error) {
  function generateCodeVerifier (line 37) | func generateCodeVerifier() (string, error) {
  function generateCodeChallenge (line 53) | func generateCodeChallenge(codeVerifier string) string {

FILE: internal/auth/codex/token.go
  type CodexTokenStorage (line 18) | type CodexTokenStorage struct
    method SetMetadata (line 42) | func (ts *CodexTokenStorage) SetMetadata(meta map[string]any) {
    method SaveTokenToFile (line 56) | func (ts *CodexTokenStorage) SaveTokenToFile(authFilePath string) error {

FILE: internal/auth/empty/token.go
  type EmptyStorage (line 9) | type EmptyStorage struct
    method SaveTokenToFile (line 23) | func (ts *EmptyStorage) SaveTokenToFile(_ string) error {

FILE: internal/auth/gemini/gemini_auth.go
  constant ClientID (line 31) | ClientID            = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.app...
  constant ClientSecret (line 32) | ClientSecret        = "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"
  constant DefaultCallbackPort (line 33) | DefaultCallbackPort = 8085
  type GeminiAuth (line 46) | type GeminiAuth struct
    method GetAuthenticatedClient (line 74) | func (g *GeminiAuth) GetAuthenticatedClient(ctx context.Context, ts *G...
    method createTokenStorage (line 140) | func (g *GeminiAuth) createTokenStorage(ctx context.Context, config *o...
    method getTokenFromWeb (line 206) | func (g *GeminiAuth) getTokenFromWeb(ctx context.Context, config *oaut...
  type WebLoginOptions (line 50) | type WebLoginOptions struct
  function NewGeminiAuth (line 57) | func NewGeminiAuth() *GeminiAuth {

FILE: internal/auth/gemini/gemini_token.go
  type GeminiTokenStorage (line 20) | type GeminiTokenStorage struct
    method SetMetadata (line 45) | func (ts *GeminiTokenStorage) SetMetadata(meta map[string]any) {
    method SaveTokenToFile (line 59) | func (ts *GeminiTokenStorage) SaveTokenToFile(authFilePath string) err...
  function CredentialFileName (line 93) | func CredentialFileName(email, projectID string, includeProviderPrefix b...

FILE: internal/auth/iflow/cookie_helpers.go
  function NormalizeCookie (line 12) | func NormalizeCookie(raw string) (string, error) {
  function SanitizeIFlowFileName (line 29) | func SanitizeIFlowFileName(raw string) string {
  function ExtractBXAuth (line 44) | func ExtractBXAuth(cookie string) string {
  function CheckDuplicateBXAuth (line 57) | func CheckDuplicateBXAuth(authDir, bxAuth string) (string, error) {

FILE: internal/auth/iflow/iflow_auth.go
  constant iFlowOAuthTokenEndpoint (line 22) | iFlowOAuthTokenEndpoint     = "https://iflow.cn/oauth/token"
  constant iFlowOAuthAuthorizeEndpoint (line 23) | iFlowOAuthAuthorizeEndpoint = "https://iflow.cn/oauth"
  constant iFlowUserInfoEndpoint (line 24) | iFlowUserInfoEndpoint       = "https://iflow.cn/api/oauth/getUserInfo"
  constant iFlowSuccessRedirectURL (line 25) | iFlowSuccessRedirectURL     = "https://iflow.cn/oauth/success"
  constant iFlowAPIKeyEndpoint (line 28) | iFlowAPIKeyEndpoint = "https://platform.iflow.cn/api/openapi/apikey"
  constant iFlowOAuthClientID (line 31) | iFlowOAuthClientID     = "10009311001"
  constant iFlowOAuthClientSecret (line 32) | iFlowOAuthClientSecret = "4Z3YjXycVsQvyGF1etiNlIBB4RsqSDtW"
  constant DefaultAPIBaseURL (line 36) | DefaultAPIBaseURL = "https://apis.iflow.cn/v1"
  constant SuccessRedirectURL (line 39) | SuccessRedirectURL = iFlowSuccessRedirectURL
  constant CallbackPort (line 42) | CallbackPort = 11451
  type IFlowAuth (line 45) | type IFlowAuth struct
    method AuthorizationURL (line 56) | func (ia *IFlowAuth) AuthorizationURL(state string, port int) (authURL...
    method ExchangeCodeForTokens (line 69) | func (ia *IFlowAuth) ExchangeCodeForTokens(ctx context.Context, code, ...
    method RefreshTokens (line 86) | func (ia *IFlowAuth) RefreshTokens(ctx context.Context, refreshToken s...
    method newTokenRequest (line 101) | func (ia *IFlowAuth) newTokenRequest(ctx context.Context, form url.Val...
    method doTokenRequest (line 114) | func (ia *IFlowAuth) doTokenRequest(ctx context.Context, req *http.Req...
    method FetchUserInfo (line 170) | func (ia *IFlowAuth) FetchUserInfo(ctx context.Context, accessToken st...
    method CreateTokenStorage (line 215) | func (ia *IFlowAuth) CreateTokenStorage(data *IFlowTokenData) *IFlowTo...
    method UpdateTokenStorage (line 232) | func (ia *IFlowAuth) UpdateTokenStorage(storage *IFlowTokenStorage, da...
    method AuthenticateWithCookie (line 307) | func (ia *IFlowAuth) AuthenticateWithCookie(ctx context.Context, cooki...
    method fetchAPIKeyInfo (line 336) | func (ia *IFlowAuth) fetchAPIKeyInfo(ctx context.Context, cookie strin...
    method RefreshAPIKey (line 398) | func (ia *IFlowAuth) RefreshAPIKey(ctx context.Context, cookie, name s...
    method CreateCookieTokenStorage (line 492) | func (ia *IFlowAuth) CreateCookieTokenStorage(data *IFlowTokenData) *I...
    method UpdateCookieTokenStorage (line 515) | func (ia *IFlowAuth) UpdateCookieTokenStorage(storage *IFlowTokenStora...
  function NewIFlowAuth (line 50) | func NewIFlowAuth(cfg *config.Config) *IFlowAuth {
  type IFlowTokenResponse (line 251) | type IFlowTokenResponse struct
  type IFlowTokenData (line 260) | type IFlowTokenData struct
  type userInfoResponse (line 272) | type userInfoResponse struct
  type userInfoData (line 277) | type userInfoData struct
  type iFlowAPIKeyResponse (line 284) | type iFlowAPIKeyResponse struct
  type iFlowKeyData (line 293) | type iFlowKeyData struct
  type iFlowRefreshRequest (line 302) | type iFlowRefreshRequest struct
  function ShouldRefreshAPIKey (line 472) | func ShouldRefreshAPIKey(expireTime string) (bool, time.Duration, error) {

FILE: internal/auth/iflow/iflow_token.go
  type IFlowTokenStorage (line 13) | type IFlowTokenStorage struct
    method SetMetadata (line 31) | func (ts *IFlowTokenStorage) SetMetadata(meta map[string]any) {
    method SaveTokenToFile (line 36) | func (ts *IFlowTokenStorage) SaveTokenToFile(authFilePath string) error {

FILE: internal/auth/iflow/oauth_server.go
  constant errorRedirectURL (line 15) | errorRedirectURL = "https://iflow.cn/oauth/error"
  type OAuthResult (line 18) | type OAuthResult struct
  type OAuthServer (line 25) | type OAuthServer struct
    method Start (line 44) | func (s *OAuthServer) Start() error {
    method Stop (line 77) | func (s *OAuthServer) Stop(ctx context.Context) error {
    method WaitForCallback (line 91) | func (s *OAuthServer) WaitForCallback(timeout time.Duration) (*OAuthRe...
    method handleCallback (line 102) | func (s *OAuthServer) handleCallback(w http.ResponseWriter, r *http.Re...
    method sendResult (line 127) | func (s *OAuthServer) sendResult(res *OAuthResult) {
    method isPortAvailable (line 135) | func (s *OAuthServer) isPortAvailable() bool {
  function NewOAuthServer (line 35) | func NewOAuthServer(port int) *OAuthServer {

FILE: internal/auth/kimi/kimi.go
  constant kimiClientID (line 25) | kimiClientID = "17e5f671-d194-4dfb-9706-5516cb48c098"
  constant kimiOAuthHost (line 27) | kimiOAuthHost = "https://auth.kimi.com"
  constant kimiDeviceCodeURL (line 29) | kimiDeviceCodeURL = kimiOAuthHost + "/api/oauth/device_authorization"
  constant kimiTokenURL (line 31) | kimiTokenURL = kimiOAuthHost + "/api/oauth/token"
  constant KimiAPIBaseURL (line 33) | KimiAPIBaseURL = "https://api.kimi.com/coding"
  constant defaultPollInterval (line 35) | defaultPollInterval = 5 * time.Second
  constant maxPollDuration (line 37) | maxPollDuration = 15 * time.Minute
  constant refreshThresholdSeconds (line 39) | refreshThresholdSeconds = 300
  type KimiAuth (line 43) | type KimiAuth struct
    method StartDeviceFlow (line 57) | func (k *KimiAuth) StartDeviceFlow(ctx context.Context) (*DeviceCodeRe...
    method WaitForAuthorization (line 62) | func (k *KimiAuth) WaitForAuthorization(ctx context.Context, deviceCod...
    method CreateTokenStorage (line 75) | func (k *KimiAuth) CreateTokenStorage(bundle *KimiAuthBundle) *KimiTok...
  function NewKimiAuth (line 49) | func NewKimiAuth(cfg *config.Config) *KimiAuth {
  type DeviceFlowClient (line 92) | type DeviceFlowClient struct
    method commonHeaders (line 152) | func (c *DeviceFlowClient) commonHeaders() map[string]string {
    method RequestDeviceCode (line 163) | func (c *DeviceFlowClient) RequestDeviceCode(ctx context.Context) (*De...
    method PollForToken (line 205) | func (c *DeviceFlowClient) PollForToken(ctx context.Context, deviceCod...
    method exchangeDeviceCode (line 249) | func (c *DeviceFlowClient) exchangeDeviceCode(ctx context.Context, dev...
    method RefreshToken (line 329) | func (c *DeviceFlowClient) RefreshToken(ctx context.Context, refreshTo...
  function NewDeviceFlowClient (line 99) | func NewDeviceFlowClient(cfg *config.Config) *DeviceFlowClient {
  function NewDeviceFlowClientWithDeviceID (line 104) | func NewDeviceFlowClientWithDeviceID(cfg *config.Config, deviceID string...
  function getOrCreateDeviceID (line 121) | func getOrCreateDeviceID() string {
  function getDeviceModel (line 126) | func getDeviceModel() string {
  function getHostname (line 143) | func getHostname() string {

FILE: internal/auth/kimi/token.go
  type KimiTokenStorage (line 17) | type KimiTokenStorage struct
    method SetMetadata (line 39) | func (ts *KimiTokenStorage) SetMetadata(meta map[string]any) {
    method SaveTokenToFile (line 82) | func (ts *KimiTokenStorage) SaveTokenToFile(authFilePath string) error {
    method IsExpired (line 113) | func (ts *KimiTokenStorage) IsExpired() bool {
    method NeedsRefresh (line 126) | func (ts *KimiTokenStorage) NeedsRefresh() bool {
  type KimiTokenData (line 44) | type KimiTokenData struct
  type KimiAuthBundle (line 58) | type KimiAuthBundle struct
  type DeviceCodeResponse (line 66) | type DeviceCodeResponse struct

FILE: internal/auth/models.go
  type TokenStorage (line 8) | type TokenStorage interface

FILE: internal/auth/qwen/qwen_auth.go
  constant QwenOAuthDeviceCodeEndpoint (line 23) | QwenOAuthDeviceCodeEndpoint = "https://chat.qwen.ai/api/v1/oauth2/device...
  constant QwenOAuthTokenEndpoint (line 25) | QwenOAuthTokenEndpoint = "https://chat.qwen.ai/api/v1/oauth2/token"
  constant QwenOAuthClientID (line 27) | QwenOAuthClientID = "f0304373b74a44d2b584a3fb70ca9e56"
  constant QwenOAuthScope (line 29) | QwenOAuthScope = "openid profile email model.completion"
  constant QwenOAuthGrantType (line 31) | QwenOAuthGrantType = "urn:ietf:params:oauth:grant-type:device_code"
  type QwenTokenData (line 35) | type QwenTokenData struct
  type DeviceFlow (line 48) | type DeviceFlow struct
  type QwenTokenResponse (line 67) | type QwenTokenResponse struct
  type QwenAuth (line 81) | type QwenAuth struct
    method generateCodeVerifier (line 93) | func (qa *QwenAuth) generateCodeVerifier() (string, error) {
    method generateCodeChallenge (line 102) | func (qa *QwenAuth) generateCodeChallenge(codeVerifier string) string {
    method generatePKCEPair (line 108) | func (qa *QwenAuth) generatePKCEPair() (string, string, error) {
    method RefreshTokens (line 118) | func (qa *QwenAuth) RefreshTokens(ctx context.Context, refreshToken st...
    method InitiateDeviceFlow (line 170) | func (qa *QwenAuth) InitiateDeviceFlow(ctx context.Context) (*DeviceFl...
    method PollForToken (line 227) | func (qa *QwenAuth) PollForToken(deviceCode, codeVerifier string) (*Qw...
    method RefreshTokensWithRetry (line 314) | func (o *QwenAuth) RefreshTokensWithRetry(ctx context.Context, refresh...
    method CreateTokenStorage (line 340) | func (o *QwenAuth) CreateTokenStorage(tokenData *QwenTokenData) *QwenT...
    method UpdateTokenStorage (line 353) | func (o *QwenAuth) UpdateTokenStorage(storage *QwenTokenStorage, token...
  function NewQwenAuth (line 86) | func NewQwenAuth(cfg *config.Config) *QwenAuth {

FILE: internal/auth/qwen/qwen_token.go
  type QwenTokenStorage (line 18) | type QwenTokenStorage struct
    method SetMetadata (line 40) | func (ts *QwenTokenStorage) SetMetadata(meta map[string]any) {
    method SaveTokenToFile (line 54) | func (ts *QwenTokenStorage) SaveTokenToFile(authFilePath string) error {

FILE: internal/auth/vertex/keyutil.go
  function NormalizeServiceAccountJSON (line 16) | func NormalizeServiceAccountJSON(raw []byte) ([]byte, error) {
  function NormalizeServiceAccountMap (line 37) | func NormalizeServiceAccountMap(sa map[string]any) (map[string]any, erro...
  function sanitizePrivateKey (line 57) | func sanitizePrivateKey(raw string) (string, error) {
  function ensureRSAPrivateKey (line 86) | func ensureRSAPrivateKey(block *pem.Block) (*pem.Block, error) {
  function rebuildPEM (line 125) | func rebuildPEM(raw string) (string, error) {
  function filterBase64 (line 150) | func filterBase64(s string) string {
  function stripANSIEscape (line 169) | func stripANSIEscape(s string) string {

FILE: internal/auth/vertex/vertex_credentials.go
  type VertexCredentialStorage (line 18) | type VertexCredentialStorage struct
    method SaveTokenToFile (line 37) | func (s *VertexCredentialStorage) SaveTokenToFile(authFilePath string)...

FILE: internal/browser/browser.go
  function OpenURL (line 23) | func OpenURL(url string) error {
  function openURLPlatformSpecific (line 47) | func openURLPlatformSpecific(url string) error {
  function IsAvailable (line 86) | func IsAvailable() bool {
  function GetPlatformInfo (line 119) | func GetPlatformInfo() map[string]interface{} {

FILE: internal/cache/signature_cache.go
  type SignatureEntry (line 12) | type SignatureEntry struct
  constant SignatureCacheTTL (line 19) | SignatureCacheTTL = 3 * time.Hour
  constant SignatureTextHashLen (line 22) | SignatureTextHashLen = 16
  constant MinValidSignatureLen (line 25) | MinValidSignatureLen = 50
  constant CacheCleanupInterval (line 28) | CacheCleanupInterval = 10 * time.Minute
  type groupCache (line 38) | type groupCache struct
  function hashText (line 44) | func hashText(text string) string {
  function getOrCreateGroupCache (line 50) | func getOrCreateGroupCache(groupKey string) *groupCache {
  function startCacheCleanup (line 64) | func startCacheCleanup() {
  function purgeExpiredCaches (line 75) | func purgeExpiredCaches() {
  function CacheSignature (line 98) | func CacheSignature(modelName, text, signature string) {
  function GetCachedSignature (line 120) | func GetCachedSignature(modelName, text string) string {
  function ClearSignatureCache (line 169) | func ClearSignatureCache(modelName string) {
  function HasValidSignature (line 182) | func HasValidSignature(modelName, signature string) bool {
  function GetModelGroup (line 186) | func GetModelGroup(modelName string) string {

FILE: internal/cache/signature_cache_test.go
  constant testModelName (line 8) | testModelName = "claude-sonnet-4-5"
  function TestCacheSignature_BasicStorageAndRetrieval (line 10) | func TestCacheSignature_BasicStorageAndRetrieval(t *testing.T) {
  function TestCacheSignature_DifferentModelGroups (line 26) | func TestCacheSignature_DifferentModelGroups(t *testing.T) {
  function TestCacheSignature_NotFound (line 45) | func TestCacheSignature_NotFound(t *testing.T) {
  function TestCacheSignature_EmptyInputs (line 60) | func TestCacheSignature_EmptyInputs(t *testing.T) {
  function TestCacheSignature_ShortSignatureRejected (line 73) | func TestCacheSignature_ShortSignatureRejected(t *testing.T) {
  function TestClearSignatureCache_ModelGroup (line 86) | func TestClearSignatureCache_ModelGroup(t *testing.T) {
  function TestClearSignatureCache_AllSessions (line 100) | func TestClearSignatureCache_AllSessions(t *testing.T) {
  function TestHasValidSignature (line 117) | func TestHasValidSignature(t *testing.T) {
  function TestCacheSignature_TextHashCollisionResistance (line 142) | func TestCacheSignature_TextHashCollisionResistance(t *testing.T) {
  function TestCacheSignature_UnicodeText (line 162) | func TestCacheSignature_UnicodeText(t *testing.T) {
  function TestCacheSignature_Overwrite (line 175) | func TestCacheSignature_Overwrite(t *testing.T) {
  function TestCacheSignature_ExpirationLogic (line 192) | func TestCacheSignature_ExpirationLogic(t *testing.T) {

FILE: internal/cmd/anthropic_login.go
  function DoClaudeLogin (line 22) | func DoClaudeLogin(cfg *config.Config, options *LoginOptions) {

FILE: internal/cmd/antigravity_login.go
  function DoAntigravityLogin (line 13) | func DoAntigravityLogin(cfg *config.Config, options *LoginOptions) {

FILE: internal/cmd/auth_manager.go
  function newAuthManager (line 13) | func newAuthManager() *sdkAuth.Manager {

FILE: internal/cmd/iflow_cookie.go
  function DoIFlowCookieAuth (line 17) | func DoIFlowCookieAuth(cfg *config.Config, options *LoginOptions) {
  function promptForCookie (line 80) | func promptForCookie(promptFn func(string) (string, error)) (string, err...
  function getAuthFilePath (line 95) | func getAuthFilePath(cfg *config.Config, provider, email string) string {

FILE: internal/cmd/iflow_login.go
  function DoIFlowLogin (line 14) | func DoIFlowLogin(cfg *config.Config, options *LoginOptions) {

FILE: internal/cmd/kimi_login.go
  function DoKimiLogin (line 19) | func DoKimiLogin(cfg *config.Config, options *LoginOptions) {

FILE: internal/cmd/login.go
  constant geminiCLIEndpoint (line 31) | geminiCLIEndpoint = "https://cloudcode-pa.googleapis.com"
  constant geminiCLIVersion (line 32) | geminiCLIVersion  = "v1internal"
  type projectSelectionRequiredError (line 35) | type projectSelectionRequiredError struct
    method Error (line 37) | func (e *projectSelectionRequiredError) Error() string {
  function DoLogin (line 49) | func DoLogin(cfg *config.Config, projectID string, options *LoginOptions) {
  function performGeminiCLISetup (line 209) | func performGeminiCLISetup(ctx context.Context, httpClient *http.Client,...
  function callGeminiCLI (line 390) | func callGeminiCLI(ctx context.Context, httpClient *http.Client, endpoin...
  function fetchGCPProjects (line 439) | func fetchGCPProjects(ctx context.Context, httpClient *http.Client) ([]i...
  function promptForProjectSelection (line 469) | func promptForProjectSelection(projects []interfaces.GCPProjectProjects,...
  function resolveProjectSelections (line 534) | func resolveProjectSelections(selection string, projects []interfaces.GC...
  function defaultProjectPrompt (line 580) | func defaultProjectPrompt() func(string) (string, error) {
  function showProjectSelectionHelp (line 595) | func showProjectSelectionHelp(email string, projects []interfaces.GCPPro...
  function checkCloudAPIIsEnabled (line 616) | func checkCloudAPIIsEnabled(ctx context.Context, httpClient *http.Client...
  function updateAuthRecord (line 677) | func updateAuthRecord(record *cliproxyauth.Auth, storage *gemini.GeminiT...

FILE: internal/cmd/openai_device_login.go
  constant codexLoginModeMetadataKey (line 16) | codexLoginModeMetadataKey = "codex_login_mode"
  constant codexLoginModeDevice (line 17) | codexLoginModeDevice      = "device"
  function DoCodexDeviceLogin (line 22) | func DoCodexDeviceLogin(cfg *config.Config, options *LoginOptions) {

FILE: internal/cmd/openai_login.go
  type LoginOptions (line 18) | type LoginOptions struct
  function DoCodexLogin (line 36) | func DoCodexLogin(cfg *config.Config, options *LoginOptions) {

FILE: internal/cmd/qwen_login.go
  function DoQwenLogin (line 20) | func DoQwenLogin(cfg *config.Config, options *LoginOptions) {

FILE: internal/cmd/run.go
  function StartService (line 27) | func StartService(cfg *config.Config, configPath string, localPassword s...
  function StartServiceBackground (line 60) | func StartServiceBackground(cfg *config.Config, configPath string, local...
  function WaitForCloudDeploy (line 88) | func WaitForCloudDeploy() {

FILE: internal/cmd/vertex_import.go
  function DoVertexImport (line 23) | func DoVertexImport(cfg *config.Config, keyPath string) {
  function sanitizeFilePart (line 101) | func sanitizeFilePart(s string) string {
  function labelForVertex (line 110) | func labelForVertex(projectID, email string) string {

FILE: internal/config/codex_websocket_header_defaults_test.go
  function TestLoadConfigOptional_CodexHeaderDefaults (line 9) | func TestLoadConfigOptional_CodexHeaderDefaults(t *testing.T) {

FILE: internal/config/config.go
  constant DefaultPanelGitHubRepository (line 22) | DefaultPanelGitHubRepository = "https://github.com/router-for-me/Cli-Pro...
  constant DefaultPprofAddr (line 23) | DefaultPprofAddr             = "127.0.0.1:8316"
  type Config (line 27) | type Config struct
    method SanitizePayloadRules (line 668) | func (cfg *Config) SanitizePayloadRules() {
    method SanitizeCodexHeaderDefaults (line 724) | func (cfg *Config) SanitizeCodexHeaderDefaults() {
    method SanitizeOAuthModelAlias (line 735) | func (cfg *Config) SanitizeOAuthModelAlias() {
    method SanitizeOpenAICompatibility (line 773) | func (cfg *Config) SanitizeOpenAICompatibility() {
    method SanitizeCodexKeys (line 795) | func (cfg *Config) SanitizeCodexKeys() {
    method SanitizeClaudeKeys (line 815) | func (cfg *Config) SanitizeClaudeKeys() {
    method SanitizeGeminiKeys (line 828) | func (cfg *Config) SanitizeGeminiKeys() {
    method migrateLegacyGeminiKeys (line 1681) | func (cfg *Config) migrateLegacyGeminiKeys(legacy []string) bool {
    method migrateLegacyOpenAICompatibilityKeys (line 1709) | func (cfg *Config) migrateLegacyOpenAICompatibilityKeys(legacy []legac...
    method migrateLegacyAmpConfig (line 1785) | func (cfg *Config) migrateLegacyAmpConfig(legacy *legacyConfigData) bo...
  type ClaudeHeaderDefaults (line 133) | type ClaudeHeaderDefaults struct
  type CodexHeaderDefaults (line 143) | type CodexHeaderDefaults struct
  type TLSConfig (line 149) | type TLSConfig struct
  type PprofConfig (line 159) | type PprofConfig struct
  type RemoteManagement (line 167) | type RemoteManagement struct
  type QuotaExceeded (line 181) | type QuotaExceeded struct
  type RoutingConfig (line 190) | type RoutingConfig struct
  type OAuthModelAlias (line 200) | type OAuthModelAlias struct
  type AmpModelMapping (line 209) | type AmpModelMapping struct
  type AmpCode (line 225) | type AmpCode struct
  type AmpUpstreamAPIKeyEntry (line 255) | type AmpUpstreamAPIKeyEntry struct
  type PayloadConfig (line 264) | type PayloadConfig struct
  type PayloadFilterRule (line 278) | type PayloadFilterRule struct
  type PayloadRule (line 286) | type PayloadRule struct
  type PayloadModelRule (line 295) | type PayloadModelRule struct
  type CloakConfig (line 304) | type CloakConfig struct
  type ClaudeKey (line 327) | type ClaudeKey struct
    method GetAPIKey (line 358) | func (k ClaudeKey) GetAPIKey() string  { return k.APIKey }
    method GetBaseURL (line 359) | func (k ClaudeKey) GetBaseURL() string { return k.BaseURL }
  type ClaudeModel (line 362) | type ClaudeModel struct
    method GetName (line 370) | func (m ClaudeModel) GetName() string  { return m.Name }
    method GetAlias (line 371) | func (m ClaudeModel) GetAlias() string { return m.Alias }
  type CodexKey (line 375) | type CodexKey struct
    method GetAPIKey (line 406) | func (k CodexKey) GetAPIKey() string  { return k.APIKey }
    method GetBaseURL (line 407) | func (k CodexKey) GetBaseURL() string { return k.BaseURL }
  type CodexModel (line 410) | type CodexModel struct
    method GetName (line 418) | func (m CodexModel) GetName() string  { return m.Name }
    method GetAlias (line 419) | func (m CodexModel) GetAlias() string { return m.Alias }
  type GeminiKey (line 423) | type GeminiKey struct
    method GetAPIKey (line 450) | func (k GeminiKey) GetAPIKey() string  { return k.APIKey }
    method GetBaseURL (line 451) | func (k GeminiKey) GetBaseURL() string { return k.BaseURL }
  type GeminiModel (line 454) | type GeminiModel struct
    method GetName (line 462) | func (m GeminiModel) GetName() string  { return m.Name }
    method GetAlias (line 463) | func (m GeminiModel) GetAlias() string { return m.Alias }
  type OpenAICompatibility (line 467) | type OpenAICompatibility struct
  type OpenAICompatibilityAPIKey (line 492) | type OpenAICompatibilityAPIKey struct
  type OpenAICompatibilityModel (line 502) | type OpenAICompatibilityModel struct
    method GetName (line 510) | func (m OpenAICompatibilityModel) GetName() string  { return m.Name }
    method GetAlias (line 511) | func (m OpenAICompatibilityModel) GetAlias() string { return m.Alias }
  function LoadConfig (line 523) | func LoadConfig(configFile string) (*Config, error) {
  function LoadConfigOptional (line 530) | func LoadConfigOptional(configFile string, optional bool) (*Config, erro...
  function sanitizePayloadRawRules (line 676) | func sanitizePayloadRawRules(rules []PayloadRule, section string) []Payl...
  function payloadRawString (line 711) | func payloadRawString(value any) ([]byte, bool) {
  function normalizeModelPrefix (line 855) | func normalizeModelPrefix(prefix string) string {
  function looksLikeBcrypt (line 868) | func looksLikeBcrypt(s string) bool {
  function NormalizeHeaders (line 873) | func NormalizeHeaders(headers map[string]string) map[string]string {
  function NormalizeExcludedModels (line 894) | func NormalizeExcludedModels(models []string) []string {
  function NormalizeOAuthExcludedModels (line 919) | func NormalizeOAuthExcludedModels(entries map[string][]string) map[strin...
  function hashSecret (line 942) | func hashSecret(secret string) (string, error) {
  function SaveConfigPreserveComments (line 953) | func SaveConfigPreserveComments(configFile string, cfg *Config) error {
  function SaveConfigPreserveCommentsUpdateNestedScalar (line 1024) | func SaveConfigPreserveCommentsUpdateNestedScalar(configFile string, pat...
  function NormalizeCommentIndentation (line 1075) | func NormalizeCommentIndentation(data []byte) []byte {
  function getOrCreateMapValue (line 1097) | func getOrCreateMapValue(mapNode *yaml.Node, key string) *yaml.Node {
  function mergeMappingPreserve (line 1119) | func mergeMappingPreserve(dst, src *yaml.Node, path ...[]string) {
  function mergeNodePreserve (line 1158) | func mergeNodePreserve(dst, src *yaml.Node, path ...[]string) {
  function findMapKeyIndex (line 1225) | func findMapKeyIndex(mapNode *yaml.Node, key string) int {
  function appendPath (line 1238) | func appendPath(path []string, key string) []string {
  function isKnownDefaultValue (line 1251) | func isKnownDefaultValue(path []string, node *yaml.Node) bool {
  function pruneKnownDefaultsInNewNode (line 1289) | func pruneKnownDefaultsInNewNode(path []string, node *yaml.Node) {
  function isZeroValueNode (line 1328) | func isZeroValueNode(node *yaml.Node) bool {
  function deepCopyNode (line 1371) | func deepCopyNode(n *yaml.Node) *yaml.Node {
  function copyNodeShallow (line 1387) | func copyNodeShallow(dst, src *yaml.Node) {
  function reorderSequenceForMerge (line 1405) | func reorderSequenceForMerge(dst, src *yaml.Node) {
  function matchSequenceElement (line 1427) | func matchSequenceElement(original []*yaml.Node, used []bool, target *ya...
  function sequenceElementIdentity (line 1470) | func sequenceElementIdentity(node *yaml.Node) string {
  function mappingScalarValue (line 1494) | func mappingScalarValue(node *yaml.Node, key string) string {
  function nodesStructurallyEqual (line 1512) | func nodesStructurallyEqual(a, b *yaml.Node) bool {
  function removeMapKey (line 1552) | func removeMapKey(mapNode *yaml.Node, key string) {
  function pruneMappingToGeneratedKeys (line 1564) | func pruneMappingToGeneratedKeys(dstRoot, srcRoot *yaml.Node, key string) {
  function pruneMissingMapKeys (line 1607) | func pruneMissingMapKeys(dstMap, srcMap *yaml.Node) {
  function normalizeCollectionNodeStyles (line 1641) | func normalizeCollectionNodeStyles(node *yaml.Node) {
  type legacyConfigData (line 1666) | type legacyConfigData struct
  type legacyOpenAICompatibility (line 1675) | type legacyOpenAICompatibility struct
  function mergeLegacyOpenAICompatAPIKeys (line 1729) | func mergeLegacyOpenAICompatAPIKeys(entry *OpenAICompatibility, keys []s...
  function findOpenAICompatTarget (line 1757) | func findOpenAICompatTarget(entries []OpenAICompatibility, legacyName, l...
  function removeLegacyOpenAICompatAPIKeys (line 1813) | func removeLegacyOpenAICompatAPIKeys(root *yaml.Node) {
  function removeLegacyAmpKeys (line 1832) | func removeLegacyAmpKeys(root *yaml.Node) {
  function removeLegacyGenerativeLanguageKeys (line 1842) | func removeLegacyGenerativeLanguageKeys(root *yaml.Node) {
  function removeLegacyAuthBlock (line 1849) | func removeLegacyAuthBlock(root *yaml.Node) {

FILE: internal/config/oauth_model_alias_test.go
  function TestSanitizeOAuthModelAlias_PreservesForkFlag (line 5) | func TestSanitizeOAuthModelAlias_PreservesForkFlag(t *testing.T) {
  function TestSanitizeOAuthModelAlias_AllowsMultipleAliasesForSameName (line 29) | func TestSanitizeOAuthModelAlias_AllowsMultipleAliasesForSameName(t *tes...

FILE: internal/config/sdk_config.go
  type SDKConfig (line 8) | type SDKConfig struct
  type StreamingConfig (line 36) | type StreamingConfig struct

FILE: internal/config/vertex_compat.go
  type VertexCompatKey (line 11) | type VertexCompatKey struct
    method GetAPIKey (line 42) | func (k VertexCompatKey) GetAPIKey() string  { return k.APIKey }
    method GetBaseURL (line 43) | func (k VertexCompatKey) GetBaseURL() string { return k.BaseURL }
  type VertexCompatModel (line 47) | type VertexCompatModel struct
    method GetName (line 55) | func (m VertexCompatModel) GetName() string  { return m.Name }
    method GetAlias (line 56) | func (m VertexCompatModel) GetAlias() string { return m.Alias }
  method SanitizeVertexCompatKeys (line 59) | func (cfg *Config) SanitizeVertexCompatKeys() {

FILE: internal/constant/constant.go
  constant Gemini (line 8) | Gemini = "gemini"
  constant GeminiCLI (line 11) | GeminiCLI = "gemini-cli"
  constant Codex (line 14) | Codex = "codex"
  constant Claude (line 17) | Claude = "claude"
  constant OpenAI (line 20) | OpenAI = "openai"
  constant OpenaiResponse (line 23) | OpenaiResponse = "openai-response"
  constant Antigravity (line 26) | Antigravity = "antigravity"

FILE: internal/interfaces/api_handler.go
  type APIHandler (line 9) | type APIHandler interface

FILE: internal/interfaces/client_models.go
  type GCPProject (line 12) | type GCPProject struct
  type GCPProjectLabels (line 19) | type GCPProjectLabels struct
  type GCPProjectProjects (line 26) | type GCPProjectProjects struct
  type Content (line 48) | type Content struct
  type Part (line 58) | type Part struct
  type InlineData (line 79) | type InlineData struct
  type FunctionCall (line 89) | type FunctionCall struct
  type FunctionResponse (line 102) | type FunctionResponse struct
  type GenerateContentRequest (line 115) | type GenerateContentRequest struct
  type GenerationConfig (line 131) | type GenerationConfig struct
  type GenerationConfigThinkingConfig (line 150) | type GenerationConfigThinkingConfig struct
  type ToolDeclaration (line 158) | type ToolDeclaration struct

FILE: internal/interfaces/error_message.go
  type ErrorMessage (line 11) | type ErrorMessage struct

FILE: internal/logging/gin_logger.go
  constant skipGinLogKey (line 29) | skipGinLogKey = "__gin_skip_request_logging__"
  function GinLogrusLogger (line 40) | func GinLogrusLogger() gin.HandlerFunc {
  function isAIAPIPath (line 99) | func isAIAPIPath(path string) bool {
  function GinLogrusRecovery (line 114) | func GinLogrusRecovery() gin.HandlerFunc {
  function SkipGinRequestLogging (line 133) | func SkipGinRequestLogging(c *gin.Context) {
  function shouldSkipGinRequestLogging (line 140) | func shouldSkipGinRequestLogging(c *gin.Context) bool {

FILE: internal/logging/gin_logger_test.go
  function TestGinLogrusRecoveryRepanicsErrAbortHandler (line 12) | func TestGinLogrusRecoveryRepanicsErrAbortHandler(t *testing.T) {
  function TestGinLogrusRecoveryHandlesRegularPanic (line 44) | func TestGinLogrusRecoveryHandlesRegularPanic(t *testing.T) {

FILE: internal/logging/global_logger.go
  type LogFormatter (line 30) | type LogFormatter struct
    method Format (line 36) | func (m *LogFormatter) Format(entry *log.Entry) ([]byte, error) {
  function SetupBaseLogger (line 85) | func SetupBaseLogger() {
  function isDirWritable (line 105) | func isDirWritable(dir string) bool {
  function ResolveLogDirectory (line 125) | func ResolveLogDirectory(cfg *config.Config) string {
  function ConfigureLogOutput (line 148) | func ConfigureLogOutput(cfg *config.Config) error {
  function closeLogOutputs (line 185) | func closeLogOutputs() {

FILE: internal/logging/log_dir_cleaner.go
  constant logDirCleanerInterval (line 14) | logDirCleanerInterval = time.Minute
  function configureLogDirCleanerLocked (line 18) | func configureLogDirCleanerLocked(logDir string, maxTotalSizeMB int, pro...
  function stopLogDirCleanerLocked (line 40) | func stopLogDirCleanerLocked() {
  function runLogDirCleaner (line 48) | func runLogDirCleaner(ctx context.Context, logDir string, maxBytes int64...
  function enforceLogDirSizeLimit (line 74) | func enforceLogDirSizeLimit(logDir string, maxBytes int64, protectedPath...
  function isLogFileName (line 159) | func isLogFileName(name string) bool {

FILE: internal/logging/log_dir_cleaner_test.go
  function TestEnforceLogDirSizeLimitDeletesOldest (line 10) | func TestEnforceLogDirSizeLimitDeletesOldest(t *testing.T) {
  function TestEnforceLogDirSizeLimitSkipsProtected (line 37) | func TestEnforceLogDirSizeLimitSkipsProtected(t *testing.T) {
  function writeLogFile (line 60) | func writeLogFile(t *testing.T, path string, size int, modTime time.Time) {

FILE: internal/logging/request_logger.go
  type RequestLogger (line 33) | type RequestLogger interface
  type StreamingLogWriter (line 77) | type StreamingLogWriter interface
  type FileRequestLogger (line 129) | type FileRequestLogger struct
    method IsEnabled (line 170) | func (l *FileRequestLogger) IsEnabled() bool {
    method SetEnabled (line 179) | func (l *FileRequestLogger) SetEnabled(enabled bool) {
    method SetErrorLogsMaxFiles (line 184) | func (l *FileRequestLogger) SetErrorLogsMaxFiles(maxFiles int) {
    method LogRequest (line 206) | func (l *FileRequestLogger) LogRequest(url, method string, requestHead...
    method LogRequestWithOptions (line 212) | func (l *FileRequestLogger) LogRequestWithOptions(url, method string, ...
    method logRequest (line 216) | func (l *FileRequestLogger) logRequest(url, method string, requestHead...
    method LogStreamingRequest (line 304) | func (l *FileRequestLogger) LogStreamingRequest(url, method string, he...
    method generateErrorFilename (line 359) | func (l *FileRequestLogger) generateErrorFilename(url string, requestI...
    method ensureLogsDir (line 367) | func (l *FileRequestLogger) ensureLogsDir() error {
    method generateFilename (line 383) | func (l *FileRequestLogger) generateFilename(url string, requestID ......
    method sanitizeForFilename (line 420) | func (l *FileRequestLogger) sanitizeForFilename(path string) string {
    method cleanupOldErrorLogs (line 447) | func (l *FileRequestLogger) cleanupOldErrorLogs() error {
    method writeRequestBodyTempFile (line 496) | func (l *FileRequestLogger) writeRequestBodyTempFile(body []byte) (str...
    method writeNonStreamingLog (line 515) | func (l *FileRequestLogger) writeNonStreamingLog(
    method formatLogContent (line 735) | func (l *FileRequestLogger) formatLogContent(url, method string, heade...
    method decompressResponse (line 804) | func (l *FileRequestLogger) decompressResponse(responseHeaders map[str...
    method decompressGzip (line 841) | func (l *FileRequestLogger) decompressGzip(data []byte) ([]byte, error) {
    method decompressDeflate (line 868) | func (l *FileRequestLogger) decompressDeflate(data []byte) ([]byte, er...
    method decompressBrotli (line 892) | func (l *FileRequestLogger) decompressBrotli(data []byte) ([]byte, err...
    method decompressZstd (line 911) | func (l *FileRequestLogger) decompressZstd(data []byte) ([]byte, error) {
    method formatRequestInfo (line 936) | func (l *FileRequestLogger) formatRequestInfo(url, method string, head...
  function NewFileRequestLogger (line 151) | func NewFileRequestLogger(enabled bool, logsDir string, configDir string...
  function writeRequestInfoWithBody (line 549) | func writeRequestInfoWithBody(
  function writeAPISection (line 617) | func writeAPISection(w io.Writer, sectionHeader string, sectionPrefix st...
  function writeAPIErrorResponses (line 654) | func writeAPIErrorResponses(w io.Writer, apiResponseErrors []*interfaces...
  function writeResponseSection (line 677) | func writeResponseSection(w io.Writer, statusCode int, statusWritten boo...
  type FileStreamingLogWriter (line 965) | type FileStreamingLogWriter struct
    method WriteChunkAsync (line 1022) | func (w *FileStreamingLogWriter) WriteChunkAsync(chunk []byte) {
    method WriteStatus (line 1047) | func (w *FileStreamingLogWriter) WriteStatus(status int, headers map[s...
    method WriteAPIRequest (line 1072) | func (w *FileStreamingLogWriter) WriteAPIRequest(apiRequest []byte) er...
    method WriteAPIResponse (line 1087) | func (w *FileStreamingLogWriter) WriteAPIResponse(apiResponse []byte) ...
    method SetFirstChunkTimestamp (line 1095) | func (w *FileStreamingLogWriter) SetFirstChunkTimestamp(timestamp time...
    method Close (line 1107) | func (w *FileStreamingLogWriter) Close() error {
    method asyncWriter (line 1150) | func (w *FileStreamingLogWriter) asyncWriter() {
    method writeFinalLog (line 1184) | func (w *FileStreamingLogWriter) writeFinalLog(logFile *os.File) error {
    method cleanupTempFiles (line 1208) | func (w *FileStreamingLogWriter) cleanupTempFiles() {
  type NoOpStreamingLogWriter (line 1226) | type NoOpStreamingLogWriter struct
    method WriteChunkAsync (line 1232) | func (w *NoOpStreamingLogWriter) WriteChunkAsync(_ []byte) {}
    method WriteStatus (line 1242) | func (w *NoOpStreamingLogWriter) WriteStatus(_ int, _ map[string][]str...
    method WriteAPIRequest (line 1253) | func (w *NoOpStreamingLogWriter) WriteAPIRequest(_ []byte) error {
    method WriteAPIResponse (line 1264) | func (w *NoOpStreamingLogWriter) WriteAPIResponse(_ []byte) error {
    method SetFirstChunkTimestamp (line 1268) | func (w *NoOpStreamingLogWriter) SetFirstChunkTimestamp(_ time.Time) {}
    method Close (line 1274) | func (w *NoOpStreamingLogWriter) Close() error { return nil }

FILE: internal/logging/requestid.go
  type requestIDKey (line 12) | type requestIDKey struct
  constant ginRequestIDKey (line 15) | ginRequestIDKey = "__request_id__"
  function GenerateRequestID (line 18) | func GenerateRequestID() string {
  function WithRequestID (line 27) | func WithRequestID(ctx context.Context, requestID string) context.Context {
  function GetRequestID (line 33) | func GetRequestID(ctx context.Context) string {
  function SetGinRequestID (line 44) | func SetGinRequestID(c *gin.Context, requestID string) {
  function GetGinRequestID (line 51) | func GetGinRequestID(c *gin.Context) string {

FILE: internal/managementasset/updater.go
  constant defaultManagementReleaseURL (line 28) | defaultManagementReleaseURL  = "https://api.github.com/repos/router-for-...
  constant defaultManagementFallbackURL (line 29) | defaultManagementFallbackURL = "https://cpamc.router-for.me/"
  constant managementAssetName (line 30) | managementAssetName          = "management.html"
  constant httpUserAgent (line 31) | httpUserAgent                = "CLIProxyAPI-management-updater"
  constant managementSyncMinInterval (line 32) | managementSyncMinInterval    = 30 * time.Second
  constant updateCheckInterval (line 33) | updateCheckInterval          = 3 * time.Hour
  constant ManagementFileName (line 37) | ManagementFileName = managementAssetName
  function SetCurrentConfig (line 49) | func SetCurrentConfig(cfg *config.Config) {
  function StartAutoUpdater (line 59) | func StartAutoUpdater(ctx context.Context, configFilePath string) {
  function runAutoUpdater (line 73) | func runAutoUpdater(ctx context.Context) {
  function newHTTPClient (line 109) | func newHTTPClient(proxyURL string) *http.Client {
  type releaseAsset (line 118) | type releaseAsset struct
  type releaseResponse (line 124) | type releaseResponse struct
  function StaticDir (line 129) | func StaticDir(configFilePath string) string {
  function FilePath (line 159) | func FilePath(configFilePath string) string {
  function EnsureLatestManagementHTML (line 177) | func EnsureLatestManagementHTML(ctx context.Context, staticDir string, p...
  function ensureFallbackManagementHTML (line 278) | func ensureFallbackManagementHTML(ctx context.Context, client *http.Clie...
  function resolveReleaseURL (line 294) | func resolveReleaseURL(repo string) string {
  function fetchLatestAsset (line 326) | func fetchLatestAsset(ctx context.Context, client *http.Client, releaseU...
  function downloadAsset (line 371) | func downloadAsset(ctx context.Context, client *http.Client, downloadURL...
  function fileSHA256 (line 404) | func fileSHA256(path string) (string, error) {
  function atomicWriteFile (line 421) | func atomicWriteFile(path string, data []byte) error {
  function parseDigest (line 452) | func parseDigest(digest string) string {

FILE: internal/misc/copy-example-config.go
  function CopyConfigTemplate (line 11) | func CopyConfigTemplate(src, dst string) error {

FILE: internal/misc/credentials.go
  function LogSavingCredentials (line 16) | func LogSavingCredentials(path string) {
  function LogCredentialSeparator (line 25) | func LogCredentialSeparator() {
  function MergeMetadata (line 30) | func MergeMetadata(source any, metadata map[string]any) (map[string]any,...

FILE: internal/misc/header_utils.go
  constant GeminiCLIVersion (line 15) | GeminiCLIVersion = "0.31.0"
  constant GeminiCLIApiClientHeader (line 18) | GeminiCLIApiClientHeader = "google-genai-sdk/1.41.0 gl-node/v22.19.0"
  function geminiCLIOS (line 22) | func geminiCLIOS() string {
  function geminiCLIArch (line 32) | func geminiCLIArch() string {
  function GeminiCLIUserAgent (line 45) | func GeminiCLIUserAgent(model string) string {
  function ScrubProxyAndFingerprintHeaders (line 57) | func ScrubProxyAndFingerprintHeaders(req *http.Request) {
  function EnsureHeader (line 109) | func EnsureHeader(target http.Header, source http.Header, key, defaultVa...

FILE: internal/misc/oauth.go
  function GenerateRandomState (line 17) | func GenerateRandomState() (string, error) {
  type OAuthCallback (line 26) | type OAuthCallback struct
  function ParseOAuthCallback (line 35) | func ParseOAuthCallback(input string) (*OAuthCallback, error) {

FILE: internal/registry/model_definitions.go
  type staticModelsJSON (line 10) | type staticModelsJSON struct
  function GetClaudeModels (line 27) | func GetClaudeModels() []*ModelInfo {
  function GetGeminiModels (line 32) | func GetGeminiModels() []*ModelInfo {
  function GetGeminiVertexModels (line 37) | func GetGeminiVertexModels() []*ModelInfo {
  function GetGeminiCLIModels (line 42) | func GetGeminiCLIModels() []*ModelInfo {
  function GetAIStudioModels (line 47) | func GetAIStudioModels() []*ModelInfo {
  function GetCodexFreeModels (line 52) | func GetCodexFreeModels() []*ModelInfo {
  function GetCodexTeamModels (line 57) | func GetCodexTeamModels() []*ModelInfo {
  function GetCodexPlusModels (line 62) | func GetCodexPlusModels() []*ModelInfo {
  function GetCodexProModels (line 67) | func GetCodexProModels() []*ModelInfo {
  function GetQwenModels (line 72) | func GetQwenModels() []*ModelInfo {
  function GetIFlowModels (line 77) | func GetIFlowModels() []*ModelInfo {
  function GetKimiModels (line 82) | func GetKimiModels() []*ModelInfo {
  function GetAntigravityModels (line 87) | func GetAntigravityModels() []*ModelInfo {
  function cloneModelInfos (line 92) | func cloneModelInfos(models []*ModelInfo) []*ModelInfo {
  function GetStaticModelDefinitionsByChannel (line 117) | func GetStaticModelDefinitionsByChannel(channel string) []*ModelInfo {
  function LookupStaticModelInfo (line 147) | func LookupStaticModelInfo(modelID string) *ModelInfo {

FILE: internal/registry/model_registry.go
  type ModelInfo (line 19) | type ModelInfo struct
  type availableModelsCacheEntry (line 65) | type availableModelsCacheEntry struct
  type ThinkingSupport (line 72) | type ThinkingSupport struct
  type ModelRegistration (line 87) | type ModelRegistration struct
  type ModelRegistryHook (line 106) | type ModelRegistryHook interface
  type ModelRegistry (line 112) | type ModelRegistry struct
    method ensureAvailableModelsCacheLocked (line 148) | func (r *ModelRegistry) ensureAvailableModelsCacheLocked() {
    method invalidateAvailableModelsCacheLocked (line 154) | func (r *ModelRegistry) invalidateAvailableModelsCacheLocked() {
    method SetHook (line 180) | func (r *ModelRegistry) SetHook(hook ModelRegistryHook) {
    method triggerModelsRegistered (line 192) | func (r *ModelRegistry) triggerModelsRegistered(provider, clientID str...
    method triggerModelsUnregistered (line 210) | func (r *ModelRegistry) triggerModelsUnregistered(provider, clientID s...
    method RegisterClient (line 232) | func (r *ModelRegistry) RegisterClient(clientID, clientProvider string...
    method addModelRegistration (line 447) | func (r *ModelRegistry) addModelRegistration(modelID, provider string,...
    method removeModelRegistration (line 488) | func (r *ModelRegistry) removeModelRegistration(clientID, modelID, pro...
    method UnregisterClient (line 572) | func (r *ModelRegistry) UnregisterClient(clientID string) {
    method unregisterClientInternal (line 580) | func (r *ModelRegistry) unregisterClientInternal(clientID string) {
    method SetModelQuotaExceeded (line 640) | func (r *ModelRegistry) SetModelQuotaExceeded(clientID, modelID string) {
    method ClearModelQuotaExceeded (line 657) | func (r *ModelRegistry) ClearModelQuotaExceeded(clientID, modelID stri...
    method SuspendClientModel (line 674) | func (r *ModelRegistry) SuspendClientModel(clientID, modelID, reason s...
    method ResumeClientModel (line 706) | func (r *ModelRegistry) ResumeClientModel(clientID, modelID string) {
    method ClientSupportsModel (line 728) | func (r *ModelRegistry) ClientSupportsModel(clientID, modelID string) ...
    method GetAvailableModels (line 758) | func (r *ModelRegistry) GetAvailableModels(handlerType string) []map[s...
    method buildAvailableModelsLocked (line 786) | func (r *ModelRegistry) buildAvailableModelsLocked(handlerType string,...
    method GetAvailableModelsByProvider (line 878) | func (r *ModelRegistry) GetAvailableModelsByProvider(provider string) ...
    method GetModelCount (line 1002) | func (r *ModelRegistry) GetModelCount(modelID string) int {
    method GetModelProviders (line 1035) | func (r *ModelRegistry) GetModelProviders(modelID string) []string {
    method GetModelInfo (line 1087) | func (r *ModelRegistry) GetModelInfo(modelID, provider string) *ModelI...
    method convertModelToMap (line 1108) | func (r *ModelRegistry) convertModelToMap(model *ModelInfo, handlerTyp...
    method CleanupExpiredQuotas (line 1216) | func (r *ModelRegistry) CleanupExpiredQuotas() {
    method GetFirstAvailableModel (line 1247) | func (r *ModelRegistry) GetFirstAvailableModel(handlerType string) (st...
    method GetModelsForClient (line 1284) | func (r *ModelRegistry) GetModelsForClient(clientID string) []*ModelIn...
  function GetGlobalRegistry (line 135) | func GetGlobalRegistry() *ModelRegistry {
  function LookupModelInfo (line 162) | func LookupModelInfo(modelID string, provider ...string) *ModelInfo {
  constant defaultModelRegistryHookTimeout (line 189) | defaultModelRegistryHookTimeout = 5 * time.Second
  constant modelQuotaExceededWindow (line 190) | modelQuotaExceededWindow = 5 * time.Minute
  function cloneModelInfo (line 523) | func cloneModelInfo(model *ModelInfo) *ModelInfo {
  function cloneModelInfosUnique (line 550) | func cloneModelInfosUnique(models []*ModelInfo) []*ModelInfo {
  function cloneModelMaps (line 835) | func cloneModelMaps(models []map[string]any) []map[string]any {
  function cloneModelMapValue (line 851) | func cloneModelMapValue(value any) any {

FILE: internal/registry/model_registry_cache_test.go
  function TestGetAvailableModelsReturnsClonedSnapshots (line 5) | func TestGetAvailableModelsReturnsClonedSnapshots(t *testing.T) {
  function TestGetAvailableModelsInvalidatesCacheOnRegistryChanges (line 25) | func TestGetAvailableModelsInvalidatesCacheOnRegistryChanges(t *testing....

FILE: internal/registry/model_registry_hook_test.go
  function newTestModelRegistry (line 10) | func newTestModelRegistry() *ModelRegistry {
  type registeredCall (line 20) | type registeredCall struct
  type unregisteredCall (line 26) | type unregisteredCall struct
  type capturingHook (line 31) | type capturingHook struct
    method OnModelsRegistered (line 36) | func (h *capturingHook) OnModelsRegistered(ctx context.Context, provid...
    method OnModelsUnregistered (line 40) | func (h *capturingHook) OnModelsUnregistered(ctx context.Context, prov...
  function TestModelRegistryHook_OnModelsRegisteredCalled (line 44) | func TestModelRegistryHook_OnModelsRegisteredCalled(t *testing.T) {
  function TestModelRegistryHook_OnModelsUnregisteredCalled (line 80) | func TestModelRegistryHook_OnModelsUnregisteredCalled(t *testing.T) {
  type blockingHook (line 110) | type blockingHook struct
    method OnModelsRegistered (line 115) | func (h *blockingHook) OnModelsRegistered(ctx context.Context, provide...
    method OnModelsUnregistered (line 124) | func (h *blockingHook) OnModelsUnregistered(ctx context.Context, provi...
  function TestModelRegistryHook_DoesNotBlockRegisterClient (line 126) | func TestModelRegistryHook_DoesNotBlockRegisterClient(t *testing.T) {
  type panicHook (line 158) | type panicHook struct
    method OnModelsRegistered (line 163) | func (h *panicHook) OnModelsRegistered(ctx context.Context, provider, ...
    method OnModelsUnregistered (line 170) | func (h *panicHook) OnModelsUnregistered(ctx context.Context, provider...
  function TestModelRegistryHook_PanicDoesNotAffectRegistry (line 177) | func TestModelRegistryHook_PanicDoesNotAffectRegistry(t *testing.T) {

FILE: internal/registry/model_registry_safety_test.go
  function TestGetModelInfoReturnsClone (line 8) | func TestGetModelInfoReturnsClone(t *testing.T) {
  function TestGetModelsForClientReturnsClones (line 32) | func TestGetModelsForClientReturnsClones(t *testing.T) {
  function TestGetAvailableModelsByProviderReturnsClones (line 59) | func TestGetAvailableModelsByProviderReturnsClones(t *testing.T) {
  function TestCleanupExpiredQuotasInvalidatesAvailableModelsCache (line 86) | func TestCleanupExpiredQuotasInvalidatesAvailableModelsCache(t *testing....
  function TestGetAvailableModelsReturnsClonedSupportedParameters (line 113) | func TestGetAvailableModelsReturnsClonedSupportedParameters(t *testing.T) {
  function TestLookupModelInfoReturnsCloneForStaticDefinitions (line 138) | func TestLookupModelInfoReturnsCloneForStaticDefinitions(t *testing.T) {

FILE: internal/registry/model_updater.go
  constant modelsFetchTimeout (line 18) | modelsFetchTimeout    = 30 * time.Second
  constant modelsRefreshInterval (line 19) | modelsRefreshInterval = 3 * time.Hour
  type modelStore (line 30) | type modelStore struct
  type ModelRefreshCallback (line 41) | type ModelRefreshCallback
  function SetModelRefreshCallback (line 52) | func SetModelRefreshCallback(cb ModelRefreshCallback) {
  function init (line 67) | func init() {
  function StartModelsUpdater (line 77) | func StartModelsUpdater(ctx context.Context) {
  function runModelsUpdater (line 83) | func runModelsUpdater(ctx context.Context) {
  function periodicRefresh (line 88) | func periodicRefresh(ctx context.Context) {
  function tryPeriodicRefresh (line 104) | func tryPeriodicRefresh(ctx context.Context) {
  function tryStartupRefresh (line 111) | func tryStartupRefresh(ctx context.Context) {
  function tryRefreshModels (line 115) | func tryRefreshModels(ctx context.Context, label string) {
  function fetchModelsFromRemote (line 143) | func fetchModelsFromRemote(ctx context.Context) (*staticModelsJSON, stri...
  function detectChangedProviders (line 195) | func detectChangedProviders(oldData, newData *staticModelsJSON) []string {
  function modelSectionChanged (line 237) | func modelSectionChanged(a, b []*ModelInfo) bool {
  function notifyModelRefresh (line 252) | func notifyModelRefresh(changedProviders []string) {
  function mergeProviderNames (line 268) | func mergeProviderNames(existing, incoming []string) []string {
  function loadModelsFromBytes (line 299) | func loadModelsFromBytes(data []byte, source string) error {
  function getModels (line 314) | func getModels() *staticModelsJSON {
  function validateModelsCatalog (line 320) | func validateModelsCatalog(data *staticModelsJSON) error {
  function validateModelSection (line 352) | func validateModelSection(section string, models []*ModelInfo) error {

FILE: internal/runtime/executor/aistudio_executor.go
  type AIStudioExecutor (line 27) | type AIStudioExecutor struct
    method Identifier (line 47) | func (e *AIStudioExecutor) Identifier() string { return "aistudio" }
    method PrepareRequest (line 50) | func (e *AIStudioExecutor) PrepareRequest(_ *http.Request, _ *cliproxy...
    method HttpRequest (line 55) | func (e *AIStudioExecutor) HttpRequest(ctx context.Context, auth *clip...
    method Execute (line 113) | func (e *AIStudioExecutor) Execute(ctx context.Context, auth *cliproxy...
    method ExecuteStream (line 172) | func (e *AIStudioExecutor) ExecuteStream(ctx context.Context, auth *cl...
    method CountTokens (line 324) | func (e *AIStudioExecutor) CountTokens(ctx context.Context, auth *clip...
    method Refresh (line 380) | func (e *AIStudioExecutor) Refresh(_ context.Context, auth *cliproxyau...
    method translateRequest (line 390) | func (e *AIStudioExecutor) translateRequest(req cliproxyexecutor.Reque...
    method buildEndpoint (line 426) | func (e *AIStudioExecutor) buildEndpoint(model, action, alt string) st...
  function NewAIStudioExecutor (line 42) | func NewAIStudioExecutor(cfg *config.Config, provider string, relay *wsr...
  type translatedPayload (line 384) | type translatedPayload struct
  function ensureColonSpacedJSON (line 442) | func ensureColonSpacedJSON(payload []byte) []byte {

FILE: internal/runtime/executor/antigravity_executor.go
  constant antigravityBaseURLDaily (line 39) | antigravityBaseURLDaily        = "https://daily-cloudcode-pa.googleapis....
  constant antigravitySandboxBaseURLDaily (line 40) | antigravitySandboxBaseURLDaily = "https://daily-cloudcode-pa.sandbox.goo...
  constant antigravityBaseURLProd (line 41) | antigravityBaseURLProd         = "https://cloudcode-pa.googleapis.com"
  constant antigravityCountTokensPath (line 42) | antigravityCountTokensPath     = "/v1internal:countTokens"
  constant antigravityStreamPath (line 43) | antigravityStreamPath          = "/v1internal:streamGenerateContent"
  constant antigravityGeneratePath (line 44) | antigravityGeneratePath        = "/v1internal:generateContent"
  constant antigravityClientID (line 45) | antigravityClientID            = "1071006060591-tmhssin2h21lcre235vtoloj...
  constant antigravityClientSecret (line 46) | antigravityClientSecret        = "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"
  constant defaultAntigravityAgent (line 47) | defaultAntigravityAgent        = "antigravity/1.19.6 darwin/arm64"
  constant antigravityAuthType (line 48) | antigravityAuthType            = "antigravity"
  constant refreshSkew (line 49) | refreshSkew                    = 3000 * time.Second
  type AntigravityExecutor (line 59) | type AntigravityExecutor struct
    method Identifier (line 131) | func (e *AntigravityExecutor) Identifier() string { return antigravity...
    method PrepareRequest (line 134) | func (e *AntigravityExecutor) PrepareRequest(req *http.Request, auth *...
    method HttpRequest (line 152) | func (e *AntigravityExecutor) HttpRequest(ctx context.Context, auth *c...
    method Execute (line 187) | func (e *AntigravityExecutor) Execute(ctx context.Context, auth *clipr...
    method executeClaudeNonStream (line 337) | func (e *AntigravityExecutor) executeClaudeNonStream(ctx context.Conte...
    method convertStreamToNonStream (line 541) | func (e *AntigravityExecutor) convertStreamToNonStream(stream []byte) ...
    method ExecuteStream (line 724) | func (e *AntigravityExecutor) ExecuteStream(ctx context.Context, auth ...
    method Refresh (line 922) | func (e *AntigravityExecutor) Refresh(ctx context.Context, auth *clipr...
    method CountTokens (line 934) | func (e *AntigravityExecutor) CountTokens(ctx context.Context, auth *c...
    method ensureAccessToken (line 1081) | func (e *AntigravityExecutor) ensureAccessToken(ctx context.Context, a...
    method refreshToken (line 1103) | func (e *AntigravityExecutor) refreshToken(ctx context.Context, auth *...
    method ensureAntigravityProjectID (line 1181) | func (e *AntigravityExecutor) ensureAntigravityProjectID(ctx context.C...
    method buildRequest (line 1214) | func (e *AntigravityExecutor) buildRequest(ctx context.Context, auth *...
  function NewAntigravityExecutor (line 70) | func NewAntigravityExecutor(cfg *config.Config) *AntigravityExecutor {
  function cloneTransportWithHTTP11 (line 82) | func cloneTransportWithHTTP11(base *http.Transport) *http.Transport {
  function initAntigravityTransport (line 102) | func initAntigravityTransport() {
  function newAntigravityHTTPClient (line 113) | func newAntigravityHTTPClient(ctx context.Context, cfg *config.Config, a...
  function tokenExpiry (line 1322) | func tokenExpiry(metadata map[string]any) time.Time {
  function metaStringValue (line 1342) | func metaStringValue(metadata map[string]any, key string) string {
  function int64Value (line 1357) | func int64Value(value any) (int64, bool) {
  function buildBaseURL (line 1380) | func buildBaseURL(auth *cliproxyauth.Auth) string {
  function resolveHost (line 1387) | func resolveHost(base string) string {
  function resolveUserAgent (line 1398) | func resolveUserAgent(auth *cliproxyauth.Auth) string {
  function antigravityRetryAttempts (line 1414) | func antigravityRetryAttempts(auth *cliproxyauth.Auth, cfg *config.Confi...
  function antigravityShouldRetryNoCapacity (line 1434) | func antigravityShouldRetryNoCapacity(statusCode int, body []byte) bool {
  function antigravityNoCapacityRetryDelay (line 1445) | func antigravityNoCapacityRetryDelay(attempt int) time.Duration {
  function antigravityWait (line 1456) | func antigravityWait(ctx context.Context, wait time.Duration) error {
  function antigravityBaseURLFallbackOrder (line 1470) | func antigravityBaseURLFallbackOrder(auth *cliproxyauth.Auth) []string {
  function resolveCustomAntigravityBaseURL (line 1481) | func resolveCustomAntigravityBaseURL(auth *cliproxyauth.Auth) string {
  function geminiToAntigravity (line 1501) | func geminiToAntigravity(modelName string, payload []byte, projectID str...
  function generateRequestID (line 1537) | func generateRequestID() string {
  function generateImageGenRequestID (line 1541) | func generateImageGenRequestID() string {
  function generateSessionID (line 1545) | func generateSessionID() string {
  function generateStableSessionID (line 1552) | func generateStableSessionID(payload []byte) string {
  function generateProjectID (line 1569) | func generateProjectID() string {

FILE: internal/runtime/executor/antigravity_executor_buildrequest_test.go
  function TestAntigravityBuildRequest_SanitizesGeminiToolSchema (line 12) | func TestAntigravityBuildRequest_SanitizesGeminiToolSchema(t *testing.T) {
  function TestAntigravityBuildRequest_SanitizesAntigravityToolSchema (line 27) | func TestAntigravityBuildRequest_SanitizesAntigravityToolSchema(t *testi...
  function buildRequestBodyFromPayload (line 38) | func buildRequestBodyFromPayload(t *testing.T, modelName string) map[str...
  function extractFirstFunctionDeclaration (line 97) | func extractFirstFunctionDeclaration(t *testing.T, body map[string]any) ...
  function assertSchemaSanitizedAndPropertyPreserved (line 123) | func assertSchemaSanitizedAndPropertyPreserved(t *testing.T, params map[...

FILE: internal/runtime/executor/cache_helpers.go
  type codexCache (line 8) | type codexCache struct
  constant codexCacheCleanupInterval (line 21) | codexCacheCleanupInterval = 15 * time.Minute
  function startCodexCacheCleanup (line 28) | func startCodexCacheCleanup() {
  function purgeExpiredCodexCache (line 39) | func purgeExpiredCodexCache() {
  function getCodexCache (line 51) | func getCodexCache(key string) (codexCache, bool) {
  function setCodexCache (line 63) | func setCodexCache(key string, cache codexCache) {

FILE: internal/runtime/executor/caching_verify_test.go
  function TestEnsureCacheControl (line 10) | func TestEnsureCacheControl(t *testing.T) {
  function TestCacheControlOrder (line 219) | func TestCacheControlOrder(t *testing.T) {

FILE: internal/runtime/executor/claude_executor.go
  type ClaudeExecutor (line 40) | type ClaudeExecutor struct
    method Identifier (line 50) | func (e *ClaudeExecutor) Identifier() string { return "claude" }
    method PrepareRequest (line 53) | func (e *ClaudeExecutor) PrepareRequest(req *http.Request, auth *clipr...
    method HttpRequest (line 79) | func (e *ClaudeExecutor) HttpRequest(ctx context.Context, auth *clipro...
    method Execute (line 94) | func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyau...
    method ExecuteStream (line 262) | func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *clip...
    method CountTokens (line 458) | func (e *ClaudeExecutor) CountTokens(ctx context.Context, auth *clipro...
    method Refresh (line 567) | func (e *ClaudeExecutor) Refresh(ctx context.Context, auth *cliproxyau...
  constant claudeToolPrefix (line 46) | claudeToolPrefix = ""
  function NewClaudeExecutor (line 48) | func NewClaudeExecutor(cfg *config.Config) *ClaudeExecutor { return &Cla...
  function extractAndRemoveBetas (line 603) | func extractAndRemoveBetas(body []byte) ([]string, []byte) {
  function disableThinkingIfToolChoiceForced (line 625) | func disableThinkingIfToolChoiceForced(body []byte) []byte {
  type compositeReadCloser (line 641) | type compositeReadCloser struct
    method Close (line 646) | func (c *compositeReadCloser) Close() error {
  type peekableBody (line 661) | type peekableBody struct
    method Close (line 666) | func (p *peekableBody) Close() error {
  function decodeResponseBody (line 670) | func decodeResponseBody(body io.ReadCloser, contentEncoding string) (io....
  function mapStainlessOS (line 771) | func mapStainlessOS() string {
  function mapStainlessArch (line 787) | func mapStainlessArch() string {
  function applyClaudeHeaders (line 800) | func applyClaudeHeaders(r *http.Request, auth *cliproxyauth.Auth, apiKey...
  function claudeCreds (line 915) | func claudeCreds(a *cliproxyauth.Auth) (apiKey, baseURL string) {
  function checkSystemInstructions (line 931) | func checkSystemInstructions(payload []byte) []byte {
  function isClaudeOAuthToken (line 935) | func isClaudeOAuthToken(apiKey string) bool {
  function applyClaudeToolPrefix (line 939) | func applyClaudeToolPrefix(body []byte, prefix string) []byte {
  function stripClaudeToolPrefixFromResponse (line 1026) | func stripClaudeToolPrefixFromResponse(body []byte, prefix string) []byte {
  function stripClaudeToolPrefixFromStreamLine (line 1072) | func stripClaudeToolPrefixFromStreamLine(line []byte, prefix string) []b...
  function getClientUserAgent (line 1120) | func getClientUserAgent(ctx context.Context) string {
  function getCloakConfigFromAuth (line 1129) | func getCloakConfigFromAuth(auth *cliproxyauth.Auth) (string, bool, []st...
  function resolveClaudeKeyCloakConfig (line 1155) | func resolveClaudeKeyCloakConfig(cfg *config.Config, auth *cliproxyauth....
  function injectFakeUserID (line 1185) | func injectFakeUserID(payload []byte, apiKey string, useCache bool) []by...
  function generateBillingHeader (line 1209) | func generateBillingHeader(payload []byte) string {
  function checkSystemInstructionsWithMode (line 1228) | func checkSystemInstructionsWithMode(payload []byte, strictMode bool) []...
  function applyCloaking (line 1282) | func applyCloaking(ctx context.Context, cfg *config.Config, auth *clipro...
  function ensureCacheControl (line 1353) | func ensureCacheControl(payload []byte) []byte {
  function countCacheControls (line 1369) | func countCacheControls(payload []byte) int {
  function parsePayloadObject (line 1414) | func parsePayloadObject(payload []byte) (map[string]any, bool) {
  function marshalPayloadObject (line 1425) | func marshalPayloadObject(original []byte, root map[string]any) []byte {
  function asObject (line 1436) | func asObject(v any) (map[string]any, bool) {
  function asArray (line 1441) | func asArray(v any) ([]any, bool) {
  function countCacheControlsMap (line 1446) | func countCacheControlsMap(root map[string]any) int {
  function normalizeTTLForBlock (line 1492) | func normalizeTTLForBlock(obj map[string]any, seen5m *bool) bool {
  function findLastCacheControlIndex (line 1515) | func findLastCacheControlIndex(arr []any) int {
  function stripCacheControlExceptIndex (line 1529) | func stripCacheControlExceptIndex(arr []any, preserveIdx int, excess *in...
  function stripAllCacheControl (line 1545) | func stripAllCacheControl(arr []any, excess *int) {
  function stripMessageCacheControl (line 1561) | func stripMessageCacheControl(messages []any, excess *int) {
  function normalizeCacheControlTTL (line 1601) | func normalizeCacheControlTTL(payload []byte) []byte {
  function enforceCacheControlLimit (line 1672) | func enforceCacheControlLimit(payload []byte, maxBlocks int) []byte {
  function injectMessagesCacheControl (line 1739) | func injectMessagesCacheControl(payload []byte) []byte {
  function injectToolsCacheControl (line 1823) | func injectToolsCacheControl(payload []byte) []byte {
  function injectSystemCacheControl (line 1861) | func injectSystemCacheControl(payload []byte) []byte {

FILE: internal/runtime/executor/claude_executor_test.go
  function TestApplyClaudeToolPrefix (line 22) | func TestApplyClaudeToolPrefix(t *testing.T) {
  function TestApplyClaudeToolPrefix_WithToolReference (line 40) | func TestApplyClaudeToolPrefix_WithToolReference(t *testing.T) {
  function TestApplyClaudeToolPrefix_SkipsBuiltinTools (line 52) | func TestApplyClaudeToolPrefix_SkipsBuiltinTools(t *testing.T) {
  function TestApplyClaudeToolPrefix_BuiltinToolSkipped (line 64) | func TestApplyClaudeToolPrefix_BuiltinToolSkipped(t *testing.T) {
  function TestApplyClaudeToolPrefix_KnownBuiltinInHistoryOnly (line 93) | func TestApplyClaudeToolPrefix_KnownBuiltinInHistoryOnly(t *testing.T) {
  function TestApplyClaudeToolPrefix_CustomToolsPrefixed (line 114) | func TestApplyClaudeToolPrefix_CustomToolsPrefixed(t *testing.T) {
  function TestApplyClaudeToolPrefix_ToolChoiceBuiltin (line 140) | func TestApplyClaudeToolPrefix_ToolChoiceBuiltin(t *testing.T) {
  function TestStripClaudeToolPrefixFromResponse (line 155) | func TestStripClaudeToolPrefixFromResponse(t *testing.T) {
  function TestStripClaudeToolPrefixFromResponse_WithToolReference (line 167) | func TestStripClaudeToolPrefixFromResponse_WithToolReference(t *testing....
  function TestStripClaudeToolPrefixFromStreamLine (line 179) | func TestStripClaudeToolPrefixFromStreamLine(t *testing.T) {
  function TestStripClaudeToolPrefixFromStreamLine_WithToolReference (line 192) | func TestStripClaudeToolPrefixFromStreamLine_WithToolReference(t *testin...
  function TestApplyClaudeToolPrefix_NestedToolReference (line 205) | func TestApplyClaudeToolPrefix_NestedToolReference(t *testing.T) {
  function TestClaudeExecutor_ReusesUserIDAcrossModelsWhenCacheEnabled (line 214) | func TestClaudeExecutor_ReusesUserIDAcrossModelsWhenCacheEnabled(t *test...
  function TestClaudeExecutor_GeneratesNewUserIDByDefault (line 282) | func TestClaudeExecutor_GeneratesNewUserIDByDefault(t *testing.T) {
  function TestStripClaudeToolPrefixFromResponse_NestedToolReference (line 327) | func TestStripClaudeToolPrefixFromResponse_NestedToolReference(t *testin...
  function TestApplyClaudeToolPrefix_NestedToolReferenceWithStringContent (line 336) | func TestApplyClaudeToolPrefix_NestedToolReferenceWithStringContent(t *t...
  function TestApplyClaudeToolPrefix_SkipsBuiltinToolReference (line 346) | func TestApplyClaudeToolPrefix_SkipsBuiltinToolReference(t *testing.T) {
  function TestNormalizeCacheControlTTL_DowngradesLaterOneHourBlocks (line 355) | func TestNormalizeCacheControlTTL_DowngradesLaterOneHourBlocks(t *testin...
  function TestNormalizeCacheControlTTL_PreservesOriginalBytesWhenNoChange (line 372) | func TestNormalizeCacheControlTTL_PreservesOriginalBytesWhenNoChange(t *...
  function TestEnforceCacheControlLimit_StripsNonLastToolBeforeMessages (line 385) | func TestEnforceCacheControlLimit_StripsNonLastToolBeforeMessages(t *tes...
  function TestEnforceCacheControlLimit_ToolOnlyPayloadStillRespectsLimit (line 414) | func TestEnforceCacheControlLimit_ToolOnlyPayloadStillRespectsLimit(t *t...
  function TestClaudeExecutor_CountTokens_AppliesCacheControlGuards (line 438) | func TestClaudeExecutor_CountTokens_AppliesCacheControlGuards(t *testing...
  function hasTTLOrderingViolation (line 488) | func hasTTLOrderingViolation(payload []byte) bool {
  function TestClaudeExecutor_Execute_InvalidGzipErrorBodyReturnsDecodeMessage (line 539) | func TestClaudeExecutor_Execute_InvalidGzipErrorBodyReturnsDecodeMessage...
  function TestClaudeExecutor_ExecuteStream_InvalidGzipErrorBodyReturnsDecodeMessage (line 549) | func TestClaudeExecutor_ExecuteStream_InvalidGzipErrorBodyReturnsDecodeM...
  function TestClaudeExecutor_CountTokens_InvalidGzipErrorBodyReturnsDecodeMessage (line 559) | func TestClaudeExecutor_CountTokens_InvalidGzipErrorBodyReturnsDecodeMes...
  function testClaudeExecutorInvalidCompressedErrorBody (line 569) | func testClaudeExecutorInvalidCompressedErrorBody(
  function TestClaudeExecutor_ExecuteStream_SetsIdentityAcceptEncoding (line 605) | func TestClaudeExecutor_ExecuteStream_SetsIdentityAcceptEncoding(t *test...
  function TestClaudeExecutor_Execute_SetsCompressedAcceptEncoding (line 648) | func TestClaudeExecutor_Execute_SetsCompressedAcceptEncoding(t *testing....
  function TestClaudeExecutor_ExecuteStream_GzipSuccessBodyDecoded (line 686) | func TestClaudeExecutor_ExecuteStream_GzipSuccessBodyDecoded(t *testing....
  function TestDecodeResponseBody_MagicByteGzipNoHeader (line 735) | func TestDecodeResponseBody_MagicByteGzipNoHeader(t *testing.T) {
  function TestDecodeResponseBody_PlainTextNoHeader (line 761) | func TestDecodeResponseBody_PlainTextNoHeader(t *testing.T) {
  function TestClaudeExecutor_ExecuteStream_GzipNoContentEncodingHeader (line 783) | func TestClaudeExecutor_ExecuteStream_GzipNoContentEncodingHeader(t *tes...
  function TestClaudeExecutor_ExecuteStream_AcceptEncodingOverrideCannotBypassIdentity (line 833) | func TestClaudeExecutor_ExecuteStream_AcceptEncodingOverrideCannotBypass...
  function TestDecodeResponseBody_MagicByteZstdNoHeader (line 874) | func TestDecodeResponseBody_MagicByteZstdNoHeader(t *testing.T) {
  function TestClaudeExecutor_Execute_GzipErrorBodyNoContentEncodingHeader (line 905) | func TestClaudeExecutor_Execute_GzipErrorBodyNoContentEncodingHeader(t *...
  function TestClaudeExecutor_ExecuteStream_GzipErrorBodyNoContentEncodingHeader (line 946) | func TestClaudeExecutor_ExecuteStream_GzipErrorBodyNoContentEncodingHead...
  function TestCheckSystemInstructionsWithMode_StringSystemPreserved (line 985) | func TestCheckSystemInstructionsWithMode_StringSystemPreserved(t *testin...
  function TestCheckSystemInstructionsWithMode_StringSystemStrict (line 1015) | func TestCheckSystemInstructionsWithMode_StringSystemStrict(t *testing.T) {
  function TestCheckSystemInstructionsWithMode_EmptyStringSystemIgnored (line 1027) | func TestCheckSystemInstructionsWithMode_EmptyStringSystemIgnored(t *tes...
  function TestCheckSystemInstructionsWithMode_ArraySystemStillWorks (line 1039) | func TestCheckSystemInstructionsWithMode_ArraySystemStillWorks(t *testin...
  function TestCheckSystemInstructionsWithMode_StringWithSpecialChars (line 1054) | func TestCheckSystemInstructionsWithMode_StringWithSpecialChars(t *testi...

FILE: internal/runtime/executor/cloak_obfuscate.go
  constant zeroWidthSpace (line 14) | zeroWidthSpace = "\u200B"
  type SensitiveWordMatcher (line 17) | type SensitiveWordMatcher struct
    method obfuscateText (line 77) | func (m *SensitiveWordMatcher) obfuscateText(text string) string {
  function buildSensitiveWordMatcher (line 23) | func buildSensitiveWordMatcher(words []string) *SensitiveWordMatcher {
  function obfuscateWord (line 62) | func obfuscateWord(word string) string {
  function obfuscateSensitiveWords (line 86) | func obfuscateSensitiveWords(payload []byte, matcher *SensitiveWordMatch...
  function obfuscateSystemBlocks (line 101) | func obfuscateSystemBlocks(payload []byte, matcher *SensitiveWordMatcher...
  function obfuscateMessages (line 136) | func obfuscateMessages(payload []byte, matcher *SensitiveWordMatcher) []...

FILE: internal/runtime/executor/cloak_utils.go
  function generateFakeUserID (line 17) | func generateFakeUserID() string {
  function isValidUserID (line 27) | func isValidUserID(userID string) bool {
  function shouldCloak (line 33) | func shouldCloak(cloakMode string, userAgent string) bool {
  function isClaudeCodeClient (line 46) | func isClaudeCodeClient(userAgent string) bool {

FILE: internal/runtime/executor/codex_executor.go
  constant codexClientVersion (line 31) | codexClientVersion = "0.101.0"
  constant codexUserAgent (line 32) | codexUserAgent     = "codex_cli_rs/0.101.0 (Mac OS 26.0.1; arm64) Apple_...
  type CodexExecutor (line 39) | type CodexExecutor struct
    method Identifier (line 45) | func (e *CodexExecutor) Identifier() string { return "codex" }
    method PrepareRequest (line 48) | func (e *CodexExecutor) PrepareRequest(req *http.Request, auth *clipro...
    method HttpRequest (line 65) | func (e *CodexExecutor) HttpRequest(ctx context.Context, auth *cliprox...
    method Execute (line 80) | func (e *CodexExecutor) Execute(ctx context.Context, auth *cliproxyaut...
    method executeCompact (line 193) | func (e *CodexExecutor) executeCompact(ctx context.Context, auth *clip...
    method ExecuteStream (line 280) | func (e *CodexExecutor) ExecuteStream(ctx context.Context, auth *clipr...
    method CountTokens (line 402) | func (e *CodexExecutor) CountTokens(ctx context.Context, auth *cliprox...
    method Refresh (line 560) | func (e *CodexExecutor) Refresh(ctx context.Context, auth *cliproxyaut...
    method cacheHelper (line 599) | func (e *CodexExecutor) cacheHelper(ctx context.Context, from sdktrans...
    method resolveCodexConfig (line 726) | func (e *CodexExecutor) resolveCodexConfig(auth *cliproxyauth.Auth) *c...
  function NewCodexExecutor (line 43) | func NewCodexExecutor(cfg *config.Config) *CodexExecutor { return &Codex...
  function tokenizerForCodexModel (line 438) | func tokenizerForCodexModel(model string) (tokenizer.Codec, error) {
  function countCodexInputTokens (line 458) | func countCodexInputTokens(enc tokenizer.Codec, body []byte) (int64, err...
  function applyCodexHeaders (line 639) | func applyCodexHeaders(r *http.Request, auth *cliproxyauth.Auth, token s...
  function newCodexStatusErr (line 681) | func newCodexStatusErr(statusCode int, body []byte) statusErr {
  function parseCodexRetryAfter (line 689) | func parseCodexRetryAfter(statusCode int, errorBody []byte, now time.Tim...
  function codexCreds (line 710) | func codexCreds(a *cliproxyauth.Auth) (apiKey, baseURL string) {

FILE: internal/runtime/executor/codex_executor_cache_test.go
  function TestCodexExecutorCacheHelper_OpenAIChatCompletions_StablePromptCacheKeyFromAPIKey (line 16) | func TestCodexExecutorCacheHelper_OpenAIChatCompletions_StablePromptCach...

FILE: internal/runtime/executor/codex_executor_retry_test.go
  function TestParseCodexRetryAfter (line 10) | func TestParseCodexRetryAfter(t *testing.T) {
  function itoa (line 63) | func itoa(v int64) string {

FILE: internal/runtime/executor/codex_websockets_executor.go
  constant codexResponsesWebsocketBetaHeaderValue (line 35) | codexResponsesWebsocketBetaHeaderValue = "responses_websockets=2026-02-06"
  constant codexResponsesWebsocketIdleTimeout (line 36) | codexResponsesWebsocketIdleTimeout     = 5 * time.Minute
  constant codexResponsesWebsocketHandshakeTO (line 37) | codexResponsesWebsocketHandshakeTO     = 30 * time.Second
  type CodexWebsocketsExecutor (line 44) | type CodexWebsocketsExecutor struct
    method Execute (line 144) | func (e *CodexWebsocketsExecutor) Execute(ctx context.Context, auth *c...
    method ExecuteStream (line 352) | func (e *CodexWebsocketsExecutor) ExecuteStream(ctx context.Context, a...
    method dialCodexWebsocket (line 610) | func (e *CodexWebsocketsExecutor) dialCodexWebsocket(ctx context.Conte...
    method getOrCreateSession (line 1048) | func (e *CodexWebsocketsExecutor) getOrCreateSession(sessionID string)...
    method ensureUpstreamConn (line 1066) | func (e *CodexWebsocketsExecutor) ensureUpstreamConn(ctx context.Conte...
    method readUpstreamLoop (line 1112) | func (e *CodexWebsocketsExecutor) readUpstreamLoop(sess *codexWebsocke...
    method invalidateUpstreamConn (line 1173) | func (e *CodexWebsocketsExecutor) invalidateUpstreamConn(sess *codexWe...
    method CloseExecutionSession (line 1199) | func (e *CodexWebsocketsExecutor) CloseExecutionSession(sessionID stri...
    method closeAllExecutionSessions (line 1220) | func (e *CodexWebsocketsExecutor) closeAllExecutionSessions(reason str...
    method closeExecutionSession (line 1240) | func (e *CodexWebsocketsExecutor) closeExecutionSession(sess *codexWeb...
  type codexWebsocketSession (line 51) | type codexWebsocketSession struct
    method setActive (line 85) | func (s *codexWebsocketSession) setActive(ch chan codexWebsocketRead) {
    method clearActive (line 104) | func (s *codexWebsocketSession) clearActive(ch chan codexWebsocketRead) {
    method writeMessage (line 120) | func (s *codexWebsocketSession) writeMessage(conn *websocket.Conn, msg...
    method configureConn (line 132) | func (s *codexWebsocketSession) configureConn(conn *websocket.Conn) {
  function NewCodexWebsocketsExecutor (line 71) | func NewCodexWebsocketsExecutor(cfg *config.Config) *CodexWebsocketsExec...
  type codexWebsocketRead (line 78) | type codexWebsocketRead struct
  function writeCodexWebsocketMessage (line 626) | func writeCodexWebsocketMessage(sess *codexWebsocketSession, conn *webso...
  function buildCodexWebsocketRequestBody (line 636) | func buildCodexWebsocketRequestBody(body []byte) []byte {
  function readCodexWebsocketMessage (line 653) | func readCodexWebsocketMessage(ctx context.Context, sess *codexWebsocket...
  function newProxyAwareWebsocketDialer (line 687) | func newProxyAwareWebsocketDialer(cfg *config.Config, auth *cliproxyauth...
  function buildCodexResponsesWebsocketURL (line 750) | func buildCodexResponsesWebsocketURL(httpURL string) (string, error) {
  function applyCodexPromptCacheHeaders (line 764) | func applyCodexPromptCacheHeaders(from sdktranslator.Format, req cliprox...
  function applyCodexWebsocketHeaders (line 800) | func applyCodexWebsocketHeaders(ctx context.Context, headers http.Header...
  function codexHeaderDefaults (line 857) | func codexHeaderDefaults(cfg *config.Config, auth *cliproxyauth.Auth) (s...
  function ensureHeaderWithPriority (line 869) | func ensureHeaderWithPriority(target http.Header, source http.Header, ke...
  function ensureHeaderWithConfigPrecedence (line 891) | func ensureHeaderWithConfigPrecedence(target http.Header, source http.He...
  type statusErrWithHeaders (line 913) | type statusErrWithHeaders struct
    method Headers (line 918) | func (e statusErrWithHeaders) Headers() http.Header {
  function parseCodexWebsocketError (line 925) | func parseCodexWebsocketError(payload []byte) (error, bool) {
  function parseCodexWebsocketErrorHeaders (line 959) | func parseCodexWebsocketErrorHeaders(payload []byte) http.Header {
  function normalizeCodexWebsocketCompletion (line 989) | func normalizeCodexWebsocketCompletion(payload []byte) []byte {
  function encodeCodexWebsocketAsSSE (line 999) | func encodeCodexWebsocketAsSSE(payload []byte) []byte {
  function websocketHandshakeBody (line 1009) | func websocketHandshakeBody(resp *http.Response) []byte {
  function closeHTTPResponseBody (line 1021) | func closeHTTPResponseBody(resp *http.Response, logPrefix string) {
  function executionSessionIDFromOptions (line 1030) | func executionSessionIDFromOptions(opts cliproxyexecutor.Options) string {
  function logCodexWebsocketConnected (line 1269) | func logCodexWebsocketConnected(sessionID string, authID string, wsURL s...
  function logCodexWebsocketDisconnected (line 1273) | func logCodexWebsocketDisconnected(sessionID string, authID string, wsUR...
  type CodexAutoExecutor (line 1286) | type CodexAutoExecutor struct
    method Identifier (line 1298) | func (e *CodexAutoExecutor) Identifier() string { return "codex" }
    method PrepareRequest (line 1300) | func (e *CodexAutoExecutor) PrepareRequest(req *http.Request, auth *cl...
    method HttpRequest (line 1307) | func (e *CodexAutoExecutor) HttpRequest(ctx context.Context, auth *cli...
    method Execute (line 1314) | func (e *CodexAutoExecutor) Execute(ctx context.Context, auth *cliprox...
    method ExecuteStream (line 1324) | func (e *CodexAutoExecutor) ExecuteStream(ctx context.Context, auth *c...
    method Refresh (line 1334) | func (e *CodexAutoExecutor) Refresh(ctx context.Context, auth *cliprox...
    method CountTokens (line 1341) | func (e *CodexAutoExecutor) CountTokens(ctx context.Context, auth *cli...
    method CloseExecutionSession (line 1348) | func (e *CodexAutoExecutor) CloseExecutionSession(sessionID string) {
  function NewCodexAutoExecutor (line 1291) | func NewCodexAutoExecutor(cfg *config.Config) *CodexAutoExecutor {
  function codexWebsocketsEnabled (line 1355) | func codexWebsocketsEnabled(auth *cliproxyauth.Auth) bool {

FILE: internal/runtime/executor/codex_websockets_executor_test.go
  function TestBuildCodexWebsocketRequestBodyPreservesPreviousResponseID (line 16) | func TestBuildCodexWebsocketRequestBodyPreservesPreviousResponseID(t *te...
  function TestApplyCodexWebsocketHeadersDefaultsToCurrentResponsesBeta (line 35) | func TestApplyCodexWebsocketHeadersDefaultsToCurrentResponsesBeta(t *tes...
  function TestApplyCodexWebsocketHeadersUsesConfigDefaultsForOAuth (line 49) | func TestApplyCodexWebsocketHeadersUsesConfigDefaultsForOAuth(t *testing...
  function TestApplyCodexWebsocketHeadersPrefersExistingHeadersOverClientAndConfig (line 74) | func TestApplyCodexWebsocketHeadersPrefersExistingHeadersOverClientAndCo...
  function TestApplyCodexWebsocketHeadersConfigUserAgentOverridesClientHeader (line 103) | func TestApplyCodexWebsocketHeadersConfigUserAgentOverridesClientHeader(...
  function TestApplyCodexWebsocketHeadersIgnoresConfigForAPIKeyAuth (line 129) | func TestApplyCodexWebsocketHeadersIgnoresConfigForAPIKeyAuth(t *testing...
  function TestApplyCodexHeadersUsesConfigUserAgentForOAuth (line 151) | func TestApplyCodexHeadersUsesConfigUserAgentForOAuth(t *testing.T) {
  function contextWithGinHeaders (line 180) | func contextWithGinHeaders(headers map[string]string) context.Context {
  function TestNewProxyAwareWebsocketDialerDirectDisablesProxy (line 192) | func TestNewProxyAwareWebsocketDialerDirectDisablesProxy(t *testing.T) {

FILE: internal/runtime/executor/gemini_cli_executor.go
  constant codeAssistEndpoint (line 35) | codeAssistEndpoint      = "https://cloudcode-pa.googleapis.com"
  constant codeAssistVersion (line 36) | codeAssistVersion       = "v1internal"
  constant geminiOAuthClientID (line 37) | geminiOAuthClientID     = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j...
  constant geminiOAuthClientSecret (line 38) | geminiOAuthClientSecret = "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"
  type GeminiCLIExecutor (line 48) | type GeminiCLIExecutor struct
    method Identifier (line 64) | func (e *GeminiCLIExecutor) Identifier() string { return "gemini-cli" }
    method PrepareRequest (line 67) | func (e *GeminiCLIExecutor) PrepareRequest(req *http.Request, auth *cl...
    method HttpRequest (line 88) | func (e *GeminiCLIExecutor) HttpRequest(ctx context.Context, auth *cli...
    method Execute (line 104) | func (e *GeminiCLIExecutor) Execute(ctx context.Context, auth *cliprox...
    method ExecuteStream (line 258) | func (e *GeminiCLIExecutor) ExecuteStream(ctx context.Context, auth *c...
    method CountTokens (line 456) | func (e *GeminiCLIExecutor) CountTokens(ctx context.Context, auth *cli...
    method Refresh (line 565) | func (e *GeminiCLIExecutor) Refresh(_ context.Context, auth *cliproxya...
  function NewGeminiCLIExecutor (line 59) | func NewGeminiCLIExecutor(cfg *config.Config) *GeminiCLIExecutor {
  function prepareGeminiCLITokenSource (line 569) | func prepareGeminiCLITokenSource(ctx context.Context, cfg *config.Config...
  function updateGeminiCLITokenMetadata (line 627) | func updateGeminiCLITokenMetadata(auth *cliproxyauth.Auth, base map[stri...
  function buildGeminiTokenMap (line 649) | func buildGeminiTokenMap(base map[string]any, tok *oauth2.Token) map[str...
  function buildGeminiTokenFields (line 665) | func buildGeminiTokenFields(tok *oauth2.Token, merged map[string]any) ma...
  function resolveGeminiProjectID (line 685) | func resolveGeminiProjectID(auth *cliproxyauth.Auth) string {
  function geminiOAuthMetadata (line 697) | func geminiOAuthMetadata(auth *cliproxyauth.Auth) map[string]any {
  function newHTTPClient (line 709) | func newHTTPClient(ctx context.Context, cfg *config.Config, auth *clipro...
  function cloneMap (line 713) | func cloneMap(in map[string]any) map[string]any {
  function stringValue (line 724) | func stringValue(m map[string]any, key string) string {
  function applyGeminiCLIHeaders (line 742) | func applyGeminiCLIHeaders(r *http.Request, model string) {
  function cliPreviewFallbackOrder (line 748) | func cliPreviewFallbackOrder(model string) []string {
  function setJSONField (line 770) | func setJSONField(body []byte, key, value string) []byte {
  function deleteJSONField (line 782) | func deleteJSONField(body []byte, key string) []byte {
  function fixGeminiCLIImageAspectRatio (line 793) | func fixGeminiCLIImageAspectRatio(modelName string, rawJSON []byte) []by...
  function newGeminiStatusErr (line 835) | func newGeminiStatusErr(statusCode int, body []byte) statusErr {
  function parseRetryDelay (line 848) | func parseRetryDelay(errorBody []byte) (*time.Duration, error) {

FILE: internal/runtime/executor/gemini_executor.go
  constant glEndpoint (line 28) | glEndpoint = "https://generativelanguage.googleapis.com"
  constant glAPIVersion (line 31) | glAPIVersion = "v1beta"
  constant streamScannerBuffer (line 34) | streamScannerBuffer = 52_428_800
  type GeminiExecutor (line 40) | type GeminiExecutor struct
    method Identifier (line 57) | func (e *GeminiExecutor) Identifier() string { return "gemini" }
    method PrepareRequest (line 60) | func (e *GeminiExecutor) PrepareRequest(req *http.Request, auth *clipr...
    method HttpRequest (line 77) | func (e *GeminiExecutor) HttpRequest(ctx context.Context, auth *clipro...
    method Execute (line 105) | func (e *GeminiExecutor) Execute(ctx context.Context, auth *cliproxyau...
    method ExecuteStream (line 213) | func (e *GeminiExecutor) ExecuteStream(ctx context.Context, auth *clip...
    method CountTokens (line 341) | func (e *GeminiExecutor) CountTokens(ctx context.Context, auth *clipro...
    method Refresh (line 422) | func (e *GeminiExecutor) Refresh(_ context.Context, auth *cliproxyauth...
    method resolveGeminiConfig (line 462) | func (e *GeminiExecutor) resolveGeminiConfig(auth *cliproxyauth.Auth) ...
  function NewGeminiExecutor (line 52) | func NewGeminiExecutor(cfg *config.Config) *GeminiExecutor {
  function geminiCreds (line 426) | func geminiCreds(a *cliproxyauth.Auth) (apiKey, bearer string) {
  function resolveGeminiBaseURL (line 449) | func resolveGeminiBaseURL(auth *cliproxyauth.Auth) string {
  function applyGeminiHeaders (line 501) | func applyGeminiHeaders(req *http.Request, auth *cliproxyauth.Auth) {
  function fixGeminiImageAspectRatio (line 509) | func fixGeminiImageAspectRatio(modelName string, rawJSON []byte) []byte {

FILE: internal/runtime/executor/gemini_vertex_executor.go
  constant vertexAPIVersion (line 32) | vertexAPIVersion = "v1"
  function isImagenModel (line 37) | func isImagenModel(model string) bool {
  function getVertexAction (line 44) | func getVertexAction(model string, isStream bool) string {
  function convertImagenToGeminiResponse (line 57) | func convertImagenToGeminiResponse(data []byte, model string) []byte {
  function convertToImagenRequest (line 111) | func convertToImagenRequest(payload []byte) ([]byte, error) {
  type GeminiVertexExecutor (line 173) | type GeminiVertexExecutor struct
    method Identifier (line 189) | func (e *GeminiVertexExecutor) Identifier() string { return "vertex" }
    method PrepareRequest (line 192) | func (e *GeminiVertexExecutor) PrepareRequest(req *http.Request, auth ...
    method HttpRequest (line 219) | func (e *GeminiVertexExecutor) HttpRequest(ctx context.Context, auth *...
    method Execute (line 235) | func (e *GeminiVertexExecutor) Execute(ctx context.Context, auth *clip...
    method ExecuteStream (line 256) | func (e *GeminiVertexExecutor) ExecuteStream(ctx context.Context, auth...
    method CountTokens (line 277) | func (e *GeminiVertexExecutor) CountTokens(ctx context.Context, auth *...
    method Refresh (line 295) | func (e *GeminiVertexExecutor) Refresh(_ context.Context, auth *clipro...
    method executeWithServiceAccount (line 301) | func (e *GeminiVertexExecutor) executeWithServiceAccount(ctx context.C...
    method executeWithAPIKey (line 427) | func (e *GeminiVertexExecutor) executeWithAPIKey(ctx context.Context, ...
    method executeStreamWithServiceAccount (line 532) | func (e *GeminiVertexExecutor) executeStreamWithServiceAccount(ctx con...
    method executeStreamWithAPIKey (line 656) | func (e *GeminiVertexExecutor) executeStreamWithAPIKey(ctx context.Con...
    method countTokensWithServiceAccount (line 780) | func (e *GeminiVertexExecutor) countTokensWithServiceAccount(ctx conte...
    method countTokensWithAPIKey (line 864) | func (e *GeminiVertexExecutor) countTokensWithAPIKey(ctx context.Conte...
    method resolveVertexConfig (line 1031) | func (e *GeminiVertexExecutor) resolveVertexConfig(auth *cliproxyauth....
  function NewGeminiVertexExecutor (line 184) | func NewGeminiVertexExecutor(cfg *config.Config) *GeminiVertexExecutor {
  function vertexCreds (line 948) | func vertexCreds(a *cliproxyauth.Auth) (projectID, location string, serv...
  function vertexAPICreds (line 988) | func vertexAPICreds(a *cliproxyauth.Auth) (apiKey, baseURL string) {
  function vertexBaseURL (line 1004) | func vertexBaseURL(location string) string {
  function vertexAccessToken (line 1014) | func vertexAccessToken(ctx context.Context, cfg *config.Config, auth *cl...

FILE: internal/runtime/executor/iflow_executor.go
  constant iflowDefaultEndpoint (line 30) | iflowDefaultEndpoint = "/chat/completions"
  constant iflowUserAgent (line 31) | iflowUserAgent       = "iFlow-Cli"
  type IFlowExecutor (line 35) | type IFlowExecutor struct
    method Identifier (line 43) | func (e *IFlowExecutor) Identifier() string { return "iflow" }
    method PrepareRequest (line 46) | func (e *IFlowExecutor) PrepareRequest(req *http.Request, auth *clipro...
    method HttpRequest (line 58) | func (e *IFlowExecutor) HttpRequest(ctx context.Context, auth *cliprox...
    method Execute (line 74) | func (e *IFlowExecutor) Execute(ctx context.Context, auth *cliproxyaut...
    method ExecuteStream (line 177) | func (e *IFlowExecutor) ExecuteStream(ctx context.Context, auth *clipr...
    method CountTokens (line 299) | func (e *IFlowExecutor) CountTokens(ctx context.Context, auth *cliprox...
    method Refresh (line 322) | func (e *IFlowExecutor) Refresh(ctx context.Context, auth *cliproxyaut...
    method refreshCookieBased (line 350) | func (e *IFlowExecutor) refreshCookieBased(ctx context.Context, auth *...
    method refreshOAuthBased (line 401) | func (e *IFlowExecutor) refreshOAuthBased(ctx context.Context, auth *c...
  function NewIFlowExecutor (line 40) | func NewIFlowExecutor(cfg *config.Config) *IFlowExecutor { return &IFlow...
  function applyIFlowHeaders (line 455) | func applyIFlowHeaders(r *http.Request, apiKey string, stream bool) {
  function createIFlowSignature (line 482) | func createIFlowSignature(userAgent, sessionID string, timestamp int64, ...
  function generateUUID (line 493) | func generateUUID() string {
  function iflowCreds (line 497) | func iflowCreds(a *cliproxyauth.Auth) (apiKey, baseURL string) {
  function ensureToolsArray (line 522) | func ensureToolsArray(body []byte) []byte {
  function preserveReasoningContentInMessages (line 538) | func preserveReasoningContentInMessages(body []byte) []byte {

FILE: internal/runtime/executor/iflow_executor_test.go
  function TestIFlowExecutorParseSuffix (line 9) | func TestIFlowExecutorParseSuffix(t *testing.T) {
  function TestPreserveReasoningContentInMessages (line 32) | func TestPreserveReasoningContentInMessages(t *testing.T) {

FILE: internal/runtime/executor/kimi_executor.go
  type KimiExecutor (line 28) | type KimiExecutor struct
    method Identifier (line 37) | func (e *KimiExecutor) Identifier() string { return "kimi" }
    method PrepareRequest (line 40) | func (e *KimiExecutor) PrepareRequest(req *http.Request, auth *cliprox...
    method HttpRequest (line 52) | func (e *KimiExecutor) HttpRequest(ctx context.Context, auth *cliproxy...
    method Execute (line 68) | func (e *KimiExecutor) Execute(ctx context.Context, auth *cliproxyauth...
    method ExecuteStream (line 169) | func (e *KimiExecutor) ExecuteStream(ctx context.Context, auth *clipro...
    method CountTokens (line 291) | func (e *KimiExecutor) CountTokens(ctx context.Context, auth *cliproxy...
    method Refresh (line 441) | func (e *KimiExecutor) Refresh(ctx context.Context, auth *cliproxyauth...
  function NewKimiExecutor (line 34) | func NewKimiExecutor(cfg *config.Config) *KimiExecutor { return &KimiExe...
  function normalizeKimiToolMessageLinks (line 296) | func normalizeKimiToolMessageLinks(body []byte) ([]byte, error) {
  function fallbackAssistantReasoning (line 412) | func fallbackAssistantReasoning(msg gjson.Result, hasLatest bool, latest...
  function applyKimiHeaders (line 482) | func applyKimiHeaders(r *http.Request, token string, stream bool) {
  function resolveKimiDeviceIDFromAuth (line 499) | func resolveKimiDeviceIDFromAuth(auth *cliproxyauth.Auth) string {
  function resolveKimiDeviceIDFromStorage (line 517) | func resolveKimiDeviceIDFromStorage(auth *cliproxyauth.Auth) string {
  function resolveKimiDeviceID (line 530) | func resolveKimiDeviceID(auth *cliproxyauth.Auth) string {
  function applyKimiHeadersWithAuth (line 538) | func applyKimiHeadersWithAuth(r *http.Request, token string, stream bool...
  function getKimiHostname (line 547) | func getKimiHostname() string {
  function getKimiDeviceModel (line 556) | func getKimiDeviceModel() string {
  function getKimiDeviceID (line 561) | func getKimiDeviceID() string {
  function kimiCreds (line 588) | func kimiCreds(a *cliproxyauth.Auth) (token string) {
  function stripKimiPrefix (line 611) | func stripKimiPrefix(model string) string {

FILE: internal/runtime/executor/kimi_executor_test.go
  function TestNormalizeKimiToolMessageLinks_UsesCallIDFallback (line 9) | func TestNormalizeKimiToolMessageLinks_UsesCallIDFallback(t *testing.T) {
  function TestNormalizeKimiToolMessageLinks_InferSinglePendingID (line 28) | func TestNormalizeKimiToolMessageLinks_InferSinglePendingID(t *testing.T) {
  function TestNormalizeKimiToolMessageLinks_AmbiguousMissingIDIsNotInferred (line 47) | func TestNormalizeKimiToolMessageLinks_AmbiguousMissingIDIsNotInferred(t...
  function TestNormalizeKimiToolMessageLinks_PreservesExistingToolCallID (line 68) | func TestNormalizeKimiToolMessageLinks_PreservesExistingToolCallID(t *te...
  function TestNormalizeKimiToolMessageLinks_InheritsPreviousReasoningForAssistantToolCalls (line 87) | func TestNormalizeKimiToolMessageLinks_InheritsPreviousReasoningForAssis...
  function TestNormalizeKimiToolMessageLinks_InsertsFallbackReasoningWhenMissing (line 106) | func TestNormalizeKimiToolMessageLinks_InsertsFallbackReasoningWhenMissi...
  function TestNormalizeKimiToolMessageLinks_UsesContentAsReasoningFallback (line 127) | func TestNormalizeKimiToolMessageLinks_UsesContentAsReasoningFallback(t ...
  function TestNormalizeKimiToolMessageLinks_ReplacesEmptyReasoningContent (line 145) | func TestNormalizeKimiToolMessageLinks_ReplacesEmptyReasoningContent(t *...
  function TestNormalizeKimiToolMessageLinks_PreservesExistingAssistantReasoning (line 163) | func TestNormalizeKimiToolMessageLinks_PreservesExistingAssistantReasoni...
  function TestNormalizeKimiToolMessageLinks_RepairsIDsAndReasoningTogether (line 181) | func TestNormalizeKimiToolMessageLinks_RepairsIDsAndReasoningTogether(t ...

FILE: internal/runtime/executor/logging_helpers.go
  constant apiAttemptsKey (line 22) | apiAttemptsKey = "API_UPSTREAM_ATTEMPTS"
  constant apiRequestKey (line 23) | apiRequestKey  = "API_REQUEST"
  constant apiResponseKey (line 24) | apiResponseKey = "API_RESPONSE"
  type upstreamRequestLog (line 28) | type upstreamRequestLog struct
  type upstreamAttempt (line 40) | type upstreamAttempt struct
  function recordAPIRequest (line 53) | func recordAPIRequest(ctx context.Context, cfg *config.Config, info upst...
  function recordAPIResponseMetadata (line 100) | func recordAPIResponseMetadata(ctx context.Context, cfg *config.Config, ...
  function recordAPIResponseError (line 126) | func recordAPIResponseError(ctx context.Context, cfg *config.Config, err...
  function appendAPIResponseChunk (line 151) | func appendAPIResponseChunk(ctx context.Context, cfg *config.Config, chu...
  function ginContextFrom (line 185) | func ginContextFrom(ctx context.Context) *gin.Context {
  function getAttempts (line 190) | func getAttempts(ginCtx *gin.Context) []*upstreamAttempt {
  function ensureAttempt (line 202) | func ensureAttempt(ginCtx *gin.Context) ([]*upstreamAttempt, *upstreamAt...
  function ensureResponseIntro (line 217) | func ensureResponseIntro(attempt *upstreamAttempt) {
  function updateAggregatedRequest (line 227) | func updateAggregatedRequest(ginCtx *gin.Context, attempts []*upstreamAt...
  function updateAggregatedResponse (line 238) | func updateAggregatedResponse(ginCtx *gin.Context, attempts []*upstreamA...
  function writeHeaders (line 262) | func writeHeaders(builder *strings.Builder, headers http.Header) {
  function formatAuthInfo (line 288) | func formatAuthInfo(info upstreamRequestLog) string {
  function summarizeErrorBody (line 324) | func summarizeErrorBody(contentType string, body []byte) string {
  function extractHTMLTitle (line 347) | func extractHTMLTitle(body []byte) string {
  function extractJSONErrorMessage (line 372) | func extractJSONErrorMessage(body []byte) string {
  function logWithRequestID (line 382) | func logWithRequestID(ctx context.Context) *log.Entry {

FILE: internal/runtime/executor/openai_compat_executor.go
  type OpenAICompatExecutor (line 26) | type OpenAICompatExecutor struct
    method Identifier (line 37) | func (e *OpenAICompatExecutor) Identifier() string { return e.provider }
    method PrepareRequest (line 40) | func (e *OpenAICompatExecutor) PrepareRequest(req *http.Request, auth ...
    method HttpRequest (line 57) | func (e *OpenAICompatExecutor) HttpRequest(ctx context.Context, auth *...
    method Execute (line 72) | func (e *OpenAICompatExecutor) Execute(ctx context.Context, auth *clip...
    method ExecuteStream (line 179) | func (e *OpenAICompatExecutor) ExecuteStream(ctx context.Context, auth...
    method CountTokens (line 307) | func (e *OpenAICompatExecutor) CountTokens(ctx context.Context, auth *...
    method Refresh (line 337) | func (e *OpenAICompatExecutor) Refresh(ctx context.Context, auth *clip...
    method resolveCredentials (line 343) | func (e *OpenAICompatExecutor) resolveCredentials(auth *cliproxyauth.A...
    method resolveCompatConfig (line 354) | func (e *OpenAICompatExecutor) resolveCompatConfig(auth *cliproxyauth....
    method overrideModel (line 381) | func (e *OpenAICompatExecutor) overrideModel(payload []byte, model str...
  function NewOpenAICompatExecutor (line 32) | func NewOpenAICompatExecutor(provider string, cfg *config.Config) *OpenA...
  type statusErr (line 389) | type statusErr struct
    method Error (line 395) | func (e statusErr) Error() string {
    method StatusCode (line 401) | func (e statusErr) StatusCode() int            { return e.code }
    method RetryAfter (line 402) | func (e statusErr) RetryAfter() *time.Duration { return e.retryAfter }

FILE: internal/runtime/executor/openai_compat_executor_compact_test.go
  function TestOpenAICompatExecutorCompactPassthrough (line 17) | func TestOpenAICompatExecutorCompactPassthrough(t *testing.T) {

FILE: internal/runtime/executor/payload_helpers.go
  function applyPayloadConfigWithRoot (line 19) | func applyPayloadConfigWithRoot(cfg *config.Config, model, protocol, roo...
  function payloadModelRulesMatch (line 154) | func payloadModelRulesMatch(rules []config.PayloadModelRule, protocol st...
  function payloadModelCandidates (line 175) | func payloadModelCandidates(model, requestedModel string) []string {
  function buildPayloadPath (line 214) | func buildPayloadPath(root, path string) string {
  function payloadRawValue (line 229) | func payloadRawValue(value any) ([]byte, bool) {
  function payloadRequestedModel (line 247) | func payloadRequestedModel(opts cliproxyexecutor.Options, fallback strin...
  function matchModelPattern (line 282) | func matchModelPattern(pattern, model string) bool {

FILE: internal/runtime/executor/proxy_helpers.go
  function newProxyAwareHTTPClient (line 28) | func newProxyAwareHTTPClient(ctx context.Context, cfg *config.Config, au...
  function buildProxyTransport (line 72) | func buildProxyTransport(proxyURL string) *http.Transport {

FILE: internal/runtime/executor/proxy_helpers_test.go
  function TestNewProxyAwareHTTPClientDirectBypassesGlobalProxy (line 13) | func TestNewProxyAwareHTTPClientDirectBypassesGlobalProxy(t *testing.T) {

FILE: internal/runtime/executor/qwen_executor.go
  constant qwenUserAgent (line 26) | qwenUserAgent       = "QwenCode/0.10.3 (darwin; arm64)"
  constant qwenRateLimitPerMin (line 27) | qwenRateLimitPerMin = 60
  constant qwenRateLimitWindow (line 28) | qwenRateLimitWindow = time.Minute
  function redactAuthID (line 58) | func redactAuthID(id string) string {
  function checkQwenRateLimit (line 70) | func checkQwenRateLimit(authID string) error {
  function isQwenQuotaError (line 124) | func isQwenQuotaError(body []byte) bool {
  function wrapQwenError (line 149) | func wrapQwenError(ctx context.Context, httpCode int, body []byte) (errC...
  function timeUntilNextDay (line 164) | func timeUntilNextDay() time.Duration {
  type QwenExecutor (line 173) | type QwenExecutor struct
    method Identifier (line 179) | func (e *QwenExecutor) Identifier() string { return "qwen" }
    method PrepareRequest (line 182) | func (e *QwenExecutor) PrepareRequest(req *http.Request, auth *cliprox...
    method HttpRequest (line 194) | func (e *QwenExecutor) HttpRequest(ctx context.Context, auth *cliproxy...
    method Execute (line 209) | func (e *QwenExecutor) Execute(ctx context.Context, auth *cliproxyauth...
    method ExecuteStream (line 312) | func (e *QwenExecutor) ExecuteStream(ctx context.Context, auth *clipro...
    method CountTokens (line 440) | func (e *QwenExecutor) CountTokens(ctx context.Context, auth *cliproxy...
    method Refresh (line 467) | func (e *QwenExecutor) Refresh(ctx context.Context, auth *cliproxyauth...
  function NewQwenExecutor (line 177) | func NewQwenExecutor(cfg *config.Config) *QwenExecutor { return &QwenExe...
  function applyQwenHeaders (line 507) | func applyQwenHeaders(r *http.Request, token string, stream bool) {
  function qwenCreds (line 530) | func qwenCreds(a *cliproxyauth.Auth) (token, baseURL string) {

FILE: internal/runtime/executor/qwen_executor_test.go
  function TestQwenExecutorParseSuffix (line 9) | func TestQwenExecutorParseSuffix(t *testing.T) {

FILE: internal/runtime/executor/token_helpers.go
  function tokenizerForModel (line 12) | func tokenizerForModel(model string) (tokenizer.Codec, error) {
  function countOpenAIChatTokens (line 41) | func countOpenAIChatTokens(enc tokenizer.Codec, payload []byte) (int64, ...
  function buildOpenAIUsageJSON (line 73) | func buildOpenAIUsageJSON(count int64) []byte {
  function collectOpenAIMessages (line 77) | func collectOpenAIMessages(messages gjson.Result, segments *[]string) {
  function collectOpenAIContent (line 91) | func collectOpenAIContent(content gjson.Result, segments *[]string) {
  function collectOpenAIToolCalls (line 132) | func collectOpenAIToolCalls(calls gjson.Result, segments *[]string) {
  function collectOpenAIFunctionCall (line 152) | func collectOpenAIFunctionCall(call gjson.Result, segments *[]string) {
  function collectOpenAITools (line 160) | func collectOpenAITools(tools gjson.Result, segments *[]string) {
  function collectOpenAIFunctions (line 174) | func collectOpenAIFunctions(functions gjson.Result, segments *[]string) {
  function collectOpenAIToolChoice (line 188) | func collectOpenAIToolChoice(choice gjson.Result, segments *[]string) {
  function collectOpenAIResponseFormat (line 199) | func collectOpenAIResponseFormat(format gjson.Result, segments *[]string) {
  function appendToolPayload (line 213) | func appendToolPayload(tool gjson.Result, segments *[]string) {
  function addIfNotEmpty (line 229) | func addIfNotEmpty(segments *[]string, value string) {

FILE: internal/runtime/executor/usage_helpers.go
  type usageReporter (line 18) | type usageReporter struct
    method publish (line 45) | func (r *usageReporter) publish(ctx context.Context, detail usage.Deta...
    method publishFailure (line 49) | func (r *usageReporter) publishFailure(ctx context.Context) {
    method trackFailure (line 53) | func (r *usageReporter) trackFailure(ctx context.Context, errPtr *erro...
    method publishWithOutcome (line 62) | func (r *usageReporter) publishWithOutcome(ctx context.Context, detail...
    method ensurePublished (line 94) | func (r *usageReporter) ensurePublished(ctx context.Context) {
  function newUsageReporter (line 29) | func newUsageReporter(ctx context.Context, provider, model string, auth ...
  function apiKeyFromContext (line 113) | func apiKeyFromContext(ctx context.Context) string {
  function resolveUsageSource (line 134) | func resolveUsageSource(auth *cliproxyauth.Auth, ctxAPIKey string) string {
  function parseCodexUsage (line 178) | func parseCodexUsage(data []byte) (usage.Detail, bool) {
  function parseOpenAIUsage (line 197) | func parseOpenAIUsage(data []byte) usage.Detail {
  function parseOpenAIStreamUsage (line 232) | func parseOpenAIStreamUsage(line []byte) (usage.Detail, bool) {
  function parseClaudeUsage (line 255) | func parseClaudeUsage(data []byte) usage.Detail {
  function parseClaudeStreamUsage (line 273) | func parseClaudeStreamUsage(line []byte) (usage.Detail, bool) {
  function parseGeminiFamilyUsageDetail (line 294) | func parseGeminiFamilyUsageDetail(node gjson.Result) usage.Detail {
  function parseGeminiCLIUsage (line 308) | func parseGeminiCLIUsage(data []byte) usage.Detail {
  function parseGeminiUsage (line 320) | func parseGeminiUsage(data []byte) usage.Detail {
  function parseGeminiStreamUsage (line 332) | func parseGeminiStreamUsage(line []byte) (usage.Detail, bool) {
  function parseGeminiCLIStreamUsage (line 347) | func parseGeminiCLIStreamUsage(line []byte) (usage.Detail, bool) {
  function parseAntigravityUsage (line 362) | func parseAntigravityUsage(data []byte) usage.Detail {
  function parseAntigravityStreamUsage (line 377) | func parseAntigravityStreamUsage(line []byte) (usage.Detail, bool) {
  function rememberStopWithoutUsage (line 397) | func rememberStopWithoutUsage(traceID string) {
  function FilterSSEUsageMetadata (line 405) | func FilterSSEUsageMetadata(payload []byte) []byte {
  function StripUsageMetadataFromJSON (line 469) | func StripUsageMetadataFromJSON(rawJSON []byte) ([]byte, bool) {
  function hasUsageMetadata (line 518) | func hasUsageMetadata(jsonBytes []byte) bool {
  function isStopChunkWithoutUsage (line 531) | func isStopChunkWithoutUsage(jsonBytes []byte) bool {
  function jsonPayload (line 546) | func jsonPayload(line []byte) []byte {

FILE: internal/runtime/executor/usage_helpers_test.go
  function TestParseOpenAIUsageChatCompletions (line 5) | func TestParseOpenAIUsageChatCompletions(t *testing.T) {
  function TestParseOpenAIUsageResponses (line 25) | func TestParseOpenAIUsageResponses(t *testing.T) {

FILE: internal/runtime/executor/user_id_cache.go
  type userIDCacheEntry (line 10) | type userIDCacheEntry struct
  constant userIDTTL (line 22) | userIDTTL                = time.Hour
  constant userIDCacheCleanupPeriod (line 23) | userIDCacheCleanupPeriod = 15 * time.Minute
  function startUserIDCacheCleanup (line 26) | func startUserIDCacheCleanup() {
  function purgeExpiredUserIDs (line 36) | func purgeExpiredUserIDs() {
  function userIDCacheKey (line 47) | func userIDCacheKey(apiKey string) string {
  function cachedUserID (line 52) | func cachedUserID(apiKey string) string {

FILE: internal/runtime/executor/user_id_cache_test.go
  function resetUserIDCache (line 8) | func resetUserIDCache() {
  function TestCachedUserID_ReusesWithinTTL (line 14) | func TestCachedUserID_ReusesWithinTTL(t *testing.T) {
  function TestCachedUserID_ExpiresAfterTTL (line 28) | func TestCachedUserID_ExpiresAfterTTL(t *testing.T) {
  function TestCachedUserID_IsScopedByAPIKey (line 49) | func TestCachedUserID_IsScopedByAPIKey(t *testing.T) {
  function TestCachedUserID_RenewsTTLOnHit (line 60) | func TestCachedUserID_RenewsTTLOnHit(t *testing.T) {

FILE: internal/runtime/geminicli/state.go
  type SharedCredential (line 9) | type SharedCredential struct
    method PrimaryID (line 28) | func (s *SharedCredential) PrimaryID() string {
    method Email (line 36) | func (s *SharedCredential) Email() string {
    method ProjectIDs (line 44) | func (s *SharedCredential) ProjectIDs() []string {
    method MetadataSnapshot (line 52) | func (s *SharedCredential) MetadataSnapshot() map[string]any {
    method MergeMetadata (line 62) | func (s *SharedCredential) MergeMetadata(values map[string]any) map[st...
    method SetProjectIDs (line 85) | func (s *SharedCredential) SetProjectIDs(ids []string) {
  function NewSharedCredential (line 18) | func NewSharedCredential(primaryID, email string, metadata map[string]an...
  type VirtualCredential (line 95) | type VirtualCredential struct
  function NewVirtualCredential (line 101) | func NewVirtualCredential(projectID string, parent *SharedCredential) *V...
  function ResolveSharedCredential (line 106) | func ResolveSharedCredential(runtime any) *SharedCredential {
  function IsVirtual (line 118) | func IsVirtual(runtime any) bool {
  function cloneMap (line 126) | func cloneMap(in map[string]any) map[string]any {
  function cloneStrings (line 137) | func cloneStrings(in []string) []string {

FILE: internal/store/gitstore.go
  constant gcInterval (line 25) | gcInterval = 5 * time.Minute
  type GitTokenStore (line 28) | type GitTokenStore struct
    method SetBaseDir (line 51) | func (s *GitTokenStore) SetBaseDir(dir string) {
    method AuthDir (line 77) | func (s *GitTokenStore) AuthDir() string {
    method ConfigPath (line 82) | func (s *GitTokenStore) ConfigPath() string {
    method EnsureRepository (line 92) | func (s *GitTokenStore) EnsureRepository() error {
    method Save (line 216) | func (s *GitTokenStore) Save(_ context.Context, auth *cliproxyauth.Aut...
    method List (line 299) | func (s *GitTokenStore) List(_ context.Context) ([]*cliproxyauth.Auth,...
    method Delete (line 334) | func (s *GitTokenStore) Delete(_ context.Context, id string) error {
    method PersistAuthFiles (line 368) | func (s *GitTokenStore) PersistAuthFiles(_ context.Context, message st...
    method resolveDeletePath (line 401) | func (s *GitTokenStore) resolveDeletePath(id string) (string, error) {
    method readAuthFile (line 412) | func (s *GitTokenStore) readAuthFile(path, baseDir string) (*cliproxya...
    method idFor (line 452) | func (s *GitTokenStore) idFor(path, baseDir string) string {
    method resolveAuthPath (line 463) | func (s *GitTokenStore) resolveAuthPath(auth *cliproxyauth.Auth) (stri...
    method labelFor (line 494) | func (s *GitTokenStore) labelFor(metadata map[string]any) string {
    method baseDirSnapshot (line 510) | func (s *GitTokenStore) baseDirSnapshot() string {
    method repoDirSnapshot (line 516) | func (s *GitTokenStore) repoDirSnapshot() string {
    method gitAuth (line 522) | func (s *GitTokenStore) gitAuth() transport.AuthMethod {
    method relativeToRepo (line 533) | func (s *GitTokenStore) relativeToRepo(path string) (string, error) {
    method commitAndPushLocked (line 556) | func (s *GitTokenStore) commitAndPushLocked(message string, relPaths ....
    method rewriteHeadAsSingleCommit (line 631) | func (s *GitTokenStore) rewriteHeadAsSingleCommit(repo *git.Repository...
    method maybeRunGC (line 660) | func (s *GitTokenStore) maybeRunGC(repo *git.Repository) {
    method PersistConfig (line 678) | func (s *GitTokenStore) PersistConfig(_ context.Context) error {
  function NewGitTokenStore (line 42) | func NewGitTokenStore(remote, username, password string) *GitTokenStore {
  function ensureEmptyFile (line 701) | func ensureEmptyFile(path string) error {
  function jsonEqual (line 711) | func jsonEqual(a, b []byte) bool {
  function deepEqualJSON (line 723) | func deepEqualJSON(a, b any) bool {

FILE: internal/store/objectstore.go
  constant objectStoreConfigKey (line 26) | objectStoreConfigKey  = "config/config.yaml"
  constant objectStoreAuthPrefix (line 27) | objectStoreAuthPrefix = "auths"
  type ObjectStoreConfig (line 31) | type ObjectStoreConfig struct
  type ObjectTokenStore (line 45) | type ObjectTokenStore struct
    method SetBaseDir (line 123) | func (s *ObjectTokenStore) SetBaseDir(string) {}
    method ConfigPath (line 126) | func (s *ObjectTokenStore) ConfigPath() string {
    method AuthDir (line 134) | func (s *ObjectTokenStore) AuthDir() string {
    method Bootstrap (line 142) | func (s *ObjectTokenStore) Bootstrap(ctx context.Context, exampleConfi...
    method Save (line 159) | func (s *ObjectTokenStore) Save(ctx context.Context, auth *cliproxyaut...
    method List (line 229) | func (s *ObjectTokenStore) List(_ context.Context) ([]*cliproxyauth.Au...
    method Delete (line 262) | func (s *ObjectTokenStore) Delete(ctx context.Context, id string) error {
    method PersistAuthFiles (line 285) | func (s *ObjectTokenStore) PersistAuthFiles(ctx context.Context, _ str...
    method PersistConfig (line 310) | func (s *ObjectTokenStore) PersistConfig(ctx context.Context) error {
    method ensureBucket (line 327) | func (s *ObjectTokenStore) ensureBucket(ctx context.Context) error {
    method syncConfigFromBucket (line 341) | func (s *ObjectTokenStore) syncConfigFromBucket(ctx context.Context, e...
    method syncAuthFromBucket (line 388) | func (s *ObjectTokenStore) syncAuthFromBucket(ctx context.Context) err...
    method uploadAuth (line 440) | func (s *ObjectTokenStore) uploadAuth(ctx context.Context, path string...
    method deleteAuthObject (line 462) | func (s *ObjectTokenStore) deleteAuthObject(ctx context.Context, path ...
    method putObject (line 474) | func (s *ObjectTokenStore) putObject(ctx context.Context, key string, ...
    method deleteObject (line 489) | func (s *ObjectTokenStore) deleteObject(ctx context.Context, key strin...
    method prefixedKey (line 501) | func (s *ObjectTokenStore) prefixedKey(key string) string {
    method resolveAuthPath (line 509) | func (s *ObjectTokenStore) resolveAuthPath(auth *cliproxyauth.Auth) (s...
    method resolveDeletePath (line 534) | func (s *ObjectTokenStore) resolveDeletePath(id string) (string, error) {
    method readAuthFile (line 556) | func (s *ObjectTokenStore) readAuthFile(path, baseDir string) (*clipro...
  function NewObjectTokenStore (line 55) | func NewObjectTokenStore(cfg ObjectStoreConfig) (*ObjectTokenStore, erro...
  function normalizeLineEndingsBytes (line 601) | func normalizeLineEndingsBytes(data []byte) []byte {
  function isObjectNotFound (line 606) | func isObjectNotFound(err error) bool {

FILE: internal/store/postgresstore.go
  constant defaultConfigTable (line 23) | defaultConfigTable = "config_store"
  constant defaultAuthTable (line 24) | defaultAuthTable   = "auth_store"
  constant defaultConfigKey (line 25) | defaultConfigKey   = "config"
  type PostgresStoreConfig (line 29) | type PostgresStoreConfig struct
  type PostgresStore (line 39) | type PostgresStore struct
    method Close (line 103) | func (s *PostgresStore) Close() error {
    method EnsureSchema (line 111) | func (s *PostgresStore) EnsureSchema(ctx context.Context) error {
    method Bootstrap (line 147) | func (s *PostgresStore) Bootstrap(ctx context.Context, exampleConfigPa...
    method ConfigPath (line 161) | func (s *PostgresStore) ConfigPath() string {
    method AuthDir (line 169) | func (s *PostgresStore) AuthDir() string {
    method WorkDir (line 177) | func (s *PostgresStore) WorkDir() string {
    method SetBaseDir (line 186) | func (s *PostgresStore) SetBaseDir(string) {}
    method Save (line 189) | func (s *PostgresStore) Save(ctx context.Context, auth *cliproxyauth.A...
    method List (line 263) | func (s *PostgresStore) List(ctx context.Context) ([]*cliproxyauth.Aut...
    method Delete (line 322) | func (s *PostgresStore) Delete(ctx context.Context, id string) error {
    method PersistAuthFiles (line 346) | func (s *PostgresStore) PersistAuthFiles(ctx context.Context, _ string...
    method PersistConfig (line 380) | func (s *PostgresStore) PersistConfig(ctx context.Context) error {
    method syncConfigFromDatabase (line 395) | func (s *PostgresStore) syncConfigFromDatabase(ctx context.Context, ex...
    method syncAuthFromDatabase (line 437) | func (s *PostgresStore) syncAuthFromDatabase(ctx context.Context) error {
    method syncAuthFile (line 478) | func (s *PostgresStore) syncAuthFile(ctx context.Context, relID, path ...
    method upsertAuthRecord (line 492) | func (s *PostgresStore) upsertAuthRecord(ctx context.Context, relID, p...
    method persistAuth (line 503) | func (s *PostgresStore) persistAuth(ctx context.Context, relID string,...
    method deleteAuthRecord (line 517) | func (s *PostgresStore) deleteAuthRecord(ctx context.Context, relID st...
    method persistConfig (line 525) | func (s *PostgresStore) persistConfig(ctx context.Context, data []byte...
    method deleteConfigRecord (line 539) | func (s *PostgresStore) deleteConfigRecord(ctx context.Context) error {
    method resolveAuthPath (line 547) | func (s *PostgresStore) resolveAuthPath(auth *cliproxyauth.Auth) (stri...
    method resolveDeletePath (line 571) | func (s *PostgresStore) resolveDeletePath(id string) (string, error) {
    method relativeAuthID (line 578) | func (s *PostgresStore) relativeAuthID(path string) (string, error) {
    method absoluteAuthPath (line 596) | func (s *PostgresStore) absoluteAuthPath(id string) (string, error) {
    method fullTableName (line 615) | func (s *PostgresStore) fullTableName(name string) string {
  function NewPostgresStore (line 49) | func NewPostgresStore(ctx context.Context, cfg PostgresStoreConfig) (*Po...
  function quoteIdentifier (line 622) | func quoteIdentifier(identifier string) string {
  function valueAsString (line 627) | func valueAsString(v any) string {
  function labelFor (line 638) | func labelFor(metadata map[string]any) string {
  function normalizeAuthID (line 654) | func normalizeAuthID(id string) string {
  function normalizeLineEndings (line 658) | func normalizeLineEndings(s string) string {

FILE: internal/thinking/apply.go
  function GetProviderApplier (line 26) | func GetProviderApplier(provider string) ProviderApplier {
  function RegisterProvider (line 31) | func RegisterProvider(name string, applier ProviderApplier) {
  function IsUserDefinedModel (line 44) | func IsUserDefinedModel(modelInfo *registry.ModelInfo) bool {
  function ApplyThinking (line 88) | func ApplyThinking(body []byte, model string, fromFormat string, toForma...
  function parseSuffixToConfig (line 211) | func parseSuffixToConfig(rawSuffix, provider, model string) ThinkingConf...
  function applyUserDefinedModel (line 246) | func applyUserDefinedModel(body []byte, modelInfo *registry.ModelInfo, f...
  function normalizeUserDefinedConfig (line 295) | func normalizeUserDefinedConfig(config ThinkingConfig, fromFormat, toFor...
  function extractThinkingConfig (line 316) | func extractThinkingConfig(body []byte, provider string) ThinkingConfig {
  function hasThinkingConfig (line 344) | func hasThinkingConfig(config ThinkingConfig) bool {
  function extractClaudeConfig (line 357) | func extractClaudeConfig(body []byte) ThinkingConfig {
  function extractGeminiConfig (line 414) | func extractGeminiConfig(body []byte, provider string) ThinkingConfig {
  function extractOpenAIConfig (line 466) | func extractOpenAIConfig(body []byte) ThinkingConfig {
  function extractCodexConfig (line 485) | func extractCodexConfig(body []byte) ThinkingConfig {
  function extractIFlowConfig (line 507) | func extractIFlowConfig(body []byte) ThinkingConfig {

FILE: internal/thinking/apply_user_defined_test.go
  function TestApplyThinking_UserDefinedClaudePreservesAdaptiveLevel (line 12) | func TestApplyThinking_UserDefinedClaudePreservesAdaptiveLevel(t *testin...

FILE: internal/thinking/convert.go
  function ConvertLevelToBudget (line 42) | func ConvertLevelToBudget(level string) (int, bool) {
  constant ThresholdMinimal (line 51) | ThresholdMinimal = 512
  constant ThresholdLow (line 53) | ThresholdLow = 1024
  constant ThresholdMedium (line 55) | ThresholdMedium = 8192
  constant ThresholdHigh (line 57) | ThresholdHigh = 24576
  function ConvertBudgetToLevel (line 77) | func ConvertBudgetToLevel(budget int) (string, bool) {
  function HasLevel (line 101) | func HasLevel(levels []string, target string) bool {
  function MapToClaudeEffort (line 115) | func MapToClaudeEffort(level string, supportsMax bool) (string, bool) {
  type ModelCapability (line 137) | type ModelCapability
  constant CapabilityUnknown (line 141) | CapabilityUnknown ModelCapability = iota - 1
  constant CapabilityNone (line 143) | CapabilityNone
  constant CapabilityBudgetOnly (line 145) | CapabilityBudgetOnly
  constant CapabilityLevelOnly (line 147) | CapabilityLevelOnly
  constant CapabilityHybrid (line 149) | CapabilityHybrid
  function detectModelCapability (line 162) | func detectModelCapability(modelInfo *registry.ModelInfo) ModelCapability {

FILE: internal/thinking/errors.go
  type ErrorCode (line 7) | type ErrorCode
  constant ErrInvalidSuffix (line 13) | ErrInvalidSuffix ErrorCode = "INVALID_SUFFIX"
  constant ErrUnknownLevel (line 17) | ErrUnknownLevel ErrorCode = "UNKNOWN_LEVEL"
  constant ErrThinkingNotSupported (line 21) | ErrThinkingNotSupported ErrorCode = "THINKING_NOT_SUPPORTED"
  constant ErrLevelNotSupported (line 25) | ErrLevelNotSupported ErrorCode = "LEVEL_NOT_SUPPORTED"
  constant ErrBudgetOutOfRange (line 29) | ErrBudgetOutOfRange ErrorCode = "BUDGET_OUT_OF_RANGE"
  constant ErrProviderMismatch (line 33) | ErrProviderMismatch ErrorCode = "PROVIDER_MISMATCH"
  type ThinkingError (line 43) | type ThinkingError struct
    method Error (line 58) | func (e *ThinkingError) Error() string {
    method StatusCode (line 80) | func (e *ThinkingError) StatusCode() int {
  function NewThinkingError (line 63) | func NewThinkingError(code ErrorCode, message string) *ThinkingError {
  function NewThinkingErrorWithModel (line 71) | func NewThinkingErrorWithModel(code ErrorCode, message, model string) *T...

FILE: internal/thinking/provider/antigravity/apply.go
  type Applier (line 19) | type Applier struct
    method Apply (line 37) | func (a *Applier) Apply(body []byte, config thinking.ThinkingConfig, m...
    method applyCompatible (line 71) | func (a *Applier) applyCompatible(body []byte, config thinking.Thinkin...
    method applyLevelFormat (line 96) | func (a *Applier) applyLevelFormat(body []byte, config thinking.Thinki...
    method applyBudgetFormat (line 132) | func (a *Applier) applyBudgetFormat(body []byte, config thinking.Think...
    method normalizeClaudeBudget (line 195) | func (a *Applier) normalizeClaudeBudget(budget int, payload []byte, mo...
    method effectiveMaxTokens (line 228) | func (a *Applier) effectiveMaxTokens(payload []byte, modelInfo *regist...
  function NewApplier (line 24) | func NewApplier() *Applier {
  function init (line 28) | func init() {

FILE: internal/thinking/provider/claude/apply.go
  type Applier (line 20) | type Applier struct
    method Apply (line 72) | func (a *Applier) Apply(body []byte, config thinking.ThinkingConfig, m...
    method normalizeClaudeBudget (line 169) | func (a *Applier) normalizeClaudeBudget(body []byte, budgetTokens int,...
    method effectiveMaxTokens (line 211) | func (a *Applier) effectiveMaxTokens(body []byte, modelInfo *registry....
  function NewApplier (line 23) | func NewApplier() *Applier {
  function init (line 27) | func init() {
  function applyCompatibleClaude (line 221) | func applyCompatibleClaude(body []byte, config thinking.ThinkingConfig) ...

FILE: internal/thinking/provider/codex/apply.go
  type Applier (line 22) | type Applier struct
    method Apply (line 44) | func (a *Applier) Apply(body []byte, config thinking.ThinkingConfig, m...
  function NewApplier (line 27) | func NewApplier() *Applier {
  function init (line 31) | func init() {
  function applyCompatibleCodex (line 87) | func applyCompatibleCodex(body []byte, config thinking.ThinkingConfig) (...

FILE: internal/thinking/provider/gemini/apply.go
  type Applier (line 27) | type Applier struct
    method Apply (line 61) | func (a *Applier) Apply(body []byte, config thinking.ThinkingConfig, m...
    method applyCompatible (line 95) | func (a *Applier) applyCompatible(body []byte, config thinking.Thinkin...
    method applyLevelFormat (line 115) | func (a *Applier) applyLevelFormat(body []byte, config thinking.Thinki...
    method applyBudgetFormat (line 156) | func (a *Applier) applyBudgetFormat(body []byte, config thinking.Think...
  function NewApplier (line 30) | func NewApplier() *Applier {
  function init (line 34) | func init() {

FILE: internal/thinking/provider/geminicli/apply.go
  type Applier (line 15) | type Applier struct
    method Apply (line 29) | func (a *Applier) Apply(body []byte, config thinking.ThinkingConfig, m...
    method applyCompatible (line 61) | func (a *Applier) applyCompatible(body []byte, config thinking.Thinkin...
    method applyLevelFormat (line 81) | func (a *Applier) applyLevelFormat(body []byte, config thinking.Thinki...
    method applyBudgetFormat (line 117) | func (a *Applier) applyBudgetFormat(body []byte, config thinking.Think...
  function NewApplier (line 20) | func NewApplier() *Applier {
  function init (line 24) | func init() {

FILE: internal/thinking/provider/iflow/apply.go
  type Applier (line 28) | type Applier struct
    method Apply (line 57) | func (a *Applier) Apply(body []byte, config thinking.ThinkingConfig, m...
  function NewApplier (line 33) | func NewApplier() *Applier {
  function init (line 37) | func init() {
  function configToBoolean (line 86) | func configToBoolean(config thinking.ThinkingConfig) bool {
  function applyEnableThinking (line 113) | func applyEnableThinking(body []byte, config thinking.ThinkingConfig, se...
  function applyMiniMax (line 138) | func applyMiniMax(body []byte, config thinking.ThinkingConfig) []byte {
  function isEnableThinkingModel (line 151) | func isEnableThinkingModel(modelID string) bool {
  function isGLMModel (line 165) | func isGLMModel(modelID string) bool {
  function isMiniMaxModel (line 171) | func isMiniMaxModel(modelID string) bool {

FILE: internal/thinking/provider/kimi/apply.go
  type Applier (line 22) | type Applier struct
    method Apply (line 50) | func (a *Applier) Apply(body []byte, config thinking.ThinkingConfig, m...
  function NewApplier (line 27) | func NewApplier() *Applier {
  function init (line 31) | func init() {
  function applyCompatibleKimi (line 98) | func applyCompatibleKimi(body []byte, config thinking.ThinkingConfig) ([...
  function applyReasoningEffort (line 133) | func applyReasoningEffort(body []byte, effort string) ([]byte, error) {
  function applyDisabledThinking (line 145) | func applyDisabledThinking(body []byte) ([]byte, error) {

FILE: internal/thinking/provider/kimi/apply_test.go
  function TestApply_ModeNone_UsesDisabledThinking (line 11) | func TestApply_ModeNone_UsesDisabledThinking(t *testing.T) {
  function TestApply_ModeLevel_UsesReasoningEffort (line 34) | func TestApply_ModeLevel_UsesReasoningEffort(t *testing.T) {
  function TestApply_UserDefinedModeNone_UsesDisabledThinking (line 54) | func TestApply_UserDefinedModeNone_UsesDisabledThinking(t *testing.T) {

FILE: internal/thinking/provider/openai/apply.go
  type Applier (line 21) | type Applier struct
    method Apply (line 41) | func (a *Applier) Apply(body []byt
Condensed preview — 451 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,198K chars).
[
  {
    "path": ".dockerignore",
    "chars": 450,
    "preview": "# Git and GitHub folders\n.git/*\n.github/*\n\n# Docker and CI/CD related files\ndocker-compose.yml\n.dockerignore\n.gitignore\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 24,
    "preview": "github: [router-for-me]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 1359,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is it a reques"
  },
  {
    "path": ".github/workflows/docker-image.yml",
    "chars": 5324,
    "preview": "name: docker-image\n\non:\n  push:\n    tags:\n      - v*\n\nenv:\n  APP_NAME: CLIProxyAPI\n  DOCKERHUB_REPO: eceasy/cli-proxy-ap"
  },
  {
    "path": ".github/workflows/pr-path-guard.yml",
    "chars": 776,
    "preview": "name: translator-path-guard\n\non:\n  pull_request:\n    types:\n      - opened\n      - synchronize\n      - reopened\n\njobs:\n "
  },
  {
    "path": ".github/workflows/pr-test-build.yml",
    "chars": 633,
    "preview": "name: pr-test-build\n\non:\n  pull_request:\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    s"
  },
  {
    "path": ".github/workflows/release.yaml",
    "chars": 1198,
    "preview": "name: goreleaser\n\non:\n  push:\n    # run only against tags\n    tags:\n      - '*'\n\npermissions:\n  contents: write\n\njobs:\n "
  },
  {
    "path": ".gitignore",
    "chars": 474,
    "preview": "# Binaries\ncli-proxy-api\n*.exe\n\n# Configuration\nconfig.yaml\n.env\n\n# Generated content\nbin/*\nlogs/*\nconv/*\ntemp/*\nrefs/*\n"
  },
  {
    "path": ".goreleaser.yml",
    "chars": 738,
    "preview": "version: 2\n\nbuilds:\n  - id: \"cli-proxy-api\"\n    env:\n      - CGO_ENABLED=0\n    goos:\n      - linux\n      - windows\n     "
  },
  {
    "path": "Dockerfile",
    "chars": 694,
    "preview": "FROM golang:1.26-alpine AS builder\n\nWORKDIR /app\n\nCOPY go.mod go.sum ./\n\nRUN go mod download\n\nCOPY . .\n\nARG VERSION=dev\n"
  },
  {
    "path": "LICENSE",
    "chars": 1116,
    "preview": "MIT License\n\nCopyright (c) 2025-2005.9 Luis Pater\nCopyright (c) 2025.9-present Router-For.ME\n\nPermission is hereby grant"
  },
  {
    "path": "README.md",
    "chars": 10811,
    "preview": "# CLI Proxy API\n\nEnglish | [中文](README_CN.md)\n\nA proxy server that provides OpenAI/Gemini/Claude/Codex compatible API in"
  },
  {
    "path": "README_CN.md",
    "chars": 6567,
    "preview": "# CLI 代理 API\n\n[English](README.md) | 中文\n\n一个为 CLI 提供 OpenAI/Gemini/Claude/Codex 兼容 API 接口的代理服务器。\n\n现已支持通过 OAuth 登录接入 OpenA"
  },
  {
    "path": "auths/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "cmd/fetch_antigravity_models/main.go",
    "chars": 7548,
    "preview": "// Command fetch_antigravity_models connects to the Antigravity API using the\n// stored auth credentials and saves the d"
  },
  {
    "path": "cmd/server/main.go",
    "chars": 19125,
    "preview": "// Package main provides the entry point for the CLI Proxy API server.\n// This server acts as a proxy that provides Open"
  },
  {
    "path": "config.example.yaml",
    "chars": 17504,
    "preview": "# Server host/interface to bind to. Default is empty (\"\") to bind all interfaces (IPv4 + IPv6).\n# Use \"127.0.0.1\" or \"lo"
  },
  {
    "path": "docker-build.ps1",
    "chars": 2000,
    "preview": "# build.ps1 - Windows PowerShell Build Script\n#\n# This script automates the process of building and running the Docker c"
  },
  {
    "path": "docker-build.sh",
    "chars": 4910,
    "preview": "#!/usr/bin/env bash\n#\n# build.sh - Linux/macOS Build Script\n#\n# This script automates the process of building and runnin"
  },
  {
    "path": "docker-compose.yml",
    "chars": 763,
    "preview": "services:\n  cli-proxy-api:\n    image: ${CLI_PROXY_IMAGE:-eceasy/cli-proxy-api:latest}\n    pull_policy: always\n    build:"
  },
  {
    "path": "examples/custom-provider/main.go",
    "chars": 7426,
    "preview": "// Package main demonstrates how to create a custom AI provider executor\n// and integrate it with the CLI Proxy API serv"
  },
  {
    "path": "examples/http-request/main.go",
    "chars": 4358,
    "preview": "// Package main demonstrates how to use coreauth.Manager.HttpRequest/NewHttpRequest\n// to execute arbitrary HTTP request"
  },
  {
    "path": "examples/translator/main.go",
    "chars": 1840,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/sdk/translator\"\n\t_ \"github.com/route"
  },
  {
    "path": "go.mod",
    "chars": 4386,
    "preview": "module github.com/router-for-me/CLIProxyAPI/v6\n\ngo 1.26.0\n\nrequire (\n\tgithub.com/andybalholm/brotli v1.0.6\n\tgithub.com/a"
  },
  {
    "path": "go.sum",
    "chars": 22162,
    "preview": "cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=\ncloud.google.com/go/compute/"
  },
  {
    "path": "internal/access/config_access/provider.go",
    "chars": 3362,
    "preview": "package configaccess\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"strings\"\n\n\tsdkaccess \"github.com/router-for-me/CLIProxyAPI/v6/sd"
  },
  {
    "path": "internal/access/reconcile.go",
    "chars": 3778,
    "preview": "package access\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\n\tconfigaccess \"github.com/router-for-me/CLIProxyAPI/v6/in"
  },
  {
    "path": "internal/api/handlers/management/api_tools.go",
    "chars": 18823,
    "preview": "package management\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"githu"
  },
  {
    "path": "internal/api/handlers/management/api_tools_test.go",
    "chars": 3224,
    "preview": "package management\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/confi"
  },
  {
    "path": "internal/api/handlers/management/auth_files.go",
    "chars": 77959,
    "preview": "package management\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io"
  },
  {
    "path": "internal/api/handlers/management/auth_files_delete_test.go",
    "chars": 4660,
    "preview": "package management\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepa"
  },
  {
    "path": "internal/api/handlers/management/config_basic.go",
    "chars": 10064,
    "preview": "package management\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gith"
  },
  {
    "path": "internal/api/handlers/management/config_lists.go",
    "chars": 40384,
    "preview": "package management\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/router-for-me/"
  },
  {
    "path": "internal/api/handlers/management/handler.go",
    "chars": 9266,
    "preview": "// Package management provides the management API handlers and middleware\n// for configuring the server and managing aut"
  },
  {
    "path": "internal/api/handlers/management/logs.go",
    "chars": 14588,
    "preview": "package management\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"math\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\""
  },
  {
    "path": "internal/api/handlers/management/model_definitions.go",
    "chars": 893,
    "preview": "package management\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/router-for-me/CLIProxyAPI/"
  },
  {
    "path": "internal/api/handlers/management/oauth_callback.go",
    "chars": 2916,
    "preview": "package management\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype oauthCallb"
  },
  {
    "path": "internal/api/handlers/management/oauth_sessions.go",
    "chars": 6676,
    "preview": "package management\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\ncons"
  },
  {
    "path": "internal/api/handlers/management/quota.go",
    "chars": 630,
    "preview": "package management\n\nimport \"github.com/gin-gonic/gin\"\n\n// Quota exceeded toggles\nfunc (h *Handler) GetSwitchProject(c *g"
  },
  {
    "path": "internal/api/handlers/management/test_store_test.go",
    "chars": 918,
    "preview": "package management\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\tcoreauth \"github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth\"\n)"
  },
  {
    "path": "internal/api/handlers/management/usage.go",
    "chars": 2223,
    "preview": "package management\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/router-for-m"
  },
  {
    "path": "internal/api/handlers/management/vertex_import.go",
    "chars": 3848,
    "preview": "package management\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin"
  },
  {
    "path": "internal/api/middleware/request_logging.go",
    "chars": 4586,
    "preview": "// Package middleware provides HTTP middleware components for the CLI Proxy API server.\n// This file contains the reques"
  },
  {
    "path": "internal/api/middleware/request_logging_test.go",
    "chars": 3303,
    "preview": "package middleware\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestShouldSkipMethodForRequestLo"
  },
  {
    "path": "internal/api/middleware/response_writer.go",
    "chars": 14480,
    "preview": "// Package middleware provides Gin HTTP middleware for the CLI Proxy API server.\n// It includes a sophisticated response"
  },
  {
    "path": "internal/api/middleware/response_writer_test.go",
    "chars": 1160,
    "preview": "package middleware\n\nimport (\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc TestExtractRequestBody"
  },
  {
    "path": "internal/api/modules/amp/amp.go",
    "chars": 13289,
    "preview": "// Package amp implements the Amp CLI routing module, providing OAuth-based\n// integration with Amp CLI for ChatGPT and "
  },
  {
    "path": "internal/api/modules/amp/amp_test.go",
    "chars": 9325,
    "preview": "package amp\n\nimport (\n\t\"context\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/"
  },
  {
    "path": "internal/api/modules/amp/fallback_handlers.go",
    "chars": 11972,
    "preview": "package amp\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net/http/httputil\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/r"
  },
  {
    "path": "internal/api/modules/amp/fallback_handlers_test.go",
    "chars": 2003,
    "preview": "package amp\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/http/httputil\"\n\t\"testing\"\n\n\t\"gith"
  },
  {
    "path": "internal/api/modules/amp/gemini_bridge.go",
    "chars": 1923,
    "preview": "package amp\n\nimport (\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// createGeminiBridgeHandler creates a handler that bri"
  },
  {
    "path": "internal/api/modules/amp/gemini_bridge_test.go",
    "chars": 2571,
    "preview": "package amp\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc TestCreateGeminiB"
  },
  {
    "path": "internal/api/modules/amp/model_mapping.go",
    "chars": 5679,
    "preview": "// Package amp provides model mapping functionality for routing Amp CLI requests\n// to alternative models when the reque"
  },
  {
    "path": "internal/api/modules/amp/model_mapping_test.go",
    "chars": 10767,
    "preview": "package amp\n\nimport (\n\t\"testing\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/config\"\n\t\"github.com/router-for-me/"
  },
  {
    "path": "internal/api/modules/amp/proxy.go",
    "chars": 8005,
    "preview": "package amp\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net"
  },
  {
    "path": "internal/api/modules/amp/proxy_test.go",
    "chars": 19192,
    "preview": "package amp\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"te"
  },
  {
    "path": "internal/api/modules/amp/response_rewriter.go",
    "chars": 4077,
    "preview": "package amp\n\nimport (\n\t\"bytes\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n\tlog \"github.com/sirupsen/logrus\"\n\t\"g"
  },
  {
    "path": "internal/api/modules/amp/response_rewriter_test.go",
    "chars": 3928,
    "preview": "package amp\n\nimport (\n\t\"testing\"\n)\n\nfunc TestRewriteModelInResponse_TopLevel(t *testing.T) {\n\trw := &ResponseRewriter{or"
  },
  {
    "path": "internal/api/modules/amp/routes.go",
    "chars": 13393,
    "preview": "package amp\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gi"
  },
  {
    "path": "internal/api/modules/amp/routes_test.go",
    "chars": 11038,
    "preview": "package amp\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/router-for-m"
  },
  {
    "path": "internal/api/modules/amp/secret.go",
    "chars": 7226,
    "preview": "package amp\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com"
  },
  {
    "path": "internal/api/modules/amp/secret_test.go",
    "chars": 9515,
    "preview": "package amp\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/router"
  },
  {
    "path": "internal/api/modules/modules.go",
    "chars": 3599,
    "preview": "// Package modules provides a pluggable routing module system for extending\n// the API server with optional features wit"
  },
  {
    "path": "internal/api/server.go",
    "chars": 37032,
    "preview": "// Package api provides the HTTP API server implementation for the CLI Proxy API.\n// It includes the main server struct,"
  },
  {
    "path": "internal/api/server_test.go",
    "chars": 6148,
    "preview": "package api\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\tgin \"githu"
  },
  {
    "path": "internal/auth/antigravity/auth.go",
    "chars": 11032,
    "preview": "// Package antigravity provides OAuth2 authentication functionality for the Antigravity provider.\npackage antigravity\n\ni"
  },
  {
    "path": "internal/auth/antigravity/constants.go",
    "chars": 1312,
    "preview": "// Package antigravity provides OAuth2 authentication functionality for the Antigravity provider.\npackage antigravity\n\n/"
  },
  {
    "path": "internal/auth/antigravity/filename.go",
    "chars": 376,
    "preview": "package antigravity\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// CredentialFileName returns the filename used to persist Antigravit"
  },
  {
    "path": "internal/auth/claude/anthropic.go",
    "chars": 1269,
    "preview": "package claude\n\n// PKCECodes holds PKCE verification codes for OAuth2 PKCE flow\ntype PKCECodes struct {\n\t// CodeVerifier"
  },
  {
    "path": "internal/auth/claude/anthropic_auth.go",
    "chars": 11666,
    "preview": "// Package claude provides OAuth2 authentication functionality for Anthropic's Claude API.\n// This package implements th"
  },
  {
    "path": "internal/auth/claude/errors.go",
    "chars": 5676,
    "preview": "// Package claude provides authentication and token management functionality\n// for Anthropic's Claude AI services. It h"
  },
  {
    "path": "internal/auth/claude/html_templates.go",
    "chars": 7049,
    "preview": "// Package claude provides authentication and token management functionality\n// for Anthropic's Claude AI services. It h"
  },
  {
    "path": "internal/auth/claude/oauth_server.go",
    "chars": 8930,
    "preview": "// Package claude provides authentication and token management functionality\n// for Anthropic's Claude AI services. It h"
  },
  {
    "path": "internal/auth/claude/pkce.go",
    "chars": 2030,
    "preview": "// Package claude provides authentication and token management functionality\n// for Anthropic's Claude AI services. It h"
  },
  {
    "path": "internal/auth/claude/token.go",
    "chars": 3153,
    "preview": "// Package claude provides authentication and token management functionality\n// for Anthropic's Claude AI services. It h"
  },
  {
    "path": "internal/auth/claude/utls_transport.go",
    "chars": 4742,
    "preview": "// Package claude provides authentication functionality for Anthropic's Claude API.\n// This file implements a custom HTT"
  },
  {
    "path": "internal/auth/codex/errors.go",
    "chars": 5713,
    "preview": "package codex\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\n// OAuthError represents an OAuth-specific error.\ntype OAuthErro"
  },
  {
    "path": "internal/auth/codex/filename.go",
    "chars": 1167,
    "preview": "package codex\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"unicode\"\n)\n\n// CredentialFileName returns the filename used to persist Codex"
  },
  {
    "path": "internal/auth/codex/html_templates.go",
    "chars": 6701,
    "preview": "package codex\n\n// LoginSuccessHTML is the HTML template for the page shown after a successful\n// OAuth2 authentication w"
  },
  {
    "path": "internal/auth/codex/jwt_parser.go",
    "chars": 3852,
    "preview": "package codex\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n)\n\n// JWTClaims represents the cla"
  },
  {
    "path": "internal/auth/codex/oauth_server.go",
    "chars": 8690,
    "preview": "package codex\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\tlog \"github.com/siru"
  },
  {
    "path": "internal/auth/codex/openai.go",
    "chars": 1775,
    "preview": "package codex\n\n// PKCECodes holds the verification codes for the OAuth2 PKCE (Proof Key for Code Exchange) flow.\n// PKCE"
  },
  {
    "path": "internal/auth/codex/openai_auth.go",
    "chars": 10284,
    "preview": "// Package codex provides authentication and token management for OpenAI's Codex API.\n// It handles the OAuth2 flow, inc"
  },
  {
    "path": "internal/auth/codex/openai_auth_test.go",
    "chars": 1191,
    "preview": "package codex\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n)\n\ntype roundTripFunc func(*ht"
  },
  {
    "path": "internal/auth/codex/pkce.go",
    "chars": 2160,
    "preview": "// Package codex provides authentication and token management functionality\n// for OpenAI's Codex AI services. It handle"
  },
  {
    "path": "internal/auth/codex/token.go",
    "chars": 3117,
    "preview": "// Package codex provides authentication and token management functionality\n// for OpenAI's Codex AI services. It handle"
  },
  {
    "path": "internal/auth/empty/token.go",
    "chars": 1096,
    "preview": "// Package empty provides a no-operation token storage implementation.\n// This package is used when authentication token"
  },
  {
    "path": "internal/auth/gemini/gemini_auth.go",
    "chars": 12046,
    "preview": "// Package gemini provides authentication and token management functionality\n// for Google's Gemini AI services. It hand"
  },
  {
    "path": "internal/auth/gemini/gemini_token.go",
    "chars": 3722,
    "preview": "// Package gemini provides authentication and token management functionality\n// for Google's Gemini AI services. It hand"
  },
  {
    "path": "internal/auth/iflow/cookie_helpers.go",
    "chars": 2402,
    "preview": "package iflow\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\n// NormalizeCookie normalizes raw c"
  },
  {
    "path": "internal/auth/iflow/iflow_auth.go",
    "chars": 17437,
    "preview": "package iflow\n\nimport (\n\t\"compress/gzip\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/u"
  },
  {
    "path": "internal/auth/iflow/iflow_token.go",
    "chars": 1807,
    "preview": "package iflow\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/intern"
  },
  {
    "path": "internal/auth/iflow/oauth_server.go",
    "chars": 3253,
    "preview": "package iflow\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\tlog \"github.com/sirupsen/logru"
  },
  {
    "path": "internal/auth/kimi/kimi.go",
    "chars": 12474,
    "preview": "// Package kimi provides authentication and token management for Kimi (Moonshot AI) API.\n// It handles the RFC 8628 OAut"
  },
  {
    "path": "internal/auth/kimi/token.go",
    "chars": 4688,
    "preview": "// Package kimi provides authentication and token management functionality\n// for Kimi (Moonshot AI) services. It handle"
  },
  {
    "path": "internal/auth/models.go",
    "chars": 733,
    "preview": "// Package auth provides authentication functionality for various AI service providers.\n// It includes interfaces and im"
  },
  {
    "path": "internal/auth/qwen/qwen_auth.go",
    "chars": 13291,
    "preview": "package qwen\n\nimport (\n\t\"context\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net"
  },
  {
    "path": "internal/auth/qwen/qwen_token.go",
    "chars": 2967,
    "preview": "// Package qwen provides authentication and token management functionality\n// for Alibaba's Qwen AI services. It handles"
  },
  {
    "path": "internal/auth/vertex/keyutil.go",
    "chars": 5255,
    "preview": "package vertex\n\nimport (\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"strin"
  },
  {
    "path": "internal/auth/vertex/vertex_credentials.go",
    "chars": 2356,
    "preview": "// Package vertex provides token storage for Google Vertex AI Gemini via service account credentials.\n// It serialises s"
  },
  {
    "path": "internal/browser/browser.go",
    "chars": 4194,
    "preview": "// Package browser provides cross-platform functionality for opening URLs in the default web browser.\n// It abstracts th"
  },
  {
    "path": "internal/buildinfo/buildinfo.go",
    "chars": 470,
    "preview": "// Package buildinfo exposes compile-time metadata shared across the server.\npackage buildinfo\n\n// The following variabl"
  },
  {
    "path": "internal/cache/signature_cache.go",
    "chars": 5008,
    "preview": "package cache\n\nimport (\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\n// SignatureEntry holds a cached "
  },
  {
    "path": "internal/cache/signature_cache_test.go",
    "chars": 6551,
    "preview": "package cache\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nconst testModelName = \"claude-sonnet-4-5\"\n\nfunc TestCacheSignature_BasicSt"
  },
  {
    "path": "internal/cmd/anthropic_login.go",
    "chars": 1598,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/claude\""
  },
  {
    "path": "internal/cmd/antigravity_login.go",
    "chars": 1124,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/config\"\n\tsdkAuth \"github.com"
  },
  {
    "path": "internal/cmd/auth_manager.go",
    "chars": 766,
    "preview": "package cmd\n\nimport (\n\tsdkAuth \"github.com/router-for-me/CLIProxyAPI/v6/sdk/auth\"\n)\n\n// newAuthManager creates a new aut"
  },
  {
    "path": "internal/cmd/iflow_cookie.go",
    "chars": 2715,
    "preview": "package cmd\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/router-for-me/C"
  },
  {
    "path": "internal/cmd/iflow_login.go",
    "chars": 1125,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/config\"\n\tsdkAuth \""
  },
  {
    "path": "internal/cmd/kimi_login.go",
    "chars": 1279,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/config\"\n\tsdkAuth \"github.com"
  },
  {
    "path": "internal/cmd/login.go",
    "chars": 22189,
    "preview": "// Package cmd provides command-line interface functionality for the CLI Proxy API server.\n// It includes authentication"
  },
  {
    "path": "internal/cmd/openai_device_login.go",
    "chars": 1506,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/codex\"\n"
  },
  {
    "path": "internal/cmd/openai_login.go",
    "chars": 2092,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/codex\"\n"
  },
  {
    "path": "internal/cmd/qwen_login.go",
    "chars": 1528,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/config\"\n\tsdkAuth \""
  },
  {
    "path": "internal/cmd/run.go",
    "chars": 3314,
    "preview": "// Package cmd provides command-line interface functionality for the CLI Proxy API server.\n// It includes authentication"
  },
  {
    "path": "internal/cmd/vertex_import.go",
    "chars": 3624,
    "preview": "// Package cmd contains CLI helpers. This file implements importing a Vertex AI\n// service account JSON into the auth st"
  },
  {
    "path": "internal/config/codex_websocket_header_defaults_test.go",
    "chars": 870,
    "preview": "package config\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n)\n\nfunc TestLoadConfigOptional_CodexHeaderDefaults(t *testing"
  },
  {
    "path": "internal/config/config.go",
    "chars": 64027,
    "preview": "// Package config provides configuration management for the CLI Proxy API server.\n// It handles loading and parsing YAML"
  },
  {
    "path": "internal/config/oauth_model_alias_test.go",
    "chars": 2200,
    "preview": "package config\n\nimport \"testing\"\n\nfunc TestSanitizeOAuthModelAlias_PreservesForkFlag(t *testing.T) {\n\tcfg := &Config{\n\t\t"
  },
  {
    "path": "internal/config/sdk_config.go",
    "chars": 2414,
    "preview": "// Package config provides configuration management for the CLI Proxy API server.\n// It handles loading and parsing YAML"
  },
  {
    "path": "internal/config/vertex_compat.go",
    "chars": 4015,
    "preview": "package config\n\nimport \"strings\"\n\n// VertexCompatKey represents the configuration for Vertex AI-compatible API keys.\n// "
  },
  {
    "path": "internal/constant/constant.go",
    "chars": 865,
    "preview": "// Package constant defines provider name constants used throughout the CLI Proxy API.\n// These constants identify diffe"
  },
  {
    "path": "internal/interfaces/api_handler.go",
    "chars": 838,
    "preview": "// Package interfaces defines the core interfaces and shared structures for the CLI Proxy API server.\n// These interface"
  },
  {
    "path": "internal/interfaces/client_models.go",
    "chars": 6806,
    "preview": "// Package interfaces defines the core interfaces and shared structures for the CLI Proxy API server.\n// These interface"
  },
  {
    "path": "internal/interfaces/error_message.go",
    "chars": 748,
    "preview": "// Package interfaces defines the core interfaces and shared structures for the CLI Proxy API server.\n// These interface"
  },
  {
    "path": "internal/interfaces/types.go",
    "chars": 711,
    "preview": "// Package interfaces provides type aliases for backwards compatibility with translator functions.\n// It defines common "
  },
  {
    "path": "internal/logging/gin_logger.go",
    "chars": 4251,
    "preview": "// Package logging provides Gin middleware for HTTP request logging and panic recovery.\n// It integrates Gin web framewo"
  },
  {
    "path": "internal/logging/gin_logger_test.go",
    "chars": 1344,
    "preview": "package logging\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc Tes"
  },
  {
    "path": "internal/logging/global_logger.go",
    "chars": 5330,
    "preview": "package logging\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/gin-gonic/gin\"\n\t"
  },
  {
    "path": "internal/logging/log_dir_cleaner.go",
    "chars": 3258,
    "preview": "package logging\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\tlog \"github.com/sirupsen/logrus"
  },
  {
    "path": "internal/logging/log_dir_cleaner_test.go",
    "chars": 2021,
    "preview": "package logging\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestEnforceLogDirSizeLimitDeletesOldest(t *t"
  },
  {
    "path": "internal/logging/request_logger.go",
    "chars": 38905,
    "preview": "// Package logging provides request logging functionality for the CLI Proxy API server.\n// It handles capturing and stor"
  },
  {
    "path": "internal/logging/requestid.go",
    "chars": 1415,
    "preview": "package logging\n\nimport (\n\t\"context\"\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// requestIDKey is t"
  },
  {
    "path": "internal/managementasset/updater.go",
    "chars": 12969,
    "preview": "package managementasset\n\nimport (\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\""
  },
  {
    "path": "internal/misc/claude_code_instructions.go",
    "chars": 675,
    "preview": "// Package misc provides miscellaneous utility functions and embedded data for the CLI Proxy API.\n// This package contai"
  },
  {
    "path": "internal/misc/claude_code_instructions.txt",
    "chars": 137,
    "preview": "[{\"type\":\"text\",\"text\":\"You are a Claude agent, built on Anthropic's Claude Agent SDK.\",\"cache_control\":{\"type\":\"ephemer"
  },
  {
    "path": "internal/misc/copy-example-config.go",
    "chars": 755,
    "preview": "package misc\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\n\tlog \"github.com/sirupsen/logrus\"\n)\n\nfunc CopyConfigTemplate(src, d"
  },
  {
    "path": "internal/misc/credentials.go",
    "chars": 1630,
    "preview": "package misc\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\tlog \"github.com/sirupsen/logrus\"\n)\n\n// Sepa"
  },
  {
    "path": "internal/misc/header_utils.go",
    "chars": 4087,
    "preview": "// Package misc provides miscellaneous utility functions for the CLI Proxy API server.\n// It includes helper functions f"
  },
  {
    "path": "internal/misc/mime-type.go",
    "chars": 33686,
    "preview": "// Package misc provides miscellaneous utility functions and embedded data for the CLI Proxy API.\n// This package contai"
  },
  {
    "path": "internal/misc/oauth.go",
    "chars": 2698,
    "preview": "package misc\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"strings\"\n)\n\n// GenerateRandomState generates a"
  },
  {
    "path": "internal/registry/model_definitions.go",
    "chars": 4799,
    "preview": "// Package registry provides model definitions and lookup helpers for various AI providers.\n// Static model metadata is "
  },
  {
    "path": "internal/registry/model_registry.go",
    "chars": 40018,
    "preview": "// Package registry provides centralized model management for all AI service providers.\n// It implements a dynamic model"
  },
  {
    "path": "internal/registry/model_registry_cache_test.go",
    "chars": 1896,
    "preview": "package registry\n\nimport \"testing\"\n\nfunc TestGetAvailableModelsReturnsClonedSnapshots(t *testing.T) {\n\tr := newTestModel"
  },
  {
    "path": "internal/registry/model_registry_hook_test.go",
    "chars": 5399,
    "preview": "package registry\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc newTestModelRegistry() *ModelRegistry {\n\treturn"
  },
  {
    "path": "internal/registry/model_registry_safety_test.go",
    "chars": 5066,
    "preview": "package registry\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestGetModelInfoReturnsClone(t *testing.T) {\n\tr := newTestModelReg"
  },
  {
    "path": "internal/registry/model_updater.go",
    "chars": 9976,
    "preview": "package registry\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t"
  },
  {
    "path": "internal/registry/models/models.json",
    "chars": 69929,
    "preview": "{\n  \"claude\": [\n    {\n      \"id\": \"claude-haiku-4-5-20251001\",\n      \"object\": \"model\",\n      \"created\": 1759276800,\n   "
  },
  {
    "path": "internal/runtime/executor/aistudio_executor.go",
    "chars": 16529,
    "preview": "// Package executor provides runtime execution capabilities for various AI service providers.\n// This file implements th"
  },
  {
    "path": "internal/runtime/executor/antigravity_executor.go",
    "chars": 52382,
    "preview": "// Package executor provides runtime execution capabilities for various AI service providers.\n// This file implements th"
  },
  {
    "path": "internal/runtime/executor/antigravity_executor_buildrequest_test.go",
    "chars": 4173,
    "preview": "package executor\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"testing\"\n\n\tcliproxyauth \"github.com/router-for-me/CLIProx"
  },
  {
    "path": "internal/runtime/executor/cache_helpers.go",
    "chars": 1750,
    "preview": "package executor\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype codexCache struct {\n\tID     string\n\tExpire time.Time\n}\n\n// codexCache"
  },
  {
    "path": "internal/runtime/executor/caching_verify_test.go",
    "chars": 9182,
    "preview": "package executor\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/tidwall/gjson\"\n)\n\nfunc TestEnsureCacheControl(t *testing.T) {"
  },
  {
    "path": "internal/runtime/executor/claude_executor.go",
    "chars": 61356,
    "preview": "package executor\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"compress/flate\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"crypto/sha25"
  },
  {
    "path": "internal/runtime/executor/claude_executor_test.go",
    "chars": 40456,
    "preview": "package executor\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"test"
  },
  {
    "path": "internal/runtime/executor/cloak_obfuscate.go",
    "chars": 4490,
    "preview": "package executor\n\nimport (\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\t\"unicode/utf8\"\n\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwal"
  },
  {
    "path": "internal/runtime/executor/cloak_utils.go",
    "chars": 1591,
    "preview": "package executor\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/google/uuid\"\n)\n\n// userIDPa"
  },
  {
    "path": "internal/runtime/executor/codex_executor.go",
    "chars": 25114,
    "preview": "package executor\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\tcodexauth \"github."
  },
  {
    "path": "internal/runtime/executor/codex_executor_cache_test.go",
    "chars": 2197,
    "preview": "package executor\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/go"
  },
  {
    "path": "internal/runtime/executor/codex_executor_retry_test.go",
    "chars": 2171,
    "preview": "package executor\n\nimport (\n\t\"net/http\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestParseCodexRetryAfter(t *testing.T) {\n\tn"
  },
  {
    "path": "internal/runtime/executor/codex_websockets_executor.go",
    "chars": 40738,
    "preview": "// Package executor provides runtime execution capabilities for various AI service providers.\n// This file implements a "
  },
  {
    "path": "internal/runtime/executor/codex_websockets_executor_test.go",
    "chars": 6814,
    "preview": "package executor\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github."
  },
  {
    "path": "internal/runtime/executor/gemini_cli_executor.go",
    "chars": 29503,
    "preview": "// Package executor provides runtime execution capabilities for various AI service providers.\n// This file implements th"
  },
  {
    "path": "internal/runtime/executor/gemini_executor.go",
    "chars": 19342,
    "preview": "// Package executor provides runtime execution capabilities for various AI service providers.\n// It includes stateless e"
  },
  {
    "path": "internal/runtime/executor/gemini_vertex_executor.go",
    "chars": 39218,
    "preview": "// Package executor provides runtime execution capabilities for various AI service providers.\n// This file implements th"
  },
  {
    "path": "internal/runtime/executor/iflow_executor.go",
    "chars": 19445,
    "preview": "package executor\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/hmac\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"io\"\n\t\"n"
  },
  {
    "path": "internal/runtime/executor/iflow_executor_test.go",
    "chars": 1631,
    "preview": "package executor\n\nimport (\n\t\"testing\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking\"\n)\n\nfunc TestIFlowExe"
  },
  {
    "path": "internal/runtime/executor/kimi_executor.go",
    "chars": 19480,
    "preview": "package executor\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"str"
  },
  {
    "path": "internal/runtime/executor/kimi_executor_test.go",
    "chars": 7074,
    "preview": "package executor\n\nimport (\n\t\"testing\"\n\n\t\"github.com/tidwall/gjson\"\n)\n\nfunc TestNormalizeKimiToolMessageLinks_UsesCallIDF"
  },
  {
    "path": "internal/runtime/executor/logging_helpers.go",
    "chars": 10495,
    "preview": "package executor\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"html\"\n\t\"net/http\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-g"
  },
  {
    "path": "internal/runtime/executor/openai_compat_executor.go",
    "chars": 14235,
    "preview": "package executor\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/router"
  },
  {
    "path": "internal/runtime/executor/openai_compat_executor_compact_test.go",
    "chars": 2026,
    "preview": "package executor\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/router-for-me/CLIP"
  },
  {
    "path": "internal/runtime/executor/payload_helpers.go",
    "chars": 8033,
    "preview": "package executor\n\nimport (\n\t\"encoding/json\"\n\t\"strings\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/config\"\n\t\"git"
  },
  {
    "path": "internal/runtime/executor/proxy_helpers.go",
    "chars": 2540,
    "preview": "package executor\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal"
  },
  {
    "path": "internal/runtime/executor/proxy_helpers_test.go",
    "chars": 801,
    "preview": "package executor\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/config\""
  },
  {
    "path": "internal/runtime/executor/qwen_executor.go",
    "chars": 19833,
    "preview": "package executor\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\tqwenauth \""
  },
  {
    "path": "internal/runtime/executor/qwen_executor_test.go",
    "chars": 777,
    "preview": "package executor\n\nimport (\n\t\"testing\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking\"\n)\n\nfunc TestQwenExec"
  },
  {
    "path": "internal/runtime/executor/thinking_providers.go",
    "chars": 665,
    "preview": "package executor\n\nimport (\n\t_ \"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking/provider/antigravity\"\n\t_ \"githu"
  },
  {
    "path": "internal/runtime/executor/token_helpers.go",
    "chars": 7112,
    "preview": "package executor\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tiktoken-go/tokenizer\"\n)\n\n// toke"
  },
  {
    "path": "internal/runtime/executor/usage_helpers.go",
    "chars": 15975,
    "preview": "package executor\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\tcliproxya"
  },
  {
    "path": "internal/runtime/executor/usage_helpers_test.go",
    "chars": 1588,
    "preview": "package executor\n\nimport \"testing\"\n\nfunc TestParseOpenAIUsageChatCompletions(t *testing.T) {\n\tdata := []byte(`{\"usage\":{"
  },
  {
    "path": "internal/runtime/executor/user_id_cache.go",
    "chars": 1849,
    "preview": "package executor\n\nimport (\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype userIDCacheEntry struct {\n\tvalue  st"
  },
  {
    "path": "internal/runtime/executor/user_id_cache_test.go",
    "chars": 1986,
    "preview": "package executor\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc resetUserIDCache() {\n\tuserIDCacheMu.Lock()\n\tuserIDCache = make(map"
  },
  {
    "path": "internal/runtime/geminicli/state.go",
    "chars": 3402,
    "preview": "package geminicli\n\nimport (\n\t\"strings\"\n\t\"sync\"\n)\n\n// SharedCredential keeps canonical OAuth metadata for a multi-project"
  },
  {
    "path": "internal/store/gitstore.go",
    "chars": 20806,
    "preview": "package store\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n"
  },
  {
    "path": "internal/store/objectstore.go",
    "chars": 18212,
    "preview": "package store\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/fi"
  },
  {
    "path": "internal/store/postgresstore.go",
    "chars": 19841,
    "preview": "package store\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"s"
  },
  {
    "path": "internal/thinking/apply.go",
    "chars": 18377,
    "preview": "// Package thinking provides unified thinking configuration processing.\npackage thinking\n\nimport (\n\t\"strings\"\n\n\t\"github."
  },
  {
    "path": "internal/thinking/apply_user_defined_test.go",
    "chars": 1675,
    "preview": "package thinking_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/registry\"\n\t\"github.com/ro"
  },
  {
    "path": "internal/thinking/convert.go",
    "chars": 5658,
    "preview": "package thinking\n\nimport (\n\t\"strings\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/registry\"\n)\n\n// levelToBudgetM"
  },
  {
    "path": "internal/thinking/errors.go",
    "chars": 3009,
    "preview": "// Package thinking provides unified thinking configuration processing logic.\npackage thinking\n\nimport \"net/http\"\n\n// Er"
  },
  {
    "path": "internal/thinking/provider/antigravity/apply.go",
    "chars": 9300,
    "preview": "// Package antigravity implements thinking configuration for Antigravity API format.\n//\n// Antigravity uses request.gene"
  },
  {
    "path": "internal/thinking/provider/claude/apply.go",
    "chars": 10034,
    "preview": "// Package claude implements thinking configuration scaffolding for Claude models.\n//\n// Claude models support two think"
  },
  {
    "path": "internal/thinking/provider/codex/apply.go",
    "chars": 3264,
    "preview": "// Package codex implements thinking configuration for Codex (OpenAI Responses API) models.\n//\n// Codex models use the r"
  },
  {
    "path": "internal/thinking/provider/gemini/apply.go",
    "chars": 7659,
    "preview": "// Package gemini implements thinking configuration for Gemini models.\n//\n// Gemini models have two formats:\n//   - Gemi"
  },
  {
    "path": "internal/thinking/provider/geminicli/apply.go",
    "chars": 6472,
    "preview": "// Package geminicli implements thinking configuration for Gemini CLI API format.\n//\n// Gemini CLI uses request.generati"
  },
  {
    "path": "internal/thinking/provider/iflow/apply.go",
    "chars": 4803,
    "preview": "// Package iflow implements thinking configuration for iFlow models.\n//\n// iFlow models use boolean toggle semantics:\n//"
  },
  {
    "path": "internal/thinking/provider/kimi/apply.go",
    "chars": 4463,
    "preview": "// Package kimi implements thinking configuration for Kimi (Moonshot AI) models.\n//\n// Kimi models use the OpenAI-compat"
  },
  {
    "path": "internal/thinking/provider/kimi/apply_test.go",
    "chars": 2739,
    "preview": "package kimi\n\nimport (\n\t\"testing\"\n\n\t\"github.com/router-for-me/CLIProxyAPI/v6/internal/registry\"\n\t\"github.com/router-for-"
  },
  {
    "path": "internal/thinking/provider/openai/apply.go",
    "chars": 3181,
    "preview": "// Package openai implements thinking configuration for OpenAI/Codex models.\n//\n// OpenAI models use the reasoning_effor"
  }
]

// ... and 251 more files (download for full content)

About this extraction

This page contains the full source code of the router-for-me/CLIProxyAPI GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 451 files (3.6 MB), approximately 967.3k tokens, and a symbol index with 3936 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!