klaude-code 1.7.1__tar.gz → 1.9.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 (229) hide show
  1. {klaude_code-1.7.1 → klaude_code-1.9.0}/PKG-INFO +37 -5
  2. {klaude_code-1.7.1 → klaude_code-1.9.0}/README.md +36 -4
  3. {klaude_code-1.7.1 → klaude_code-1.9.0}/pyproject.toml +1 -1
  4. klaude_code-1.9.0/src/klaude_code/auth/base.py +101 -0
  5. klaude_code-1.9.0/src/klaude_code/auth/claude/__init__.py +6 -0
  6. klaude_code-1.9.0/src/klaude_code/auth/claude/exceptions.py +9 -0
  7. klaude_code-1.9.0/src/klaude_code/auth/claude/oauth.py +172 -0
  8. klaude_code-1.9.0/src/klaude_code/auth/claude/token_manager.py +26 -0
  9. klaude_code-1.9.0/src/klaude_code/auth/codex/token_manager.py +44 -0
  10. klaude_code-1.9.0/src/klaude_code/cli/auth_cmd.py +154 -0
  11. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/cli/config_cmd.py +4 -2
  12. klaude_code-1.9.0/src/klaude_code/cli/cost_cmd.py +343 -0
  13. klaude_code-1.9.0/src/klaude_code/cli/list_model.py +355 -0
  14. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/cli/main.py +2 -0
  15. klaude_code-1.9.0/src/klaude_code/command/prompt-commit.md +73 -0
  16. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/status_cmd.py +8 -8
  17. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/config/assets/builtin_config.yaml +36 -3
  18. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/config/config.py +24 -5
  19. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/config/thinking.py +4 -4
  20. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/prompt.py +1 -1
  21. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/anthropic/client.py +28 -3
  22. klaude_code-1.9.0/src/klaude_code/llm/claude/__init__.py +3 -0
  23. klaude_code-1.9.0/src/klaude_code/llm/claude/client.py +95 -0
  24. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/codex/client.py +1 -1
  25. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/registry.py +3 -1
  26. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/protocol/llm_param.py +2 -1
  27. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/protocol/sub_agent/__init__.py +1 -2
  28. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/session/session.py +4 -4
  29. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/metadata.py +6 -26
  30. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/rich/theme.py +6 -5
  31. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/terminal/selector.py +25 -2
  32. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/utils/common.py +46 -0
  33. klaude_code-1.7.1/src/klaude_code/auth/codex/token_manager.py +0 -84
  34. klaude_code-1.7.1/src/klaude_code/cli/auth_cmd.py +0 -73
  35. klaude_code-1.7.1/src/klaude_code/cli/list_model.py +0 -307
  36. klaude_code-1.7.1/src/klaude_code/command/prompt-jj-describe.md +0 -32
  37. klaude_code-1.7.1/src/klaude_code/core/prompts/prompt-sub-agent-oracle.md +0 -22
  38. klaude_code-1.7.1/src/klaude_code/protocol/sub_agent/oracle.py +0 -91
  39. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/__init__.py +0 -0
  40. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/auth/__init__.py +0 -0
  41. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/auth/codex/__init__.py +0 -0
  42. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/auth/codex/exceptions.py +0 -0
  43. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/auth/codex/jwt_utils.py +0 -0
  44. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/auth/codex/oauth.py +0 -0
  45. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/cli/__init__.py +0 -0
  46. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/cli/debug.py +0 -0
  47. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/cli/runtime.py +0 -0
  48. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/cli/self_update.py +0 -0
  49. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/cli/session_cmd.py +0 -0
  50. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/__init__.py +0 -0
  51. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/clear_cmd.py +0 -0
  52. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/command_abc.py +0 -0
  53. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/debug_cmd.py +0 -0
  54. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/export_cmd.py +0 -0
  55. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/export_online_cmd.py +0 -0
  56. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/fork_session_cmd.py +0 -0
  57. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/help_cmd.py +0 -0
  58. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/model_cmd.py +0 -0
  59. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/model_select.py +0 -0
  60. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/prompt-init.md +0 -0
  61. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/prompt_command.py +0 -0
  62. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/refresh_cmd.py +0 -0
  63. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/registry.py +0 -0
  64. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/release_notes_cmd.py +0 -0
  65. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/resume_cmd.py +0 -0
  66. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/terminal_setup_cmd.py +0 -0
  67. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/command/thinking_cmd.py +0 -0
  68. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/config/__init__.py +0 -0
  69. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/config/assets/__init__.py +0 -0
  70. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/config/builtin_config.py +0 -0
  71. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/config/select_model.py +0 -0
  72. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/const.py +0 -0
  73. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/__init__.py +0 -0
  74. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/agent.py +0 -0
  75. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/executor.py +0 -0
  76. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/manager/__init__.py +0 -0
  77. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/manager/llm_clients.py +0 -0
  78. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/manager/llm_clients_builder.py +0 -0
  79. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/manager/sub_agent_manager.py +0 -0
  80. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/prompts/prompt-claude-code.md +0 -0
  81. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +0 -0
  82. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +0 -0
  83. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/prompts/prompt-codex.md +0 -0
  84. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/prompts/prompt-gemini.md +0 -0
  85. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/prompts/prompt-minimal.md +0 -0
  86. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/prompts/prompt-sub-agent-explore.md +0 -0
  87. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/prompts/prompt-sub-agent-web.md +0 -0
  88. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/prompts/prompt-sub-agent.md +0 -0
  89. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/reminders.py +0 -0
  90. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/task.py +0 -0
  91. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/__init__.py +0 -0
  92. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/__init__.py +0 -0
  93. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/_utils.py +0 -0
  94. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/apply_patch.py +0 -0
  95. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/apply_patch_tool.md +0 -0
  96. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/apply_patch_tool.py +0 -0
  97. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/diff_builder.py +0 -0
  98. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/edit_tool.md +0 -0
  99. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/edit_tool.py +0 -0
  100. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/move_tool.md +0 -0
  101. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/move_tool.py +0 -0
  102. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/read_tool.md +0 -0
  103. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/read_tool.py +0 -0
  104. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/write_tool.md +0 -0
  105. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/file/write_tool.py +0 -0
  106. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/report_back_tool.py +0 -0
  107. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/shell/__init__.py +0 -0
  108. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/shell/bash_tool.md +0 -0
  109. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/shell/bash_tool.py +0 -0
  110. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/shell/command_safety.py +0 -0
  111. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/skill/__init__.py +0 -0
  112. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/skill/skill_tool.md +0 -0
  113. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/skill/skill_tool.py +0 -0
  114. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/sub_agent_tool.py +0 -0
  115. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/todo/__init__.py +0 -0
  116. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/todo/todo_write_tool.md +0 -0
  117. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/todo/todo_write_tool.py +0 -0
  118. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/todo/todo_write_tool_raw.md +0 -0
  119. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/todo/update_plan_tool.md +0 -0
  120. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/todo/update_plan_tool.py +0 -0
  121. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/tool_abc.py +0 -0
  122. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/tool_context.py +0 -0
  123. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/tool_registry.py +0 -0
  124. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/tool_runner.py +0 -0
  125. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/truncation.py +0 -0
  126. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/web/__init__.py +0 -0
  127. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/web/mermaid_tool.md +0 -0
  128. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/web/mermaid_tool.py +0 -0
  129. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/web/web_fetch_tool.md +0 -0
  130. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/web/web_fetch_tool.py +0 -0
  131. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/web/web_search_tool.md +0 -0
  132. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/tool/web/web_search_tool.py +0 -0
  133. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/core/turn.py +0 -0
  134. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/__init__.py +0 -0
  135. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/anthropic/__init__.py +0 -0
  136. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/anthropic/input.py +0 -0
  137. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/bedrock/__init__.py +0 -0
  138. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/bedrock/client.py +0 -0
  139. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/client.py +0 -0
  140. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/codex/__init__.py +0 -0
  141. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/google/__init__.py +0 -0
  142. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/google/client.py +0 -0
  143. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/google/input.py +0 -0
  144. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/input_common.py +0 -0
  145. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/openai_compatible/__init__.py +0 -0
  146. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/openai_compatible/client.py +0 -0
  147. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/openai_compatible/input.py +0 -0
  148. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/openai_compatible/stream.py +0 -0
  149. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/openai_compatible/tool_call_accumulator.py +0 -0
  150. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/openrouter/__init__.py +0 -0
  151. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/openrouter/client.py +0 -0
  152. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/openrouter/input.py +0 -0
  153. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/openrouter/reasoning.py +0 -0
  154. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/responses/__init__.py +0 -0
  155. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/responses/client.py +0 -0
  156. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/responses/input.py +0 -0
  157. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/llm/usage.py +0 -0
  158. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/protocol/__init__.py +0 -0
  159. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/protocol/commands.py +0 -0
  160. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/protocol/events.py +0 -0
  161. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/protocol/model.py +0 -0
  162. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/protocol/op.py +0 -0
  163. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/protocol/op_handler.py +0 -0
  164. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/protocol/sub_agent/explore.py +0 -0
  165. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/protocol/sub_agent/task.py +0 -0
  166. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/protocol/sub_agent/web.py +0 -0
  167. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/protocol/tools.py +0 -0
  168. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/session/__init__.py +0 -0
  169. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/session/codec.py +0 -0
  170. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/session/export.py +0 -0
  171. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/session/selector.py +0 -0
  172. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/session/store.py +0 -0
  173. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/session/templates/export_session.html +0 -0
  174. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/session/templates/mermaid_viewer.html +0 -0
  175. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/skill/__init__.py +0 -0
  176. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/skill/assets/deslop/SKILL.md +0 -0
  177. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/skill/assets/dev-docs/SKILL.md +0 -0
  178. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/skill/assets/handoff/SKILL.md +0 -0
  179. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/skill/assets/jj-workspace/SKILL.md +0 -0
  180. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/skill/assets/skill-creator/SKILL.md +0 -0
  181. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/skill/loader.py +0 -0
  182. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/skill/manager.py +0 -0
  183. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/skill/system_skills.py +0 -0
  184. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/trace/__init__.py +0 -0
  185. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/trace/log.py +0 -0
  186. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/__init__.py +0 -0
  187. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/core/__init__.py +0 -0
  188. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/core/display.py +0 -0
  189. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/core/input.py +0 -0
  190. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/core/stage_manager.py +0 -0
  191. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/__init__.py +0 -0
  192. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/debug/__init__.py +0 -0
  193. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/debug/display.py +0 -0
  194. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/exec/__init__.py +0 -0
  195. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/exec/display.py +0 -0
  196. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/repl/__init__.py +0 -0
  197. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/repl/clipboard.py +0 -0
  198. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/repl/completers.py +0 -0
  199. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/repl/display.py +0 -0
  200. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/repl/event_handler.py +0 -0
  201. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/repl/input_prompt_toolkit.py +0 -0
  202. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/repl/key_bindings.py +0 -0
  203. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/modes/repl/renderer.py +0 -0
  204. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/__init__.py +0 -0
  205. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/assistant.py +0 -0
  206. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/bash_syntax.py +0 -0
  207. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/common.py +0 -0
  208. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/developer.py +0 -0
  209. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/diffs.py +0 -0
  210. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/errors.py +0 -0
  211. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/mermaid_viewer.py +0 -0
  212. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/sub_agent.py +0 -0
  213. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/thinking.py +0 -0
  214. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/tools.py +0 -0
  215. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/renderers/user_input.py +0 -0
  216. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/rich/__init__.py +0 -0
  217. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/rich/cjk_wrap.py +0 -0
  218. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/rich/code_panel.py +0 -0
  219. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/rich/live.py +0 -0
  220. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/rich/markdown.py +0 -0
  221. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/rich/quote.py +0 -0
  222. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/rich/searchable_text.py +0 -0
  223. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/rich/status.py +0 -0
  224. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/terminal/__init__.py +0 -0
  225. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/terminal/color.py +0 -0
  226. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/terminal/control.py +0 -0
  227. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/terminal/notifier.py +0 -0
  228. {klaude_code-1.7.1 → klaude_code-1.9.0}/src/klaude_code/ui/terminal/progress_bar.py +0 -0
  229. {klaude_code-1.7.1 → klaude_code-1.9.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.7.1
3
+ Version: 1.9.0
4
4
  Summary: Minimal code agent CLI
5
5
  Requires-Dist: anthropic>=0.66.0
6
6
  Requires-Dist: chardet>=5.2.0
@@ -120,11 +120,12 @@ On first run, you'll be prompted to select a model. Your choice is saved as `mai
120
120
  | Provider | Env Variable | Models |
121
121
  |-------------|-----------------------|-------------------------------------------------------------------------------|
122
122
  | anthropic | `ANTHROPIC_API_KEY` | sonnet, opus |
123
+ | claude | N/A (OAuth) | sonnet@claude, opus@claude (requires Claude Pro/Max subscription) |
123
124
  | openai | `OPENAI_API_KEY` | gpt-5.2 |
124
125
  | openrouter | `OPENROUTER_API_KEY` | gpt-5.2, gpt-5.2-fast, gpt-5.1-codex-max, sonnet, opus, haiku, kimi, gemini-* |
125
126
  | deepseek | `DEEPSEEK_API_KEY` | deepseek |
126
127
  | moonshot | `MOONSHOT_API_KEY` | kimi@moonshot |
127
- | codex | N/A (OAuth) | gpt-5.2-codex |
128
+ | codex | N/A (OAuth) | gpt-5.2-codex (requires ChatGPT Pro subscription) |
128
129
 
129
130
  List all configured providers and models:
130
131
 
@@ -134,6 +135,26 @@ klaude list
134
135
 
135
136
  Models from providers without a valid API key are shown as dimmed/unavailable.
136
137
 
138
+ #### OAuth Login
139
+
140
+ For subscription-based providers (Claude Pro/Max, ChatGPT Pro), use the login command:
141
+
142
+ ```bash
143
+ # Interactive provider selection
144
+ klaude login
145
+
146
+ # Or specify provider directly
147
+ klaude login claude # Claude Pro/Max subscription
148
+ klaude login codex # ChatGPT Pro subscription
149
+ ```
150
+
151
+ To logout:
152
+
153
+ ```bash
154
+ klaude logout claude
155
+ klaude logout codex
156
+ ```
157
+
137
158
  #### Custom Configuration
138
159
 
139
160
  User config file: `~/.klaude/klaude-config.yaml`
@@ -240,7 +261,6 @@ provider_list:
240
261
  main_model: opus
241
262
 
242
263
  sub_agent_models:
243
- oracle: gpt-4.1
244
264
  explore: sonnet
245
265
  task: opus
246
266
  webagent: sonnet
@@ -269,12 +289,13 @@ provider_list:
269
289
  ##### Supported Protocols
270
290
 
271
291
  - `anthropic` - Anthropic Claude API
292
+ - `claude_oauth` - Claude OAuth (for Claude Pro/Max subscribers)
272
293
  - `openai` - OpenAI-compatible API
273
294
  - `responses` - OpenAI Responses API (for o-series, GPT-5, Codex)
274
295
  - `openrouter` - OpenRouter API
275
296
  - `google` - Google Gemini API
276
297
  - `bedrock` - AWS Bedrock (uses AWS credentials instead of api_key)
277
- - `codex` - OpenAI Codex CLI (OAuth-based)
298
+ - `codex_oauth` - OpenAI Codex CLI (OAuth-based, for ChatGPT Pro subscribers)
278
299
 
279
300
  List configured providers and models:
280
301
 
@@ -297,6 +318,18 @@ klaude session clean --min 10
297
318
  klaude session clean-all
298
319
  ```
299
320
 
321
+ ### Cost Tracking
322
+
323
+ View aggregated usage statistics across all sessions:
324
+
325
+ ```bash
326
+ # Show all historical usage data
327
+ klaude cost
328
+
329
+ # Show usage for the last 7 days only
330
+ klaude cost --days 7
331
+ ```
332
+
300
333
  ### Slash Commands
301
334
 
302
335
  Inside the interactive session (`klaude`), use these commands to streamline your workflow:
@@ -362,4 +395,3 @@ The main agent can spawn specialized sub-agents for specific tasks:
362
395
  | **Explore** | Fast codebase exploration - find files, search code, answer questions about the codebase |
363
396
  | **Task** | Handle complex multi-step tasks autonomously |
364
397
  | **WebAgent** | Search the web, fetch pages, and analyze content |
365
- | **Oracle** | Advanced reasoning advisor for code reviews, architecture planning, and bug analysis |
@@ -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
 
@@ -276,6 +297,18 @@ klaude session clean --min 10
276
297
  klaude session clean-all
277
298
  ```
278
299
 
300
+ ### Cost Tracking
301
+
302
+ View aggregated usage statistics across all sessions:
303
+
304
+ ```bash
305
+ # Show all historical usage data
306
+ klaude cost
307
+
308
+ # Show usage for the last 7 days only
309
+ klaude cost --days 7
310
+ ```
311
+
279
312
  ### Slash Commands
280
313
 
281
314
  Inside the interactive session (`klaude`), use these commands to streamline your workflow:
@@ -341,4 +374,3 @@ The main agent can spawn specialized sub-agents for specific tasks:
341
374
  | **Explore** | Fast codebase exploration - find files, search code, answer questions about the codebase |
342
375
  | **Task** | Handle complex multi-step tasks autonomously |
343
376
  | **WebAgent** | Search the web, fetch pages, and analyze content |
344
- | **Oracle** | Advanced reasoning advisor for code reviews, architecture planning, and bug analysis |
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "klaude-code"
7
- version = "1.7.1"
7
+ version = "1.9.0"
8
8
  description = "Minimal code agent CLI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -0,0 +1,101 @@
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, Generic, TypeVar, cast
8
+
9
+ from pydantic import BaseModel
10
+
11
+
12
+ KLAUDE_AUTH_FILE = Path.home() / ".klaude" / "klaude-auth.json"
13
+
14
+
15
+ class BaseAuthState(BaseModel):
16
+ """Base authentication state with common OAuth fields."""
17
+
18
+ access_token: str
19
+ refresh_token: str
20
+ expires_at: int # Unix timestamp
21
+
22
+ def is_expired(self, buffer_seconds: int = 300) -> bool:
23
+ """Check if token is expired or will expire soon."""
24
+ return time.time() + buffer_seconds >= self.expires_at
25
+
26
+
27
+ T = TypeVar("T", bound=BaseAuthState)
28
+
29
+
30
+ class BaseTokenManager(ABC, Generic[T]):
31
+ """Base class for OAuth token management."""
32
+
33
+ def __init__(self, auth_file: Path | None = None):
34
+ self.auth_file = auth_file or KLAUDE_AUTH_FILE
35
+ self._state: T | None = None
36
+
37
+ @property
38
+ @abstractmethod
39
+ def storage_key(self) -> str:
40
+ """Key used to store this auth state in the JSON file."""
41
+ ...
42
+
43
+ @abstractmethod
44
+ def _create_state(self, data: dict[str, Any]) -> T:
45
+ """Create state instance from dict data."""
46
+ ...
47
+
48
+ def _load_store(self) -> dict[str, Any]:
49
+ if not self.auth_file.exists():
50
+ return {}
51
+ try:
52
+ data: Any = json.loads(self.auth_file.read_text())
53
+ if isinstance(data, dict):
54
+ return cast(dict[str, Any], data)
55
+ return {}
56
+ except (json.JSONDecodeError, ValueError):
57
+ return {}
58
+
59
+ def _save_store(self, data: dict[str, Any]) -> None:
60
+ self.auth_file.parent.mkdir(parents=True, exist_ok=True)
61
+ self.auth_file.write_text(json.dumps(data, indent=2))
62
+
63
+ def load(self) -> T | None:
64
+ """Load authentication state from file."""
65
+ data: Any = self._load_store().get(self.storage_key)
66
+ if not isinstance(data, dict):
67
+ return None
68
+ try:
69
+ self._state = self._create_state(cast(dict[str, Any], data))
70
+ return self._state
71
+ except ValueError:
72
+ return None
73
+
74
+ def save(self, state: T) -> None:
75
+ """Save authentication state to file."""
76
+ store = self._load_store()
77
+ store[self.storage_key] = state.model_dump(mode="json")
78
+ self._save_store(store)
79
+ self._state = state
80
+
81
+ def delete(self) -> None:
82
+ """Delete stored tokens."""
83
+ store = self._load_store()
84
+ store.pop(self.storage_key, None)
85
+ if len(store) == 0:
86
+ if self.auth_file.exists():
87
+ self.auth_file.unlink()
88
+ else:
89
+ self._save_store(store)
90
+ self._state = None
91
+
92
+ def is_logged_in(self) -> bool:
93
+ """Check if user is logged in."""
94
+ state = self._state or self.load()
95
+ return state is not None
96
+
97
+ def get_state(self) -> T | None:
98
+ """Get current authentication state."""
99
+ if self._state is None:
100
+ self._state = self.load()
101
+ 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