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,294 @@
1
+ """Hook implementation for command replay generation."""
2
+
3
+ from ccproxy.core.logging import get_plugin_logger
4
+ from ccproxy.core.plugins.hooks import Hook
5
+ from ccproxy.core.plugins.hooks.base import HookContext
6
+ from ccproxy.core.plugins.hooks.events import HookEvent
7
+ from ccproxy.core.request_context import RequestContext
8
+ from ccproxy.utils.command_line import (
9
+ format_command_output,
10
+ generate_curl_command,
11
+ generate_xh_command,
12
+ )
13
+
14
+ from .config import CommandReplayConfig
15
+ from .formatter import CommandFileFormatter
16
+
17
+
18
+ logger = get_plugin_logger(__name__)
19
+
20
+
21
+ class CommandReplayHook(Hook):
22
+ """Hook for generating curl and xh command replays of provider requests.
23
+
24
+ Listens for PROVIDER_REQUEST_PREPARED events and generates command line
25
+ equivalents that can be used to replay the exact same HTTP requests.
26
+ """
27
+
28
+ name = "command_replay"
29
+ events = [
30
+ HookEvent.PROVIDER_REQUEST_PREPARED,
31
+ # Also listen to HTTP_REQUEST for broader coverage
32
+ HookEvent.HTTP_REQUEST,
33
+ ]
34
+ priority = 200 # Run after core tracing but before heavy processing
35
+
36
+ def __init__(
37
+ self,
38
+ config: CommandReplayConfig | None = None,
39
+ file_formatter: CommandFileFormatter | None = None,
40
+ ) -> None:
41
+ """Initialize the command replay hook.
42
+
43
+ Args:
44
+ config: Command replay configuration
45
+ file_formatter: File formatter for writing commands to files
46
+ """
47
+ self.config = config or CommandReplayConfig()
48
+ self.file_formatter = file_formatter
49
+
50
+ logger.debug(
51
+ "command_replay_hook_initialized",
52
+ enabled=self.config.enabled,
53
+ generate_curl=self.config.generate_curl,
54
+ generate_xh=self.config.generate_xh,
55
+ include_patterns=self.config.include_url_patterns,
56
+ only_provider_requests=self.config.only_provider_requests,
57
+ include_client_requests=self.config.include_client_requests,
58
+ write_to_files=self.config.write_to_files,
59
+ log_dir=self.config.log_dir,
60
+ )
61
+
62
+ async def __call__(self, context: HookContext) -> None:
63
+ """Handle hook events for command replay generation.
64
+
65
+ Args:
66
+ context: Hook context with event data
67
+ """
68
+ if not self.config.enabled:
69
+ return
70
+
71
+ # Debug logging
72
+ logger.debug(
73
+ "command_replay_hook_called",
74
+ hook_event=context.event.value if context.event else "unknown",
75
+ data_keys=list(context.data.keys()) if context.data else [],
76
+ )
77
+
78
+ try:
79
+ if context.event == HookEvent.PROVIDER_REQUEST_PREPARED:
80
+ await self._handle_provider_request(context)
81
+ elif context.event == HookEvent.HTTP_REQUEST:
82
+ await self._handle_http_request(context)
83
+ except Exception as e:
84
+ logger.error(
85
+ "command_replay_hook_error",
86
+ hook_event=context.event.value if context.event else "unknown",
87
+ error=str(e),
88
+ exc_info=e,
89
+ )
90
+
91
+ async def _handle_provider_request(self, context: HookContext) -> None:
92
+ """Handle PROVIDER_REQUEST_PREPARED event."""
93
+ await self._generate_commands(context, is_provider_request=True)
94
+
95
+ async def _handle_http_request(self, context: HookContext) -> None:
96
+ """Handle HTTP_REQUEST event - for both provider and client requests."""
97
+ url = context.data.get("url", "")
98
+ is_provider = self._is_provider_request(url)
99
+
100
+ # Apply filtering based on configuration
101
+ if self.config.only_provider_requests and not is_provider:
102
+ return
103
+
104
+ if not self.config.include_client_requests and not is_provider:
105
+ return
106
+
107
+ await self._generate_commands(context, is_provider_request=is_provider)
108
+
109
+ async def _generate_commands(
110
+ self, context: HookContext, is_provider_request: bool = False
111
+ ) -> None:
112
+ """Generate curl and xh commands from request context.
113
+
114
+ Args:
115
+ context: Hook context with request data
116
+ is_provider_request: Whether this came from PROVIDER_REQUEST_PREPARED
117
+ """
118
+ # Extract request data
119
+ method = context.data.get("method", "GET")
120
+ url = context.data.get("url", "")
121
+ headers = context.data.get("headers", {})
122
+ body = context.data.get("body")
123
+ is_json = context.data.get("is_json", False)
124
+
125
+ # Get request ID for correlation
126
+ request_id = (
127
+ context.data.get("request_id")
128
+ or context.metadata.get("request_id")
129
+ or "unknown"
130
+ )
131
+
132
+ # Get provider name if available
133
+ provider = context.provider or self._extract_provider_from_url(url)
134
+
135
+ # Check if we should generate commands for this URL
136
+ if not self.config.should_generate_for_url(url, is_provider_request):
137
+ logger.debug(
138
+ "command_replay_skipped_url_filter",
139
+ request_id=request_id,
140
+ url=url,
141
+ provider=provider,
142
+ is_provider_request=is_provider_request,
143
+ )
144
+ return
145
+
146
+ # Validate we have minimum required data
147
+ if not url or not method:
148
+ logger.warning(
149
+ "command_replay_insufficient_data",
150
+ request_id=request_id,
151
+ has_url=bool(url),
152
+ has_method=bool(method),
153
+ )
154
+ return
155
+
156
+ commands = []
157
+
158
+ # Generate curl command
159
+ if self.config.generate_curl:
160
+ try:
161
+ curl_cmd = generate_curl_command(
162
+ method=method,
163
+ url=url,
164
+ headers=headers,
165
+ body=body,
166
+ is_json=is_json,
167
+ pretty=self.config.pretty_format,
168
+ )
169
+ commands.append(("curl", curl_cmd))
170
+ except Exception as e:
171
+ logger.error(
172
+ "command_replay_curl_generation_error",
173
+ request_id=request_id,
174
+ error=str(e),
175
+ )
176
+
177
+ # Generate xh command
178
+ if self.config.generate_xh:
179
+ try:
180
+ xh_cmd = generate_xh_command(
181
+ method=method,
182
+ url=url,
183
+ headers=headers,
184
+ body=body,
185
+ is_json=is_json,
186
+ pretty=self.config.pretty_format,
187
+ )
188
+ commands.append(("xh", xh_cmd))
189
+ except Exception as e:
190
+ logger.error(
191
+ "command_replay_xh_generation_error",
192
+ request_id=request_id,
193
+ error=str(e),
194
+ )
195
+
196
+ # Process generated commands
197
+ if commands:
198
+ curl_cmd = next((cmd for tool, cmd in commands if tool == "curl"), "")
199
+ xh_cmd = next((cmd for tool, cmd in commands if tool == "xh"), "")
200
+
201
+ # Write to files if enabled
202
+ written_files = []
203
+ if self.config.write_to_files and self.file_formatter:
204
+ try:
205
+ # Get timestamp prefix from current request context if available
206
+ timestamp_prefix = None
207
+ try:
208
+ current_context = RequestContext.get_current()
209
+ if current_context:
210
+ timestamp_prefix = (
211
+ current_context.get_log_timestamp_prefix()
212
+ )
213
+ except Exception:
214
+ pass
215
+
216
+ written_files = await self.file_formatter.write_commands(
217
+ request_id=request_id,
218
+ curl_command=curl_cmd,
219
+ xh_command=xh_cmd,
220
+ provider=provider,
221
+ timestamp_prefix=timestamp_prefix,
222
+ method=method,
223
+ url=url,
224
+ headers=headers,
225
+ body=body,
226
+ is_json=is_json,
227
+ )
228
+
229
+ if written_files:
230
+ logger.debug(
231
+ "command_replay_files_written",
232
+ request_id=request_id,
233
+ files=written_files,
234
+ provider=provider,
235
+ )
236
+ except Exception as e:
237
+ logger.error(
238
+ "command_replay_file_write_failed",
239
+ request_id=request_id,
240
+ error=str(e),
241
+ exc_info=e,
242
+ )
243
+
244
+ # Log to console if enabled
245
+ if self.config.log_to_console:
246
+ output = format_command_output(
247
+ request_id=request_id,
248
+ curl_command=curl_cmd,
249
+ xh_command=xh_cmd,
250
+ provider=provider,
251
+ )
252
+
253
+ # Add file info to console output if files were written
254
+ if written_files:
255
+ output += f"\n📁 Files written: {', '.join(written_files)}\n"
256
+
257
+ logger.debug("command_replay_generated", output=output)
258
+
259
+ def _is_provider_request(self, url: str) -> bool:
260
+ """Determine if this is a request to a provider API.
261
+
262
+ Args:
263
+ url: The request URL
264
+
265
+ Returns:
266
+ True if this is a provider request
267
+ """
268
+ provider_domains = [
269
+ "api.anthropic.com",
270
+ "claude.ai",
271
+ "api.openai.com",
272
+ "chatgpt.com",
273
+ ]
274
+
275
+ url_lower = url.lower()
276
+ return any(domain in url_lower for domain in provider_domains)
277
+
278
+ def _extract_provider_from_url(self, url: str) -> str | None:
279
+ """Extract provider name from URL.
280
+
281
+ Args:
282
+ url: The request URL
283
+
284
+ Returns:
285
+ Provider name or None if not recognized
286
+ """
287
+ url_lower = url.lower()
288
+
289
+ if "anthropic.com" in url_lower or "claude.ai" in url_lower:
290
+ return "anthropic"
291
+ elif "openai.com" in url_lower or "chatgpt.com" in url_lower:
292
+ return "openai"
293
+
294
+ return None
@@ -0,0 +1,161 @@
1
+ """Command Replay plugin implementation."""
2
+
3
+ from typing import Any
4
+
5
+ from ccproxy.core.logging import get_plugin_logger
6
+ from ccproxy.core.plugins import (
7
+ PluginManifest,
8
+ SystemPluginFactory,
9
+ SystemPluginRuntime,
10
+ )
11
+ from ccproxy.core.plugins.hooks import HookRegistry
12
+
13
+ from .config import CommandReplayConfig
14
+ from .formatter import CommandFileFormatter
15
+ from .hook import CommandReplayHook
16
+
17
+
18
+ logger = get_plugin_logger()
19
+
20
+
21
+ class CommandReplayRuntime(SystemPluginRuntime):
22
+ """Runtime for the command replay plugin.
23
+
24
+ Generates curl and xh commands for provider requests to enable
25
+ easy replay and debugging of API calls.
26
+ """
27
+
28
+ def __init__(self, manifest: PluginManifest):
29
+ """Initialize runtime."""
30
+ super().__init__(manifest)
31
+ self.config: CommandReplayConfig | None = None
32
+ self.hook: CommandReplayHook | None = None
33
+ self.file_formatter: CommandFileFormatter | None = None
34
+
35
+ async def _on_initialize(self) -> None:
36
+ """Initialize the command replay plugin."""
37
+ if not self.context:
38
+ raise RuntimeError("Context not set")
39
+
40
+ # Get configuration
41
+ config = self.context.get("config")
42
+ if not isinstance(config, CommandReplayConfig):
43
+ logger.debug("plugin_no_config")
44
+ config = CommandReplayConfig()
45
+ logger.debug("plugin_using_default_config")
46
+ self.config = config
47
+
48
+ # Debug log the configuration being used
49
+ logger.debug(
50
+ "plugin_configuration_loaded",
51
+ enabled=config.enabled,
52
+ generate_curl=config.generate_curl,
53
+ generate_xh=config.generate_xh,
54
+ include_patterns=config.include_url_patterns,
55
+ exclude_patterns=config.exclude_url_patterns,
56
+ log_to_console=config.log_to_console,
57
+ log_level=config.log_level,
58
+ only_provider_requests=config.only_provider_requests,
59
+ )
60
+
61
+ if self.config.enabled:
62
+ # Initialize file formatter if file writing is enabled
63
+ if self.config.write_to_files:
64
+ self.file_formatter = CommandFileFormatter(
65
+ log_dir=self.config.log_dir,
66
+ enabled=True,
67
+ separate_files_per_command=self.config.separate_files_per_command,
68
+ )
69
+ logger.debug(
70
+ "command_replay_file_formatter_initialized",
71
+ log_dir=self.config.log_dir,
72
+ separate_files=self.config.separate_files_per_command,
73
+ )
74
+
75
+ # Register hook for provider request events
76
+ self.hook = CommandReplayHook(
77
+ config=self.config,
78
+ file_formatter=self.file_formatter,
79
+ )
80
+
81
+ # Try to get hook registry from context
82
+ hook_registry = self.context.get("hook_registry")
83
+
84
+ # If not found, try app state
85
+ if not hook_registry:
86
+ app = self.context.get("app")
87
+ if app and hasattr(app.state, "hook_registry"):
88
+ hook_registry = app.state.hook_registry
89
+
90
+ if hook_registry and isinstance(hook_registry, HookRegistry):
91
+ hook_registry.register(self.hook)
92
+ logger.debug(
93
+ "command_replay_hook_registered",
94
+ events=self.hook.events,
95
+ priority=self.hook.priority,
96
+ generate_curl=self.config.generate_curl,
97
+ generate_xh=self.config.generate_xh,
98
+ write_to_files=self.config.write_to_files,
99
+ log_dir=self.config.log_dir if self.config.write_to_files else None,
100
+ )
101
+ else:
102
+ logger.warning(
103
+ "hook_registry_not_available",
104
+ fallback="disabled",
105
+ )
106
+ else:
107
+ logger.debug("command_replay_plugin_disabled")
108
+
109
+ async def _on_shutdown(self) -> None:
110
+ """Clean up plugin resources."""
111
+ if self.hook:
112
+ logger.info("command_replay_plugin_shutdown")
113
+ self.hook = None
114
+
115
+ if self.file_formatter:
116
+ self.file_formatter.cleanup()
117
+ self.file_formatter = None
118
+
119
+ def get_health_info(self) -> dict[str, Any]:
120
+ """Get plugin health information."""
121
+ return {
122
+ "enabled": self.config.enabled if self.config else False,
123
+ "hook_registered": self.hook is not None,
124
+ "generate_curl": self.config.generate_curl if self.config else False,
125
+ "generate_xh": self.config.generate_xh if self.config else False,
126
+ "write_to_files": self.config.write_to_files if self.config else False,
127
+ "file_formatter_enabled": self.file_formatter is not None,
128
+ "log_dir": self.config.log_dir if self.config else None,
129
+ }
130
+
131
+
132
+ class CommandReplayFactory(SystemPluginFactory):
133
+ """Factory for creating command replay plugin instances."""
134
+
135
+ def __init__(self) -> None:
136
+ """Initialize factory with manifest."""
137
+ # Create manifest with static declarations
138
+ manifest = PluginManifest(
139
+ name="command_replay",
140
+ version="0.1.0",
141
+ description="Generates curl and xh commands for provider requests",
142
+ is_provider=False,
143
+ config_class=CommandReplayConfig,
144
+ )
145
+
146
+ # Initialize with manifest
147
+ super().__init__(manifest)
148
+
149
+ def create_runtime(self) -> CommandReplayRuntime:
150
+ """Create runtime instance."""
151
+ return CommandReplayRuntime(self.manifest)
152
+
153
+ def create_context(self, core_services: Any) -> Any:
154
+ """Create context for the plugin."""
155
+ # Get base context from parent
156
+ context = super().create_context(core_services)
157
+ return context
158
+
159
+
160
+ # Export the factory for plugin discovery
161
+ factory = CommandReplayFactory()
@@ -0,0 +1,39 @@
1
+ # Copilot Plugin
2
+
3
+ Adds GitHub Copilot as a provider with OAuth, detection, and streaming support.
4
+
5
+ ## Highlights
6
+ - Wraps the Copilot HTTP adapter while emitting OpenAI-compatible streams
7
+ - Manages OAuth exchange and token refresh through `CopilotOAuthProvider`
8
+ - Exposes GitHub-flavored routes under `/copilot` alongside v1 proxy APIs
9
+
10
+ ## Configuration
11
+ - `CopilotConfig` controls base URLs, scopes, cache paths, and CLI detection
12
+ - Depends on `CopilotTokenManager` for credential storage and refresh logic
13
+ - Generate defaults with `python3 scripts/generate_config_from_model.py \
14
+ --format toml --plugin copilot --config-class CopilotConfig`
15
+
16
+ ```toml
17
+ [plugins.copilot]
18
+ # enabled = true
19
+ # base_url = "https://api.githubcopilot.com"
20
+ # auth_type = "oauth"
21
+ # supports_streaming = true
22
+ # default_max_tokens = 4096
23
+ # account_type = "individual"
24
+ # request_timeout = 30
25
+ # max_retries = 3
26
+ # retry_delay = 1.0
27
+
28
+ [plugins.copilot.oauth]
29
+ # client_id = "Iv1.b507a08c87ecfe98"
30
+ # authorize_url = "https://github.com/login/device/code"
31
+ # token_url = "https://github.com/login/oauth/access_token"
32
+ # callback_port = 8080
33
+ # scopes = ["read:user"]
34
+ ```
35
+
36
+ ## Related Components
37
+ - `adapter.py`: request translation and HTTP execution layer
38
+ - `oauth/provider.py`: OAuth flow implementation for GitHub accounts
39
+ - `routes.py`: FastAPI routers for GitHub and proxy endpoints
@@ -0,0 +1,11 @@
1
+ """GitHub Copilot provider plugin for CCProxy.
2
+
3
+ This plugin provides OAuth authentication with GitHub and API proxying
4
+ capabilities for GitHub Copilot services, following the established patterns
5
+ from existing OAuth Claude and Codex plugins.
6
+ """
7
+
8
+ from .plugin import CopilotPluginFactory, CopilotPluginRuntime, factory
9
+
10
+
11
+ __all__ = ["CopilotPluginFactory", "CopilotPluginRuntime", "factory"]