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
@@ -0,0 +1,254 @@
1
+ """Status command for displaying system information."""
2
+
3
+ import typer
4
+ from rich import box
5
+ from rich.console import Console
6
+ from rich.panel import Panel
7
+ from rich.table import Table
8
+ from rich.text import Text
9
+
10
+ from ccproxy.cli.helpers import get_rich_toolkit
11
+ from ccproxy.config.settings import Settings
12
+ from ccproxy.core._version import __version__
13
+ from ccproxy.core.logging import get_logger
14
+ from ccproxy.core.status_report import (
15
+ ConfigSnapshot,
16
+ PluginSnapshot,
17
+ SystemSnapshot,
18
+ collect_config_snapshot,
19
+ collect_plugin_snapshot,
20
+ collect_system_snapshot,
21
+ )
22
+
23
+
24
+ app = typer.Typer(name="status", help="Show system status and information")
25
+ logger = get_logger(__name__)
26
+
27
+
28
+ @app.callback(invoke_without_command=True)
29
+ def status_main(
30
+ ctx: typer.Context,
31
+ plugins: bool = typer.Option(
32
+ True, "--plugins/--no-plugins", help="Show plugin status"
33
+ ),
34
+ config: bool = typer.Option(
35
+ True, "--config/--no-config", help="Show configuration info"
36
+ ),
37
+ ) -> None:
38
+ """Show CCProxy system status and configuration."""
39
+ if ctx.invoked_subcommand is not None:
40
+ return
41
+
42
+ toolkit = get_rich_toolkit()
43
+ console = Console()
44
+
45
+ # Header
46
+ toolkit.print("[bold cyan]CCProxy Status[/bold cyan]", centered=True)
47
+ toolkit.print(f"Version: {__version__}", tag="info")
48
+ toolkit.print_line()
49
+
50
+ try:
51
+ settings = Settings.from_config()
52
+
53
+ system_snapshot = collect_system_snapshot(settings)
54
+ _show_system_info(console, system_snapshot)
55
+
56
+ if config:
57
+ config_snapshot = collect_config_snapshot()
58
+ _show_config_info(console, config_snapshot)
59
+
60
+ if plugins:
61
+ plugin_snapshot = collect_plugin_snapshot(settings)
62
+ _show_plugin_status(console, plugin_snapshot)
63
+
64
+ except Exception as e:
65
+ logger.error("status_command_error", error=str(e), exc_info=e)
66
+ toolkit.print(f"[red]✗[/red] Error gathering status: {e}", tag="error")
67
+
68
+
69
+ def _show_system_info(console: Console, snapshot: SystemSnapshot) -> None:
70
+ """Show basic system information."""
71
+ table = Table(
72
+ show_header=False,
73
+ box=box.ROUNDED,
74
+ title="System Information",
75
+ title_style="bold white",
76
+ border_style="blue",
77
+ )
78
+ table.add_column("Property", style="cyan", no_wrap=True)
79
+ table.add_column("Value", style="white")
80
+
81
+ # Server configuration
82
+ table.add_row("Server Host", snapshot.host)
83
+ table.add_row("Server Port", str(snapshot.port))
84
+ table.add_row("Log Level", snapshot.log_level)
85
+
86
+ # Authentication
87
+ auth_token_status = (
88
+ "✓ Configured" if snapshot.auth_token_configured else "✗ Not set"
89
+ )
90
+ table.add_row("API Token", auth_token_status)
91
+
92
+ # Plugin directories
93
+ plugin_dirs = snapshot.plugin_directories
94
+ plugins_enabled = "✓ Enabled" if snapshot.plugins_enabled else "✗ Disabled"
95
+ table.add_row("Plugins", plugins_enabled)
96
+
97
+ # Show each plugin directory with existence check
98
+ for i, plugin_dir in enumerate(plugin_dirs):
99
+ dir_exists = "✓ Exists" if plugin_dir.exists else "✗ Missing"
100
+ dir_label = (
101
+ f"Plugin Directory {i + 1}" if len(plugin_dirs) > 1 else "Plugin Directory"
102
+ )
103
+ table.add_row(dir_label, f"{plugin_dir.path} ({dir_exists})")
104
+
105
+ console.print(table)
106
+ console.print()
107
+
108
+
109
+ def _show_config_info(console: Console, snapshot: ConfigSnapshot) -> None:
110
+ """Show configuration source information."""
111
+ if snapshot.sources:
112
+ info_text = Text()
113
+ info_text.append("Configuration Sources:\n", style="bold")
114
+ for source in snapshot.sources:
115
+ prefix = "✓" if source.exists else "✗"
116
+ info_text.append(f"{prefix} {source.path}\n")
117
+
118
+ console.print(Panel(info_text, title="Configuration", border_style="green"))
119
+ console.print()
120
+
121
+
122
+ def _show_plugin_status(console: Console, snapshot: PluginSnapshot) -> None:
123
+ """Show plugin discovery and status information."""
124
+
125
+ # Short-circuit if plugins are entirely disabled
126
+ if not snapshot.plugin_system_enabled:
127
+ table = Table(
128
+ show_header=True,
129
+ header_style="bold cyan",
130
+ box=box.ROUNDED,
131
+ title="Plugin Status",
132
+ title_style="bold white",
133
+ border_style="yellow",
134
+ )
135
+ table.add_column("Plugin", style="cyan", no_wrap=True)
136
+ table.add_column("Status", style="white", no_wrap=True)
137
+ table.add_column("Version", style="yellow")
138
+ table.add_column("Description", style="dim white")
139
+
140
+ table.add_row(
141
+ "Plugin System",
142
+ "[red]✗ Disabled[/red]",
143
+ "",
144
+ "Plugin system disabled in configuration",
145
+ )
146
+
147
+ console.print(table)
148
+ console.print(
149
+ Panel(
150
+ "Plugin system is disabled. No plugins will be loaded or executed.",
151
+ title="Plugin Summary",
152
+ border_style="yellow",
153
+ )
154
+ )
155
+ if snapshot.configuration_notes:
156
+ _print_plugin_configuration_notes(console, snapshot.configuration_notes)
157
+ return
158
+
159
+ table = Table(
160
+ show_header=True,
161
+ header_style="bold cyan",
162
+ box=box.ROUNDED,
163
+ title="Plugin Status",
164
+ title_style="bold white",
165
+ border_style="green",
166
+ )
167
+ table.add_column("Plugin", style="cyan", no_wrap=True)
168
+ table.add_column("Status", style="white", no_wrap=True)
169
+ table.add_column("Version", style="yellow")
170
+ table.add_column("Description", style="dim white")
171
+
172
+ for info in snapshot.enabled_plugins:
173
+ status = "[green]✓ Enabled[/green]"
174
+ version = info.version or "unknown"
175
+ description = info.description or ""
176
+
177
+ if info.state == "error":
178
+ status = "[red]✗ Error[/red]"
179
+ error_message = info.error or "Unknown error"
180
+ preview = error_message[:50]
181
+ if len(error_message) > 50:
182
+ preview += "..."
183
+ description = f"Error: {preview}"
184
+
185
+ table.add_row(info.name, status, version, description)
186
+
187
+ for name in snapshot.disabled_plugins:
188
+ table.add_row(
189
+ name,
190
+ "[yellow]⦸ Disabled[/yellow]",
191
+ "unknown",
192
+ "Plugin disabled in configuration",
193
+ )
194
+
195
+ if not snapshot.enabled_plugins and not snapshot.disabled_plugins:
196
+ table.add_row("No plugins found", "[yellow]⚠ Warning[/yellow]", "", "")
197
+
198
+ console.print(table)
199
+
200
+ summary_text = Text()
201
+ summary_text.append(f"Total plugins: {snapshot.total_count} ", style="bold")
202
+ summary_text.append(f"(Enabled: {snapshot.enabled_count}, ", style="green")
203
+ summary_text.append(f"Disabled: {snapshot.disabled_count})", style="yellow")
204
+
205
+ console.print(Panel(summary_text, title="Plugin Summary", border_style="blue"))
206
+
207
+ if snapshot.configuration_notes:
208
+ _print_plugin_configuration_notes(console, snapshot.configuration_notes)
209
+
210
+
211
+ def _print_plugin_configuration_notes(console: Console, notes: tuple[str, ...]) -> None:
212
+ config_text = Text()
213
+ config_text.append("Configuration: ", style="bold")
214
+ config_text.append(" • ".join(notes))
215
+ console.print(Panel(config_text, title="Plugin Configuration", border_style="blue"))
216
+ console.print()
217
+
218
+
219
+ @app.command()
220
+ def plugins() -> None:
221
+ """Show detailed plugin information only."""
222
+ toolkit = get_rich_toolkit()
223
+ console = Console()
224
+
225
+ toolkit.print("[bold cyan]Plugin Status[/bold cyan]", centered=True)
226
+ toolkit.print_line()
227
+
228
+ try:
229
+ settings = Settings.from_config()
230
+ plugin_snapshot = collect_plugin_snapshot(settings)
231
+ _show_plugin_status(console, plugin_snapshot)
232
+ except Exception as e:
233
+ logger.error("plugin_status_command_error", error=str(e), exc_info=e)
234
+ toolkit.print(f"[red]✗[/red] Error: {e}", tag="error")
235
+
236
+
237
+ @app.command()
238
+ def config() -> None:
239
+ """Show configuration information only."""
240
+ toolkit = get_rich_toolkit()
241
+ console = Console()
242
+
243
+ toolkit.print("[bold cyan]Configuration Status[/bold cyan]", centered=True)
244
+ toolkit.print_line()
245
+
246
+ try:
247
+ settings = Settings.from_config()
248
+ system_snapshot = collect_system_snapshot(settings)
249
+ config_snapshot = collect_config_snapshot()
250
+ _show_system_info(console, system_snapshot)
251
+ _show_config_info(console, config_snapshot)
252
+ except Exception as e:
253
+ logger.error("config_status_command_error", error=str(e), exc_info=e)
254
+ toolkit.print(f"[red]✗[/red] Error: {e}", tag="error")
@@ -0,0 +1,83 @@
1
+ """CLI command decorators for plugin dependency management."""
2
+
3
+ from collections.abc import Callable
4
+ from typing import Any, ParamSpec, TypeVar
5
+
6
+
7
+ P = ParamSpec("P")
8
+ R = TypeVar("R")
9
+
10
+
11
+ def needs_auth_provider() -> Callable[[Callable[P, R]], Callable[P, R]]:
12
+ """Decorator to mark CLI commands that need an auth provider.
13
+
14
+ This decorator marks the command as requiring the auth provider specified
15
+ in the command arguments. The actual plugin loading is handled by the
16
+ command implementation using load_cli_plugins().
17
+
18
+ Usage:
19
+ @app.command()
20
+ @needs_auth_provider()
21
+ async def auth_status(provider: str):
22
+ # Command implementation
23
+ pass
24
+ """
25
+
26
+ def decorator(func: Callable[P, R]) -> Callable[P, R]:
27
+ # Add metadata to the function
28
+ func._needs_auth_provider = True # type: ignore
29
+ return func
30
+
31
+ return decorator
32
+
33
+
34
+ def allows_plugins(
35
+ plugin_names: list[str],
36
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
37
+ """Decorator to specify additional plugins a CLI command can use.
38
+
39
+ This decorator specifies additional CLI-safe plugins that the command
40
+ wants to use beyond the default set. These plugins must still be marked
41
+ as cli_safe = True to be loaded.
42
+
43
+ Args:
44
+ plugin_names: List of plugin names to allow (e.g., ["request_tracer", "metrics"])
45
+
46
+ Usage:
47
+ @app.command()
48
+ @allows_plugins(["request_tracer", "metrics"])
49
+ async def my_command():
50
+ # Command implementation
51
+ pass
52
+ """
53
+
54
+ def decorator(func: Callable[P, R]) -> Callable[P, R]:
55
+ # Add metadata to the function
56
+ func._allows_plugins = plugin_names # type: ignore
57
+ return func
58
+
59
+ return decorator
60
+
61
+
62
+ def get_command_auth_provider(func: Callable[..., Any]) -> bool:
63
+ """Check if a command needs an auth provider.
64
+
65
+ Args:
66
+ func: Function to check
67
+
68
+ Returns:
69
+ True if the command is decorated with @needs_auth_provider()
70
+ """
71
+ return getattr(func, "_needs_auth_provider", False)
72
+
73
+
74
+ def get_command_allowed_plugins(func: Callable[..., Any]) -> list[str]:
75
+ """Get the allowed plugins for a command.
76
+
77
+ Args:
78
+ func: Function to check
79
+
80
+ Returns:
81
+ List of allowed plugin names (empty list if none specified)
82
+ """
83
+ return getattr(func, "_allows_plugins", [])
ccproxy/cli/helpers.py CHANGED
@@ -3,11 +3,10 @@
3
3
  from pathlib import Path
4
4
  from typing import Any
5
5
 
6
+ import typer
6
7
  from rich_toolkit import RichToolkit, RichToolkitTheme
7
8
  from rich_toolkit.styles import TaggedStyle
8
9
 
9
- from ccproxy.core.async_utils import patched_typing
10
-
11
10
 
12
11
  def get_rich_toolkit() -> RichToolkit:
13
12
  theme = RichToolkitTheme(
@@ -79,64 +78,27 @@ def link(text: str, link: str) -> str:
79
78
  return f"[link={link}]{text}[/link]"
80
79
 
81
80
 
82
- def merge_claude_code_options(base_options: Any, **overrides: Any) -> Any:
83
- """
84
- Create a new ClaudeCodeOptions instance by merging base options with overrides.
81
+ def is_running_in_docker() -> bool:
82
+ return Path("/.dockerenv").exists()
85
83
 
86
- Args:
87
- base_options: Base ClaudeCodeOptions instance to copy from
88
- **overrides: Dictionary of option overrides
89
84
 
90
- Returns:
91
- New ClaudeCodeOptions instance with merged options
92
- """
93
- with patched_typing():
94
- from claude_code_sdk import ClaudeCodeOptions
95
-
96
- # Create a new options instance with the base values
97
- options = ClaudeCodeOptions()
98
-
99
- # Copy all attributes from base_options
100
- if base_options:
101
- for attr in [
102
- "model",
103
- "max_thinking_tokens",
104
- "max_turns",
105
- "cwd",
106
- "system_prompt",
107
- "append_system_prompt",
108
- "permission_mode",
109
- "permission_prompt_tool_name",
110
- "continue_conversation",
111
- "resume",
112
- "allowed_tools",
113
- "disallowed_tools",
114
- "mcp_servers",
115
- "mcp_tools",
116
- # Anthropic API fields
117
- "temperature",
118
- "top_p",
119
- "top_k",
120
- "stop_sequences",
121
- "tools",
122
- "metadata",
123
- "service_tier",
124
- ]:
125
- if hasattr(base_options, attr):
126
- base_value = getattr(base_options, attr)
127
- if base_value is not None:
128
- setattr(options, attr, base_value)
129
-
130
- # Apply overrides
131
- for key, value in overrides.items():
132
- if value is not None and hasattr(options, key):
133
- # Handle special type conversions for specific fields
134
- if key == "cwd" and not isinstance(value, str):
135
- value = str(value)
136
- setattr(options, key, value)
137
-
138
- return options
85
+ def get_plugin_cli_args(ctx: typer.Context | None = None) -> dict[str, Any]:
86
+ """Return plugin-injected CLI args from Typer context.
139
87
 
140
-
141
- def is_running_in_docker() -> bool:
142
- return Path("/.dockerenv").exists()
88
+ Ensures a dict is returned. If no context is provided, attempts to fetch the
89
+ current Click/Typer context. This is safe to call from command bodies.
90
+ """
91
+ try:
92
+ if ctx is None:
93
+ # Lazy import to avoid hard dependency when not in CLI execution
94
+ from click import get_current_context as _get_ctx
95
+
96
+ ctx = _get_ctx(silent=True) # type: ignore[assignment]
97
+ if ctx and getattr(ctx, "obj", None) and isinstance(ctx.obj, dict):
98
+ data = ctx.obj.get("plugin_cli_args")
99
+ if isinstance(data, dict):
100
+ # Only return non-None values to simplify merging
101
+ return {k: v for k, v in data.items() if v is not None}
102
+ except Exception:
103
+ pass
104
+ return {}