code-puppy-core-plugins 0.0.2__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 (313) hide show
  1. code_puppy_core_plugins/__init__.py +5 -0
  2. code_puppy_core_plugins/acp/README.md +266 -0
  3. code_puppy_core_plugins/acp/__init__.py +8 -0
  4. code_puppy_core_plugins/acp/agent.py +494 -0
  5. code_puppy_core_plugins/acp/bridge.py +309 -0
  6. code_puppy_core_plugins/acp/capabilities.py +73 -0
  7. code_puppy_core_plugins/acp/commands.py +116 -0
  8. code_puppy_core_plugins/acp/content.py +115 -0
  9. code_puppy_core_plugins/acp/io_delegation.py +231 -0
  10. code_puppy_core_plugins/acp/mcp_config.py +66 -0
  11. code_puppy_core_plugins/acp/permissions.py +159 -0
  12. code_puppy_core_plugins/acp/persistence.py +197 -0
  13. code_puppy_core_plugins/acp/register_callbacks.py +110 -0
  14. code_puppy_core_plugins/acp/replay.py +114 -0
  15. code_puppy_core_plugins/acp/session.py +329 -0
  16. code_puppy_core_plugins/acp/session_config.py +179 -0
  17. code_puppy_core_plugins/acp/state.py +135 -0
  18. code_puppy_core_plugins/agent_skills/__init__.py +22 -0
  19. code_puppy_core_plugins/agent_skills/config.py +208 -0
  20. code_puppy_core_plugins/agent_skills/discovery.py +337 -0
  21. code_puppy_core_plugins/agent_skills/downloader.py +392 -0
  22. code_puppy_core_plugins/agent_skills/enabled_skills.py +82 -0
  23. code_puppy_core_plugins/agent_skills/installer.py +22 -0
  24. code_puppy_core_plugins/agent_skills/metadata.py +219 -0
  25. code_puppy_core_plugins/agent_skills/prompt_builder.py +49 -0
  26. code_puppy_core_plugins/agent_skills/provider.py +52 -0
  27. code_puppy_core_plugins/agent_skills/register_callbacks.py +309 -0
  28. code_puppy_core_plugins/agent_skills/remote_catalog.py +322 -0
  29. code_puppy_core_plugins/agent_skills/skill_catalog.py +257 -0
  30. code_puppy_core_plugins/agent_skills/skill_commands.py +102 -0
  31. code_puppy_core_plugins/agent_skills/skills_install_menu.py +698 -0
  32. code_puppy_core_plugins/agent_skills/skills_menu.py +794 -0
  33. code_puppy_core_plugins/aws_bedrock/__init__.py +14 -0
  34. code_puppy_core_plugins/aws_bedrock/config.py +101 -0
  35. code_puppy_core_plugins/aws_bedrock/register_callbacks.py +243 -0
  36. code_puppy_core_plugins/aws_bedrock/utils.py +186 -0
  37. code_puppy_core_plugins/azure_foundry/README.md +238 -0
  38. code_puppy_core_plugins/azure_foundry/__init__.py +15 -0
  39. code_puppy_core_plugins/azure_foundry/config.py +126 -0
  40. code_puppy_core_plugins/azure_foundry/discovery.py +189 -0
  41. code_puppy_core_plugins/azure_foundry/register_callbacks.py +506 -0
  42. code_puppy_core_plugins/azure_foundry/token.py +182 -0
  43. code_puppy_core_plugins/azure_foundry/utils.py +375 -0
  44. code_puppy_core_plugins/btw/__init__.py +1 -0
  45. code_puppy_core_plugins/btw/inline_view.py +135 -0
  46. code_puppy_core_plugins/btw/register_callbacks.py +106 -0
  47. code_puppy_core_plugins/btw/side_query.py +91 -0
  48. code_puppy_core_plugins/chatgpt_oauth/IMAGEGEN_SKILL.md +83 -0
  49. code_puppy_core_plugins/chatgpt_oauth/__init__.py +8 -0
  50. code_puppy_core_plugins/chatgpt_oauth/config.py +52 -0
  51. code_puppy_core_plugins/chatgpt_oauth/image_generation.py +253 -0
  52. code_puppy_core_plugins/chatgpt_oauth/image_tool.py +70 -0
  53. code_puppy_core_plugins/chatgpt_oauth/oauth_flow.py +461 -0
  54. code_puppy_core_plugins/chatgpt_oauth/register_callbacks.py +288 -0
  55. code_puppy_core_plugins/chatgpt_oauth/test_plugin.py +663 -0
  56. code_puppy_core_plugins/chatgpt_oauth/usage.py +113 -0
  57. code_puppy_core_plugins/chatgpt_oauth/utils.py +534 -0
  58. code_puppy_core_plugins/claude_code_hooks/__init__.py +1 -0
  59. code_puppy_core_plugins/claude_code_hooks/config.py +137 -0
  60. code_puppy_core_plugins/claude_code_hooks/register_callbacks.py +383 -0
  61. code_puppy_core_plugins/claude_code_oauth/README.md +167 -0
  62. code_puppy_core_plugins/claude_code_oauth/SETUP.md +93 -0
  63. code_puppy_core_plugins/claude_code_oauth/__init__.py +25 -0
  64. code_puppy_core_plugins/claude_code_oauth/config.py +59 -0
  65. code_puppy_core_plugins/claude_code_oauth/fast_mode.py +122 -0
  66. code_puppy_core_plugins/claude_code_oauth/prompt_handler.py +64 -0
  67. code_puppy_core_plugins/claude_code_oauth/register_callbacks.py +719 -0
  68. code_puppy_core_plugins/claude_code_oauth/test_plugin.py +285 -0
  69. code_puppy_core_plugins/claude_code_oauth/token_refresh_heartbeat.py +240 -0
  70. code_puppy_core_plugins/claude_code_oauth/utils.py +663 -0
  71. code_puppy_core_plugins/code_puppy_agent/AGENTS_AND_TOOLS.md +155 -0
  72. code_puppy_core_plugins/code_puppy_agent/MODELS_AND_MCP.md +104 -0
  73. code_puppy_core_plugins/code_puppy_agent/PLUGINS_AND_CALLBACKS.md +147 -0
  74. code_puppy_core_plugins/code_puppy_agent/SESSIONS_AND_HISTORY.md +39 -0
  75. code_puppy_core_plugins/code_puppy_agent/SKILL.md +180 -0
  76. code_puppy_core_plugins/code_puppy_agent/SKILLS_SYSTEM.md +136 -0
  77. code_puppy_core_plugins/code_puppy_agent/SYSTEM_PROMPT_CONFIG_AND_I18N.md +160 -0
  78. code_puppy_core_plugins/code_puppy_agent/__init__.py +5 -0
  79. code_puppy_core_plugins/code_puppy_agent/register_callbacks.py +46 -0
  80. code_puppy_core_plugins/computer_use/NativeCapture.swift +123 -0
  81. code_puppy_core_plugins/computer_use/README.md +109 -0
  82. code_puppy_core_plugins/computer_use/__init__.py +1 -0
  83. code_puppy_core_plugins/computer_use/accessibility.py +163 -0
  84. code_puppy_core_plugins/computer_use/activation.py +75 -0
  85. code_puppy_core_plugins/computer_use/backend.py +584 -0
  86. code_puppy_core_plugins/computer_use/backend_types.py +5 -0
  87. code_puppy_core_plugins/computer_use/capture.py +110 -0
  88. code_puppy_core_plugins/computer_use/commands.py +62 -0
  89. code_puppy_core_plugins/computer_use/geometry.py +81 -0
  90. code_puppy_core_plugins/computer_use/inline_image.py +56 -0
  91. code_puppy_core_plugins/computer_use/keycodes.py +47 -0
  92. code_puppy_core_plugins/computer_use/policy.py +144 -0
  93. code_puppy_core_plugins/computer_use/register_callbacks.py +96 -0
  94. code_puppy_core_plugins/computer_use/safety.py +18 -0
  95. code_puppy_core_plugins/computer_use/settle.py +64 -0
  96. code_puppy_core_plugins/computer_use/state.py +86 -0
  97. code_puppy_core_plugins/computer_use/tools.py +469 -0
  98. code_puppy_core_plugins/context_indicator/__init__.py +14 -0
  99. code_puppy_core_plugins/context_indicator/register_callbacks.py +249 -0
  100. code_puppy_core_plugins/context_indicator/usage.py +40 -0
  101. code_puppy_core_plugins/copilot_auth/__init__.py +11 -0
  102. code_puppy_core_plugins/copilot_auth/config.py +93 -0
  103. code_puppy_core_plugins/copilot_auth/reasoning_client.py +409 -0
  104. code_puppy_core_plugins/copilot_auth/register_callbacks.py +460 -0
  105. code_puppy_core_plugins/copilot_auth/utils.py +587 -0
  106. code_puppy_core_plugins/customizable_commands/__init__.py +0 -0
  107. code_puppy_core_plugins/customizable_commands/register_callbacks.py +464 -0
  108. code_puppy_core_plugins/dbos_durable_exec/__init__.py +1 -0
  109. code_puppy_core_plugins/dbos_durable_exec/cancel.py +15 -0
  110. code_puppy_core_plugins/dbos_durable_exec/commands.py +31 -0
  111. code_puppy_core_plugins/dbos_durable_exec/config.py +31 -0
  112. code_puppy_core_plugins/dbos_durable_exec/lifecycle.py +83 -0
  113. code_puppy_core_plugins/dbos_durable_exec/register_callbacks.py +45 -0
  114. code_puppy_core_plugins/dbos_durable_exec/runtime.py +65 -0
  115. code_puppy_core_plugins/dbos_durable_exec/startup_lock.py +155 -0
  116. code_puppy_core_plugins/dbos_durable_exec/workflow_ids.py +16 -0
  117. code_puppy_core_plugins/dbos_durable_exec/wrapper.py +58 -0
  118. code_puppy_core_plugins/destructive_command_guard/AGENTS.md +79 -0
  119. code_puppy_core_plugins/destructive_command_guard/__init__.py +14 -0
  120. code_puppy_core_plugins/destructive_command_guard/detector.py +374 -0
  121. code_puppy_core_plugins/destructive_command_guard/register_callbacks.py +165 -0
  122. code_puppy_core_plugins/emoji_filter/__init__.py +12 -0
  123. code_puppy_core_plugins/emoji_filter/config.py +26 -0
  124. code_puppy_core_plugins/emoji_filter/register_callbacks.py +238 -0
  125. code_puppy_core_plugins/emoji_filter/stripper.py +45 -0
  126. code_puppy_core_plugins/empty_html_comment_filter/register_callbacks.py +116 -0
  127. code_puppy_core_plugins/example_custom_command/README.md +328 -0
  128. code_puppy_core_plugins/example_custom_command/register_callbacks.py +66 -0
  129. code_puppy_core_plugins/file_permission_handler/__init__.py +4 -0
  130. code_puppy_core_plugins/file_permission_handler/register_callbacks.py +575 -0
  131. code_puppy_core_plugins/flux_bootstrap/__init__.py +5 -0
  132. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/README.md +173 -0
  133. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/cheatsheet.md +86 -0
  134. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/pipeline.md +167 -0
  135. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/synopsis.md +392 -0
  136. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/about.md +51 -0
  137. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/address-feedback.md +129 -0
  138. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/ask.md +211 -0
  139. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/aug.md +181 -0
  140. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/auto-pilot.md +71 -0
  141. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/cheatsheet.md +16 -0
  142. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/commit.md +103 -0
  143. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/config.md +108 -0
  144. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/create-pr.md +195 -0
  145. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/exec.md +221 -0
  146. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/new.md +151 -0
  147. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/qa.md +208 -0
  148. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/rebase.md +180 -0
  149. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/review.md +304 -0
  150. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/split.md +120 -0
  151. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/squash-commits.md +179 -0
  152. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/status.md +46 -0
  153. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/tests.md +155 -0
  154. code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_about.py +154 -0
  155. code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_cheatsheet.py +248 -0
  156. code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_status.py +345 -0
  157. code_puppy_core_plugins/flux_bootstrap/installer.py +256 -0
  158. code_puppy_core_plugins/flux_bootstrap/register_callbacks.py +71 -0
  159. code_puppy_core_plugins/force_push_guard/__init__.py +5 -0
  160. code_puppy_core_plugins/force_push_guard/detector.py +96 -0
  161. code_puppy_core_plugins/force_push_guard/register_callbacks.py +161 -0
  162. code_puppy_core_plugins/fork/__init__.py +7 -0
  163. code_puppy_core_plugins/fork/register_callbacks.py +445 -0
  164. code_puppy_core_plugins/frontend_emitter/__init__.py +25 -0
  165. code_puppy_core_plugins/frontend_emitter/emitter.py +249 -0
  166. code_puppy_core_plugins/frontend_emitter/register_callbacks.py +412 -0
  167. code_puppy_core_plugins/frontend_emitter/session_context.py +50 -0
  168. code_puppy_core_plugins/grok_oauth/__init__.py +6 -0
  169. code_puppy_core_plugins/grok_oauth/config.py +49 -0
  170. code_puppy_core_plugins/grok_oauth/oauth_flow.py +213 -0
  171. code_puppy_core_plugins/grok_oauth/register_callbacks.py +132 -0
  172. code_puppy_core_plugins/grok_oauth/utils.py +195 -0
  173. code_puppy_core_plugins/herdr/README.md +168 -0
  174. code_puppy_core_plugins/herdr/__init__.py +9 -0
  175. code_puppy_core_plugins/herdr/client.py +339 -0
  176. code_puppy_core_plugins/herdr/register_callbacks.py +140 -0
  177. code_puppy_core_plugins/herdr/reporter.py +212 -0
  178. code_puppy_core_plugins/herdr/smoke.py +143 -0
  179. code_puppy_core_plugins/herdr/sources.py +133 -0
  180. code_puppy_core_plugins/hook_creator/__init__.py +1 -0
  181. code_puppy_core_plugins/hook_creator/register_callbacks.py +33 -0
  182. code_puppy_core_plugins/hook_manager/__init__.py +1 -0
  183. code_puppy_core_plugins/hook_manager/config.py +290 -0
  184. code_puppy_core_plugins/hook_manager/hooks_menu.py +571 -0
  185. code_puppy_core_plugins/hook_manager/register_callbacks.py +227 -0
  186. code_puppy_core_plugins/meta_oauth/__init__.py +1 -0
  187. code_puppy_core_plugins/meta_oauth/config.py +55 -0
  188. code_puppy_core_plugins/meta_oauth/oauth_flow.py +109 -0
  189. code_puppy_core_plugins/meta_oauth/register_callbacks.py +154 -0
  190. code_puppy_core_plugins/meta_oauth/utils.py +252 -0
  191. code_puppy_core_plugins/namespace_skill_search/README.md +118 -0
  192. code_puppy_core_plugins/namespace_skill_search/__init__.py +15 -0
  193. code_puppy_core_plugins/namespace_skill_search/namespaces.py +125 -0
  194. code_puppy_core_plugins/namespace_skill_search/register_callbacks.py +99 -0
  195. code_puppy_core_plugins/namespace_skill_search/search_tool.py +158 -0
  196. code_puppy_core_plugins/no_tools/__init__.py +0 -0
  197. code_puppy_core_plugins/no_tools/register_callbacks.py +42 -0
  198. code_puppy_core_plugins/oauth_pasteback.py +110 -0
  199. code_puppy_core_plugins/oauth_puppy_html.py +194 -0
  200. code_puppy_core_plugins/obsidian_agent/README.md +52 -0
  201. code_puppy_core_plugins/obsidian_agent/__init__.py +8 -0
  202. code_puppy_core_plugins/obsidian_agent/agent_obsidian.py +147 -0
  203. code_puppy_core_plugins/obsidian_agent/register_callbacks.py +13 -0
  204. code_puppy_core_plugins/ollama/__init__.py +1 -0
  205. code_puppy_core_plugins/ollama/register_callbacks.py +127 -0
  206. code_puppy_core_plugins/ollama_setup/__init__.py +5 -0
  207. code_puppy_core_plugins/ollama_setup/completer.py +38 -0
  208. code_puppy_core_plugins/ollama_setup/register_callbacks.py +419 -0
  209. code_puppy_core_plugins/plugin_list/__init__.py +0 -0
  210. code_puppy_core_plugins/plugin_list/plugin_contributions.py +348 -0
  211. code_puppy_core_plugins/plugin_list/plugin_meta.py +67 -0
  212. code_puppy_core_plugins/plugin_list/plugin_text_utils.py +173 -0
  213. code_puppy_core_plugins/plugin_list/plugins_menu.py +455 -0
  214. code_puppy_core_plugins/plugin_list/plugins_menu_layout.py +194 -0
  215. code_puppy_core_plugins/plugin_list/plugins_menu_render.py +431 -0
  216. code_puppy_core_plugins/plugin_list/project_trust_flow.py +162 -0
  217. code_puppy_core_plugins/plugin_list/register_callbacks.py +281 -0
  218. code_puppy_core_plugins/pop_command/__init__.py +1 -0
  219. code_puppy_core_plugins/pop_command/register_callbacks.py +191 -0
  220. code_puppy_core_plugins/prompt_newline/__init__.py +13 -0
  221. code_puppy_core_plugins/prompt_newline/config.py +21 -0
  222. code_puppy_core_plugins/prompt_newline/register_callbacks.py +163 -0
  223. code_puppy_core_plugins/prune/__init__.py +1 -0
  224. code_puppy_core_plugins/prune/prune_menu.py +308 -0
  225. code_puppy_core_plugins/prune/prune_model.py +517 -0
  226. code_puppy_core_plugins/prune/prune_render.py +354 -0
  227. code_puppy_core_plugins/prune/register_callbacks.py +388 -0
  228. code_puppy_core_plugins/puppy_kennel/README.md +203 -0
  229. code_puppy_core_plugins/puppy_kennel/__init__.py +6 -0
  230. code_puppy_core_plugins/puppy_kennel/commands.py +206 -0
  231. code_puppy_core_plugins/puppy_kennel/config.py +36 -0
  232. code_puppy_core_plugins/puppy_kennel/kennel.py +368 -0
  233. code_puppy_core_plugins/puppy_kennel/packer.py +152 -0
  234. code_puppy_core_plugins/puppy_kennel/recorder.py +83 -0
  235. code_puppy_core_plugins/puppy_kennel/register_callbacks.py +109 -0
  236. code_puppy_core_plugins/puppy_kennel/retriever.py +28 -0
  237. code_puppy_core_plugins/puppy_kennel/schema.py +78 -0
  238. code_puppy_core_plugins/puppy_kennel/state.py +68 -0
  239. code_puppy_core_plugins/puppy_kennel/tools.py +449 -0
  240. code_puppy_core_plugins/puppy_kennel/wings.py +110 -0
  241. code_puppy_core_plugins/puppy_spinner/README.md +93 -0
  242. code_puppy_core_plugins/puppy_spinner/__init__.py +11 -0
  243. code_puppy_core_plugins/puppy_spinner/builtin_frames.py +172 -0
  244. code_puppy_core_plugins/puppy_spinner/commands.py +116 -0
  245. code_puppy_core_plugins/puppy_spinner/picker.py +379 -0
  246. code_puppy_core_plugins/puppy_spinner/register_callbacks.py +193 -0
  247. code_puppy_core_plugins/puppy_spinner/spinners.py +444 -0
  248. code_puppy_core_plugins/quick_resume/__init__.py +0 -0
  249. code_puppy_core_plugins/quick_resume/register_callbacks.py +77 -0
  250. code_puppy_core_plugins/review_pr/__init__.py +1 -0
  251. code_puppy_core_plugins/review_pr/register_callbacks.py +162 -0
  252. code_puppy_core_plugins/shell_safety/__init__.py +6 -0
  253. code_puppy_core_plugins/shell_safety/agent_shell_safety.py +69 -0
  254. code_puppy_core_plugins/shell_safety/command_cache.py +156 -0
  255. code_puppy_core_plugins/shell_safety/register_callbacks.py +203 -0
  256. code_puppy_core_plugins/stack_dump/__init__.py +1 -0
  257. code_puppy_core_plugins/stack_dump/register_callbacks.py +79 -0
  258. code_puppy_core_plugins/statusline/__init__.py +14 -0
  259. code_puppy_core_plugins/statusline/config.py +68 -0
  260. code_puppy_core_plugins/statusline/payload.py +138 -0
  261. code_puppy_core_plugins/statusline/prompt_patch.py +76 -0
  262. code_puppy_core_plugins/statusline/register_callbacks.py +45 -0
  263. code_puppy_core_plugins/statusline/runner.py +122 -0
  264. code_puppy_core_plugins/statusline/statusline_command.py +197 -0
  265. code_puppy_core_plugins/steer_queue/__init__.py +10 -0
  266. code_puppy_core_plugins/steer_queue/queue_menu.py +444 -0
  267. code_puppy_core_plugins/steer_queue/register_callbacks.py +138 -0
  268. code_puppy_core_plugins/subagent_panel/README.md +102 -0
  269. code_puppy_core_plugins/subagent_panel/__init__.py +24 -0
  270. code_puppy_core_plugins/subagent_panel/coalesce_patch.py +242 -0
  271. code_puppy_core_plugins/subagent_panel/panel_render.py +145 -0
  272. code_puppy_core_plugins/subagent_panel/register_callbacks.py +535 -0
  273. code_puppy_core_plugins/subagent_panel/resume_repaint.py +112 -0
  274. code_puppy_core_plugins/subagent_panel/state.py +231 -0
  275. code_puppy_core_plugins/switch_agent_resume/__init__.py +5 -0
  276. code_puppy_core_plugins/switch_agent_resume/register_callbacks.py +314 -0
  277. code_puppy_core_plugins/theme/README.md +86 -0
  278. code_puppy_core_plugins/theme/__init__.py +14 -0
  279. code_puppy_core_plugins/theme/bundled_palettes.py +346 -0
  280. code_puppy_core_plugins/theme/content_styles.py +146 -0
  281. code_puppy_core_plugins/theme/osc_palette.py +178 -0
  282. code_puppy_core_plugins/theme/picker.py +358 -0
  283. code_puppy_core_plugins/theme/prompt_toolkit_theme.py +113 -0
  284. code_puppy_core_plugins/theme/register_callbacks.py +348 -0
  285. code_puppy_core_plugins/theme/rich_themes.py +202 -0
  286. code_puppy_core_plugins/theme/themes.py +720 -0
  287. code_puppy_core_plugins/timestamp_heartbeat/__init__.py +1 -0
  288. code_puppy_core_plugins/timestamp_heartbeat/register_callbacks.py +114 -0
  289. code_puppy_core_plugins/token_ratio_learner/__init__.py +5 -0
  290. code_puppy_core_plugins/token_ratio_learner/ratios.py +208 -0
  291. code_puppy_core_plugins/token_ratio_learner/register_callbacks.py +213 -0
  292. code_puppy_core_plugins/universal_constructor/__init__.py +13 -0
  293. code_puppy_core_plugins/universal_constructor/models.py +138 -0
  294. code_puppy_core_plugins/universal_constructor/provider.py +62 -0
  295. code_puppy_core_plugins/universal_constructor/register_callbacks.py +75 -0
  296. code_puppy_core_plugins/universal_constructor/registry.py +302 -0
  297. code_puppy_core_plugins/universal_constructor/sandbox.py +584 -0
  298. code_puppy_core_plugins/wide_completion_menu/__init__.py +6 -0
  299. code_puppy_core_plugins/wide_completion_menu/register_callbacks.py +165 -0
  300. code_puppy_core_plugins/wiggum/__init__.py +1 -0
  301. code_puppy_core_plugins/wiggum/goal_runs.py +91 -0
  302. code_puppy_core_plugins/wiggum/judge.py +299 -0
  303. code_puppy_core_plugins/wiggum/judge_config.py +249 -0
  304. code_puppy_core_plugins/wiggum/judges_menu.py +814 -0
  305. code_puppy_core_plugins/wiggum/register_callbacks.py +569 -0
  306. code_puppy_core_plugins/wiggum/state.py +89 -0
  307. code_puppy_core_plugins/yolo_cli/__init__.py +1 -0
  308. code_puppy_core_plugins/yolo_cli/register_callbacks.py +41 -0
  309. code_puppy_core_plugins-0.0.2.dist-info/METADATA +64 -0
  310. code_puppy_core_plugins-0.0.2.dist-info/RECORD +313 -0
  311. code_puppy_core_plugins-0.0.2.dist-info/WHEEL +4 -0
  312. code_puppy_core_plugins-0.0.2.dist-info/entry_points.txt +55 -0
  313. code_puppy_core_plugins-0.0.2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,113 @@
1
+ """Fetch and cache Codex plan usage without blocking the interactive UI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import threading
7
+ import time
8
+ from dataclasses import dataclass
9
+ from typing import Any
10
+
11
+ import requests
12
+
13
+ from .config import CHATGPT_OAUTH_CONFIG
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ _USAGE_URL = "https://chatgpt.com/backend-api/wham/usage"
18
+ _CACHE_TTL_SECONDS = 60.0
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class CodexUsage:
23
+ """Remaining percentages for Codex's rolling usage windows."""
24
+
25
+ primary_remaining: int | None = None
26
+ secondary_remaining: int | None = None
27
+
28
+ def format_status(self) -> str:
29
+ parts: list[str] = []
30
+ if self.primary_remaining is not None:
31
+ parts.append(f"5h {self.primary_remaining}% remaining")
32
+ if self.secondary_remaining is not None:
33
+ parts.append(f"week {self.secondary_remaining}% remaining")
34
+ return " · ".join(parts)
35
+
36
+
37
+ _lock = threading.Lock()
38
+ _cached_usage: CodexUsage | None = None
39
+ _last_attempt = 0.0
40
+ _fetch_in_progress = False
41
+
42
+
43
+ def _remaining_percent(window: Any) -> int | None:
44
+ if not isinstance(window, dict):
45
+ return None
46
+ used = window.get("used_percent")
47
+ if not isinstance(used, (int, float)):
48
+ return None
49
+ return max(0, min(100, round(100 - used)))
50
+
51
+
52
+ def parse_usage_payload(payload: Any) -> CodexUsage | None:
53
+ """Parse the response returned by the Codex ``wham/usage`` endpoint."""
54
+ if not isinstance(payload, dict):
55
+ return None
56
+ rate_limit = payload.get("rate_limit")
57
+ if not isinstance(rate_limit, dict):
58
+ return None
59
+ usage = CodexUsage(
60
+ primary_remaining=_remaining_percent(rate_limit.get("primary_window")),
61
+ secondary_remaining=_remaining_percent(rate_limit.get("secondary_window")),
62
+ )
63
+ if usage.primary_remaining is None and usage.secondary_remaining is None:
64
+ return None
65
+ return usage
66
+
67
+
68
+ def _fetch(access_token: str, account_id: str) -> None:
69
+ global _cached_usage, _fetch_in_progress
70
+ headers = {
71
+ "Authorization": f"Bearer {access_token}",
72
+ "ChatGPT-Account-Id": account_id,
73
+ "Accept": "application/json",
74
+ "originator": CHATGPT_OAUTH_CONFIG.get("originator", "codex_cli_rs"),
75
+ }
76
+ try:
77
+ response = requests.get(_USAGE_URL, headers=headers, timeout=10)
78
+ response.raise_for_status()
79
+ usage = parse_usage_payload(response.json())
80
+ if usage is not None:
81
+ with _lock:
82
+ _cached_usage = usage
83
+ except (requests.RequestException, ValueError):
84
+ logger.debug("Unable to refresh Codex usage", exc_info=True)
85
+ finally:
86
+ with _lock:
87
+ _fetch_in_progress = False
88
+
89
+
90
+ def refresh_usage_in_background(access_token: str, account_id: str) -> None:
91
+ """Refresh the cache at most once per minute on a daemon thread."""
92
+ global _fetch_in_progress, _last_attempt
93
+ if not access_token or not account_id:
94
+ return
95
+ now = time.monotonic()
96
+ with _lock:
97
+ if _fetch_in_progress or now - _last_attempt < _CACHE_TTL_SECONDS:
98
+ return
99
+ _fetch_in_progress = True
100
+ _last_attempt = now
101
+ threading.Thread(
102
+ target=_fetch,
103
+ args=(access_token, account_id),
104
+ name="codex-usage-refresh",
105
+ daemon=True,
106
+ ).start()
107
+
108
+
109
+ def get_usage_status() -> str:
110
+ """Return cached status text; this function never performs I/O."""
111
+ with _lock:
112
+ usage = _cached_usage
113
+ return usage.format_status() if usage is not None else ""
@@ -0,0 +1,534 @@
1
+ """Utility helpers for the ChatGPT OAuth plugin."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import base64
6
+ import datetime
7
+ import hashlib
8
+ import json
9
+ import logging
10
+ import secrets
11
+ import time
12
+ from dataclasses import dataclass
13
+ from typing import Any, Dict, List, Optional
14
+ from urllib.parse import parse_qs as urllib_parse_qs
15
+ from urllib.parse import urlencode, urlparse
16
+
17
+ import requests
18
+
19
+ from .config import (
20
+ CHATGPT_OAUTH_CONFIG,
21
+ get_chatgpt_models_path,
22
+ get_token_storage_path,
23
+ )
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ @dataclass
29
+ class OAuthContext:
30
+ """Runtime state for an in-progress OAuth flow."""
31
+
32
+ state: str
33
+ code_verifier: str
34
+ code_challenge: str
35
+ created_at: float
36
+ redirect_uri: Optional[str] = None
37
+ expires_at: Optional[float] = None # Add expiration time
38
+
39
+ def is_expired(self) -> bool:
40
+ """Check if this OAuth context has expired."""
41
+ if self.expires_at is None:
42
+ # Default 5 minute expiration if not set
43
+ return time.time() - self.created_at > 300
44
+ return time.time() > self.expires_at
45
+
46
+
47
+ def _urlsafe_b64encode(data: bytes) -> str:
48
+ return base64.urlsafe_b64encode(data).decode("utf-8").rstrip("=")
49
+
50
+
51
+ def _generate_code_verifier() -> str:
52
+ return secrets.token_hex(64)
53
+
54
+
55
+ def _compute_code_challenge(code_verifier: str) -> str:
56
+ digest = hashlib.sha256(code_verifier.encode("utf-8")).digest()
57
+ return _urlsafe_b64encode(digest)
58
+
59
+
60
+ def prepare_oauth_context() -> OAuthContext:
61
+ """Create a fresh OAuth PKCE context."""
62
+ state = secrets.token_hex(32)
63
+ code_verifier = _generate_code_verifier()
64
+ code_challenge = _compute_code_challenge(code_verifier)
65
+
66
+ # Set expiration 4 minutes from now (OpenAI sessions are short)
67
+ expires_at = time.time() + 240
68
+
69
+ return OAuthContext(
70
+ state=state,
71
+ code_verifier=code_verifier,
72
+ code_challenge=code_challenge,
73
+ created_at=time.time(),
74
+ expires_at=expires_at,
75
+ )
76
+
77
+
78
+ def assign_redirect_uri(context: OAuthContext, port: int) -> str:
79
+ """Assign redirect URI for the given OAuth context."""
80
+ if context is None:
81
+ raise RuntimeError("OAuth context cannot be None")
82
+ host = CHATGPT_OAUTH_CONFIG["redirect_host"].rstrip("/")
83
+ path = CHATGPT_OAUTH_CONFIG["redirect_path"].lstrip("/")
84
+ required_port = CHATGPT_OAUTH_CONFIG.get("required_port")
85
+ if required_port and port != required_port:
86
+ raise RuntimeError(
87
+ f"OAuth flow must use port {required_port}; attempted to assign port {port}"
88
+ )
89
+ redirect_uri = f"{host}:{port}/{path}"
90
+ context.redirect_uri = redirect_uri
91
+ return redirect_uri
92
+
93
+
94
+ def build_authorization_url(context: OAuthContext) -> str:
95
+ """Return the OpenAI authorization URL with PKCE parameters."""
96
+ if not context.redirect_uri:
97
+ raise RuntimeError("Redirect URI has not been assigned for this OAuth context")
98
+
99
+ params = {
100
+ "response_type": "code",
101
+ "client_id": CHATGPT_OAUTH_CONFIG["client_id"],
102
+ "redirect_uri": context.redirect_uri,
103
+ "scope": CHATGPT_OAUTH_CONFIG["scope"],
104
+ "code_challenge": context.code_challenge,
105
+ "code_challenge_method": "S256",
106
+ "id_token_add_organizations": "true",
107
+ "codex_cli_simplified_flow": "true",
108
+ "state": context.state,
109
+ }
110
+ return f"{CHATGPT_OAUTH_CONFIG['auth_url']}?{urlencode(params)}"
111
+
112
+
113
+ def parse_authorization_error(url: str) -> Optional[str]:
114
+ """Parse error from OAuth callback URL."""
115
+ try:
116
+ parsed = urlparse(url)
117
+ params = urllib_parse_qs(parsed.query)
118
+ error = params.get("error", [None])[0]
119
+ error_description = params.get("error_description", [None])[0]
120
+ if error:
121
+ return f"{error}: {error_description or 'Unknown error'}"
122
+ except Exception as exc:
123
+ logger.error("Failed to parse OAuth error: %s", exc)
124
+ return None
125
+
126
+
127
+ def parse_jwt_claims(token: str) -> Optional[Dict[str, Any]]:
128
+ """Parse JWT token to extract claims."""
129
+ if not token or token.count(".") != 2:
130
+ return None
131
+ try:
132
+ _, payload, _ = token.split(".")
133
+ padded = payload + "=" * (-len(payload) % 4)
134
+ data = base64.urlsafe_b64decode(padded.encode())
135
+ return json.loads(data.decode())
136
+ except Exception as exc:
137
+ logger.error("Failed to parse JWT: %s", exc)
138
+ return None
139
+
140
+
141
+ def load_stored_tokens() -> Optional[Dict[str, Any]]:
142
+ try:
143
+ token_path = get_token_storage_path()
144
+ if token_path.exists():
145
+ with open(token_path, "r", encoding="utf-8") as handle:
146
+ return json.load(handle)
147
+ except Exception as exc:
148
+ logger.error("Failed to load tokens: %s", exc)
149
+ return None
150
+
151
+
152
+ def get_valid_access_token() -> Optional[str]:
153
+ """Get a valid access token, refreshing if expired.
154
+
155
+ Returns:
156
+ Valid access token string, or None if not authenticated or refresh failed.
157
+ """
158
+ tokens = load_stored_tokens()
159
+ if not tokens:
160
+ logger.debug("No stored ChatGPT OAuth tokens found")
161
+ return None
162
+
163
+ access_token = tokens.get("access_token")
164
+ if not access_token:
165
+ logger.debug("No access_token in stored tokens")
166
+ return None
167
+
168
+ # Check if token is expired by parsing JWT claims
169
+ claims = parse_jwt_claims(access_token)
170
+ if claims:
171
+ exp = claims.get("exp")
172
+ if exp and isinstance(exp, (int, float)):
173
+ # Add 30 second buffer before expiry
174
+ if time.time() > exp - 30:
175
+ logger.info("ChatGPT OAuth token expired, attempting refresh")
176
+ refreshed = refresh_access_token()
177
+ if refreshed:
178
+ return refreshed
179
+ logger.warning("Token refresh failed")
180
+ return None
181
+
182
+ return access_token
183
+
184
+
185
+ def refresh_access_token() -> Optional[str]:
186
+ """Refresh the access token using the refresh token.
187
+
188
+ Returns:
189
+ New access token if refresh succeeded, None otherwise.
190
+ """
191
+ tokens = load_stored_tokens()
192
+ if not tokens:
193
+ return None
194
+
195
+ refresh_token = tokens.get("refresh_token")
196
+ if not refresh_token:
197
+ logger.debug("No refresh_token available")
198
+ return None
199
+
200
+ payload = {
201
+ "grant_type": "refresh_token",
202
+ "refresh_token": refresh_token,
203
+ "client_id": CHATGPT_OAUTH_CONFIG["client_id"],
204
+ }
205
+
206
+ headers = {
207
+ "Content-Type": "application/x-www-form-urlencoded",
208
+ }
209
+
210
+ try:
211
+ response = requests.post(
212
+ CHATGPT_OAUTH_CONFIG["token_url"],
213
+ data=payload,
214
+ headers=headers,
215
+ timeout=30,
216
+ )
217
+
218
+ if response.status_code == 200:
219
+ new_tokens = response.json()
220
+ # Merge with existing tokens (preserve account_id, etc.)
221
+ tokens.update(
222
+ {
223
+ "access_token": new_tokens.get("access_token"),
224
+ "refresh_token": new_tokens.get("refresh_token", refresh_token),
225
+ "id_token": new_tokens.get("id_token", tokens.get("id_token")),
226
+ "last_refresh": datetime.datetime.now(datetime.timezone.utc)
227
+ .isoformat()
228
+ .replace("+00:00", "Z"),
229
+ }
230
+ )
231
+ if save_tokens(tokens):
232
+ logger.info("Successfully refreshed ChatGPT OAuth token")
233
+ return tokens["access_token"]
234
+ else:
235
+ logger.error(
236
+ "Token refresh failed: %s - %s", response.status_code, response.text
237
+ )
238
+ except Exception as exc:
239
+ logger.error("Token refresh error: %s", exc)
240
+
241
+ return None
242
+
243
+
244
+ def save_tokens(tokens: Dict[str, Any]) -> bool:
245
+ if tokens is None:
246
+ raise TypeError("tokens cannot be None")
247
+ try:
248
+ token_path = get_token_storage_path()
249
+ with open(token_path, "w", encoding="utf-8") as handle:
250
+ json.dump(tokens, handle, indent=2)
251
+ token_path.chmod(0o600)
252
+ return True
253
+ except Exception as exc:
254
+ logger.error("Failed to save tokens: %s", exc)
255
+ return False
256
+
257
+
258
+ def load_chatgpt_models() -> Dict[str, Any]:
259
+ try:
260
+ models_path = get_chatgpt_models_path()
261
+ if models_path.exists():
262
+ with open(models_path, "r", encoding="utf-8") as handle:
263
+ return json.load(handle)
264
+ except Exception as exc:
265
+ logger.error("Failed to load ChatGPT models: %s", exc)
266
+ return {}
267
+
268
+
269
+ def save_chatgpt_models(models: Dict[str, Any]) -> bool:
270
+ try:
271
+ models_path = get_chatgpt_models_path()
272
+ with open(models_path, "w", encoding="utf-8") as handle:
273
+ json.dump(models, handle, indent=2)
274
+ return True
275
+ except Exception as exc:
276
+ logger.error("Failed to save ChatGPT models: %s", exc)
277
+ return False
278
+
279
+
280
+ def exchange_code_for_tokens(
281
+ auth_code: str, context: OAuthContext
282
+ ) -> Optional[Dict[str, Any]]:
283
+ """Exchange authorization code for access tokens."""
284
+ if not context.redirect_uri:
285
+ raise RuntimeError("Redirect URI missing from OAuth context")
286
+
287
+ if context.is_expired():
288
+ logger.error("OAuth context expired, cannot exchange code")
289
+ return None
290
+
291
+ payload = {
292
+ "grant_type": "authorization_code",
293
+ "code": auth_code,
294
+ "redirect_uri": context.redirect_uri,
295
+ "client_id": CHATGPT_OAUTH_CONFIG["client_id"],
296
+ "code_verifier": context.code_verifier,
297
+ }
298
+
299
+ headers = {
300
+ "Content-Type": "application/x-www-form-urlencoded",
301
+ }
302
+
303
+ logger.info("Exchanging code for tokens: %s", CHATGPT_OAUTH_CONFIG["token_url"])
304
+ try:
305
+ response = requests.post(
306
+ CHATGPT_OAUTH_CONFIG["token_url"],
307
+ data=payload,
308
+ headers=headers,
309
+ timeout=30,
310
+ )
311
+ logger.info("Token exchange response: %s", response.status_code)
312
+ if response.status_code == 200:
313
+ token_data = response.json()
314
+ # Add timestamp
315
+ token_data["last_refresh"] = (
316
+ datetime.datetime.now(datetime.timezone.utc)
317
+ .isoformat()
318
+ .replace("+00:00", "Z")
319
+ )
320
+ return token_data
321
+ else:
322
+ logger.error(
323
+ "Token exchange failed: %s - %s",
324
+ response.status_code,
325
+ response.text,
326
+ )
327
+ # Try to parse OAuth error
328
+ if response.headers.get("content-type", "").startswith("application/json"):
329
+ try:
330
+ error_data = response.json()
331
+ if "error" in error_data:
332
+ logger.error(
333
+ "OAuth error: %s",
334
+ error_data.get("error_description", error_data["error"]),
335
+ )
336
+ except Exception:
337
+ pass
338
+ except Exception as exc:
339
+ logger.error("Token exchange error: %s", exc)
340
+ return None
341
+
342
+
343
+ # Models known to work with ChatGPT OAuth tokens (sourced from codex-rs CLI
344
+ # and shell-scripts/codex-call.sh).
345
+ DEFAULT_CODEX_MODELS = [
346
+ "gpt-5.6-sol",
347
+ "gpt-5.6-terra",
348
+ "gpt-5.6-luna",
349
+ "gpt-5.5",
350
+ "gpt-5.4",
351
+ "gpt-5.4-mini",
352
+ "gpt-5.3-codex-spark",
353
+ "codex-auto-review",
354
+ ]
355
+
356
+ # Per-model context length overrides (tokens).
357
+ # Models not listed here use CHATGPT_OAUTH_CONFIG["default_context_length"] (272,000).
358
+ CODEX_MODEL_CONTEXT_LENGTHS = {
359
+ "gpt-5.6-sol": 1050000,
360
+ "gpt-5.6-terra": 1050000,
361
+ "gpt-5.6-luna": 1050000,
362
+ "gpt-5.3-codex-spark": 131000,
363
+ "gpt-5.3-instant": 192000,
364
+ }
365
+
366
+
367
+ def _supports_xhigh_reasoning(model_name: str) -> bool:
368
+ """Return whether a ChatGPT OAuth model supports xhigh reasoning."""
369
+ normalized_model_name = model_name.lower()
370
+ return "codex" in normalized_model_name or normalized_model_name.startswith(
371
+ ("gpt-5.4", "gpt-5.5", "gpt-5.6")
372
+ )
373
+
374
+
375
+ def _supports_max_reasoning(model_name: str) -> bool:
376
+ """Return whether a ChatGPT OAuth model supports max reasoning effort."""
377
+ return model_name.lower().startswith("gpt-5.6")
378
+
379
+
380
+ def fetch_chatgpt_models(access_token: str, account_id: str) -> Optional[List[str]]:
381
+ """Fetch available models from ChatGPT Codex API.
382
+
383
+ Attempts to fetch models from the API, but falls back to a default list
384
+ of known Codex-compatible models if the API is unavailable.
385
+
386
+ Args:
387
+ access_token: OAuth access token for authentication
388
+ account_id: ChatGPT account ID (required for the API)
389
+
390
+ Returns:
391
+ List of model IDs, or default list if API fails
392
+ """
393
+ import platform
394
+
395
+ # Build the models URL with client version
396
+ client_version = CHATGPT_OAUTH_CONFIG.get("client_version", "0.144.1")
397
+ base_url = CHATGPT_OAUTH_CONFIG["api_base_url"].rstrip("/")
398
+ models_url = f"{base_url}/models"
399
+
400
+ # Build User-Agent to match codex-rs CLI format
401
+ originator = CHATGPT_OAUTH_CONFIG.get("originator", "codex_cli_rs")
402
+ os_name = platform.system()
403
+ if os_name == "Darwin":
404
+ os_name = "Mac OS"
405
+ os_version = platform.release()
406
+ arch = platform.machine()
407
+ user_agent = (
408
+ f"{originator}/{client_version} ({os_name} {os_version}; {arch}) "
409
+ "Terminal_Codex_CLI"
410
+ )
411
+
412
+ headers = {
413
+ "Authorization": f"Bearer {access_token}",
414
+ "ChatGPT-Account-Id": account_id,
415
+ "User-Agent": user_agent,
416
+ "originator": originator,
417
+ "Accept": "application/json",
418
+ }
419
+
420
+ # Query params
421
+ params = {"client_version": client_version}
422
+
423
+ try:
424
+ response = requests.get(models_url, headers=headers, params=params, timeout=30)
425
+
426
+ if response.status_code == 200:
427
+ # Parse JSON response
428
+ try:
429
+ data = response.json()
430
+ # The response has a "models" key with list of model objects
431
+ if "models" in data and isinstance(data["models"], list):
432
+ models = []
433
+ seen_models = set()
434
+ for model in data["models"]:
435
+ if model is None:
436
+ continue
437
+ model_id = (
438
+ model.get("slug") or model.get("id") or model.get("name")
439
+ )
440
+ if model_id and model_id not in seen_models:
441
+ seen_models.add(model_id)
442
+ models.append(model_id)
443
+ if models:
444
+ return models
445
+ except (json.JSONDecodeError, ValueError) as exc:
446
+ logger.warning("Failed to parse models response: %s", exc)
447
+
448
+ # API didn't return valid models, use default list
449
+ logger.info(
450
+ "Models endpoint returned %d, using default model list",
451
+ response.status_code,
452
+ )
453
+
454
+ except requests.exceptions.Timeout:
455
+ logger.warning("Timeout fetching models, using default list")
456
+ except requests.exceptions.RequestException as exc:
457
+ logger.warning("Network error fetching models: %s, using default list", exc)
458
+ except Exception as exc:
459
+ logger.warning("Error fetching models: %s, using default list", exc)
460
+
461
+ # Return default models when API fails
462
+ logger.info("Using default Codex models: %s", DEFAULT_CODEX_MODELS)
463
+ return DEFAULT_CODEX_MODELS
464
+
465
+
466
+ def add_models_to_extra_config(models: List[str]) -> bool:
467
+ """Add ChatGPT models to chatgpt_models.json configuration."""
468
+ try:
469
+ chatgpt_models = load_chatgpt_models()
470
+ desired_model_keys = {
471
+ f"{CHATGPT_OAUTH_CONFIG['prefix']}{model_name}" for model_name in models
472
+ }
473
+ chatgpt_models = {
474
+ key: config
475
+ for key, config in chatgpt_models.items()
476
+ if config.get("oauth_source") != "chatgpt-oauth-plugin"
477
+ or key in desired_model_keys
478
+ }
479
+ added = 0
480
+ for model_name in models:
481
+ prefixed = f"{CHATGPT_OAUTH_CONFIG['prefix']}{model_name}"
482
+
483
+ # Responses-API models always support reasoning effort, summaries,
484
+ # and text verbosity; per-model extras are added below.
485
+ supported_settings = ["reasoning_effort", "summary", "verbosity"]
486
+ if model_name.lower().startswith("gpt-5.6"):
487
+ supported_settings.extend(["reasoning_context", "reasoning_mode"])
488
+
489
+ # xhigh: codex + GPT-5.4+; max: GPT-5.6+. Separate flags so a stronger
490
+ # effort never leaks into older model menus.
491
+ supports_xhigh_reasoning = _supports_xhigh_reasoning(model_name)
492
+ supports_max_reasoning = _supports_max_reasoning(model_name)
493
+
494
+ chatgpt_models[prefixed] = {
495
+ "type": "chatgpt_oauth",
496
+ "name": model_name,
497
+ "custom_endpoint": {
498
+ # Codex API uses chatgpt.com/backend-api/codex, not api.openai.com
499
+ "url": CHATGPT_OAUTH_CONFIG["api_base_url"],
500
+ },
501
+ "context_length": CODEX_MODEL_CONTEXT_LENGTHS.get(
502
+ model_name, CHATGPT_OAUTH_CONFIG["default_context_length"]
503
+ ),
504
+ "oauth_source": "chatgpt-oauth-plugin",
505
+ "supported_settings": supported_settings,
506
+ "supports_xhigh_reasoning": supports_xhigh_reasoning,
507
+ "supports_max_reasoning": supports_max_reasoning,
508
+ }
509
+ added += 1
510
+ if save_chatgpt_models(chatgpt_models):
511
+ logger.info("Added %s ChatGPT models", added)
512
+ return True
513
+ except Exception as exc:
514
+ logger.error("Error adding models to config: %s", exc)
515
+ return False
516
+
517
+
518
+ def remove_chatgpt_models() -> int:
519
+ """Remove ChatGPT OAuth models from chatgpt_models.json."""
520
+ try:
521
+ chatgpt_models = load_chatgpt_models()
522
+ to_remove = [
523
+ name
524
+ for name, config in chatgpt_models.items()
525
+ if config.get("oauth_source") == "chatgpt-oauth-plugin"
526
+ ]
527
+ for model_name in to_remove:
528
+ chatgpt_models.pop(model_name, None)
529
+ # Always save, even if no models were removed (to match test expectations)
530
+ if save_chatgpt_models(chatgpt_models):
531
+ return len(to_remove)
532
+ except Exception as exc:
533
+ logger.error("Error removing ChatGPT models: %s", exc)
534
+ return 0
@@ -0,0 +1 @@
1
+ """Claude Code hooks plugin."""