ccproxy-api 0.1.7__py3-none-any.whl → 0.2.0a4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (481) hide show
  1. ccproxy/api/__init__.py +1 -15
  2. ccproxy/api/app.py +434 -219
  3. ccproxy/api/bootstrap.py +30 -0
  4. ccproxy/api/decorators.py +85 -0
  5. ccproxy/api/dependencies.py +144 -168
  6. ccproxy/api/format_validation.py +54 -0
  7. ccproxy/api/middleware/cors.py +6 -3
  8. ccproxy/api/middleware/errors.py +388 -524
  9. ccproxy/api/middleware/hooks.py +563 -0
  10. ccproxy/api/middleware/normalize_headers.py +59 -0
  11. ccproxy/api/middleware/request_id.py +35 -16
  12. ccproxy/api/middleware/streaming_hooks.py +292 -0
  13. ccproxy/api/routes/__init__.py +5 -14
  14. ccproxy/api/routes/health.py +39 -672
  15. ccproxy/api/routes/plugins.py +277 -0
  16. ccproxy/auth/__init__.py +2 -19
  17. ccproxy/auth/bearer.py +25 -15
  18. ccproxy/auth/dependencies.py +123 -157
  19. ccproxy/auth/exceptions.py +0 -12
  20. ccproxy/auth/manager.py +35 -49
  21. ccproxy/auth/managers/__init__.py +10 -0
  22. ccproxy/auth/managers/base.py +523 -0
  23. ccproxy/auth/managers/base_enhanced.py +63 -0
  24. ccproxy/auth/managers/token_snapshot.py +77 -0
  25. ccproxy/auth/models/base.py +65 -0
  26. ccproxy/auth/models/credentials.py +40 -0
  27. ccproxy/auth/oauth/__init__.py +4 -18
  28. ccproxy/auth/oauth/base.py +533 -0
  29. ccproxy/auth/oauth/cli_errors.py +37 -0
  30. ccproxy/auth/oauth/flows.py +430 -0
  31. ccproxy/auth/oauth/protocol.py +366 -0
  32. ccproxy/auth/oauth/registry.py +408 -0
  33. ccproxy/auth/oauth/router.py +396 -0
  34. ccproxy/auth/oauth/routes.py +186 -113
  35. ccproxy/auth/oauth/session.py +151 -0
  36. ccproxy/auth/oauth/templates.py +342 -0
  37. ccproxy/auth/storage/__init__.py +2 -5
  38. ccproxy/auth/storage/base.py +279 -5
  39. ccproxy/auth/storage/generic.py +134 -0
  40. ccproxy/cli/__init__.py +1 -2
  41. ccproxy/cli/_settings_help.py +351 -0
  42. ccproxy/cli/commands/auth.py +1519 -793
  43. ccproxy/cli/commands/config/commands.py +209 -276
  44. ccproxy/cli/commands/plugins.py +669 -0
  45. ccproxy/cli/commands/serve.py +75 -810
  46. ccproxy/cli/commands/status.py +254 -0
  47. ccproxy/cli/decorators.py +83 -0
  48. ccproxy/cli/helpers.py +22 -60
  49. ccproxy/cli/main.py +359 -10
  50. ccproxy/cli/options/claude_options.py +0 -25
  51. ccproxy/config/__init__.py +7 -11
  52. ccproxy/config/core.py +227 -0
  53. ccproxy/config/env_generator.py +232 -0
  54. ccproxy/config/runtime.py +67 -0
  55. ccproxy/config/security.py +36 -3
  56. ccproxy/config/settings.py +382 -441
  57. ccproxy/config/toml_generator.py +299 -0
  58. ccproxy/config/utils.py +452 -0
  59. ccproxy/core/__init__.py +7 -271
  60. ccproxy/{_version.py → core/_version.py} +16 -3
  61. ccproxy/core/async_task_manager.py +516 -0
  62. ccproxy/core/async_utils.py +47 -14
  63. ccproxy/core/auth/__init__.py +6 -0
  64. ccproxy/core/constants.py +16 -50
  65. ccproxy/core/errors.py +53 -0
  66. ccproxy/core/id_utils.py +20 -0
  67. ccproxy/core/interfaces.py +16 -123
  68. ccproxy/core/logging.py +473 -18
  69. ccproxy/core/plugins/__init__.py +77 -0
  70. ccproxy/core/plugins/cli_discovery.py +211 -0
  71. ccproxy/core/plugins/declaration.py +455 -0
  72. ccproxy/core/plugins/discovery.py +604 -0
  73. ccproxy/core/plugins/factories.py +967 -0
  74. ccproxy/core/plugins/hooks/__init__.py +30 -0
  75. ccproxy/core/plugins/hooks/base.py +58 -0
  76. ccproxy/core/plugins/hooks/events.py +46 -0
  77. ccproxy/core/plugins/hooks/implementations/__init__.py +16 -0
  78. ccproxy/core/plugins/hooks/implementations/formatters/__init__.py +11 -0
  79. ccproxy/core/plugins/hooks/implementations/formatters/json.py +552 -0
  80. ccproxy/core/plugins/hooks/implementations/formatters/raw.py +370 -0
  81. ccproxy/core/plugins/hooks/implementations/http_tracer.py +431 -0
  82. ccproxy/core/plugins/hooks/layers.py +44 -0
  83. ccproxy/core/plugins/hooks/manager.py +186 -0
  84. ccproxy/core/plugins/hooks/registry.py +139 -0
  85. ccproxy/core/plugins/hooks/thread_manager.py +203 -0
  86. ccproxy/core/plugins/hooks/types.py +22 -0
  87. ccproxy/core/plugins/interfaces.py +416 -0
  88. ccproxy/core/plugins/loader.py +166 -0
  89. ccproxy/core/plugins/middleware.py +233 -0
  90. ccproxy/core/plugins/models.py +59 -0
  91. ccproxy/core/plugins/protocol.py +180 -0
  92. ccproxy/core/plugins/runtime.py +519 -0
  93. ccproxy/{observability/context.py → core/request_context.py} +137 -94
  94. ccproxy/core/status_report.py +211 -0
  95. ccproxy/core/transformers.py +13 -8
  96. ccproxy/data/claude_headers_fallback.json +540 -19
  97. ccproxy/data/codex_headers_fallback.json +114 -7
  98. ccproxy/http/__init__.py +30 -0
  99. ccproxy/http/base.py +95 -0
  100. ccproxy/http/client.py +323 -0
  101. ccproxy/http/hooks.py +642 -0
  102. ccproxy/http/pool.py +279 -0
  103. ccproxy/llms/formatters/__init__.py +7 -0
  104. ccproxy/llms/formatters/anthropic_to_openai/__init__.py +55 -0
  105. ccproxy/llms/formatters/anthropic_to_openai/errors.py +65 -0
  106. ccproxy/llms/formatters/anthropic_to_openai/requests.py +356 -0
  107. ccproxy/llms/formatters/anthropic_to_openai/responses.py +153 -0
  108. ccproxy/llms/formatters/anthropic_to_openai/streams.py +1546 -0
  109. ccproxy/llms/formatters/base.py +140 -0
  110. ccproxy/llms/formatters/base_model.py +33 -0
  111. ccproxy/llms/formatters/common/__init__.py +51 -0
  112. ccproxy/llms/formatters/common/identifiers.py +48 -0
  113. ccproxy/llms/formatters/common/streams.py +254 -0
  114. ccproxy/llms/formatters/common/thinking.py +74 -0
  115. ccproxy/llms/formatters/common/usage.py +135 -0
  116. ccproxy/llms/formatters/constants.py +55 -0
  117. ccproxy/llms/formatters/context.py +116 -0
  118. ccproxy/llms/formatters/mapping.py +33 -0
  119. ccproxy/llms/formatters/openai_to_anthropic/__init__.py +55 -0
  120. ccproxy/llms/formatters/openai_to_anthropic/_helpers.py +141 -0
  121. ccproxy/llms/formatters/openai_to_anthropic/errors.py +53 -0
  122. ccproxy/llms/formatters/openai_to_anthropic/requests.py +674 -0
  123. ccproxy/llms/formatters/openai_to_anthropic/responses.py +285 -0
  124. ccproxy/llms/formatters/openai_to_anthropic/streams.py +530 -0
  125. ccproxy/llms/formatters/openai_to_openai/__init__.py +53 -0
  126. ccproxy/llms/formatters/openai_to_openai/_helpers.py +325 -0
  127. ccproxy/llms/formatters/openai_to_openai/errors.py +6 -0
  128. ccproxy/llms/formatters/openai_to_openai/requests.py +388 -0
  129. ccproxy/llms/formatters/openai_to_openai/responses.py +594 -0
  130. ccproxy/llms/formatters/openai_to_openai/streams.py +1832 -0
  131. ccproxy/llms/formatters/utils.py +306 -0
  132. ccproxy/llms/models/__init__.py +9 -0
  133. ccproxy/llms/models/anthropic.py +619 -0
  134. ccproxy/llms/models/openai.py +844 -0
  135. ccproxy/llms/streaming/__init__.py +26 -0
  136. ccproxy/llms/streaming/accumulators.py +1074 -0
  137. ccproxy/llms/streaming/formatters.py +251 -0
  138. ccproxy/{adapters/openai/streaming.py → llms/streaming/processors.py} +193 -240
  139. ccproxy/models/__init__.py +8 -159
  140. ccproxy/models/detection.py +92 -193
  141. ccproxy/models/provider.py +75 -0
  142. ccproxy/plugins/access_log/README.md +32 -0
  143. ccproxy/plugins/access_log/__init__.py +20 -0
  144. ccproxy/plugins/access_log/config.py +33 -0
  145. ccproxy/plugins/access_log/formatter.py +126 -0
  146. ccproxy/plugins/access_log/hook.py +763 -0
  147. ccproxy/plugins/access_log/logger.py +254 -0
  148. ccproxy/plugins/access_log/plugin.py +137 -0
  149. ccproxy/plugins/access_log/writer.py +109 -0
  150. ccproxy/plugins/analytics/README.md +24 -0
  151. ccproxy/plugins/analytics/__init__.py +1 -0
  152. ccproxy/plugins/analytics/config.py +5 -0
  153. ccproxy/plugins/analytics/ingest.py +85 -0
  154. ccproxy/plugins/analytics/models.py +97 -0
  155. ccproxy/plugins/analytics/plugin.py +121 -0
  156. ccproxy/plugins/analytics/routes.py +163 -0
  157. ccproxy/plugins/analytics/service.py +284 -0
  158. ccproxy/plugins/claude_api/README.md +29 -0
  159. ccproxy/plugins/claude_api/__init__.py +10 -0
  160. ccproxy/plugins/claude_api/adapter.py +829 -0
  161. ccproxy/plugins/claude_api/config.py +52 -0
  162. ccproxy/plugins/claude_api/detection_service.py +461 -0
  163. ccproxy/plugins/claude_api/health.py +175 -0
  164. ccproxy/plugins/claude_api/hooks.py +284 -0
  165. ccproxy/plugins/claude_api/models.py +256 -0
  166. ccproxy/plugins/claude_api/plugin.py +298 -0
  167. ccproxy/plugins/claude_api/routes.py +118 -0
  168. ccproxy/plugins/claude_api/streaming_metrics.py +68 -0
  169. ccproxy/plugins/claude_api/tasks.py +84 -0
  170. ccproxy/plugins/claude_sdk/README.md +35 -0
  171. ccproxy/plugins/claude_sdk/__init__.py +80 -0
  172. ccproxy/plugins/claude_sdk/adapter.py +749 -0
  173. ccproxy/plugins/claude_sdk/auth.py +57 -0
  174. ccproxy/{claude_sdk → plugins/claude_sdk}/client.py +63 -39
  175. ccproxy/plugins/claude_sdk/config.py +210 -0
  176. ccproxy/{claude_sdk → plugins/claude_sdk}/converter.py +6 -6
  177. ccproxy/plugins/claude_sdk/detection_service.py +163 -0
  178. ccproxy/{services/claude_sdk_service.py → plugins/claude_sdk/handler.py} +123 -304
  179. ccproxy/plugins/claude_sdk/health.py +113 -0
  180. ccproxy/plugins/claude_sdk/hooks.py +115 -0
  181. ccproxy/{claude_sdk → plugins/claude_sdk}/manager.py +42 -32
  182. ccproxy/{claude_sdk → plugins/claude_sdk}/message_queue.py +8 -8
  183. ccproxy/{models/claude_sdk.py → plugins/claude_sdk/models.py} +64 -16
  184. ccproxy/plugins/claude_sdk/options.py +154 -0
  185. ccproxy/{claude_sdk → plugins/claude_sdk}/parser.py +23 -5
  186. ccproxy/plugins/claude_sdk/plugin.py +269 -0
  187. ccproxy/plugins/claude_sdk/routes.py +104 -0
  188. ccproxy/{claude_sdk → plugins/claude_sdk}/session_client.py +124 -12
  189. ccproxy/plugins/claude_sdk/session_pool.py +700 -0
  190. ccproxy/{claude_sdk → plugins/claude_sdk}/stream_handle.py +48 -43
  191. ccproxy/{claude_sdk → plugins/claude_sdk}/stream_worker.py +22 -18
  192. ccproxy/{claude_sdk → plugins/claude_sdk}/streaming.py +50 -16
  193. ccproxy/plugins/claude_sdk/tasks.py +97 -0
  194. ccproxy/plugins/claude_shared/README.md +18 -0
  195. ccproxy/plugins/claude_shared/__init__.py +12 -0
  196. ccproxy/plugins/claude_shared/model_defaults.py +171 -0
  197. ccproxy/plugins/codex/README.md +35 -0
  198. ccproxy/plugins/codex/__init__.py +6 -0
  199. ccproxy/plugins/codex/adapter.py +635 -0
  200. ccproxy/{config/codex.py → plugins/codex/config.py} +78 -12
  201. ccproxy/plugins/codex/detection_service.py +544 -0
  202. ccproxy/plugins/codex/health.py +162 -0
  203. ccproxy/plugins/codex/hooks.py +263 -0
  204. ccproxy/plugins/codex/model_defaults.py +39 -0
  205. ccproxy/plugins/codex/models.py +263 -0
  206. ccproxy/plugins/codex/plugin.py +275 -0
  207. ccproxy/plugins/codex/routes.py +129 -0
  208. ccproxy/plugins/codex/streaming_metrics.py +324 -0
  209. ccproxy/plugins/codex/tasks.py +106 -0
  210. ccproxy/plugins/codex/utils/__init__.py +1 -0
  211. ccproxy/plugins/codex/utils/sse_parser.py +106 -0
  212. ccproxy/plugins/command_replay/README.md +34 -0
  213. ccproxy/plugins/command_replay/__init__.py +17 -0
  214. ccproxy/plugins/command_replay/config.py +133 -0
  215. ccproxy/plugins/command_replay/formatter.py +432 -0
  216. ccproxy/plugins/command_replay/hook.py +294 -0
  217. ccproxy/plugins/command_replay/plugin.py +161 -0
  218. ccproxy/plugins/copilot/README.md +39 -0
  219. ccproxy/plugins/copilot/__init__.py +11 -0
  220. ccproxy/plugins/copilot/adapter.py +465 -0
  221. ccproxy/plugins/copilot/config.py +155 -0
  222. ccproxy/plugins/copilot/data/copilot_fallback.json +41 -0
  223. ccproxy/plugins/copilot/detection_service.py +255 -0
  224. ccproxy/plugins/copilot/manager.py +275 -0
  225. ccproxy/plugins/copilot/model_defaults.py +284 -0
  226. ccproxy/plugins/copilot/models.py +148 -0
  227. ccproxy/plugins/copilot/oauth/__init__.py +16 -0
  228. ccproxy/plugins/copilot/oauth/client.py +494 -0
  229. ccproxy/plugins/copilot/oauth/models.py +385 -0
  230. ccproxy/plugins/copilot/oauth/provider.py +602 -0
  231. ccproxy/plugins/copilot/oauth/storage.py +170 -0
  232. ccproxy/plugins/copilot/plugin.py +360 -0
  233. ccproxy/plugins/copilot/routes.py +294 -0
  234. ccproxy/plugins/credential_balancer/README.md +124 -0
  235. ccproxy/plugins/credential_balancer/__init__.py +6 -0
  236. ccproxy/plugins/credential_balancer/config.py +270 -0
  237. ccproxy/plugins/credential_balancer/factory.py +415 -0
  238. ccproxy/plugins/credential_balancer/hook.py +51 -0
  239. ccproxy/plugins/credential_balancer/manager.py +587 -0
  240. ccproxy/plugins/credential_balancer/plugin.py +146 -0
  241. ccproxy/plugins/dashboard/README.md +25 -0
  242. ccproxy/plugins/dashboard/__init__.py +1 -0
  243. ccproxy/plugins/dashboard/config.py +8 -0
  244. ccproxy/plugins/dashboard/plugin.py +71 -0
  245. ccproxy/plugins/dashboard/routes.py +67 -0
  246. ccproxy/plugins/docker/README.md +32 -0
  247. ccproxy/{docker → plugins/docker}/__init__.py +3 -0
  248. ccproxy/{docker → plugins/docker}/adapter.py +108 -10
  249. ccproxy/plugins/docker/config.py +82 -0
  250. ccproxy/{docker → plugins/docker}/docker_path.py +4 -3
  251. ccproxy/{docker → plugins/docker}/middleware.py +2 -2
  252. ccproxy/plugins/docker/plugin.py +198 -0
  253. ccproxy/{docker → plugins/docker}/stream_process.py +3 -3
  254. ccproxy/plugins/duckdb_storage/README.md +26 -0
  255. ccproxy/plugins/duckdb_storage/__init__.py +1 -0
  256. ccproxy/plugins/duckdb_storage/config.py +22 -0
  257. ccproxy/plugins/duckdb_storage/plugin.py +128 -0
  258. ccproxy/plugins/duckdb_storage/routes.py +51 -0
  259. ccproxy/plugins/duckdb_storage/storage.py +633 -0
  260. ccproxy/plugins/max_tokens/README.md +38 -0
  261. ccproxy/plugins/max_tokens/__init__.py +12 -0
  262. ccproxy/plugins/max_tokens/adapter.py +235 -0
  263. ccproxy/plugins/max_tokens/config.py +86 -0
  264. ccproxy/plugins/max_tokens/models.py +53 -0
  265. ccproxy/plugins/max_tokens/plugin.py +200 -0
  266. ccproxy/plugins/max_tokens/service.py +271 -0
  267. ccproxy/plugins/max_tokens/token_limits.json +54 -0
  268. ccproxy/plugins/metrics/README.md +35 -0
  269. ccproxy/plugins/metrics/__init__.py +10 -0
  270. ccproxy/{observability/metrics.py → plugins/metrics/collector.py} +20 -153
  271. ccproxy/plugins/metrics/config.py +85 -0
  272. ccproxy/plugins/metrics/grafana/dashboards/ccproxy-dashboard.json +1720 -0
  273. ccproxy/plugins/metrics/hook.py +403 -0
  274. ccproxy/plugins/metrics/plugin.py +268 -0
  275. ccproxy/{observability → plugins/metrics}/pushgateway.py +57 -59
  276. ccproxy/plugins/metrics/routes.py +107 -0
  277. ccproxy/plugins/metrics/tasks.py +117 -0
  278. ccproxy/plugins/oauth_claude/README.md +35 -0
  279. ccproxy/plugins/oauth_claude/__init__.py +14 -0
  280. ccproxy/plugins/oauth_claude/client.py +270 -0
  281. ccproxy/plugins/oauth_claude/config.py +84 -0
  282. ccproxy/plugins/oauth_claude/manager.py +482 -0
  283. ccproxy/plugins/oauth_claude/models.py +266 -0
  284. ccproxy/plugins/oauth_claude/plugin.py +149 -0
  285. ccproxy/plugins/oauth_claude/provider.py +571 -0
  286. ccproxy/plugins/oauth_claude/storage.py +212 -0
  287. ccproxy/plugins/oauth_codex/README.md +38 -0
  288. ccproxy/plugins/oauth_codex/__init__.py +14 -0
  289. ccproxy/plugins/oauth_codex/client.py +224 -0
  290. ccproxy/plugins/oauth_codex/config.py +95 -0
  291. ccproxy/plugins/oauth_codex/manager.py +256 -0
  292. ccproxy/plugins/oauth_codex/models.py +239 -0
  293. ccproxy/plugins/oauth_codex/plugin.py +146 -0
  294. ccproxy/plugins/oauth_codex/provider.py +574 -0
  295. ccproxy/plugins/oauth_codex/storage.py +92 -0
  296. ccproxy/plugins/permissions/README.md +28 -0
  297. ccproxy/plugins/permissions/__init__.py +22 -0
  298. ccproxy/plugins/permissions/config.py +28 -0
  299. ccproxy/{cli/commands/permission_handler.py → plugins/permissions/handlers/cli.py} +49 -25
  300. ccproxy/plugins/permissions/handlers/protocol.py +33 -0
  301. ccproxy/plugins/permissions/handlers/terminal.py +675 -0
  302. ccproxy/{api/routes → plugins/permissions}/mcp.py +34 -7
  303. ccproxy/{models/permissions.py → plugins/permissions/models.py} +65 -1
  304. ccproxy/plugins/permissions/plugin.py +153 -0
  305. ccproxy/{api/routes/permissions.py → plugins/permissions/routes.py} +20 -16
  306. ccproxy/{api/services/permission_service.py → plugins/permissions/service.py} +65 -11
  307. ccproxy/{api → plugins/permissions}/ui/permission_handler_protocol.py +1 -1
  308. ccproxy/{api → plugins/permissions}/ui/terminal_permission_handler.py +66 -10
  309. ccproxy/plugins/pricing/README.md +34 -0
  310. ccproxy/plugins/pricing/__init__.py +6 -0
  311. ccproxy/{pricing → plugins/pricing}/cache.py +7 -6
  312. ccproxy/{config/pricing.py → plugins/pricing/config.py} +32 -6
  313. ccproxy/plugins/pricing/exceptions.py +35 -0
  314. ccproxy/plugins/pricing/loader.py +440 -0
  315. ccproxy/{pricing → plugins/pricing}/models.py +13 -23
  316. ccproxy/plugins/pricing/plugin.py +169 -0
  317. ccproxy/plugins/pricing/service.py +191 -0
  318. ccproxy/plugins/pricing/tasks.py +300 -0
  319. ccproxy/{pricing → plugins/pricing}/updater.py +86 -72
  320. ccproxy/plugins/pricing/utils.py +99 -0
  321. ccproxy/plugins/request_tracer/README.md +40 -0
  322. ccproxy/plugins/request_tracer/__init__.py +7 -0
  323. ccproxy/plugins/request_tracer/config.py +120 -0
  324. ccproxy/plugins/request_tracer/hook.py +415 -0
  325. ccproxy/plugins/request_tracer/plugin.py +255 -0
  326. ccproxy/scheduler/__init__.py +2 -14
  327. ccproxy/scheduler/core.py +26 -41
  328. ccproxy/scheduler/manager.py +61 -105
  329. ccproxy/scheduler/registry.py +6 -32
  330. ccproxy/scheduler/tasks.py +268 -276
  331. ccproxy/services/__init__.py +0 -1
  332. ccproxy/services/adapters/__init__.py +11 -0
  333. ccproxy/services/adapters/base.py +123 -0
  334. ccproxy/services/adapters/chain_composer.py +88 -0
  335. ccproxy/services/adapters/chain_validation.py +44 -0
  336. ccproxy/services/adapters/chat_accumulator.py +200 -0
  337. ccproxy/services/adapters/delta_utils.py +142 -0
  338. ccproxy/services/adapters/format_adapter.py +136 -0
  339. ccproxy/services/adapters/format_context.py +11 -0
  340. ccproxy/services/adapters/format_registry.py +158 -0
  341. ccproxy/services/adapters/http_adapter.py +1045 -0
  342. ccproxy/services/adapters/mock_adapter.py +118 -0
  343. ccproxy/services/adapters/protocols.py +35 -0
  344. ccproxy/services/adapters/simple_converters.py +571 -0
  345. ccproxy/services/auth_registry.py +180 -0
  346. ccproxy/services/cache/__init__.py +6 -0
  347. ccproxy/services/cache/response_cache.py +261 -0
  348. ccproxy/services/cli_detection.py +437 -0
  349. ccproxy/services/config/__init__.py +6 -0
  350. ccproxy/services/config/proxy_configuration.py +111 -0
  351. ccproxy/services/container.py +256 -0
  352. ccproxy/services/factories.py +380 -0
  353. ccproxy/services/handler_config.py +76 -0
  354. ccproxy/services/interfaces.py +298 -0
  355. ccproxy/services/mocking/__init__.py +6 -0
  356. ccproxy/services/mocking/mock_handler.py +291 -0
  357. ccproxy/services/tracing/__init__.py +7 -0
  358. ccproxy/services/tracing/interfaces.py +61 -0
  359. ccproxy/services/tracing/null_tracer.py +57 -0
  360. ccproxy/streaming/__init__.py +23 -0
  361. ccproxy/streaming/buffer.py +1056 -0
  362. ccproxy/streaming/deferred.py +897 -0
  363. ccproxy/streaming/handler.py +117 -0
  364. ccproxy/streaming/interfaces.py +77 -0
  365. ccproxy/streaming/simple_adapter.py +39 -0
  366. ccproxy/streaming/sse.py +109 -0
  367. ccproxy/streaming/sse_parser.py +127 -0
  368. ccproxy/templates/__init__.py +6 -0
  369. ccproxy/templates/plugin_scaffold.py +695 -0
  370. ccproxy/testing/endpoints/__init__.py +33 -0
  371. ccproxy/testing/endpoints/cli.py +215 -0
  372. ccproxy/testing/endpoints/config.py +874 -0
  373. ccproxy/testing/endpoints/console.py +57 -0
  374. ccproxy/testing/endpoints/models.py +100 -0
  375. ccproxy/testing/endpoints/runner.py +1903 -0
  376. ccproxy/testing/endpoints/tools.py +308 -0
  377. ccproxy/testing/mock_responses.py +70 -1
  378. ccproxy/testing/response_handlers.py +20 -0
  379. ccproxy/utils/__init__.py +0 -6
  380. ccproxy/utils/binary_resolver.py +476 -0
  381. ccproxy/utils/caching.py +327 -0
  382. ccproxy/utils/cli_logging.py +101 -0
  383. ccproxy/utils/command_line.py +251 -0
  384. ccproxy/utils/headers.py +228 -0
  385. ccproxy/utils/model_mapper.py +120 -0
  386. ccproxy/utils/startup_helpers.py +68 -446
  387. ccproxy/utils/version_checker.py +273 -6
  388. ccproxy_api-0.2.0a4.dist-info/METADATA +212 -0
  389. ccproxy_api-0.2.0a4.dist-info/RECORD +417 -0
  390. {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0a4.dist-info}/WHEEL +1 -1
  391. ccproxy_api-0.2.0a4.dist-info/entry_points.txt +24 -0
  392. ccproxy/__init__.py +0 -4
  393. ccproxy/adapters/__init__.py +0 -11
  394. ccproxy/adapters/base.py +0 -80
  395. ccproxy/adapters/codex/__init__.py +0 -11
  396. ccproxy/adapters/openai/__init__.py +0 -42
  397. ccproxy/adapters/openai/adapter.py +0 -953
  398. ccproxy/adapters/openai/models.py +0 -412
  399. ccproxy/adapters/openai/response_adapter.py +0 -355
  400. ccproxy/adapters/openai/response_models.py +0 -178
  401. ccproxy/api/middleware/headers.py +0 -49
  402. ccproxy/api/middleware/logging.py +0 -180
  403. ccproxy/api/middleware/request_content_logging.py +0 -297
  404. ccproxy/api/middleware/server_header.py +0 -58
  405. ccproxy/api/responses.py +0 -89
  406. ccproxy/api/routes/claude.py +0 -371
  407. ccproxy/api/routes/codex.py +0 -1251
  408. ccproxy/api/routes/metrics.py +0 -1029
  409. ccproxy/api/routes/proxy.py +0 -211
  410. ccproxy/api/services/__init__.py +0 -6
  411. ccproxy/auth/conditional.py +0 -84
  412. ccproxy/auth/credentials_adapter.py +0 -93
  413. ccproxy/auth/models.py +0 -118
  414. ccproxy/auth/oauth/models.py +0 -48
  415. ccproxy/auth/openai/__init__.py +0 -13
  416. ccproxy/auth/openai/credentials.py +0 -166
  417. ccproxy/auth/openai/oauth_client.py +0 -334
  418. ccproxy/auth/openai/storage.py +0 -184
  419. ccproxy/auth/storage/json_file.py +0 -158
  420. ccproxy/auth/storage/keyring.py +0 -189
  421. ccproxy/claude_sdk/__init__.py +0 -18
  422. ccproxy/claude_sdk/options.py +0 -194
  423. ccproxy/claude_sdk/session_pool.py +0 -550
  424. ccproxy/cli/docker/__init__.py +0 -34
  425. ccproxy/cli/docker/adapter_factory.py +0 -157
  426. ccproxy/cli/docker/params.py +0 -274
  427. ccproxy/config/auth.py +0 -153
  428. ccproxy/config/claude.py +0 -348
  429. ccproxy/config/cors.py +0 -79
  430. ccproxy/config/discovery.py +0 -95
  431. ccproxy/config/docker_settings.py +0 -264
  432. ccproxy/config/observability.py +0 -158
  433. ccproxy/config/reverse_proxy.py +0 -31
  434. ccproxy/config/scheduler.py +0 -108
  435. ccproxy/config/server.py +0 -86
  436. ccproxy/config/validators.py +0 -231
  437. ccproxy/core/codex_transformers.py +0 -389
  438. ccproxy/core/http.py +0 -328
  439. ccproxy/core/http_transformers.py +0 -812
  440. ccproxy/core/proxy.py +0 -143
  441. ccproxy/core/validators.py +0 -288
  442. ccproxy/models/errors.py +0 -42
  443. ccproxy/models/messages.py +0 -269
  444. ccproxy/models/requests.py +0 -107
  445. ccproxy/models/responses.py +0 -270
  446. ccproxy/models/types.py +0 -102
  447. ccproxy/observability/__init__.py +0 -51
  448. ccproxy/observability/access_logger.py +0 -457
  449. ccproxy/observability/sse_events.py +0 -303
  450. ccproxy/observability/stats_printer.py +0 -753
  451. ccproxy/observability/storage/__init__.py +0 -1
  452. ccproxy/observability/storage/duckdb_simple.py +0 -677
  453. ccproxy/observability/storage/models.py +0 -70
  454. ccproxy/observability/streaming_response.py +0 -107
  455. ccproxy/pricing/__init__.py +0 -19
  456. ccproxy/pricing/loader.py +0 -251
  457. ccproxy/services/claude_detection_service.py +0 -243
  458. ccproxy/services/codex_detection_service.py +0 -252
  459. ccproxy/services/credentials/__init__.py +0 -55
  460. ccproxy/services/credentials/config.py +0 -105
  461. ccproxy/services/credentials/manager.py +0 -561
  462. ccproxy/services/credentials/oauth_client.py +0 -481
  463. ccproxy/services/proxy_service.py +0 -1827
  464. ccproxy/static/.keep +0 -0
  465. ccproxy/utils/cost_calculator.py +0 -210
  466. ccproxy/utils/disconnection_monitor.py +0 -83
  467. ccproxy/utils/model_mapping.py +0 -199
  468. ccproxy/utils/models_provider.py +0 -150
  469. ccproxy/utils/simple_request_logger.py +0 -284
  470. ccproxy/utils/streaming_metrics.py +0 -199
  471. ccproxy_api-0.1.7.dist-info/METADATA +0 -615
  472. ccproxy_api-0.1.7.dist-info/RECORD +0 -191
  473. ccproxy_api-0.1.7.dist-info/entry_points.txt +0 -4
  474. /ccproxy/{api/middleware/auth.py → auth/models/__init__.py} +0 -0
  475. /ccproxy/{claude_sdk → plugins/claude_sdk}/exceptions.py +0 -0
  476. /ccproxy/{docker → plugins/docker}/models.py +0 -0
  477. /ccproxy/{docker → plugins/docker}/protocol.py +0 -0
  478. /ccproxy/{docker → plugins/docker}/validators.py +0 -0
  479. /ccproxy/{auth/oauth/storage.py → plugins/permissions/handlers/__init__.py} +0 -0
  480. /ccproxy/{api → plugins/permissions}/ui/__init__.py +0 -0
  481. {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0a4.dist-info}/licenses/LICENSE +0 -0
@@ -1,211 +0,0 @@
1
- """Proxy endpoints for CCProxy API Server."""
2
-
3
- import json
4
- from collections.abc import AsyncIterator
5
-
6
- from fastapi import APIRouter, HTTPException, Request, Response
7
- from fastapi.responses import StreamingResponse
8
-
9
- from ccproxy.adapters.openai.adapter import OpenAIAdapter
10
- from ccproxy.api.dependencies import ProxyServiceDep
11
- from ccproxy.api.responses import ProxyResponse
12
- from ccproxy.auth.conditional import ConditionalAuthDep
13
-
14
-
15
- # Create the router for proxy endpoints
16
- router = APIRouter(tags=["proxy"])
17
-
18
-
19
- @router.post("/v1/chat/completions", response_model=None)
20
- async def create_openai_chat_completion(
21
- request: Request,
22
- proxy_service: ProxyServiceDep,
23
- auth: ConditionalAuthDep,
24
- ) -> StreamingResponse | Response:
25
- """Create a chat completion using Claude AI with OpenAI-compatible format.
26
-
27
- This endpoint handles OpenAI API format requests and forwards them
28
- directly to Claude via the proxy service.
29
- """
30
- try:
31
- # Get request body
32
- body = await request.body()
33
-
34
- # Get headers and query params
35
- headers = dict(request.headers)
36
- query_params: dict[str, str | list[str]] | None = (
37
- dict(request.query_params) if request.query_params else None
38
- )
39
-
40
- # Handle the request using proxy service directly
41
- # Strip the /api prefix from the path
42
- service_path = request.url.path.removeprefix("/api")
43
- response = await proxy_service.handle_request(
44
- method=request.method,
45
- path=service_path,
46
- headers=headers,
47
- body=body,
48
- query_params=query_params,
49
- request=request, # Pass the request object for context access
50
- )
51
-
52
- # Return appropriate response type
53
- if isinstance(response, StreamingResponse):
54
- # Already a streaming response
55
- return response
56
- else:
57
- # Tuple response - handle regular response
58
- status_code, response_headers, response_body = response
59
- if status_code >= 400:
60
- # Store headers for preservation middleware
61
- request.state.preserve_headers = response_headers
62
- # Forward error response directly with headers
63
- return ProxyResponse(
64
- content=response_body,
65
- status_code=status_code,
66
- headers=response_headers,
67
- media_type=response_headers.get("content-type", "application/json"),
68
- )
69
-
70
- # Check if this is a streaming response based on content-type
71
- content_type = response_headers.get("content-type", "")
72
- if "text/event-stream" in content_type:
73
- # Return as streaming response
74
- async def stream_generator() -> AsyncIterator[bytes]:
75
- # Split the SSE data into chunks
76
- for line in response_body.decode().split("\n"):
77
- if line.strip():
78
- yield f"{line}\n".encode()
79
-
80
- return StreamingResponse(
81
- stream_generator(),
82
- media_type="text/event-stream",
83
- headers={
84
- "Cache-Control": "no-cache",
85
- "Connection": "keep-alive",
86
- },
87
- )
88
- else:
89
- # Parse JSON response
90
- response_data = json.loads(response_body.decode())
91
-
92
- # Convert Anthropic response back to OpenAI format for /chat/completions
93
- openai_adapter = OpenAIAdapter()
94
- openai_response = openai_adapter.adapt_response(response_data)
95
-
96
- # Return response with headers
97
- return ProxyResponse(
98
- content=json.dumps(openai_response),
99
- status_code=status_code,
100
- headers=response_headers,
101
- media_type=response_headers.get("content-type", "application/json"),
102
- )
103
-
104
- except HTTPException:
105
- # Re-raise HTTPException as-is (including 401 auth errors)
106
- raise
107
- except Exception as e:
108
- raise HTTPException(
109
- status_code=500, detail=f"Internal server error: {str(e)}"
110
- ) from e
111
-
112
-
113
- @router.post("/v1/messages", response_model=None)
114
- async def create_anthropic_message(
115
- request: Request,
116
- proxy_service: ProxyServiceDep,
117
- auth: ConditionalAuthDep,
118
- ) -> StreamingResponse | Response:
119
- """Create a message using Claude AI with Anthropic format.
120
-
121
- This endpoint handles Anthropic API format requests and forwards them
122
- directly to Claude via the proxy service.
123
- """
124
- try:
125
- # Get request body
126
- body = await request.body()
127
-
128
- # Get headers and query params
129
- headers = dict(request.headers)
130
- query_params: dict[str, str | list[str]] | None = (
131
- dict(request.query_params) if request.query_params else None
132
- )
133
-
134
- # Handle the request using proxy service directly
135
- # Strip the /api prefix from the path
136
- service_path = request.url.path.removeprefix("/api")
137
- response = await proxy_service.handle_request(
138
- method=request.method,
139
- path=service_path,
140
- headers=headers,
141
- body=body,
142
- query_params=query_params,
143
- request=request, # Pass the request object for context access
144
- )
145
-
146
- # Return appropriate response type
147
- if isinstance(response, StreamingResponse):
148
- # Already a streaming response
149
- return response
150
- else:
151
- # Tuple response - handle regular response
152
- status_code, response_headers, response_body = response
153
- if status_code >= 400:
154
- # Store headers for preservation middleware
155
- request.state.preserve_headers = response_headers
156
- # Forward error response directly with headers
157
- return ProxyResponse(
158
- content=response_body,
159
- status_code=status_code,
160
- headers=response_headers,
161
- media_type=response_headers.get("content-type", "application/json"),
162
- )
163
-
164
- # Check if this is a streaming response based on content-type
165
- content_type = response_headers.get("content-type", "")
166
- if "text/event-stream" in content_type:
167
- # Return as streaming response
168
- async def stream_generator() -> AsyncIterator[bytes]:
169
- # Split the SSE data into chunks
170
- for line in response_body.decode().split("\n"):
171
- if line.strip():
172
- yield f"{line}\n".encode()
173
-
174
- # Start with the response headers from proxy service
175
- streaming_headers = response_headers.copy()
176
-
177
- # Ensure critical headers for streaming
178
- streaming_headers["Cache-Control"] = "no-cache"
179
- streaming_headers["Connection"] = "keep-alive"
180
-
181
- # Set content-type if not already set by upstream
182
- if "content-type" not in streaming_headers:
183
- streaming_headers["content-type"] = "text/event-stream"
184
-
185
- return StreamingResponse(
186
- stream_generator(),
187
- media_type="text/event-stream",
188
- headers=streaming_headers,
189
- )
190
- else:
191
- # Store headers for preservation middleware
192
- request.state.preserve_headers = response_headers
193
-
194
- # Parse JSON response
195
- response_data = json.loads(response_body.decode())
196
-
197
- # Return response with headers
198
- return ProxyResponse(
199
- content=response_body, # Use original body to preserve exact format
200
- status_code=status_code,
201
- headers=response_headers,
202
- media_type=response_headers.get("content-type", "application/json"),
203
- )
204
-
205
- except HTTPException:
206
- # Re-raise HTTPException as-is (including 401 auth errors)
207
- raise
208
- except Exception as e:
209
- raise HTTPException(
210
- status_code=500, detail=f"Internal server error: {str(e)}"
211
- ) from e
@@ -1,6 +0,0 @@
1
- """Services for CCProxy API."""
2
-
3
- from .permission_service import PermissionService, get_permission_service
4
-
5
-
6
- __all__ = ["PermissionService", "get_permission_service"]
@@ -1,84 +0,0 @@
1
- """Conditional authentication dependencies."""
2
-
3
- from typing import Annotated
4
-
5
- from fastapi import Depends, HTTPException, Request, status
6
- from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
7
-
8
- from ccproxy.api.dependencies import SettingsDep
9
- from ccproxy.auth.bearer import BearerTokenAuthManager
10
- from ccproxy.auth.exceptions import AuthenticationError
11
- from ccproxy.auth.manager import AuthManager
12
-
13
-
14
- # FastAPI security scheme for bearer tokens
15
- bearer_scheme = HTTPBearer(auto_error=False)
16
-
17
-
18
- async def get_conditional_auth_manager(
19
- request: Request,
20
- settings: SettingsDep,
21
- credentials: Annotated[
22
- HTTPAuthorizationCredentials | None, Depends(bearer_scheme)
23
- ] = None,
24
- ) -> AuthManager | None:
25
- """Get authentication manager only if auth is required.
26
-
27
- This dependency checks if authentication is configured and validates
28
- the token if required. If no auth is configured, returns None.
29
-
30
- Args:
31
- request: The FastAPI request object
32
- credentials: HTTP authorization credentials
33
- settings: Application settings
34
-
35
- Returns:
36
- AuthManager instance if authenticated, None if no auth required
37
-
38
- Raises:
39
- HTTPException: If auth is required but credentials are invalid
40
- """
41
- # Check if auth is required for this configuration
42
- if settings is None or not settings.security.auth_token:
43
- # No auth configured, return None
44
- return None
45
-
46
- # Auth is required, validate credentials
47
- if not credentials or not credentials.credentials:
48
- raise HTTPException(
49
- status_code=status.HTTP_401_UNAUTHORIZED,
50
- detail="Authentication required",
51
- headers={"WWW-Authenticate": "Bearer"},
52
- )
53
-
54
- # Validate the token
55
- if credentials.credentials != settings.security.auth_token:
56
- raise HTTPException(
57
- status_code=status.HTTP_401_UNAUTHORIZED,
58
- detail="Invalid authentication credentials",
59
- headers={"WWW-Authenticate": "Bearer"},
60
- )
61
-
62
- # Create and return auth manager
63
- try:
64
- bearer_auth = BearerTokenAuthManager(credentials.credentials)
65
- if await bearer_auth.is_authenticated():
66
- return bearer_auth
67
- else:
68
- raise HTTPException(
69
- status_code=status.HTTP_401_UNAUTHORIZED,
70
- detail="Authentication failed",
71
- headers={"WWW-Authenticate": "Bearer"},
72
- )
73
- except (AuthenticationError, ValueError) as e:
74
- raise HTTPException(
75
- status_code=status.HTTP_401_UNAUTHORIZED,
76
- detail=str(e),
77
- headers={"WWW-Authenticate": "Bearer"},
78
- ) from e
79
-
80
-
81
- # Type alias for conditional auth dependency
82
- ConditionalAuthDep = Annotated[
83
- AuthManager | None, Depends(get_conditional_auth_manager)
84
- ]
@@ -1,93 +0,0 @@
1
- """Adapter to make CredentialsManager compatible with AuthManager interface."""
2
-
3
- from typing import Any
4
-
5
- from ccproxy.auth.exceptions import (
6
- AuthenticationError,
7
- CredentialsError,
8
- CredentialsExpiredError,
9
- CredentialsNotFoundError,
10
- )
11
- from ccproxy.auth.manager import BaseAuthManager
12
- from ccproxy.auth.models import ClaudeCredentials, UserProfile
13
- from ccproxy.services.credentials.manager import CredentialsManager
14
-
15
-
16
- class CredentialsAuthManager(BaseAuthManager):
17
- """Adapter to make CredentialsManager compatible with AuthManager interface."""
18
-
19
- def __init__(self, credentials_manager: CredentialsManager | None = None) -> None:
20
- """Initialize with credentials manager.
21
-
22
- Args:
23
- credentials_manager: CredentialsManager instance, creates new if None
24
- """
25
- self._credentials_manager = credentials_manager or CredentialsManager()
26
-
27
- async def get_access_token(self) -> str:
28
- """Get valid access token from credentials manager.
29
-
30
- Returns:
31
- Access token string
32
-
33
- Raises:
34
- AuthenticationError: If authentication fails
35
- """
36
- try:
37
- return await self._credentials_manager.get_access_token()
38
- except CredentialsNotFoundError as e:
39
- raise AuthenticationError("No credentials found") from e
40
- except CredentialsExpiredError as e:
41
- raise AuthenticationError("Credentials expired") from e
42
- except CredentialsError as e:
43
- raise AuthenticationError(f"Credentials error: {e}") from e
44
-
45
- async def get_credentials(self) -> ClaudeCredentials:
46
- """Get valid credentials from credentials manager.
47
-
48
- Returns:
49
- Valid credentials
50
-
51
- Raises:
52
- AuthenticationError: If authentication fails
53
- """
54
- try:
55
- return await self._credentials_manager.get_valid_credentials()
56
- except CredentialsNotFoundError as e:
57
- raise AuthenticationError("No credentials found") from e
58
- except CredentialsExpiredError as e:
59
- raise AuthenticationError("Credentials expired") from e
60
- except CredentialsError as e:
61
- raise AuthenticationError(f"Credentials error: {e}") from e
62
-
63
- async def is_authenticated(self) -> bool:
64
- """Check if current authentication is valid.
65
-
66
- Returns:
67
- True if authenticated, False otherwise
68
- """
69
- try:
70
- await self._credentials_manager.get_valid_credentials()
71
- return True
72
- except CredentialsError:
73
- return False
74
-
75
- async def get_user_profile(self) -> UserProfile | None:
76
- """Get user profile information.
77
-
78
- Returns:
79
- UserProfile if available, None otherwise
80
- """
81
- try:
82
- return await self._credentials_manager.fetch_user_profile()
83
- except CredentialsError:
84
- return None
85
-
86
- async def __aenter__(self) -> "CredentialsAuthManager":
87
- """Async context manager entry."""
88
- await self._credentials_manager.__aenter__()
89
- return self
90
-
91
- async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
92
- """Async context manager exit."""
93
- await self._credentials_manager.__aexit__(exc_type, exc_val, exc_tb)
ccproxy/auth/models.py DELETED
@@ -1,118 +0,0 @@
1
- """Data models for authentication."""
2
-
3
- from datetime import UTC, datetime
4
-
5
- from pydantic import BaseModel, Field
6
-
7
-
8
- class OAuthToken(BaseModel):
9
- """OAuth token information from Claude credentials."""
10
-
11
- access_token: str = Field(..., alias="accessToken")
12
- refresh_token: str = Field(..., alias="refreshToken")
13
- expires_at: int | None = Field(None, alias="expiresAt")
14
- scopes: list[str] = Field(default_factory=list)
15
- subscription_type: str | None = Field(None, alias="subscriptionType")
16
- token_type: str = Field(default="Bearer", alias="tokenType")
17
-
18
- def __repr__(self) -> str:
19
- """Safe string representation that masks sensitive tokens."""
20
- access_preview = (
21
- f"{self.access_token[:8]}...{self.access_token[-8:]}"
22
- if len(self.access_token) > 16
23
- else "***"
24
- )
25
- refresh_preview = (
26
- f"{self.refresh_token[:8]}...{self.refresh_token[-8:]}"
27
- if len(self.refresh_token) > 16
28
- else "***"
29
- )
30
-
31
- expires_at = (
32
- datetime.fromtimestamp(self.expires_at / 1000, tz=UTC).isoformat()
33
- if self.expires_at is not None
34
- else "None"
35
- )
36
- return (
37
- f"OAuthToken(access_token='{access_preview}', "
38
- f"refresh_token='{refresh_preview}', "
39
- f"expires_at={expires_at}, "
40
- f"scopes={self.scopes}, "
41
- f"subscription_type='{self.subscription_type}', "
42
- f"token_type='{self.token_type}')"
43
- )
44
-
45
- @property
46
- def is_expired(self) -> bool:
47
- """Check if the token is expired."""
48
- if self.expires_at is None:
49
- # If no expiration info, assume not expired for backward compatibility
50
- return False
51
- now = datetime.now(UTC).timestamp() * 1000 # Convert to milliseconds
52
- return now >= self.expires_at
53
-
54
- @property
55
- def expires_at_datetime(self) -> datetime:
56
- """Get expiration as datetime object."""
57
- if self.expires_at is None:
58
- # Return a far future date if no expiration info
59
- return datetime.fromtimestamp(2147483647, tz=UTC) # Year 2038
60
- return datetime.fromtimestamp(self.expires_at / 1000, tz=UTC)
61
-
62
-
63
- class OrganizationInfo(BaseModel):
64
- """Organization information from OAuth API."""
65
-
66
- uuid: str
67
- name: str
68
- organization_type: str | None = None
69
- billing_type: str | None = None
70
- rate_limit_tier: str | None = None
71
-
72
-
73
- class AccountInfo(BaseModel):
74
- """Account information from OAuth API."""
75
-
76
- uuid: str
77
- email: str
78
- full_name: str | None = None
79
- display_name: str | None = None
80
- has_claude_max: bool | None = None
81
- has_claude_pro: bool | None = None
82
-
83
- @property
84
- def email_address(self) -> str:
85
- """Compatibility property for email_address."""
86
- return self.email
87
-
88
-
89
- class UserProfile(BaseModel):
90
- """User profile information from Anthropic OAuth API."""
91
-
92
- organization: OrganizationInfo | None = None
93
- account: AccountInfo | None = None
94
-
95
-
96
- class ClaudeCredentials(BaseModel):
97
- """Claude credentials from the credentials file."""
98
-
99
- claude_ai_oauth: OAuthToken = Field(..., alias="claudeAiOauth")
100
-
101
- def __repr__(self) -> str:
102
- """Safe string representation that masks sensitive tokens."""
103
- return f"ClaudeCredentials(claude_ai_oauth={repr(self.claude_ai_oauth)})"
104
-
105
-
106
- class ValidationResult(BaseModel):
107
- """Result of credentials validation."""
108
-
109
- valid: bool
110
- expired: bool | None = None
111
- credentials: ClaudeCredentials | None = None
112
- path: str | None = None
113
-
114
-
115
- # Backwards compatibility - provide common aliases
116
- User = UserProfile
117
- Credentials = ClaudeCredentials
118
- Profile = UserProfile
@@ -1,48 +0,0 @@
1
- """OAuth-specific models for authentication."""
2
-
3
- from datetime import datetime
4
-
5
- from pydantic import BaseModel, Field
6
-
7
-
8
- class OAuthState(BaseModel):
9
- """OAuth state information for pending flows."""
10
-
11
- code_verifier: str = Field(..., description="PKCE code verifier")
12
- custom_paths: list[str] | None = Field(None, description="Custom credential paths")
13
- completed: bool = Field(default=False, description="Whether the flow is completed")
14
- success: bool = Field(default=False, description="Whether the flow was successful")
15
- error: str | None = Field(None, description="Error message if failed")
16
- created_at: datetime = Field(
17
- default_factory=datetime.utcnow, description="Creation timestamp"
18
- )
19
-
20
-
21
- class OAuthCallbackRequest(BaseModel):
22
- """OAuth callback request parameters."""
23
-
24
- code: str | None = Field(None, description="Authorization code")
25
- state: str | None = Field(None, description="State parameter")
26
- error: str | None = Field(None, description="OAuth error")
27
- error_description: str | None = Field(None, description="OAuth error description")
28
-
29
-
30
- class OAuthTokenRequest(BaseModel):
31
- """OAuth token exchange request."""
32
-
33
- grant_type: str = Field(default="authorization_code")
34
- code: str = Field(..., description="Authorization code")
35
- redirect_uri: str = Field(..., description="Redirect URI")
36
- client_id: str = Field(..., description="Client ID")
37
- code_verifier: str = Field(..., description="PKCE code verifier")
38
-
39
-
40
- class OAuthTokenResponse(BaseModel):
41
- """OAuth token exchange response."""
42
-
43
- access_token: str = Field(..., description="Access token")
44
- refresh_token: str | None = Field(None, description="Refresh token")
45
- expires_in: int | None = Field(None, description="Token expiration in seconds")
46
- scope: str | None = Field(None, description="Granted scopes")
47
- subscription_type: str | None = Field(None, description="Subscription type")
48
- token_type: str = Field(default="Bearer", description="Token type")
@@ -1,13 +0,0 @@
1
- """OpenAI authentication components for Codex integration."""
2
-
3
- from .credentials import OpenAICredentials, OpenAITokenManager
4
- from .oauth_client import OpenAIOAuthClient
5
- from .storage import OpenAITokenStorage
6
-
7
-
8
- __all__ = [
9
- "OpenAICredentials",
10
- "OpenAITokenManager",
11
- "OpenAIOAuthClient",
12
- "OpenAITokenStorage",
13
- ]