free-claude-code 6.2.64__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 (429) hide show
  1. free_claude_code/__init__.py +1 -0
  2. free_claude_code/api/__init__.py +1 -0
  3. free_claude_code/api/admin_cache.py +49 -0
  4. free_claude_code/api/admin_routes.py +501 -0
  5. free_claude_code/api/admin_security.py +67 -0
  6. free_claude_code/api/admin_static/admin.css +980 -0
  7. free_claude_code/api/admin_static/admin.js +1934 -0
  8. free_claude_code/api/admin_static/code_sessions.css +109 -0
  9. free_claude_code/api/admin_static/code_sessions.js +1693 -0
  10. free_claude_code/api/admin_static/form_controls.js +15 -0
  11. free_claude_code/api/admin_static/index.html +201 -0
  12. free_claude_code/api/admin_static/model_combobox.js +194 -0
  13. free_claude_code/api/admin_static/providers/LICENSE-llamacpp.txt +21 -0
  14. free_claude_code/api/admin_static/providers/LICENSE-lobe-icons.txt +21 -0
  15. free_claude_code/api/admin_static/providers/SOURCES.md +56 -0
  16. free_claude_code/api/admin_static/providers/agnesai.svg +1 -0
  17. free_claude_code/api/admin_static/providers/azure-color.svg +1 -0
  18. free_claude_code/api/admin_static/providers/bedrock-color.svg +1 -0
  19. free_claude_code/api/admin_static/providers/cerebras-color.svg +1 -0
  20. free_claude_code/api/admin_static/providers/chutes.png +0 -0
  21. free_claude_code/api/admin_static/providers/cline.svg +1 -0
  22. free_claude_code/api/admin_static/providers/cloudflare-color.svg +1 -0
  23. free_claude_code/api/admin_static/providers/cohere-color.svg +1 -0
  24. free_claude_code/api/admin_static/providers/deepinfra-color.svg +1 -0
  25. free_claude_code/api/admin_static/providers/deepseek-color.svg +1 -0
  26. free_claude_code/api/admin_static/providers/experiential.svg +9 -0
  27. free_claude_code/api/admin_static/providers/featherless-color.svg +1 -0
  28. free_claude_code/api/admin_static/providers/fireworks-color.svg +1 -0
  29. free_claude_code/api/admin_static/providers/gemini-color.svg +1 -0
  30. free_claude_code/api/admin_static/providers/githubcopilot.svg +1 -0
  31. free_claude_code/api/admin_static/providers/groq.svg +1 -0
  32. free_claude_code/api/admin_static/providers/huggingface-color.svg +1 -0
  33. free_claude_code/api/admin_static/providers/kilocode.svg +1 -0
  34. free_claude_code/api/admin_static/providers/kimi-color.svg +1 -0
  35. free_claude_code/api/admin_static/providers/lightning.png +0 -0
  36. free_claude_code/api/admin_static/providers/llamacpp.svg +77 -0
  37. free_claude_code/api/admin_static/providers/llm7.png +0 -0
  38. free_claude_code/api/admin_static/providers/lmstudio.svg +1 -0
  39. free_claude_code/api/admin_static/providers/minimax-color.svg +1 -0
  40. free_claude_code/api/admin_static/providers/mistral-color.svg +1 -0
  41. free_claude_code/api/admin_static/providers/nararoute.png +0 -0
  42. free_claude_code/api/admin_static/providers/nebius.svg +1 -0
  43. free_claude_code/api/admin_static/providers/novita-color.svg +1 -0
  44. free_claude_code/api/admin_static/providers/nvidia-color.svg +1 -0
  45. free_claude_code/api/admin_static/providers/ollama.svg +1 -0
  46. free_claude_code/api/admin_static/providers/openai.svg +1 -0
  47. free_claude_code/api/admin_static/providers/opencode.svg +1 -0
  48. free_claude_code/api/admin_static/providers/openrouter.svg +1 -0
  49. free_claude_code/api/admin_static/providers/poolside-color.svg +1 -0
  50. free_claude_code/api/admin_static/providers/qwen-color.svg +1 -0
  51. free_claude_code/api/admin_static/providers/sambanova-color.svg +1 -0
  52. free_claude_code/api/admin_static/providers/scaleway.svg +11 -0
  53. free_claude_code/api/admin_static/providers/siliconcloud-color.svg +1 -0
  54. free_claude_code/api/admin_static/providers/together-color.svg +1 -0
  55. free_claude_code/api/admin_static/providers/tokenrouter.png +0 -0
  56. free_claude_code/api/admin_static/providers/vercel.svg +1 -0
  57. free_claude_code/api/admin_static/providers/vertexai-color.svg +1 -0
  58. free_claude_code/api/admin_static/providers/wafer.png +0 -0
  59. free_claude_code/api/admin_static/providers/wandb.png +0 -0
  60. free_claude_code/api/admin_static/providers/xai.svg +1 -0
  61. free_claude_code/api/admin_static/providers/zai.svg +1 -0
  62. free_claude_code/api/admin_static/providers/zenmux.svg +1 -0
  63. free_claude_code/api/admin_static/session_layout.css +552 -0
  64. free_claude_code/api/admin_static/session_ui.js +202 -0
  65. free_claude_code/api/app.py +149 -0
  66. free_claude_code/api/code_sessions_routes.py +386 -0
  67. free_claude_code/api/command_utils.py +164 -0
  68. free_claude_code/api/dependencies.py +128 -0
  69. free_claude_code/api/detection.py +185 -0
  70. free_claude_code/api/handlers/__init__.py +7 -0
  71. free_claude_code/api/handlers/classifier_response.py +66 -0
  72. free_claude_code/api/handlers/messages.py +359 -0
  73. free_claude_code/api/handlers/responses.py +163 -0
  74. free_claude_code/api/handlers/token_count.py +87 -0
  75. free_claude_code/api/markdown.py +71 -0
  76. free_claude_code/api/model_catalog.py +282 -0
  77. free_claude_code/api/optimization_handlers.py +162 -0
  78. free_claude_code/api/ports.py +97 -0
  79. free_claude_code/api/request_errors.py +100 -0
  80. free_claude_code/api/request_ids.py +100 -0
  81. free_claude_code/api/request_lifetime.py +130 -0
  82. free_claude_code/api/response_streams.py +422 -0
  83. free_claude_code/api/routes.py +269 -0
  84. free_claude_code/api/validation_log.py +46 -0
  85. free_claude_code/application/__init__.py +1 -0
  86. free_claude_code/application/code_sessions/__init__.py +33 -0
  87. free_claude_code/application/code_sessions/models.py +227 -0
  88. free_claude_code/application/code_sessions/ports.py +197 -0
  89. free_claude_code/application/code_sessions/service.py +1123 -0
  90. free_claude_code/application/code_sessions/state.py +659 -0
  91. free_claude_code/application/connected_accounts.py +96 -0
  92. free_claude_code/application/errors.py +41 -0
  93. free_claude_code/application/execution.py +467 -0
  94. free_claude_code/application/model_catalog.py +95 -0
  95. free_claude_code/application/model_metadata.py +26 -0
  96. free_claude_code/application/ports.py +127 -0
  97. free_claude_code/application/readiness.py +43 -0
  98. free_claude_code/application/reasoning.py +134 -0
  99. free_claude_code/application/routing.py +291 -0
  100. free_claude_code/application/session_events.py +140 -0
  101. free_claude_code/application/web_tools/__init__.py +1 -0
  102. free_claude_code/application/web_tools/ports.py +35 -0
  103. free_claude_code/application/web_tools/request.py +348 -0
  104. free_claude_code/application/web_tools/service.py +481 -0
  105. free_claude_code/assets/app-icon.icns +0 -0
  106. free_claude_code/assets/app-icon.ico +0 -0
  107. free_claude_code/assets/app-icon.png +0 -0
  108. free_claude_code/assets/app-icon.svg +14 -0
  109. free_claude_code/cli/__init__.py +5 -0
  110. free_claude_code/cli/commands.py +379 -0
  111. free_claude_code/cli/desktop.py +156 -0
  112. free_claude_code/cli/desktop_assets.py +31 -0
  113. free_claude_code/cli/desktop_entrypoint.py +29 -0
  114. free_claude_code/cli/desktop_tray.py +67 -0
  115. free_claude_code/cli/entrypoints.py +25 -0
  116. free_claude_code/cli/launchers/__init__.py +1 -0
  117. free_claude_code/cli/launchers/aider.py +53 -0
  118. free_claude_code/cli/launchers/aider_config.py +80 -0
  119. free_claude_code/cli/launchers/catalog_http.py +134 -0
  120. free_claude_code/cli/launchers/claude.py +34 -0
  121. free_claude_code/cli/launchers/cline.py +60 -0
  122. free_claude_code/cli/launchers/cline_config.py +121 -0
  123. free_claude_code/cli/launchers/codex.py +47 -0
  124. free_claude_code/cli/launchers/common.py +103 -0
  125. free_claude_code/cli/launchers/dsh.py +69 -0
  126. free_claude_code/cli/launchers/dsh_config.py +124 -0
  127. free_claude_code/cli/launchers/grok.py +116 -0
  128. free_claude_code/cli/launchers/hermes.py +85 -0
  129. free_claude_code/cli/launchers/hermes_config.py +134 -0
  130. free_claude_code/cli/launchers/muse.py +68 -0
  131. free_claude_code/cli/launchers/opencode.py +83 -0
  132. free_claude_code/cli/launchers/opencode_config.py +107 -0
  133. free_claude_code/cli/launchers/pi.py +70 -0
  134. free_claude_code/cli/launchers/pi_extension.ts +190 -0
  135. free_claude_code/cli/launchers/runner.py +155 -0
  136. free_claude_code/cli/local_http.py +12 -0
  137. free_claude_code/cli/managed/__init__.py +6 -0
  138. free_claude_code/cli/managed/claude.py +225 -0
  139. free_claude_code/cli/managed/diagnostics.py +47 -0
  140. free_claude_code/cli/managed/manager.py +207 -0
  141. free_claude_code/cli/managed/session.py +293 -0
  142. free_claude_code/cli/process_registry.py +76 -0
  143. free_claude_code/cli/server_socket.py +71 -0
  144. free_claude_code/cli/uvicorn_server.py +41 -0
  145. free_claude_code/config/__init__.py +10 -0
  146. free_claude_code/config/admin/__init__.py +1 -0
  147. free_claude_code/config/admin/manifest.py +610 -0
  148. free_claude_code/config/admin/persistence.py +202 -0
  149. free_claude_code/config/admin/provider_manifest.py +443 -0
  150. free_claude_code/config/admin/specs.py +77 -0
  151. free_claude_code/config/admin/state.py +18 -0
  152. free_claude_code/config/admin/status.py +116 -0
  153. free_claude_code/config/admin/validation.py +40 -0
  154. free_claude_code/config/admin/values.py +115 -0
  155. free_claude_code/config/constants.py +9 -0
  156. free_claude_code/config/env_files.py +74 -0
  157. free_claude_code/config/env_migrations.py +457 -0
  158. free_claude_code/config/loader.py +196 -0
  159. free_claude_code/config/logging_config.py +197 -0
  160. free_claude_code/config/model_refs.py +118 -0
  161. free_claude_code/config/nim.py +121 -0
  162. free_claude_code/config/paths.py +104 -0
  163. free_claude_code/config/provider_catalog.py +746 -0
  164. free_claude_code/config/provider_proxies.py +44 -0
  165. free_claude_code/config/reasoning.py +24 -0
  166. free_claude_code/config/server_urls.py +55 -0
  167. free_claude_code/config/settings.py +812 -0
  168. free_claude_code/core/__init__.py +15 -0
  169. free_claude_code/core/anthropic/__init__.py +99 -0
  170. free_claude_code/core/anthropic/content.py +31 -0
  171. free_claude_code/core/anthropic/conversion.py +876 -0
  172. free_claude_code/core/anthropic/errors.py +97 -0
  173. free_claude_code/core/anthropic/image_sources.py +77 -0
  174. free_claude_code/core/anthropic/models.py +193 -0
  175. free_claude_code/core/anthropic/native.py +184 -0
  176. free_claude_code/core/anthropic/native_stream.py +306 -0
  177. free_claude_code/core/anthropic/request_serialization.py +57 -0
  178. free_claude_code/core/anthropic/request_snapshot.py +35 -0
  179. free_claude_code/core/anthropic/server_tool_sse.py +234 -0
  180. free_claude_code/core/anthropic/server_tool_types.py +9 -0
  181. free_claude_code/core/anthropic/sse_aggregation.py +144 -0
  182. free_claude_code/core/anthropic/stream_contracts.py +208 -0
  183. free_claude_code/core/anthropic/streaming/__init__.py +43 -0
  184. free_claude_code/core/anthropic/streaming/decoder.py +49 -0
  185. free_claude_code/core/anthropic/streaming/emitter.py +62 -0
  186. free_claude_code/core/anthropic/streaming/ledger.py +538 -0
  187. free_claude_code/core/anthropic/streaming/recovery.py +211 -0
  188. free_claude_code/core/anthropic/thinking.py +140 -0
  189. free_claude_code/core/anthropic/tokens.py +111 -0
  190. free_claude_code/core/anthropic/tool_results.py +63 -0
  191. free_claude_code/core/anthropic/tool_schema.py +94 -0
  192. free_claude_code/core/anthropic/tools.py +493 -0
  193. free_claude_code/core/anthropic/usage.py +38 -0
  194. free_claude_code/core/anthropic/utils.py +9 -0
  195. free_claude_code/core/async_iterators.py +26 -0
  196. free_claude_code/core/async_tasks.py +26 -0
  197. free_claude_code/core/data/README.md +12 -0
  198. free_claude_code/core/data/TIKTOKEN-LICENSE +21 -0
  199. free_claude_code/core/data/cl100k_base.tiktoken +100256 -0
  200. free_claude_code/core/diagnostics.py +319 -0
  201. free_claude_code/core/failures.py +50 -0
  202. free_claude_code/core/gateway_model_ids.py +73 -0
  203. free_claude_code/core/history_replay.py +561 -0
  204. free_claude_code/core/interprocess_lock.py +90 -0
  205. free_claude_code/core/json_types.py +7 -0
  206. free_claude_code/core/model_capabilities.py +10 -0
  207. free_claude_code/core/openai_chat.py +54 -0
  208. free_claude_code/core/openai_responses/__init__.py +90 -0
  209. free_claude_code/core/openai_responses/chat_request.py +747 -0
  210. free_claude_code/core/openai_responses/errors.py +78 -0
  211. free_claude_code/core/openai_responses/events.py +82 -0
  212. free_claude_code/core/openai_responses/ids.py +32 -0
  213. free_claude_code/core/openai_responses/items.py +35 -0
  214. free_claude_code/core/openai_responses/messages_request.py +624 -0
  215. free_claude_code/core/openai_responses/messages_stream.py +404 -0
  216. free_claude_code/core/openai_responses/models.py +33 -0
  217. free_claude_code/core/openai_responses/native.py +147 -0
  218. free_claude_code/core/openai_responses/provider_input.py +409 -0
  219. free_claude_code/core/openai_responses/provider_stream.py +282 -0
  220. free_claude_code/core/openai_responses/reasoning.py +73 -0
  221. free_claude_code/core/openai_responses/streaming/__init__.py +1 -0
  222. free_claude_code/core/openai_responses/streaming/blocks.py +36 -0
  223. free_claude_code/core/openai_responses/streaming/completion.py +169 -0
  224. free_claude_code/core/openai_responses/streaming/error_mapping.py +28 -0
  225. free_claude_code/core/openai_responses/streaming/event_builders.py +198 -0
  226. free_claude_code/core/openai_responses/streaming/ledger.py +96 -0
  227. free_claude_code/core/openai_responses/tokens.py +133 -0
  228. free_claude_code/core/openai_responses/tool_adaptation.py +828 -0
  229. free_claude_code/core/openai_responses/tool_search.py +204 -0
  230. free_claude_code/core/openai_responses/tools.py +169 -0
  231. free_claude_code/core/openai_tool_names.py +178 -0
  232. free_claude_code/core/rate_limit.py +72 -0
  233. free_claude_code/core/reasoning.py +136 -0
  234. free_claude_code/core/token_estimation.py +76 -0
  235. free_claude_code/core/tool_schema_patterns.py +168 -0
  236. free_claude_code/core/trace.py +197 -0
  237. free_claude_code/core/version.py +15 -0
  238. free_claude_code/core/web_tools.py +17 -0
  239. free_claude_code/core/windows_dpi.py +27 -0
  240. free_claude_code/harnesses/__init__.py +1 -0
  241. free_claude_code/harnesses/claude.py +42 -0
  242. free_claude_code/harnesses/claude_desktop_integration.py +418 -0
  243. free_claude_code/harnesses/claude_integration.py +167 -0
  244. free_claude_code/harnesses/codex.py +94 -0
  245. free_claude_code/harnesses/codex_integration.py +147 -0
  246. free_claude_code/harnesses/codex_model_catalog.py +109 -0
  247. free_claude_code/harnesses/config_file.py +33 -0
  248. free_claude_code/harnesses/environment.py +78 -0
  249. free_claude_code/harnesses/jetbrains_acp_integration.py +242 -0
  250. free_claude_code/harnesses/launch.py +21 -0
  251. free_claude_code/harnesses/resources.py +42 -0
  252. free_claude_code/messaging/__init__.py +16 -0
  253. free_claude_code/messaging/cli_event_constants.py +67 -0
  254. free_claude_code/messaging/command_context.py +99 -0
  255. free_claude_code/messaging/command_dispatcher.py +31 -0
  256. free_claude_code/messaging/commands.py +182 -0
  257. free_claude_code/messaging/event_parser.py +184 -0
  258. free_claude_code/messaging/limiter.py +342 -0
  259. free_claude_code/messaging/managed_protocols.py +35 -0
  260. free_claude_code/messaging/models.py +57 -0
  261. free_claude_code/messaging/node_event_pipeline.py +120 -0
  262. free_claude_code/messaging/node_runner.py +408 -0
  263. free_claude_code/messaging/platforms/__init__.py +20 -0
  264. free_claude_code/messaging/platforms/discord.py +319 -0
  265. free_claude_code/messaging/platforms/discord_inbound.py +112 -0
  266. free_claude_code/messaging/platforms/discord_io.py +167 -0
  267. free_claude_code/messaging/platforms/factory.py +109 -0
  268. free_claude_code/messaging/platforms/outbox.py +143 -0
  269. free_claude_code/messaging/platforms/ports.py +99 -0
  270. free_claude_code/messaging/platforms/telegram.py +300 -0
  271. free_claude_code/messaging/platforms/telegram_inbound.py +133 -0
  272. free_claude_code/messaging/platforms/telegram_io.py +287 -0
  273. free_claude_code/messaging/platforms/voice_flow.py +388 -0
  274. free_claude_code/messaging/rendering/__init__.py +3 -0
  275. free_claude_code/messaging/rendering/discord_markdown.py +318 -0
  276. free_claude_code/messaging/rendering/markdown_tables.py +47 -0
  277. free_claude_code/messaging/rendering/profiles.py +53 -0
  278. free_claude_code/messaging/rendering/telegram_markdown.py +331 -0
  279. free_claude_code/messaging/safe_diagnostics.py +15 -0
  280. free_claude_code/messaging/session/__init__.py +5 -0
  281. free_claude_code/messaging/session/managed_message_log.py +135 -0
  282. free_claude_code/messaging/session/persistence.py +161 -0
  283. free_claude_code/messaging/session/store.py +162 -0
  284. free_claude_code/messaging/transcript/__init__.py +6 -0
  285. free_claude_code/messaging/transcript/buffer.py +220 -0
  286. free_claude_code/messaging/transcript/context.py +18 -0
  287. free_claude_code/messaging/transcript/renderer.py +65 -0
  288. free_claude_code/messaging/transcript/segments.py +176 -0
  289. free_claude_code/messaging/transcript/subagents.py +108 -0
  290. free_claude_code/messaging/transcription.py +132 -0
  291. free_claude_code/messaging/trees/__init__.py +41 -0
  292. free_claude_code/messaging/trees/graph.py +250 -0
  293. free_claude_code/messaging/trees/identity.py +16 -0
  294. free_claude_code/messaging/trees/manager.py +587 -0
  295. free_claude_code/messaging/trees/node.py +56 -0
  296. free_claude_code/messaging/trees/processor.py +272 -0
  297. free_claude_code/messaging/trees/queue.py +47 -0
  298. free_claude_code/messaging/trees/repository.py +135 -0
  299. free_claude_code/messaging/trees/runtime.py +487 -0
  300. free_claude_code/messaging/trees/snapshot.py +221 -0
  301. free_claude_code/messaging/trees/transitions.py +174 -0
  302. free_claude_code/messaging/turn_intake.py +241 -0
  303. free_claude_code/messaging/ui_updates.py +99 -0
  304. free_claude_code/messaging/voice.py +376 -0
  305. free_claude_code/messaging/workflow.py +714 -0
  306. free_claude_code/providers/__init__.py +12 -0
  307. free_claude_code/providers/admission.py +942 -0
  308. free_claude_code/providers/anthropic_messages/__init__.py +1 -0
  309. free_claude_code/providers/anthropic_messages/request_policy.py +162 -0
  310. free_claude_code/providers/anthropic_messages/transport.py +499 -0
  311. free_claude_code/providers/base.py +73 -0
  312. free_claude_code/providers/cloudflare/__init__.py +8 -0
  313. free_claude_code/providers/cloudflare/client.py +166 -0
  314. free_claude_code/providers/credential_validation.py +314 -0
  315. free_claude_code/providers/deepseek/__init__.py +5 -0
  316. free_claude_code/providers/deepseek/client.py +117 -0
  317. free_claude_code/providers/deepseek/compat.py +241 -0
  318. free_claude_code/providers/endpoint.py +21 -0
  319. free_claude_code/providers/endpoint_types.py +21 -0
  320. free_claude_code/providers/failure_policy.py +574 -0
  321. free_claude_code/providers/gemini/__init__.py +5 -0
  322. free_claude_code/providers/gemini/client.py +42 -0
  323. free_claude_code/providers/github_copilot/__init__.py +1 -0
  324. free_claude_code/providers/github_copilot/auth.py +502 -0
  325. free_claude_code/providers/github_copilot/broker.py +248 -0
  326. free_claude_code/providers/github_copilot/lifecycle.py +18 -0
  327. free_claude_code/providers/github_copilot/login.py +112 -0
  328. free_claude_code/providers/github_copilot/native_cli.py +82 -0
  329. free_claude_code/providers/github_copilot/provider.py +367 -0
  330. free_claude_code/providers/github_copilot/request_policy.py +119 -0
  331. free_claude_code/providers/github_copilot/responses_events.py +90 -0
  332. free_claude_code/providers/github_copilot/sdk.py +496 -0
  333. free_claude_code/providers/github_copilot/types.py +67 -0
  334. free_claude_code/providers/google_openai/__init__.py +17 -0
  335. free_claude_code/providers/google_openai/provider.py +73 -0
  336. free_claude_code/providers/google_openai/reasoning.py +150 -0
  337. free_claude_code/providers/google_openai/thought_signatures.py +117 -0
  338. free_claude_code/providers/groq/__init__.py +5 -0
  339. free_claude_code/providers/groq/client.py +342 -0
  340. free_claude_code/providers/groq/tpm.py +114 -0
  341. free_claude_code/providers/history_replay.py +260 -0
  342. free_claude_code/providers/http.py +105 -0
  343. free_claude_code/providers/kilo/__init__.py +5 -0
  344. free_claude_code/providers/kilo/client.py +45 -0
  345. free_claude_code/providers/kilo/models.py +60 -0
  346. free_claude_code/providers/lmstudio/__init__.py +5 -0
  347. free_claude_code/providers/lmstudio/client.py +220 -0
  348. free_claude_code/providers/mistral/__init__.py +5 -0
  349. free_claude_code/providers/mistral/client.py +91 -0
  350. free_claude_code/providers/mistral/reasoning.py +410 -0
  351. free_claude_code/providers/model_listing.py +479 -0
  352. free_claude_code/providers/nvidia_nim/__init__.py +5 -0
  353. free_claude_code/providers/nvidia_nim/client.py +224 -0
  354. free_claude_code/providers/nvidia_nim/native_tool_stream.py +693 -0
  355. free_claude_code/providers/nvidia_nim/request_options.py +141 -0
  356. free_claude_code/providers/nvidia_nim/retry.py +85 -0
  357. free_claude_code/providers/nvidia_nim/tool_schema.py +255 -0
  358. free_claude_code/providers/nvidia_nim/voice.py +107 -0
  359. free_claude_code/providers/open_router/__init__.py +5 -0
  360. free_claude_code/providers/open_router/client.py +113 -0
  361. free_claude_code/providers/openai_api/__init__.py +5 -0
  362. free_claude_code/providers/openai_api/provider.py +150 -0
  363. free_claude_code/providers/openai_chat/__init__.py +72 -0
  364. free_claude_code/providers/openai_chat/base_url.py +7 -0
  365. free_claude_code/providers/openai_chat/behavior.py +133 -0
  366. free_claude_code/providers/openai_chat/client.py +40 -0
  367. free_claude_code/providers/openai_chat/extra_body.py +63 -0
  368. free_claude_code/providers/openai_chat/output_cap.py +161 -0
  369. free_claude_code/providers/openai_chat/profiles.py +778 -0
  370. free_claude_code/providers/openai_chat/provider.py +244 -0
  371. free_claude_code/providers/openai_chat/reasoning.py +169 -0
  372. free_claude_code/providers/openai_chat/reasoning_details.py +128 -0
  373. free_claude_code/providers/openai_chat/request_policy.py +140 -0
  374. free_claude_code/providers/openai_chat/stream_output.py +752 -0
  375. free_claude_code/providers/openai_chat/tool_calls.py +462 -0
  376. free_claude_code/providers/openai_chat/transport.py +1643 -0
  377. free_claude_code/providers/openai_chat/usage.py +117 -0
  378. free_claude_code/providers/openai_client.py +74 -0
  379. free_claude_code/providers/openai_codex/__init__.py +1 -0
  380. free_claude_code/providers/openai_codex/auth.py +626 -0
  381. free_claude_code/providers/openai_codex/endpoint.py +53 -0
  382. free_claude_code/providers/openai_codex/login.py +265 -0
  383. free_claude_code/providers/openai_codex/provider.py +259 -0
  384. free_claude_code/providers/openai_responses/__init__.py +5 -0
  385. free_claude_code/providers/openai_responses/presentation.py +115 -0
  386. free_claude_code/providers/openai_responses/transport.py +621 -0
  387. free_claude_code/providers/openai_stream.py +21 -0
  388. free_claude_code/providers/opencode/__init__.py +5 -0
  389. free_claude_code/providers/opencode/catalog.py +340 -0
  390. free_claude_code/providers/opencode/provider.py +359 -0
  391. free_claude_code/providers/reasoning_compatibility.py +199 -0
  392. free_claude_code/providers/request_recovery.py +122 -0
  393. free_claude_code/providers/runtime/__init__.py +10 -0
  394. free_claude_code/providers/runtime/config.py +91 -0
  395. free_claude_code/providers/runtime/discovery.py +63 -0
  396. free_claude_code/providers/runtime/factory.py +282 -0
  397. free_claude_code/providers/runtime/model_cache.py +93 -0
  398. free_claude_code/providers/runtime/runtime.py +108 -0
  399. free_claude_code/providers/stream_recovery.py +164 -0
  400. free_claude_code/providers/vertex/__init__.py +5 -0
  401. free_claude_code/providers/vertex/auth.py +105 -0
  402. free_claude_code/providers/vertex/client.py +140 -0
  403. free_claude_code/providers/vertex/endpoint.py +50 -0
  404. free_claude_code/providers/vertex/models.py +52 -0
  405. free_claude_code/runtime/__init__.py +1 -0
  406. free_claude_code/runtime/application.py +1121 -0
  407. free_claude_code/runtime/asgi.py +63 -0
  408. free_claude_code/runtime/bootstrap.py +153 -0
  409. free_claude_code/runtime/code_sessions_sqlite.py +785 -0
  410. free_claude_code/runtime/codex_app_server.py +775 -0
  411. free_claude_code/runtime/codex_catalog.py +62 -0
  412. free_claude_code/runtime/codex_protocol.py +662 -0
  413. free_claude_code/runtime/configuration.py +53 -0
  414. free_claude_code/runtime/folder_picker.py +134 -0
  415. free_claude_code/runtime/native_folder_dialog.py +123 -0
  416. free_claude_code/runtime/provider_manager.py +716 -0
  417. free_claude_code/runtime/retired_chat.py +47 -0
  418. free_claude_code/runtime/web_tools/__init__.py +1 -0
  419. free_claude_code/runtime/web_tools/client.py +238 -0
  420. free_claude_code/runtime/web_tools/constants.py +17 -0
  421. free_claude_code/runtime/web_tools/egress.py +89 -0
  422. free_claude_code/runtime/web_tools/parsers.py +78 -0
  423. free_claude_code-6.2.64.data/scripts/fcc-update +4 -0
  424. free_claude_code-6.2.64.data/scripts/fcc-update.cmd +5 -0
  425. free_claude_code-6.2.64.dist-info/METADATA +555 -0
  426. free_claude_code-6.2.64.dist-info/RECORD +429 -0
  427. free_claude_code-6.2.64.dist-info/WHEEL +4 -0
  428. free_claude_code-6.2.64.dist-info/entry_points.txt +15 -0
  429. free_claude_code-6.2.64.dist-info/licenses/LICENSE +239 -0
@@ -0,0 +1,197 @@
1
+ """Loguru-based structured logging configuration.
2
+
3
+ Structured logs are written as JSON lines to a configurable path (default
4
+ ``logs/server.log``). Stdlib logging is intercepted and funneled to loguru.
5
+ Context vars (request_id, node_id, chat_id) from contextualize() are
6
+ included at top level for easy grep/filter.
7
+ """
8
+
9
+ import json
10
+ import logging
11
+ import re
12
+ import threading
13
+ from pathlib import Path
14
+
15
+ from loguru import logger
16
+
17
+ _configured = False
18
+ _current_path: Path | None = None
19
+ _current_level = "INFO"
20
+ _current_verbose: bool | None = None
21
+ _sink_id: int | None = None
22
+
23
+ _THIRD_PARTY_LOGGERS = (
24
+ "openai",
25
+ "httpx",
26
+ "httpcore",
27
+ "httpcore.http11",
28
+ "httpcore.connection",
29
+ "telegram",
30
+ "telegram.ext",
31
+ )
32
+
33
+ # Loguru ``logger.bind()`` key used by structured TRACE payloads; ``core/trace.py``
34
+ # uses the identical string constant ``TRACE_PAYLOAD_BINDING``.
35
+ _TRACE_PAYLOAD_BINDING = "trace_payload"
36
+
37
+ # Context keys we promote to top-level JSON for traceability / grep
38
+ _CONTEXT_KEYS = (
39
+ "request_id",
40
+ "node_id",
41
+ "chat_id",
42
+ "claude_session_id",
43
+ "http_method",
44
+ "http_path",
45
+ )
46
+
47
+ _TELEGRAM_BOT_RE = re.compile(
48
+ r"(https?://api\.telegram\.org/)bot([0-9]+:[A-Za-z0-9_-]+)(/?)",
49
+ re.IGNORECASE,
50
+ )
51
+ # Authorization: Bearer <token> (HTTP client / proxy debug lines)
52
+ _AUTH_BEARER_RE = re.compile(
53
+ r"(\bAuthorization\s*:\s*Bearer\s+)([^\s'\"]+)",
54
+ re.IGNORECASE,
55
+ )
56
+
57
+
58
+ def _redact_sensitive_substrings(message: str) -> str:
59
+ """Remove obvious API tokens and secrets before JSON log line emission."""
60
+ text = _TELEGRAM_BOT_RE.sub(r"\1bot<redacted>\3", message)
61
+ return _AUTH_BEARER_RE.sub(r"\1<redacted>", text)
62
+
63
+
64
+ def _serialize_with_context(record) -> str:
65
+ """Format record as JSON with context vars at top level.
66
+ Returns a format template; we inject _json into record for output.
67
+ """
68
+ extra = record.get("extra", {})
69
+ out = {
70
+ "time": str(record["time"]),
71
+ "level": record["level"].name,
72
+ "message": _redact_sensitive_substrings(str(record["message"])),
73
+ "module": record["name"],
74
+ "function": record["function"],
75
+ "line": record["line"],
76
+ }
77
+ trace_payload = extra.get(_TRACE_PAYLOAD_BINDING)
78
+ for key in _CONTEXT_KEYS:
79
+ if key in extra and extra[key] is not None:
80
+ out[key] = extra[key]
81
+ if isinstance(trace_payload, dict):
82
+ for tk, tv in trace_payload.items():
83
+ if tk in out:
84
+ continue
85
+ out[tk] = tv
86
+ out["trace"] = True
87
+ record["_json"] = json.dumps(out, default=str)
88
+ return "{_json}\n"
89
+
90
+
91
+ class InterceptHandler(logging.Handler):
92
+ """Redirect stdlib logging to loguru."""
93
+
94
+ def __init__(self) -> None:
95
+ super().__init__()
96
+ self._local = threading.local()
97
+
98
+ def emit(self, record: logging.LogRecord) -> None:
99
+ if getattr(self._local, "active", False):
100
+ # Avoid deadlock when nested stdlib records fire during a loguru emit.
101
+ return
102
+ self._local.active = True
103
+ try:
104
+ try:
105
+ level = logger.level(record.levelname).name
106
+ except ValueError:
107
+ level = record.levelno
108
+
109
+ frame, depth = logging.currentframe(), 2
110
+ while frame is not None and frame.f_code.co_filename == logging.__file__:
111
+ frame = frame.f_back
112
+ depth += 1
113
+
114
+ logger.opt(depth=depth, exception=record.exc_info).log(
115
+ level, record.getMessage()
116
+ )
117
+ finally:
118
+ self._local.active = False
119
+
120
+
121
+ def _set_third_party_levels(verbose: bool) -> None:
122
+ level = logging.NOTSET if verbose else logging.WARNING
123
+ for name in _THIRD_PARTY_LOGGERS:
124
+ logging.getLogger(name).setLevel(level)
125
+
126
+
127
+ def _add_file_sink(log_file: str | Path, level: str) -> int:
128
+ log_path = Path(log_file)
129
+ return logger.add(
130
+ log_path,
131
+ level=level,
132
+ format=_serialize_with_context,
133
+ encoding="utf-8",
134
+ mode="a",
135
+ rotation="50 MB",
136
+ retention=5,
137
+ enqueue=True,
138
+ )
139
+
140
+
141
+ def configure_logging(
142
+ log_file: str | Path,
143
+ *,
144
+ force: bool = False,
145
+ verbose_third_party: bool = False,
146
+ level: str = "INFO",
147
+ ) -> None:
148
+ """Configure loguru with JSON output to log_file and intercept stdlib logging.
149
+
150
+ Idempotent: skips if already configured with the same path, level, and verbosity.
151
+ On path or level change, replaces only the file sink without truncating.
152
+ On verbosity change alone, updates only the third-party logger levels.
153
+ Use force=True to reconfigure from scratch.
154
+
155
+ When ``verbose_third_party`` is false, managed noisy third-party loggers
156
+ are capped at WARNING unless explicitly configured otherwise.
157
+ """
158
+ global _configured, _current_path, _current_level, _current_verbose, _sink_id
159
+
160
+ log_path = Path(log_file).expanduser().resolve()
161
+ log_path.parent.mkdir(parents=True, exist_ok=True)
162
+
163
+ if (
164
+ _configured
165
+ and not force
166
+ and log_path == _current_path
167
+ and level == _current_level
168
+ and verbose_third_party == _current_verbose
169
+ ):
170
+ return
171
+
172
+ if not _configured or force:
173
+ _configured = True
174
+
175
+ logger.remove()
176
+
177
+ log_path.write_text("")
178
+
179
+ _sink_id = _add_file_sink(log_path, level)
180
+
181
+ intercept = InterceptHandler()
182
+ logging.root.handlers = [intercept]
183
+ logging.root.setLevel(logging.DEBUG)
184
+
185
+ _set_third_party_levels(verbose_third_party)
186
+ elif log_path != _current_path or level != _current_level:
187
+ if _sink_id is not None:
188
+ logger.remove(_sink_id)
189
+ _sink_id = _add_file_sink(log_path, level)
190
+ if verbose_third_party != _current_verbose:
191
+ _set_third_party_levels(verbose_third_party)
192
+ else:
193
+ _set_third_party_levels(verbose_third_party)
194
+
195
+ _current_path = log_path
196
+ _current_level = level
197
+ _current_verbose = verbose_third_party
@@ -0,0 +1,118 @@
1
+ """Provider-prefixed model reference helpers."""
2
+
3
+ from collections.abc import Mapping
4
+ from dataclasses import dataclass
5
+ from typing import Protocol
6
+
7
+ from .constants import DEFAULT_MODEL
8
+
9
+ RETIRED_PROVIDER_IDS = frozenset({"github_models"})
10
+
11
+
12
+ def is_retired_model_ref(model_ref: str) -> bool:
13
+ """Recognize complete references owned by a retired provider."""
14
+
15
+ provider, separator, model = model_ref.strip().partition("/")
16
+ return provider in RETIRED_PROVIDER_IDS and bool(separator and model.strip())
17
+
18
+
19
+ def parse_model_fallbacks(value: object) -> object:
20
+ if value is None:
21
+ return None
22
+ if isinstance(value, str):
23
+ if not value.strip():
24
+ return None
25
+ return tuple(part.strip() for part in value.split(","))
26
+ if isinstance(value, list | tuple) and not value:
27
+ return None
28
+ return value
29
+
30
+
31
+ def normalize_retired_model_settings(
32
+ values: Mapping[str, str], *, preserve_empty_overrides: bool
33
+ ) -> dict[str, str]:
34
+ """Repair one source without changing its precedence or unrelated validation."""
35
+
36
+ normalized = dict(values)
37
+ for key in ("MODEL", "MODEL_FABLE", "MODEL_OPUS", "MODEL_SONNET", "MODEL_HAIKU"):
38
+ if is_retired_model_ref(normalized.get(key, "")):
39
+ if preserve_empty_overrides:
40
+ normalized[key] = DEFAULT_MODEL if key == "MODEL" else ""
41
+ else:
42
+ normalized.pop(key)
43
+ fallbacks = parse_model_fallbacks(normalized.get("MODEL_FALLBACKS"))
44
+ if isinstance(fallbacks, tuple) and all(fallbacks):
45
+ retained = tuple(ref for ref in fallbacks if not is_retired_model_ref(ref))
46
+ if retained != fallbacks:
47
+ if retained or preserve_empty_overrides:
48
+ normalized["MODEL_FALLBACKS"] = ",".join(retained)
49
+ else:
50
+ normalized.pop("MODEL_FALLBACKS")
51
+ return normalized
52
+
53
+
54
+ @dataclass(frozen=True, slots=True)
55
+ class ConfiguredChatModelRef:
56
+ """A unique configured chat model reference."""
57
+
58
+ model_ref: str
59
+ provider_id: str
60
+ model_id: str
61
+
62
+
63
+ class ChatModelConfig(Protocol):
64
+ model: str
65
+ model_fable: str | None
66
+ model_opus: str | None
67
+ model_sonnet: str | None
68
+ model_haiku: str | None
69
+ model_fallbacks: tuple[str, ...] | None
70
+
71
+
72
+ def split_provider_model_ref(model_ref: str) -> tuple[str, str]:
73
+ """Split one complete ``provider/model`` reference."""
74
+
75
+ provider_id, separator, model_id = model_ref.partition("/")
76
+ if not separator or not provider_id or not model_id:
77
+ raise ValueError("Model reference must contain provider and model names.")
78
+ return provider_id, model_id
79
+
80
+
81
+ def parse_provider_type(model_ref: str) -> str:
82
+ """Extract provider type from any 'provider/model' string."""
83
+
84
+ return split_provider_model_ref(model_ref)[0]
85
+
86
+
87
+ def parse_model_name(model_ref: str) -> str:
88
+ """Extract model name from any 'provider/model' string."""
89
+
90
+ return split_provider_model_ref(model_ref)[1]
91
+
92
+
93
+ def configured_chat_model_refs(
94
+ settings: ChatModelConfig,
95
+ ) -> tuple[ConfiguredChatModelRef, ...]:
96
+ """Return unique configured chat provider/model refs."""
97
+
98
+ model_refs = dict.fromkeys(
99
+ model_ref
100
+ for model_ref in (
101
+ settings.model,
102
+ settings.model_fable,
103
+ settings.model_opus,
104
+ settings.model_sonnet,
105
+ settings.model_haiku,
106
+ *(settings.model_fallbacks or ()),
107
+ )
108
+ if model_ref is not None
109
+ )
110
+
111
+ return tuple(
112
+ ConfiguredChatModelRef(
113
+ model_ref=model_ref,
114
+ provider_id=parse_provider_type(model_ref),
115
+ model_id=parse_model_name(model_ref),
116
+ )
117
+ for model_ref in model_refs
118
+ )
@@ -0,0 +1,121 @@
1
+ """NVIDIA NIM settings (fixed values, no env config)."""
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator
4
+
5
+ from free_claude_code.config.constants import ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS
6
+
7
+
8
+ class NimSettings(BaseModel):
9
+ """Fixed NVIDIA NIM settings (not configurable via env)."""
10
+
11
+ temperature: float = Field(
12
+ 1.0, ge=0.0, le=2.0, description="Sampling temperature, must be >=0 and <=2."
13
+ )
14
+ top_p: float = Field(
15
+ 0.95,
16
+ ge=0.95,
17
+ le=0.95,
18
+ description="FCC's fixed NVIDIA NIM nucleus sampling probability.",
19
+ )
20
+ top_k: int = -1
21
+ max_tokens: int = Field(
22
+ ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
23
+ ge=1,
24
+ description="Maximum number of tokens in output.",
25
+ )
26
+ presence_penalty: float = Field(0.0, ge=-2.0, le=2.0)
27
+ frequency_penalty: float = Field(0.0, ge=-2.0, le=2.0)
28
+ min_p: float = Field(
29
+ 0.0, ge=0.0, le=1.0, description="Minimum probability threshold [0,1]."
30
+ )
31
+ repetition_penalty: float = Field(
32
+ 1.0, ge=0.0, description="Penalty for repeated tokens. Must be >=0."
33
+ )
34
+ seed: int | None = None
35
+ stop: str | None = None
36
+ parallel_tool_calls: bool = True
37
+ ignore_eos: bool = False
38
+ min_tokens: int = Field(0, ge=0, description="Minimum tokens in the response.")
39
+ chat_template: str | None = None
40
+ request_id: str | None = None
41
+
42
+ model_config = ConfigDict(extra="forbid")
43
+
44
+ @field_validator("top_k", mode="before")
45
+ @classmethod
46
+ def validate_top_k(cls, v, info: ValidationInfo):
47
+ if v is None or v == "":
48
+ return -1
49
+ int_v = int(v)
50
+ if int_v < -1:
51
+ raise ValueError(f"{info.field_name} must be -1 or >= 0")
52
+ return int_v
53
+
54
+ @field_validator(
55
+ "temperature",
56
+ "top_p",
57
+ "min_p",
58
+ "presence_penalty",
59
+ "frequency_penalty",
60
+ "repetition_penalty",
61
+ mode="before",
62
+ )
63
+ @classmethod
64
+ def validate_float_fields(cls, v, info: ValidationInfo):
65
+ field_defaults = {
66
+ "temperature": 1.0,
67
+ "top_p": 0.95,
68
+ "min_p": 0.0,
69
+ "presence_penalty": 0.0,
70
+ "frequency_penalty": 0.0,
71
+ "repetition_penalty": 1.0,
72
+ }
73
+ if v is None or v == "":
74
+ key = info.field_name or "temperature"
75
+ return field_defaults.get(key, 1.0)
76
+ try:
77
+ val = float(v)
78
+ except (TypeError, ValueError) as err:
79
+ raise ValueError(
80
+ f"{info.field_name} must be a float. Got {type(v).__name__}."
81
+ ) from err
82
+ return val
83
+
84
+ @field_validator("max_tokens", "min_tokens", mode="before")
85
+ @classmethod
86
+ def validate_int_fields(cls, v, info: ValidationInfo):
87
+ field_defaults = {
88
+ "max_tokens": ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
89
+ "min_tokens": 0,
90
+ }
91
+ if v is None or v == "":
92
+ key = info.field_name or "max_tokens"
93
+ return field_defaults.get(key, ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS)
94
+ try:
95
+ val = int(v)
96
+ except (TypeError, ValueError) as err:
97
+ raise ValueError(
98
+ f"{info.field_name} must be an int. Got {type(v).__name__}."
99
+ ) from err
100
+ return val
101
+
102
+ @field_validator("seed", mode="before")
103
+ @classmethod
104
+ def parse_optional_int(cls, v, info: ValidationInfo):
105
+ if v == "" or v is None:
106
+ return None
107
+ try:
108
+ return int(v)
109
+ except (TypeError, ValueError) as err:
110
+ raise ValueError(
111
+ f"{info.field_name} must be an int or empty/None."
112
+ ) from err
113
+
114
+ @field_validator("stop", "chat_template", "request_id", mode="before")
115
+ @classmethod
116
+ def parse_optional_str(cls, v, info: ValidationInfo):
117
+ if v == "":
118
+ return None
119
+ if v is not None and not isinstance(v, str):
120
+ return str(v)
121
+ return v
@@ -0,0 +1,104 @@
1
+ """Shared filesystem paths for Free Claude Code configuration."""
2
+
3
+ from pathlib import Path
4
+
5
+ FCC_CONFIG_DIRNAME = ".fcc"
6
+ FCC_ENV_FILENAME = ".env"
7
+ LEGACY_REPO_DIRNAME = "free-claude-code"
8
+ LEGACY_XDG_CONFIG_DIRNAME = ".config"
9
+ MESSAGING_STATE_DIRNAME = "agent_workspace"
10
+ FCC_LOGS_DIRNAME = "logs"
11
+ SERVER_LOG_FILENAME = "server.log"
12
+ CODEX_MODEL_CATALOG_FILENAME = "codex-model-catalog.json"
13
+ AUTH_DIRNAME = "auth"
14
+ OPENAI_AUTH_FILENAME = "openai.json"
15
+ OPENAI_AUTH_LOCK_FILENAME = "openai.lock"
16
+ CONFIG_LOCK_FILENAME = "config.lock"
17
+ FCC_TEMP_DIRNAME = "tmp"
18
+ LAUNCHER_TEMP_DIRNAME = "launchers"
19
+ CODE_STATE_DIRNAME = "code"
20
+ CODE_DATABASE_FILENAME = "code.db"
21
+ CODE_LOCK_FILENAME = "code.lock"
22
+
23
+
24
+ def config_dir_path() -> Path:
25
+ """Return the default user config directory."""
26
+
27
+ return Path.home() / FCC_CONFIG_DIRNAME
28
+
29
+
30
+ def managed_env_path() -> Path:
31
+ """Return the default user-managed env file path."""
32
+
33
+ return config_dir_path() / FCC_ENV_FILENAME
34
+
35
+
36
+ def config_lock_path() -> Path:
37
+ """Return the cross-process managed-config migration lock path."""
38
+
39
+ return config_dir_path() / CONFIG_LOCK_FILENAME
40
+
41
+
42
+ def launcher_temp_dir_path() -> Path:
43
+ """Return the base directory for private native launcher configuration."""
44
+
45
+ return config_dir_path() / FCC_TEMP_DIRNAME / LAUNCHER_TEMP_DIRNAME
46
+
47
+
48
+ def code_database_path() -> Path:
49
+ """Return the managed Code sessions database path."""
50
+ return config_dir_path() / CODE_STATE_DIRNAME / CODE_DATABASE_FILENAME
51
+
52
+
53
+ def code_lock_path() -> Path:
54
+ """Return the exclusive Code sessions process-lock path."""
55
+ return config_dir_path() / CODE_STATE_DIRNAME / CODE_LOCK_FILENAME
56
+
57
+
58
+ def legacy_env_paths() -> tuple[Path, ...]:
59
+ """Return legacy user env paths that can be migrated to ~/.fcc/.env."""
60
+
61
+ home = Path.home()
62
+ return (
63
+ home / LEGACY_REPO_DIRNAME / FCC_ENV_FILENAME,
64
+ home / LEGACY_XDG_CONFIG_DIRNAME / LEGACY_REPO_DIRNAME / FCC_ENV_FILENAME,
65
+ )
66
+
67
+
68
+ def messaging_state_dir_path() -> Path:
69
+ """Return the managed messaging state directory."""
70
+
71
+ return config_dir_path() / MESSAGING_STATE_DIRNAME
72
+
73
+
74
+ def server_log_path() -> Path:
75
+ """Return the canonical server log path."""
76
+
77
+ return config_dir_path() / FCC_LOGS_DIRNAME / SERVER_LOG_FILENAME
78
+
79
+
80
+ def codex_model_catalog_path() -> Path:
81
+ """Return the generated Codex model catalog path."""
82
+
83
+ return config_dir_path() / CODEX_MODEL_CATALOG_FILENAME
84
+
85
+
86
+ def claude_desktop_disconnect_path() -> Path:
87
+ return config_dir_path() / "claude-desktop-disconnect.json"
88
+
89
+
90
+ def openai_auth_path() -> Path:
91
+ """Return FCC's private ChatGPT credential file path."""
92
+
93
+ return config_dir_path() / AUTH_DIRNAME / OPENAI_AUTH_FILENAME
94
+
95
+
96
+ def openai_auth_lock_path() -> Path:
97
+ """Return the cross-process lock path for ChatGPT credentials."""
98
+
99
+ return config_dir_path() / AUTH_DIRNAME / OPENAI_AUTH_LOCK_FILENAME
100
+
101
+
102
+ def github_copilot_auth_path() -> Path:
103
+ """Return FCC connection state; native Copilot retains credentials."""
104
+ return config_dir_path() / AUTH_DIRNAME / "github_copilot.json"