klaude-code 1.8.0__tar.gz → 2.0.0__tar.gz

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 (242) hide show
  1. {klaude_code-1.8.0 → klaude_code-2.0.0}/PKG-INFO +28 -6
  2. {klaude_code-1.8.0 → klaude_code-2.0.0}/README.md +25 -4
  3. {klaude_code-1.8.0 → klaude_code-2.0.0}/pyproject.toml +3 -2
  4. klaude_code-2.0.0/src/klaude_code/auth/base.py +97 -0
  5. klaude_code-2.0.0/src/klaude_code/auth/claude/__init__.py +6 -0
  6. klaude_code-2.0.0/src/klaude_code/auth/claude/exceptions.py +9 -0
  7. klaude_code-2.0.0/src/klaude_code/auth/claude/oauth.py +172 -0
  8. klaude_code-2.0.0/src/klaude_code/auth/claude/token_manager.py +26 -0
  9. klaude_code-2.0.0/src/klaude_code/auth/codex/token_manager.py +44 -0
  10. klaude_code-2.0.0/src/klaude_code/cli/auth_cmd.py +154 -0
  11. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/cli/config_cmd.py +4 -2
  12. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/cli/cost_cmd.py +14 -9
  13. klaude_code-2.0.0/src/klaude_code/cli/list_model.py +355 -0
  14. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/cli/main.py +1 -1
  15. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/cli/runtime.py +7 -5
  16. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/cli/self_update.py +1 -1
  17. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/cli/session_cmd.py +1 -1
  18. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/clear_cmd.py +6 -2
  19. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/command_abc.py +2 -2
  20. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/debug_cmd.py +4 -4
  21. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/export_cmd.py +2 -2
  22. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/export_online_cmd.py +12 -12
  23. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/fork_session_cmd.py +29 -23
  24. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/help_cmd.py +4 -4
  25. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/model_cmd.py +4 -4
  26. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/model_select.py +1 -1
  27. klaude_code-2.0.0/src/klaude_code/command/prompt-commit.md +82 -0
  28. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/prompt_command.py +3 -3
  29. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/refresh_cmd.py +2 -2
  30. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/registry.py +7 -5
  31. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/release_notes_cmd.py +4 -4
  32. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/resume_cmd.py +15 -11
  33. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/status_cmd.py +4 -4
  34. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/terminal_setup_cmd.py +8 -8
  35. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/thinking_cmd.py +4 -4
  36. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/config/assets/builtin_config.yaml +52 -3
  37. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/config/builtin_config.py +16 -5
  38. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/config/config.py +31 -7
  39. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/config/thinking.py +4 -4
  40. klaude_code-2.0.0/src/klaude_code/const.py +219 -0
  41. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/agent.py +3 -12
  42. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/executor.py +21 -13
  43. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/manager/sub_agent_manager.py +71 -7
  44. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/prompt.py +1 -1
  45. klaude_code-2.0.0/src/klaude_code/core/prompts/prompt-sub-agent-image-gen.md +1 -0
  46. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/prompts/prompt-sub-agent-web.md +27 -1
  47. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/reminders.py +88 -69
  48. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/task.py +44 -45
  49. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/apply_patch_tool.py +9 -9
  50. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/diff_builder.py +3 -5
  51. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/edit_tool.py +23 -23
  52. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/move_tool.py +43 -43
  53. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/read_tool.py +44 -39
  54. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/write_tool.py +14 -14
  55. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/report_back_tool.py +4 -4
  56. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/shell/bash_tool.py +23 -23
  57. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/skill/skill_tool.py +7 -7
  58. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/sub_agent_tool.py +38 -9
  59. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/todo/todo_write_tool.py +8 -8
  60. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/todo/update_plan_tool.py +6 -6
  61. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/tool_abc.py +2 -2
  62. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/tool_context.py +27 -0
  63. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/tool_runner.py +88 -42
  64. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/truncation.py +38 -20
  65. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/web/mermaid_tool.py +6 -7
  66. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/web/web_fetch_tool.py +68 -30
  67. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/web/web_search_tool.py +15 -17
  68. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/turn.py +120 -73
  69. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/anthropic/client.py +104 -44
  70. klaude_code-2.0.0/src/klaude_code/llm/anthropic/input.py +232 -0
  71. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/bedrock/client.py +8 -5
  72. klaude_code-2.0.0/src/klaude_code/llm/claude/__init__.py +3 -0
  73. klaude_code-2.0.0/src/klaude_code/llm/claude/client.py +105 -0
  74. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/client.py +4 -3
  75. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/codex/client.py +16 -10
  76. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/google/client.py +122 -60
  77. klaude_code-2.0.0/src/klaude_code/llm/google/input.py +201 -0
  78. klaude_code-2.0.0/src/klaude_code/llm/image.py +123 -0
  79. klaude_code-2.0.0/src/klaude_code/llm/input_common.py +180 -0
  80. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/openai_compatible/client.py +17 -7
  81. klaude_code-2.0.0/src/klaude_code/llm/openai_compatible/input.py +81 -0
  82. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/openai_compatible/stream.py +119 -67
  83. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/openai_compatible/tool_call_accumulator.py +23 -11
  84. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/openrouter/client.py +34 -9
  85. klaude_code-2.0.0/src/klaude_code/llm/openrouter/input.py +147 -0
  86. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/openrouter/reasoning.py +22 -24
  87. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/registry.py +20 -15
  88. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/responses/client.py +107 -45
  89. klaude_code-2.0.0/src/klaude_code/llm/responses/input.py +203 -0
  90. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/usage.py +52 -25
  91. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/protocol/__init__.py +1 -0
  92. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/protocol/events.py +16 -12
  93. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/protocol/llm_param.py +22 -3
  94. klaude_code-2.0.0/src/klaude_code/protocol/message.py +250 -0
  95. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/protocol/model.py +94 -281
  96. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/protocol/op.py +2 -2
  97. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/protocol/sub_agent/__init__.py +2 -2
  98. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/protocol/sub_agent/explore.py +10 -0
  99. klaude_code-2.0.0/src/klaude_code/protocol/sub_agent/image_gen.py +119 -0
  100. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/protocol/sub_agent/task.py +10 -0
  101. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/protocol/sub_agent/web.py +10 -0
  102. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/session/codec.py +6 -6
  103. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/session/export.py +261 -62
  104. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/session/selector.py +7 -24
  105. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/session/session.py +125 -53
  106. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/session/store.py +5 -32
  107. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/session/templates/export_session.html +1 -1
  108. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/session/templates/mermaid_viewer.html +1 -1
  109. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/trace/log.py +11 -6
  110. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/core/input.py +1 -1
  111. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/core/stage_manager.py +1 -8
  112. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/debug/display.py +2 -2
  113. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/repl/clipboard.py +2 -2
  114. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/repl/completers.py +18 -10
  115. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/repl/event_handler.py +136 -127
  116. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/repl/input_prompt_toolkit.py +1 -1
  117. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/repl/key_bindings.py +1 -1
  118. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/repl/renderer.py +107 -15
  119. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/assistant.py +2 -2
  120. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/common.py +65 -7
  121. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/developer.py +7 -6
  122. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/diffs.py +11 -11
  123. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/mermaid_viewer.py +49 -2
  124. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/metadata.py +39 -31
  125. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/sub_agent.py +57 -16
  126. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/thinking.py +37 -2
  127. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/tools.py +180 -165
  128. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/rich/live.py +3 -1
  129. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/rich/markdown.py +39 -7
  130. klaude_code-2.0.0/src/klaude_code/ui/rich/quote.py +109 -0
  131. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/rich/status.py +14 -8
  132. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/rich/theme.py +13 -6
  133. klaude_code-2.0.0/src/klaude_code/ui/terminal/image.py +34 -0
  134. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/terminal/notifier.py +2 -1
  135. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/terminal/progress_bar.py +4 -4
  136. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/terminal/selector.py +22 -4
  137. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/utils/common.py +55 -0
  138. klaude_code-1.8.0/src/klaude_code/auth/codex/token_manager.py +0 -84
  139. klaude_code-1.8.0/src/klaude_code/cli/auth_cmd.py +0 -73
  140. klaude_code-1.8.0/src/klaude_code/cli/list_model.py +0 -307
  141. klaude_code-1.8.0/src/klaude_code/command/prompt-jj-describe.md +0 -32
  142. klaude_code-1.8.0/src/klaude_code/const.py +0 -164
  143. klaude_code-1.8.0/src/klaude_code/core/prompts/prompt-sub-agent-oracle.md +0 -22
  144. klaude_code-1.8.0/src/klaude_code/llm/anthropic/input.py +0 -224
  145. klaude_code-1.8.0/src/klaude_code/llm/google/input.py +0 -215
  146. klaude_code-1.8.0/src/klaude_code/llm/input_common.py +0 -233
  147. klaude_code-1.8.0/src/klaude_code/llm/openai_compatible/input.py +0 -111
  148. klaude_code-1.8.0/src/klaude_code/llm/openrouter/input.py +0 -148
  149. klaude_code-1.8.0/src/klaude_code/llm/responses/input.py +0 -186
  150. klaude_code-1.8.0/src/klaude_code/protocol/sub_agent/oracle.py +0 -91
  151. klaude_code-1.8.0/src/klaude_code/ui/rich/quote.py +0 -34
  152. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/__init__.py +0 -0
  153. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/auth/__init__.py +0 -0
  154. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/auth/codex/__init__.py +0 -0
  155. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/auth/codex/exceptions.py +0 -0
  156. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/auth/codex/jwt_utils.py +0 -0
  157. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/auth/codex/oauth.py +0 -0
  158. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/cli/__init__.py +0 -0
  159. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/cli/debug.py +0 -0
  160. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/__init__.py +0 -0
  161. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/command/prompt-init.md +0 -0
  162. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/config/__init__.py +0 -0
  163. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/config/assets/__init__.py +0 -0
  164. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/config/select_model.py +0 -0
  165. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/__init__.py +0 -0
  166. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/manager/__init__.py +0 -0
  167. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/manager/llm_clients.py +0 -0
  168. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/manager/llm_clients_builder.py +0 -0
  169. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/prompts/prompt-claude-code.md +0 -0
  170. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +0 -0
  171. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +0 -0
  172. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/prompts/prompt-codex.md +0 -0
  173. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/prompts/prompt-gemini.md +0 -0
  174. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/prompts/prompt-minimal.md +0 -0
  175. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/prompts/prompt-sub-agent-explore.md +0 -0
  176. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/prompts/prompt-sub-agent.md +0 -0
  177. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/__init__.py +0 -0
  178. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/__init__.py +0 -0
  179. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/_utils.py +0 -0
  180. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/apply_patch.py +0 -0
  181. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/apply_patch_tool.md +0 -0
  182. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/edit_tool.md +0 -0
  183. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/move_tool.md +0 -0
  184. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/read_tool.md +0 -0
  185. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/file/write_tool.md +0 -0
  186. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/shell/__init__.py +0 -0
  187. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/shell/bash_tool.md +0 -0
  188. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/shell/command_safety.py +0 -0
  189. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/skill/__init__.py +0 -0
  190. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/skill/skill_tool.md +0 -0
  191. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/todo/__init__.py +0 -0
  192. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/todo/todo_write_tool.md +0 -0
  193. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/todo/todo_write_tool_raw.md +0 -0
  194. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/todo/update_plan_tool.md +0 -0
  195. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/tool_registry.py +0 -0
  196. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/web/__init__.py +0 -0
  197. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/web/mermaid_tool.md +0 -0
  198. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/web/web_fetch_tool.md +0 -0
  199. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/core/tool/web/web_search_tool.md +0 -0
  200. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/__init__.py +0 -0
  201. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/anthropic/__init__.py +0 -0
  202. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/bedrock/__init__.py +0 -0
  203. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/codex/__init__.py +0 -0
  204. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/google/__init__.py +0 -0
  205. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/openai_compatible/__init__.py +0 -0
  206. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/openrouter/__init__.py +0 -0
  207. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/llm/responses/__init__.py +0 -0
  208. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/protocol/commands.py +0 -0
  209. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/protocol/op_handler.py +0 -0
  210. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/protocol/tools.py +0 -0
  211. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/session/__init__.py +0 -0
  212. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/skill/__init__.py +0 -0
  213. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/skill/assets/deslop/SKILL.md +0 -0
  214. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/skill/assets/dev-docs/SKILL.md +0 -0
  215. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/skill/assets/handoff/SKILL.md +0 -0
  216. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/skill/assets/jj-workspace/SKILL.md +0 -0
  217. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/skill/assets/skill-creator/SKILL.md +0 -0
  218. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/skill/loader.py +0 -0
  219. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/skill/manager.py +0 -0
  220. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/skill/system_skills.py +0 -0
  221. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/trace/__init__.py +0 -0
  222. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/__init__.py +0 -0
  223. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/core/__init__.py +0 -0
  224. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/core/display.py +0 -0
  225. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/__init__.py +0 -0
  226. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/debug/__init__.py +0 -0
  227. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/exec/__init__.py +0 -0
  228. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/exec/display.py +0 -0
  229. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/repl/__init__.py +0 -0
  230. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/modes/repl/display.py +0 -0
  231. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/__init__.py +0 -0
  232. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/bash_syntax.py +0 -0
  233. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/errors.py +0 -0
  234. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/renderers/user_input.py +0 -0
  235. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/rich/__init__.py +0 -0
  236. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/rich/cjk_wrap.py +0 -0
  237. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/rich/code_panel.py +0 -0
  238. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/rich/searchable_text.py +0 -0
  239. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/terminal/__init__.py +0 -0
  240. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/terminal/color.py +0 -0
  241. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/terminal/control.py +0 -0
  242. {klaude_code-1.8.0 → klaude_code-2.0.0}/src/klaude_code/ui/utils/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: klaude-code
3
- Version: 1.8.0
3
+ Version: 2.0.0
4
4
  Summary: Minimal code agent CLI
5
5
  Requires-Dist: anthropic>=0.66.0
6
6
  Requires-Dist: chardet>=5.2.0
@@ -9,11 +9,12 @@ Requires-Dist: diff-match-patch>=20241021
9
9
  Requires-Dist: google-genai>=1.56.0
10
10
  Requires-Dist: markdown-it-py>=4.0.0
11
11
  Requires-Dist: openai>=1.102.0
12
- Requires-Dist: pillow>=12.0.0
12
+ Requires-Dist: pillow>=9.1,<11.0
13
13
  Requires-Dist: prompt-toolkit>=3.0.52
14
14
  Requires-Dist: pydantic>=2.11.7
15
15
  Requires-Dist: pyyaml>=6.0.2
16
16
  Requires-Dist: rich>=14.1.0
17
+ Requires-Dist: term-image>=0.7.2
17
18
  Requires-Dist: trafilatura>=2.0.0
18
19
  Requires-Dist: typer>=0.17.3
19
20
  Requires-Python: >=3.13
@@ -120,11 +121,12 @@ On first run, you'll be prompted to select a model. Your choice is saved as `mai
120
121
  | Provider | Env Variable | Models |
121
122
  |-------------|-----------------------|-------------------------------------------------------------------------------|
122
123
  | anthropic | `ANTHROPIC_API_KEY` | sonnet, opus |
124
+ | claude | N/A (OAuth) | sonnet@claude, opus@claude (requires Claude Pro/Max subscription) |
123
125
  | openai | `OPENAI_API_KEY` | gpt-5.2 |
124
126
  | openrouter | `OPENROUTER_API_KEY` | gpt-5.2, gpt-5.2-fast, gpt-5.1-codex-max, sonnet, opus, haiku, kimi, gemini-* |
125
127
  | deepseek | `DEEPSEEK_API_KEY` | deepseek |
126
128
  | moonshot | `MOONSHOT_API_KEY` | kimi@moonshot |
127
- | codex | N/A (OAuth) | gpt-5.2-codex |
129
+ | codex | N/A (OAuth) | gpt-5.2-codex (requires ChatGPT Pro subscription) |
128
130
 
129
131
  List all configured providers and models:
130
132
 
@@ -134,6 +136,26 @@ klaude list
134
136
 
135
137
  Models from providers without a valid API key are shown as dimmed/unavailable.
136
138
 
139
+ #### OAuth Login
140
+
141
+ For subscription-based providers (Claude Pro/Max, ChatGPT Pro), use the login command:
142
+
143
+ ```bash
144
+ # Interactive provider selection
145
+ klaude login
146
+
147
+ # Or specify provider directly
148
+ klaude login claude # Claude Pro/Max subscription
149
+ klaude login codex # ChatGPT Pro subscription
150
+ ```
151
+
152
+ To logout:
153
+
154
+ ```bash
155
+ klaude logout claude
156
+ klaude logout codex
157
+ ```
158
+
137
159
  #### Custom Configuration
138
160
 
139
161
  User config file: `~/.klaude/klaude-config.yaml`
@@ -240,7 +262,6 @@ provider_list:
240
262
  main_model: opus
241
263
 
242
264
  sub_agent_models:
243
- oracle: gpt-4.1
244
265
  explore: sonnet
245
266
  task: opus
246
267
  webagent: sonnet
@@ -269,12 +290,13 @@ provider_list:
269
290
  ##### Supported Protocols
270
291
 
271
292
  - `anthropic` - Anthropic Claude API
293
+ - `claude_oauth` - Claude OAuth (for Claude Pro/Max subscribers)
272
294
  - `openai` - OpenAI-compatible API
273
295
  - `responses` - OpenAI Responses API (for o-series, GPT-5, Codex)
274
296
  - `openrouter` - OpenRouter API
275
297
  - `google` - Google Gemini API
276
298
  - `bedrock` - AWS Bedrock (uses AWS credentials instead of api_key)
277
- - `codex` - OpenAI Codex CLI (OAuth-based)
299
+ - `codex_oauth` - OpenAI Codex CLI (OAuth-based, for ChatGPT Pro subscribers)
278
300
 
279
301
  List configured providers and models:
280
302
 
@@ -374,4 +396,4 @@ The main agent can spawn specialized sub-agents for specific tasks:
374
396
  | **Explore** | Fast codebase exploration - find files, search code, answer questions about the codebase |
375
397
  | **Task** | Handle complex multi-step tasks autonomously |
376
398
  | **WebAgent** | Search the web, fetch pages, and analyze content |
377
- | **Oracle** | Advanced reasoning advisor for code reviews, architecture planning, and bug analysis |
399
+ | **ImageGen** | Generate images from text prompts via OpenRouter Nano Banana Pro |
@@ -99,11 +99,12 @@ On first run, you'll be prompted to select a model. Your choice is saved as `mai
99
99
  | Provider | Env Variable | Models |
100
100
  |-------------|-----------------------|-------------------------------------------------------------------------------|
101
101
  | anthropic | `ANTHROPIC_API_KEY` | sonnet, opus |
102
+ | claude | N/A (OAuth) | sonnet@claude, opus@claude (requires Claude Pro/Max subscription) |
102
103
  | openai | `OPENAI_API_KEY` | gpt-5.2 |
103
104
  | openrouter | `OPENROUTER_API_KEY` | gpt-5.2, gpt-5.2-fast, gpt-5.1-codex-max, sonnet, opus, haiku, kimi, gemini-* |
104
105
  | deepseek | `DEEPSEEK_API_KEY` | deepseek |
105
106
  | moonshot | `MOONSHOT_API_KEY` | kimi@moonshot |
106
- | codex | N/A (OAuth) | gpt-5.2-codex |
107
+ | codex | N/A (OAuth) | gpt-5.2-codex (requires ChatGPT Pro subscription) |
107
108
 
108
109
  List all configured providers and models:
109
110
 
@@ -113,6 +114,26 @@ klaude list
113
114
 
114
115
  Models from providers without a valid API key are shown as dimmed/unavailable.
115
116
 
117
+ #### OAuth Login
118
+
119
+ For subscription-based providers (Claude Pro/Max, ChatGPT Pro), use the login command:
120
+
121
+ ```bash
122
+ # Interactive provider selection
123
+ klaude login
124
+
125
+ # Or specify provider directly
126
+ klaude login claude # Claude Pro/Max subscription
127
+ klaude login codex # ChatGPT Pro subscription
128
+ ```
129
+
130
+ To logout:
131
+
132
+ ```bash
133
+ klaude logout claude
134
+ klaude logout codex
135
+ ```
136
+
116
137
  #### Custom Configuration
117
138
 
118
139
  User config file: `~/.klaude/klaude-config.yaml`
@@ -219,7 +240,6 @@ provider_list:
219
240
  main_model: opus
220
241
 
221
242
  sub_agent_models:
222
- oracle: gpt-4.1
223
243
  explore: sonnet
224
244
  task: opus
225
245
  webagent: sonnet
@@ -248,12 +268,13 @@ provider_list:
248
268
  ##### Supported Protocols
249
269
 
250
270
  - `anthropic` - Anthropic Claude API
271
+ - `claude_oauth` - Claude OAuth (for Claude Pro/Max subscribers)
251
272
  - `openai` - OpenAI-compatible API
252
273
  - `responses` - OpenAI Responses API (for o-series, GPT-5, Codex)
253
274
  - `openrouter` - OpenRouter API
254
275
  - `google` - Google Gemini API
255
276
  - `bedrock` - AWS Bedrock (uses AWS credentials instead of api_key)
256
- - `codex` - OpenAI Codex CLI (OAuth-based)
277
+ - `codex_oauth` - OpenAI Codex CLI (OAuth-based, for ChatGPT Pro subscribers)
257
278
 
258
279
  List configured providers and models:
259
280
 
@@ -353,4 +374,4 @@ The main agent can spawn specialized sub-agents for specific tasks:
353
374
  | **Explore** | Fast codebase exploration - find files, search code, answer questions about the codebase |
354
375
  | **Task** | Handle complex multi-step tasks autonomously |
355
376
  | **WebAgent** | Search the web, fetch pages, and analyze content |
356
- | **Oracle** | Advanced reasoning advisor for code reviews, architecture planning, and bug analysis |
377
+ | **ImageGen** | Generate images from text prompts via OpenRouter Nano Banana Pro |
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "klaude-code"
7
- version = "1.8.0"
7
+ version = "2.0.0"
8
8
  description = "Minimal code agent CLI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -16,11 +16,12 @@ dependencies = [
16
16
  "google-genai>=1.56.0",
17
17
  "markdown-it-py>=4.0.0",
18
18
  "openai>=1.102.0",
19
- "pillow>=12.0.0",
19
+ "pillow>=9.1,<11.0",
20
20
  "prompt-toolkit>=3.0.52",
21
21
  "pydantic>=2.11.7",
22
22
  "pyyaml>=6.0.2",
23
23
  "rich>=14.1.0",
24
+ "term-image>=0.7.2",
24
25
  "trafilatura>=2.0.0",
25
26
  "typer>=0.17.3",
26
27
  ]
@@ -0,0 +1,97 @@
1
+ """Base classes for authentication token management."""
2
+
3
+ import json
4
+ import time
5
+ from abc import ABC, abstractmethod
6
+ from pathlib import Path
7
+ from typing import Any, cast
8
+
9
+ from pydantic import BaseModel
10
+
11
+ KLAUDE_AUTH_FILE = Path.home() / ".klaude" / "klaude-auth.json"
12
+
13
+
14
+ class BaseAuthState(BaseModel):
15
+ """Base authentication state with common OAuth fields."""
16
+
17
+ access_token: str
18
+ refresh_token: str
19
+ expires_at: int # Unix timestamp
20
+
21
+ def is_expired(self, buffer_seconds: int = 300) -> bool:
22
+ """Check if token is expired or will expire soon."""
23
+ return time.time() + buffer_seconds >= self.expires_at
24
+
25
+
26
+ class BaseTokenManager[T: BaseAuthState](ABC):
27
+ """Base class for OAuth token management."""
28
+
29
+ def __init__(self, auth_file: Path | None = None):
30
+ self.auth_file = auth_file or KLAUDE_AUTH_FILE
31
+ self._state: T | None = None
32
+
33
+ @property
34
+ @abstractmethod
35
+ def storage_key(self) -> str:
36
+ """Key used to store this auth state in the JSON file."""
37
+ ...
38
+
39
+ @abstractmethod
40
+ def _create_state(self, data: dict[str, Any]) -> T:
41
+ """Create state instance from dict data."""
42
+ ...
43
+
44
+ def _load_store(self) -> dict[str, Any]:
45
+ if not self.auth_file.exists():
46
+ return {}
47
+ try:
48
+ data: Any = json.loads(self.auth_file.read_text())
49
+ if isinstance(data, dict):
50
+ return cast(dict[str, Any], data)
51
+ return {}
52
+ except (json.JSONDecodeError, ValueError):
53
+ return {}
54
+
55
+ def _save_store(self, data: dict[str, Any]) -> None:
56
+ self.auth_file.parent.mkdir(parents=True, exist_ok=True)
57
+ self.auth_file.write_text(json.dumps(data, indent=2))
58
+
59
+ def load(self) -> T | None:
60
+ """Load authentication state from file."""
61
+ data: Any = self._load_store().get(self.storage_key)
62
+ if not isinstance(data, dict):
63
+ return None
64
+ try:
65
+ self._state = self._create_state(cast(dict[str, Any], data))
66
+ return self._state
67
+ except ValueError:
68
+ return None
69
+
70
+ def save(self, state: T) -> None:
71
+ """Save authentication state to file."""
72
+ store = self._load_store()
73
+ store[self.storage_key] = state.model_dump(mode="json")
74
+ self._save_store(store)
75
+ self._state = state
76
+
77
+ def delete(self) -> None:
78
+ """Delete stored tokens."""
79
+ store = self._load_store()
80
+ store.pop(self.storage_key, None)
81
+ if len(store) == 0:
82
+ if self.auth_file.exists():
83
+ self.auth_file.unlink()
84
+ else:
85
+ self._save_store(store)
86
+ self._state = None
87
+
88
+ def is_logged_in(self) -> bool:
89
+ """Check if user is logged in."""
90
+ state = self._state or self.load()
91
+ return state is not None
92
+
93
+ def get_state(self) -> T | None:
94
+ """Get current authentication state."""
95
+ if self._state is None:
96
+ self._state = self.load()
97
+ return self._state
@@ -0,0 +1,6 @@
1
+ """Claude (Anthropic OAuth) authentication helpers."""
2
+
3
+ from .oauth import ClaudeOAuth
4
+ from .token_manager import ClaudeAuthState, ClaudeTokenManager
5
+
6
+ __all__ = ["ClaudeAuthState", "ClaudeOAuth", "ClaudeTokenManager"]
@@ -0,0 +1,9 @@
1
+ """Exceptions for Claude OAuth authentication."""
2
+
3
+
4
+ class ClaudeAuthError(Exception):
5
+ """Base class for Claude auth errors."""
6
+
7
+
8
+ class ClaudeNotLoggedInError(ClaudeAuthError):
9
+ """Raised when no valid Claude OAuth session is available."""
@@ -0,0 +1,172 @@
1
+ """OAuth PKCE flow for Claude (Anthropic OAuth) authentication."""
2
+
3
+ import base64
4
+ import hashlib
5
+ import secrets
6
+ import time
7
+ from collections.abc import Callable
8
+
9
+ import httpx
10
+
11
+ from klaude_code.auth.claude.exceptions import ClaudeAuthError, ClaudeNotLoggedInError
12
+ from klaude_code.auth.claude.token_manager import ClaudeAuthState, ClaudeTokenManager
13
+
14
+
15
+ def _decode_base64(value: str) -> str:
16
+ return base64.b64decode(value).decode()
17
+
18
+
19
+ # OAuth configuration (Claude Pro/Max)
20
+ CLIENT_ID = _decode_base64("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl")
21
+ AUTHORIZE_URL = "https://claude.ai/oauth/authorize"
22
+ TOKEN_URL = "https://console.anthropic.com/v1/oauth/token"
23
+ REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback"
24
+ SCOPE = "org:create_api_key user:profile user:inference"
25
+
26
+
27
+ def generate_code_verifier() -> str:
28
+ """Generate a random code verifier for PKCE."""
29
+ return secrets.token_urlsafe(64)[:128]
30
+
31
+
32
+ def generate_code_challenge(verifier: str) -> str:
33
+ """Generate code challenge from verifier using S256 method."""
34
+ digest = hashlib.sha256(verifier.encode()).digest()
35
+ return base64.urlsafe_b64encode(digest).rstrip(b"=").decode()
36
+
37
+
38
+ def build_authorize_url(code_challenge: str, state: str) -> str:
39
+ """Build the authorization URL with all required parameters."""
40
+ # Note: the `code=true` parameter is required for the console callback flow.
41
+ params = {
42
+ "code": "true",
43
+ "client_id": CLIENT_ID,
44
+ "response_type": "code",
45
+ "redirect_uri": REDIRECT_URI,
46
+ "scope": SCOPE,
47
+ "code_challenge": code_challenge,
48
+ "code_challenge_method": "S256",
49
+ "state": state,
50
+ }
51
+
52
+ encoded = httpx.QueryParams(params)
53
+ return f"{AUTHORIZE_URL}?{encoded}"
54
+
55
+
56
+ def _parse_user_code(value: str) -> tuple[str, str | None]:
57
+ raw = value.strip()
58
+ if "#" in raw:
59
+ code, state = raw.split("#", 1)
60
+ return code.strip(), state.strip()
61
+ return raw, None
62
+
63
+
64
+ class ClaudeOAuth:
65
+ """Handle OAuth PKCE flow for Claude (Anthropic OAuth) authentication."""
66
+
67
+ def __init__(self, token_manager: ClaudeTokenManager | None = None):
68
+ self.token_manager = token_manager or ClaudeTokenManager()
69
+
70
+ def login(
71
+ self,
72
+ *,
73
+ on_auth_url: Callable[[str], None],
74
+ on_prompt_code: Callable[[], str],
75
+ ) -> ClaudeAuthState:
76
+ """Run the complete OAuth login flow."""
77
+ verifier = generate_code_verifier()
78
+ challenge = generate_code_challenge(verifier)
79
+
80
+ # Some flows require `state` to be echoed back for token exchange.
81
+ state = verifier
82
+
83
+ auth_url = build_authorize_url(challenge, state)
84
+ on_auth_url(auth_url)
85
+
86
+ raw_user_code = on_prompt_code()
87
+ code, returned_state = _parse_user_code(raw_user_code)
88
+ if not code:
89
+ raise ClaudeAuthError("No authorization code provided")
90
+
91
+ exchange_state = returned_state or state
92
+ auth_state = self._exchange_code(code=code, state=exchange_state, verifier=verifier)
93
+ self.token_manager.save(auth_state)
94
+ return auth_state
95
+
96
+ def _exchange_code(self, *, code: str, state: str, verifier: str) -> ClaudeAuthState:
97
+ """Exchange authorization code for tokens."""
98
+ payload = {
99
+ "grant_type": "authorization_code",
100
+ "client_id": CLIENT_ID,
101
+ "code": code,
102
+ "state": state,
103
+ "redirect_uri": REDIRECT_URI,
104
+ "code_verifier": verifier,
105
+ }
106
+
107
+ with httpx.Client() as client:
108
+ response = client.post(
109
+ TOKEN_URL,
110
+ json=payload,
111
+ headers={"Content-Type": "application/json"},
112
+ )
113
+
114
+ if response.status_code != 200:
115
+ raise ClaudeAuthError(f"Token exchange failed: {response.text}")
116
+
117
+ tokens = response.json()
118
+ access_token = tokens["access_token"]
119
+ refresh_token = tokens["refresh_token"]
120
+ expires_in = tokens.get("expires_in", 3600)
121
+
122
+ return ClaudeAuthState(
123
+ access_token=access_token,
124
+ refresh_token=refresh_token,
125
+ expires_at=int(time.time()) + int(expires_in),
126
+ )
127
+
128
+ def refresh(self) -> ClaudeAuthState:
129
+ """Refresh the access token using refresh token."""
130
+ state = self.token_manager.get_state()
131
+ if state is None:
132
+ raise ClaudeNotLoggedInError("Not logged in to Claude. Run 'klaude login claude' first.")
133
+
134
+ payload = {
135
+ "grant_type": "refresh_token",
136
+ "client_id": CLIENT_ID,
137
+ "refresh_token": state.refresh_token,
138
+ }
139
+
140
+ with httpx.Client() as client:
141
+ response = client.post(
142
+ TOKEN_URL,
143
+ json=payload,
144
+ headers={"Content-Type": "application/json"},
145
+ )
146
+
147
+ if response.status_code != 200:
148
+ raise ClaudeAuthError(f"Token refresh failed: {response.text}")
149
+
150
+ tokens = response.json()
151
+ access_token = tokens["access_token"]
152
+ refresh_token = tokens.get("refresh_token", state.refresh_token)
153
+ expires_in = tokens.get("expires_in", 3600)
154
+
155
+ new_state = ClaudeAuthState(
156
+ access_token=access_token,
157
+ refresh_token=refresh_token,
158
+ expires_at=int(time.time()) + int(expires_in),
159
+ )
160
+ self.token_manager.save(new_state)
161
+ return new_state
162
+
163
+ def ensure_valid_token(self) -> str:
164
+ """Ensure we have a valid access token, refreshing if needed."""
165
+ state = self.token_manager.get_state()
166
+ if state is None:
167
+ raise ClaudeNotLoggedInError("Not logged in to Claude. Run 'klaude login claude' first.")
168
+
169
+ if state.is_expired():
170
+ state = self.refresh()
171
+
172
+ return state.access_token
@@ -0,0 +1,26 @@
1
+ """Token storage and management for Claude (Anthropic OAuth) authentication."""
2
+
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ from klaude_code.auth.base import BaseAuthState, BaseTokenManager
7
+
8
+
9
+ class ClaudeAuthState(BaseAuthState):
10
+ """Stored authentication state for Claude OAuth."""
11
+
12
+ pass
13
+
14
+
15
+ class ClaudeTokenManager(BaseTokenManager[ClaudeAuthState]):
16
+ """Manage Claude OAuth tokens."""
17
+
18
+ def __init__(self, auth_file: Path | None = None):
19
+ super().__init__(auth_file)
20
+
21
+ @property
22
+ def storage_key(self) -> str:
23
+ return "claude"
24
+
25
+ def _create_state(self, data: dict[str, Any]) -> ClaudeAuthState:
26
+ return ClaudeAuthState.model_validate(data)
@@ -0,0 +1,44 @@
1
+ """Token storage and management for Codex authentication."""
2
+
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ from klaude_code.auth.base import BaseAuthState, BaseTokenManager
7
+
8
+
9
+ class CodexAuthState(BaseAuthState):
10
+ """Stored authentication state for Codex."""
11
+
12
+ account_id: str
13
+
14
+
15
+ class CodexTokenManager(BaseTokenManager[CodexAuthState]):
16
+ """Manage Codex OAuth tokens."""
17
+
18
+ def __init__(self, auth_file: Path | None = None):
19
+ super().__init__(auth_file)
20
+
21
+ @property
22
+ def storage_key(self) -> str:
23
+ return "codex"
24
+
25
+ def _create_state(self, data: dict[str, Any]) -> CodexAuthState:
26
+ return CodexAuthState.model_validate(data)
27
+
28
+ def get_access_token(self) -> str:
29
+ """Get access token, raising if not logged in."""
30
+ state = self.get_state()
31
+ if state is None:
32
+ from klaude_code.auth.codex.exceptions import CodexNotLoggedInError
33
+
34
+ raise CodexNotLoggedInError("Not logged in to Codex. Run 'klaude login codex' first.")
35
+ return state.access_token
36
+
37
+ def get_account_id(self) -> str:
38
+ """Get account ID, raising if not logged in."""
39
+ state = self.get_state()
40
+ if state is None:
41
+ from klaude_code.auth.codex.exceptions import CodexNotLoggedInError
42
+
43
+ raise CodexNotLoggedInError("Not logged in to Codex. Run 'klaude login codex' first.")
44
+ return state.account_id