chattool 7.0.3__tar.gz → 7.0.4__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 (207) hide show
  1. {chattool-7.0.3/src/chattool.egg-info → chattool-7.0.4}/PKG-INFO +7 -6
  2. {chattool-7.0.3 → chattool-7.0.4}/README.md +3 -2
  3. {chattool-7.0.3 → chattool-7.0.4}/pyproject.toml +3 -4
  4. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/__init__.py +1 -1
  5. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/client/main.py +0 -1
  6. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/__init__.py +0 -2
  7. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/elements.py +0 -2
  8. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/openai.py +25 -1
  9. chattool-7.0.4/src/chattool/explore/__init__.py +5 -0
  10. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/__init__.py +4 -0
  11. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/cli.py +8 -0
  12. chattool-7.0.4/src/chattool/serve/oauth.py +249 -0
  13. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/alias.py +0 -1
  14. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/cli.py +0 -2
  15. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/options.py +0 -98
  16. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/render.py +30 -15
  17. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/templates/default/en/AGENTS.md +7 -5
  18. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/templates/default/en/projects/README.md +1 -1
  19. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/templates/default/zh/AGENTS.md +7 -5
  20. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/templates/default/zh/projects/README.md +1 -1
  21. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/__init__.py +0 -2
  22. chattool-7.0.4/src/chattool/tools/crs/__init__.py +19 -0
  23. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/crs/cli.py +92 -0
  24. chattool-7.0.4/src/chattool/tools/crs/openai_oauth.py +138 -0
  25. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/__init__.py +6 -1
  26. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/cli.py +113 -9
  27. chattool-7.0.4/src/chattool/tools/image/codex.py +393 -0
  28. chattool-7.0.4/src/chattool/tools/image/helpers.py +96 -0
  29. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/pypi/main.py +1 -0
  30. {chattool-7.0.3 → chattool-7.0.4/src/chattool.egg-info}/PKG-INFO +7 -6
  31. {chattool-7.0.3 → chattool-7.0.4}/src/chattool.egg-info/SOURCES.txt +3 -11
  32. {chattool-7.0.3 → chattool-7.0.4}/src/chattool.egg-info/requires.txt +3 -4
  33. chattool-7.0.3/src/chattool/config/github.py +0 -27
  34. chattool-7.0.3/src/chattool/explore/__init__.py +0 -5
  35. chattool-7.0.3/src/chattool/explore/github/__init__.py +0 -6
  36. chattool-7.0.3/src/chattool/explore/github/client.py +0 -106
  37. chattool-7.0.3/src/chattool/explore/github/models.py +0 -42
  38. chattool-7.0.3/src/chattool/tools/crs/__init__.py +0 -5
  39. chattool-7.0.3/src/chattool/tools/github/__init__.py +0 -11
  40. chattool-7.0.3/src/chattool/tools/github/api.py +0 -368
  41. chattool-7.0.3/src/chattool/tools/github/cli.py +0 -448
  42. chattool-7.0.3/src/chattool/tools/github/client.py +0 -153
  43. chattool-7.0.3/src/chattool/tools/github/commands.py +0 -250
  44. chattool-7.0.3/src/chattool/tools/github/render.py +0 -212
  45. chattool-7.0.3/src/chattool/tools/github/requests.py +0 -295
  46. chattool-7.0.3/src/chattool/tools/image/helpers.py +0 -28
  47. {chattool-7.0.3 → chattool-7.0.4}/LICENSE +0 -0
  48. {chattool-7.0.3 → chattool-7.0.4}/MANIFEST.in +0 -0
  49. {chattool-7.0.3 → chattool-7.0.4}/scripts/migrate_chattool_envs_to_chatarch.py +0 -0
  50. {chattool-7.0.3 → chattool-7.0.4}/setup.cfg +0 -0
  51. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/_all.py +0 -0
  52. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/client/__init__.py +0 -0
  53. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/client/cert_client.py +0 -0
  54. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/client/svg2gif_client.py +0 -0
  55. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/aliyun.py +0 -0
  56. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/azure.py +0 -0
  57. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/base.py +0 -0
  58. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/browser.py +0 -0
  59. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/crs.py +0 -0
  60. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/feishu.py +0 -0
  61. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/huggingface.py +0 -0
  62. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/liblib.py +0 -0
  63. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/pollinations.py +0 -0
  64. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/siliconflow.py +0 -0
  65. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/skills.py +0 -0
  66. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/tencent.py +0 -0
  67. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/tongyi.py +0 -0
  68. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/tplink.py +0 -0
  69. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/zulip.py +0 -0
  70. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/const.py +0 -0
  71. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/docker/__init__.py +0 -0
  72. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/docker/cli.py +0 -0
  73. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/docker/elements.py +0 -0
  74. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/docker/main.py +0 -0
  75. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/__init__.py +0 -0
  76. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/cli.py +0 -0
  77. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/client.py +0 -0
  78. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/daily.py +0 -0
  79. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/harvest.py +0 -0
  80. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/models.py +0 -0
  81. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/presets.py +0 -0
  82. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/query.py +0 -0
  83. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/cli.py +0 -0
  84. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/wordpress/__init__.py +0 -0
  85. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/wordpress/client.py +0 -0
  86. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/wordpress/models.py +0 -0
  87. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/interaction/__init__.py +0 -0
  88. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/interaction/command_schema.py +0 -0
  89. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/interaction/policy.py +0 -0
  90. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/interaction/warnings.py +0 -0
  91. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/llm/__init__.py +0 -0
  92. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/llm/chattype.py +0 -0
  93. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/llm/response.py +0 -0
  94. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/mcp/__init__.py +0 -0
  95. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/mcp/catalog.py +0 -0
  96. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/mcp/cli.py +0 -0
  97. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/mcp/server.py +0 -0
  98. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/capture.py +0 -0
  99. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/cert_server.py +0 -0
  100. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/lark_serve.py +0 -0
  101. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/local.py +0 -0
  102. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/svg2gif.py +0 -0
  103. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/__init__.py +0 -0
  104. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/hermes/install.sh +0 -0
  105. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/nvm.sh +0 -0
  106. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/README.md +0 -0
  107. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/commands/chatloop-help.md +0 -0
  108. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/commands/chatloop-project.md +0 -0
  109. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/commands/chatloop-status.md +0 -0
  110. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/commands/chatloop-stop.md +0 -0
  111. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/commands/chatloop.md +0 -0
  112. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/plugins/chatloop/index.ts +0 -0
  113. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/plugins/chatloop/package.json +0 -0
  114. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/cc_connect.py +0 -0
  115. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/chrome.py +0 -0
  116. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/claude.py +0 -0
  117. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/cli.py +0 -0
  118. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/codex.py +0 -0
  119. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/docker.py +0 -0
  120. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/elements.py +0 -0
  121. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/frp.py +0 -0
  122. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/hermes.py +0 -0
  123. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/lark_cli.py +0 -0
  124. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/mode_prompt.py +0 -0
  125. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/nodejs.py +0 -0
  126. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/opencode.py +0 -0
  127. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/opencode_chatloop.py +0 -0
  128. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/__init__.py +0 -0
  129. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/core.py +0 -0
  130. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace.py +0 -0
  131. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/zsh.py +0 -0
  132. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/skill/__init__.py +0 -0
  133. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/skill/cli.py +0 -0
  134. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/skill/interaction.py +0 -0
  135. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/skill/platforms.py +0 -0
  136. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/skill/validation.py +0 -0
  137. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/__init__.py +0 -0
  138. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/base.py +0 -0
  139. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/cli.py +0 -0
  140. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/playwright_impl.py +0 -0
  141. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/selenium.py +0 -0
  142. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/test_browser.py +0 -0
  143. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/test_module.py +0 -0
  144. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/test_remote.py +0 -0
  145. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/xhs_qrcode.py +0 -0
  146. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cc/__init__.py +0 -0
  147. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cc/cli.py +0 -0
  148. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cert/__init__.py +0 -0
  149. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cert/acme_dns_tiny.py +0 -0
  150. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cert/cert_server.py +0 -0
  151. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cert/cert_updater.py +0 -0
  152. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cert/cli.py +0 -0
  153. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cert/email.py +0 -0
  154. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/crs/api.py +0 -0
  155. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/__init__.py +0 -0
  156. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/aliyun.py +0 -0
  157. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/base.py +0 -0
  158. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/cli.py +0 -0
  159. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/domain_utils.py +0 -0
  160. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/ip_updater.py +0 -0
  161. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/mcp.py +0 -0
  162. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/tencent.py +0 -0
  163. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/utils.py +0 -0
  164. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/base.py +0 -0
  165. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/huggingface.py +0 -0
  166. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/liblib.py +0 -0
  167. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/pollinations.py +0 -0
  168. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/siliconflow.py +0 -0
  169. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/tongyi.py +0 -0
  170. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/interact.py +0 -0
  171. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/__init__.py +0 -0
  172. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/bot.py +0 -0
  173. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/cli.py +0 -0
  174. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/context.py +0 -0
  175. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/docx_blocks.py +0 -0
  176. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/elements.py +0 -0
  177. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/markdown_blocks.py +0 -0
  178. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/session.py +0 -0
  179. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/network/__init__.py +0 -0
  180. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/network/cli.py +0 -0
  181. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/network/link_check.py +0 -0
  182. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/network/mcp.py +0 -0
  183. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/network/scanner.py +0 -0
  184. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/network/service_urls.py +0 -0
  185. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/nginx/__init__.py +0 -0
  186. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/nginx/cli.py +0 -0
  187. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/nginx/templates.py +0 -0
  188. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/pypi/__init__.py +0 -0
  189. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/pypi/cli.py +0 -0
  190. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/tplogin.py +0 -0
  191. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/tplogin_cli.py +0 -0
  192. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/zulip/__init__.py +0 -0
  193. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/zulip/cli.py +0 -0
  194. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/zulip/client.py +0 -0
  195. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/zulip/legacy.py +0 -0
  196. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/zulip/mcp.py +0 -0
  197. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/__init__.py +0 -0
  198. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/basic.py +0 -0
  199. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/custom_logger.py +0 -0
  200. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/fastobj.py +0 -0
  201. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/httpclient.py +0 -0
  202. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/pathing.py +0 -0
  203. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/test_helpers.py +0 -0
  204. {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/urltool.py +0 -0
  205. {chattool-7.0.3 → chattool-7.0.4}/src/chattool.egg-info/dependency_links.txt +0 -0
  206. {chattool-7.0.3 → chattool-7.0.4}/src/chattool.egg-info/entry_points.txt +0 -0
  207. {chattool-7.0.3 → chattool-7.0.4}/src/chattool.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chattool
3
- Version: 7.0.3
3
+ Version: 7.0.4
4
4
  Summary: Toolkit for Chat API
5
5
  Author-email: Rex Wang <1073853456@qq.com>
6
6
  License: MIT license
@@ -17,6 +17,7 @@ Requires-Python: >=3.10
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: Click>=7.0
20
+ Requires-Dist: chatgh>=0.2.4
20
21
  Requires-Dist: chatstyle>=0.1.0
21
22
  Requires-Dist: chatenv>=0.1.1
22
23
  Requires-Dist: requests>=2.20
@@ -58,8 +59,6 @@ Requires-Dist: uvicorn; extra == "serve"
58
59
  Requires-Dist: pydantic>=2.0.0; extra == "serve"
59
60
  Provides-Extra: mcp
60
61
  Requires-Dist: fastmcp; python_version >= "3.10" and extra == "mcp"
61
- Provides-Extra: github
62
- Requires-Dist: PyGithub; extra == "github"
63
62
  Provides-Extra: client
64
63
  Requires-Dist: gitpython; extra == "client"
65
64
  Requires-Dist: rich; extra == "client"
@@ -67,7 +66,7 @@ Provides-Extra: pypi
67
66
  Requires-Dist: build; extra == "pypi"
68
67
  Requires-Dist: twine; extra == "pypi"
69
68
  Provides-Extra: tools
70
- Requires-Dist: chattool[batch,browser,client,dns,github,images,interactive,lark,mcp,screenshot,serve]; extra == "tools"
69
+ Requires-Dist: chattool[batch,browser,client,dns,images,interactive,lark,mcp,screenshot,serve]; extra == "tools"
71
70
  Provides-Extra: tests
72
71
  Requires-Dist: coverage; extra == "tests"
73
72
  Requires-Dist: pytest>=3; extra == "tests"
@@ -81,6 +80,7 @@ Requires-Dist: mkdocs-static-i18n>=0.53; extra == "docs"
81
80
  Requires-Dist: mkdocs-awesome-pages-plugin>=2.8.0; extra == "docs"
82
81
  Requires-Dist: mkdocs-redirects>=1.2.0; extra == "docs"
83
82
  Requires-Dist: mike>=1.1.2; extra == "docs"
83
+ Requires-Dist: pyparsing>=3.0; extra == "docs"
84
84
  Provides-Extra: dev
85
85
  Requires-Dist: wheel; extra == "dev"
86
86
  Requires-Dist: build; extra == "dev"
@@ -231,10 +231,11 @@ chattool nginx -i # 交互式选择模板并逐项填写
231
231
  ```bash
232
232
  chattool image pollinations generate "a cat in space" -o cat.png
233
233
  chattool image siliconflow generate "a cute dog" -o dog.png
234
+ chattool image codex generate "a watercolor fox"
234
235
  chattool image huggingface generate
235
236
  ```
236
237
 
237
- 交互终端里,各家 `generate` 命令缺少 `prompt` 时会自动补问;`huggingface generate` 缺少 `-o/--output` 也会自动补问。显式传 `-I` 时保持直接报错。
238
+ 交互终端里,各家 `generate` 命令缺少 `prompt` 时会自动补问;`huggingface generate` 与 `codex generate` 未传 `-o` 时会默认写到当前目录下的 `generated/image_<provider>_<model>_<timestamp>.png`。显式传 `-I` 时保持直接报错。
238
239
 
239
240
  ### 数据探索 (`chattool explore`)
240
241
 
@@ -297,7 +298,7 @@ chatpypi mychat
297
298
 
298
299
  默认模板生成的 `pyproject.toml` 会写入 `requires-python = ">=3.9"`;`chatarch` 模板依赖 `chatstyle>=0.1.0` 与 `chatenv>=0.1.1`,默认写入 `requires-python = ">=3.10"`。
299
300
 
300
- `chatarch` 模板会额外生成 `DEVELOP.md`、`CHANGELOG.md`、`AGENTS.md`、`README.en.md`、`mkdocs.yml`、`docs/`、`tests/cli-tests/`、`tests/mock-cli-tests/`、`tests/code-tests/` 与 `.github/workflows/` 骨架,并默认依赖 `chatstyle>=0.1.0` 与 `chatenv>=0.1.1` 作为 ChatArch CLI/env 运行时;其中发布 workflow 会在合并到 `main` / `master` 后按包内 `__version__` 创建 `vX.Y.Z` tag,并通过 PyPI Trusted Publishing 发布尚未存在的版本。
301
+ `chatarch` 模板会额外生成 `DEVELOP.md`、`CHANGELOG.md`、`AGENTS.md`、`README.en.md`、`mkdocs.yml`、`docs/`、`tests/cli-tests/`、`tests/mock-cli-tests/`、`tests/code-tests/` 与 `.github/workflows/` 骨架,并默认依赖 `chatstyle>=0.1.0` 与 `chatenv>=0.1.1` 作为 ChatArch CLI/env 运行时;其中发布 workflow 由显式 `v*` tag 或 `workflow_dispatch` 触发,校验 tag 与包内 `__version__` 一致,并通过 PyPI Trusted Publishing 的 `environment: pypi` 发布尚未存在的版本,不依赖仓库级 PyPI token secret。
301
302
 
302
303
  如只想生成代码与测试骨架,可以用 `--without-mkdocs` 跳过 mkdocs/docs,用 `--without-workflows` 跳过 `.github/workflows/`。
303
304
 
@@ -141,10 +141,11 @@ chattool nginx -i # 交互式选择模板并逐项填写
141
141
  ```bash
142
142
  chattool image pollinations generate "a cat in space" -o cat.png
143
143
  chattool image siliconflow generate "a cute dog" -o dog.png
144
+ chattool image codex generate "a watercolor fox"
144
145
  chattool image huggingface generate
145
146
  ```
146
147
 
147
- 交互终端里,各家 `generate` 命令缺少 `prompt` 时会自动补问;`huggingface generate` 缺少 `-o/--output` 也会自动补问。显式传 `-I` 时保持直接报错。
148
+ 交互终端里,各家 `generate` 命令缺少 `prompt` 时会自动补问;`huggingface generate` 与 `codex generate` 未传 `-o` 时会默认写到当前目录下的 `generated/image_<provider>_<model>_<timestamp>.png`。显式传 `-I` 时保持直接报错。
148
149
 
149
150
  ### 数据探索 (`chattool explore`)
150
151
 
@@ -207,7 +208,7 @@ chatpypi mychat
207
208
 
208
209
  默认模板生成的 `pyproject.toml` 会写入 `requires-python = ">=3.9"`;`chatarch` 模板依赖 `chatstyle>=0.1.0` 与 `chatenv>=0.1.1`,默认写入 `requires-python = ">=3.10"`。
209
210
 
210
- `chatarch` 模板会额外生成 `DEVELOP.md`、`CHANGELOG.md`、`AGENTS.md`、`README.en.md`、`mkdocs.yml`、`docs/`、`tests/cli-tests/`、`tests/mock-cli-tests/`、`tests/code-tests/` 与 `.github/workflows/` 骨架,并默认依赖 `chatstyle>=0.1.0` 与 `chatenv>=0.1.1` 作为 ChatArch CLI/env 运行时;其中发布 workflow 会在合并到 `main` / `master` 后按包内 `__version__` 创建 `vX.Y.Z` tag,并通过 PyPI Trusted Publishing 发布尚未存在的版本。
211
+ `chatarch` 模板会额外生成 `DEVELOP.md`、`CHANGELOG.md`、`AGENTS.md`、`README.en.md`、`mkdocs.yml`、`docs/`、`tests/cli-tests/`、`tests/mock-cli-tests/`、`tests/code-tests/` 与 `.github/workflows/` 骨架,并默认依赖 `chatstyle>=0.1.0` 与 `chatenv>=0.1.1` 作为 ChatArch CLI/env 运行时;其中发布 workflow 由显式 `v*` tag 或 `workflow_dispatch` 触发,校验 tag 与包内 `__version__` 一致,并通过 PyPI Trusted Publishing 的 `environment: pypi` 发布尚未存在的版本,不依赖仓库级 PyPI token secret。
211
212
 
212
213
  如只想生成代码与测试骨架,可以用 `--without-mkdocs` 跳过 mkdocs/docs,用 `--without-workflows` 跳过 `.github/workflows/`。
213
214
 
@@ -23,6 +23,7 @@ keywords = ["chattool"]
23
23
  license = {text = "MIT license"}
24
24
  dependencies = [
25
25
  "Click>=7.0",
26
+ "chatgh>=0.2.4",
26
27
  "chatstyle>=0.1.0",
27
28
  "chatenv>=0.1.1",
28
29
  "requests>=2.20",
@@ -76,9 +77,6 @@ serve = [
76
77
  mcp = [
77
78
  "fastmcp; python_version >= '3.10'",
78
79
  ]
79
- github = [
80
- "PyGithub",
81
- ]
82
80
  client = [
83
81
  "gitpython",
84
82
  "rich",
@@ -88,7 +86,7 @@ pypi = [
88
86
  "twine",
89
87
  ]
90
88
  tools = [
91
- "chattool[batch,browser,client,dns,github,images,interactive,lark,mcp,serve,screenshot]",
89
+ "chattool[batch,browser,client,dns,images,interactive,lark,mcp,serve,screenshot]",
92
90
  ]
93
91
  tests = [
94
92
  "coverage",
@@ -104,6 +102,7 @@ docs = [
104
102
  "mkdocs-awesome-pages-plugin>=2.8.0",
105
103
  "mkdocs-redirects>=1.2.0",
106
104
  "mike>=1.1.2",
105
+ "pyparsing>=3.0",
107
106
  ]
108
107
  dev = [
109
108
  "wheel",
@@ -4,7 +4,7 @@ import importlib
4
4
 
5
5
  __author__ = """Rex Wang"""
6
6
  __email__ = "1073853456@qq.com"
7
- __version__ = "7.0.3"
7
+ __version__ = "7.0.4"
8
8
 
9
9
  from dotenv import load_dotenv
10
10
 
@@ -98,7 +98,6 @@ cli._lazy_commands.update(
98
98
  "lark": lambda: _load_attr("chattool.tools.lark.cli", "cli"),
99
99
  "image": lambda: _load_attr("chattool.tools.image.cli", "cli"),
100
100
  "tplogin": lambda: _load_attr("chattool.tools.tplogin_cli", "cli"),
101
- "gh": lambda: _load_attr("chattool.tools.github.cli", "cli"),
102
101
  "browser": lambda: _load_attr("chattool.tools.browser.cli", "cli"),
103
102
  "zulip": lambda: _load_attr("chattool.tools.zulip.cli", "cli"),
104
103
  "skill": lambda: _load_attr("chattool.skill.cli", "skill_cli"),
@@ -14,7 +14,6 @@ from .liblib import LiblibConfig
14
14
  from .siliconflow import SiliconFlowConfig
15
15
  from .tplink import TPLinkConfig
16
16
  from .browser import BrowserConfig
17
- from .github import GitHubConfig
18
17
 
19
18
  __all__ = [
20
19
  "EnvField",
@@ -32,7 +31,6 @@ __all__ = [
32
31
  "PollinationsConfig",
33
32
  "LiblibConfig",
34
33
  "SiliconFlowConfig",
35
- "GitHubConfig",
36
34
  "BrowserConfig",
37
35
  "TPLinkConfig",
38
36
  "SkillsConfig",
@@ -16,7 +16,6 @@ from .liblib import LiblibConfig
16
16
  from .siliconflow import SiliconFlowConfig
17
17
  from .tplink import TPLinkConfig
18
18
  from .browser import BrowserConfig
19
- from .github import GitHubConfig
20
19
 
21
20
  __all__ = [
22
21
  "EnvField",
@@ -34,7 +33,6 @@ __all__ = [
34
33
  "PollinationsConfig",
35
34
  "LiblibConfig",
36
35
  "SiliconFlowConfig",
37
- "GitHubConfig",
38
36
  "BrowserConfig",
39
37
  "TPLinkConfig",
40
38
  "SkillsConfig",
@@ -11,7 +11,31 @@ class OpenAIConfig(BaseEnvConfig):
11
11
 
12
12
  OPENAI_API_BASE = EnvField("OPENAI_API_BASE", desc="The base url of the API (with suffix /v1).")
13
13
  OPENAI_API_KEY = EnvField("OPENAI_API_KEY", desc="Your API key", is_sensitive=True)
14
- OPENAI_API_MODEL = EnvField("OPENAI_API_MODEL", default="gpt-3.5-turbo", desc="The default model name")
14
+ OPENAI_API_MODEL = EnvField("OPENAI_API_MODEL", default="gpt-5.5", desc="The default model name")
15
+ OPENAI_ACCESS_TOKEN = EnvField(
16
+ "OPENAI_ACCESS_TOKEN",
17
+ desc="OpenAI OAuth access token for OAuth-backed capabilities.",
18
+ is_sensitive=True,
19
+ )
20
+ OPENAI_REFRESH_TOKEN = EnvField(
21
+ "OPENAI_REFRESH_TOKEN",
22
+ desc="OpenAI OAuth refresh token for OAuth-backed capabilities.",
23
+ is_sensitive=True,
24
+ )
25
+ OPENAI_OAUTH_BASE_URL = EnvField(
26
+ "OPENAI_OAUTH_BASE_URL",
27
+ default="https://auth.openai.com",
28
+ desc="OpenAI OAuth auth server base URL used to refresh access tokens.",
29
+ )
30
+ OPENAI_ACCESS_TOKEN_EXPIRES_AT = EnvField(
31
+ "OPENAI_ACCESS_TOKEN_EXPIRES_AT",
32
+ desc="UTC ISO timestamp when the OpenAI OAuth access token expires.",
33
+ )
34
+ OPENAI_IMAGE_MODEL = EnvField(
35
+ "OPENAI_IMAGE_MODEL",
36
+ default="gpt-image-2-medium",
37
+ desc="Default OpenAI image model preset.",
38
+ )
15
39
 
16
40
  @classmethod
17
41
  def _parse_responses_stream_event(cls, line: str):
@@ -0,0 +1,5 @@
1
+ """Explore module — arxiv and wordpress helpers."""
2
+
3
+ from . import arxiv, wordpress
4
+
5
+ __all__ = ["arxiv", "wordpress"]
@@ -7,6 +7,8 @@ _LAZY_ATTRS = {
7
7
  "cert_app": ("chattool.serve.cert_server", "cert_app"),
8
8
  "serve_cli": ("chattool.serve.cli", "serve_cli"),
9
9
  "lark_serve_cli": ("chattool.serve.lark_serve", "cli"),
10
+ "oauth_app": ("chattool.serve.oauth", "app"),
11
+ "oauth_serve_cli": ("chattool.serve.oauth", "oauth"),
10
12
  "svg2gif_app": ("chattool.serve.svg2gif", "svg2gif_app"),
11
13
  }
12
14
 
@@ -26,5 +28,7 @@ __all__ = [
26
28
  "cert_app",
27
29
  "serve_cli",
28
30
  "lark_serve_cli",
31
+ "oauth_app",
32
+ "oauth_serve_cli",
29
33
  "svg2gif_app",
30
34
  ]
@@ -33,6 +33,12 @@ def _load_local():
33
33
  return command
34
34
 
35
35
 
36
+ def _load_oauth():
37
+ command = _load_attr("chattool.serve.oauth", "oauth")
38
+ command.short_help = "Run the local OAuth token service."
39
+ return command
40
+
41
+
36
42
  def _load_svg2gif():
37
43
  command = _load_attr("chattool.serve.svg2gif", "svg2gif_app")
38
44
  command.short_help = "Run the SVG-to-GIF conversion service."
@@ -51,6 +57,7 @@ serve_cli._lazy_commands.update(
51
57
  "cert": _load_cert,
52
58
  "lark": _load_lark,
53
59
  "local": _load_local,
60
+ "oauth": _load_oauth,
54
61
  "svg2gif": _load_svg2gif,
55
62
  }
56
63
  )
@@ -60,6 +67,7 @@ serve_cli._lazy_command_help.update(
60
67
  "cert": "Run the certificate service.",
61
68
  "lark": "Run the Lark webhook service.",
62
69
  "local": "Serve a local directory or HTML file.",
70
+ "oauth": "Run the local OAuth token service.",
63
71
  "svg2gif": "Run the SVG-to-GIF conversion service.",
64
72
  }
65
73
  )
@@ -0,0 +1,249 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime, timezone
4
+ import os
5
+ from pathlib import Path
6
+ import secrets
7
+ from typing import Any
8
+
9
+ import click
10
+ from fastapi import Depends, FastAPI, Header, HTTPException
11
+ from pydantic import BaseModel, Field
12
+
13
+ from chattool.config import BaseEnvConfig, OpenAIConfig # type: ignore[attr-defined]
14
+ from chattool.const import CHATARCH_ENV_DIR
15
+ from chattool.tools.crs.openai_oauth import (
16
+ build_openai_oauth_refresh_result,
17
+ build_openai_oauth_status,
18
+ refresh_openai_oauth_token,
19
+ save_openai_oauth_token_data,
20
+ )
21
+
22
+ config: dict[str, Any] = {
23
+ "token": None,
24
+ "env_dir": CHATARCH_ENV_DIR,
25
+ "oauth_base_url": None,
26
+ "timeout_seconds": 20.0,
27
+ }
28
+
29
+ app = FastAPI(
30
+ title="ChatTool OAuth Token Service",
31
+ description="Local auth service for OAuth token status, refresh, and access-token retrieval.",
32
+ version="0.1.0",
33
+ )
34
+
35
+
36
+ class OAuthConfigRequest(BaseModel):
37
+ access_token: str | None = Field(default=None)
38
+ refresh_token: str | None = Field(default=None)
39
+ oauth_base_url: str | None = Field(default=None)
40
+ access_token_expires_at: str | None = Field(default=None)
41
+
42
+
43
+ class OAuthRefreshRequest(BaseModel):
44
+ refresh_token: str | None = Field(default=None)
45
+ oauth_base_url: str | None = Field(default=None)
46
+ timeout_seconds: float | None = Field(default=None)
47
+ save: bool = Field(default=True)
48
+
49
+
50
+ def _env_dir() -> Path:
51
+ return Path(config.get("env_dir") or CHATARCH_ENV_DIR)
52
+
53
+
54
+ def _active_openai_env_file() -> Path:
55
+ return OpenAIConfig.get_active_env_file(_env_dir())
56
+
57
+
58
+ def _load_openai_env() -> None:
59
+ if _active_openai_env_file().exists():
60
+ BaseEnvConfig.load_all(_env_dir())
61
+
62
+
63
+ def _configured_oauth_base_url(override: str | None = None) -> str:
64
+ value = (
65
+ override
66
+ or config.get("oauth_base_url")
67
+ or OpenAIConfig.OPENAI_OAUTH_BASE_URL.value
68
+ or "https://auth.openai.com"
69
+ )
70
+ return str(value).strip().rstrip("/")
71
+
72
+
73
+ def _service_token() -> str | None:
74
+ value = config.get("token") or os.getenv("CHATTOOL_OAUTH_SERVICE_TOKEN")
75
+ return str(value) if value else None
76
+
77
+
78
+ async def require_service_token(
79
+ x_chattool_token: str | None = Header(default=None),
80
+ authorization: str | None = Header(default=None),
81
+ ) -> str:
82
+ expected = _service_token()
83
+ if not expected:
84
+ raise HTTPException(status_code=403, detail="Service token is not configured")
85
+ provided = x_chattool_token
86
+ if not provided and authorization:
87
+ prefix = "Bearer "
88
+ if authorization.startswith(prefix):
89
+ provided = authorization[len(prefix):]
90
+ if not provided or not secrets.compare_digest(str(provided), expected):
91
+ raise HTTPException(status_code=403, detail="Invalid or missing service token")
92
+ return str(provided)
93
+
94
+
95
+ def _parse_iso(value: str | None) -> datetime | None:
96
+ if not value:
97
+ return None
98
+ text = value.strip()
99
+ if not text:
100
+ return None
101
+ if text.endswith("Z"):
102
+ text = text[:-1] + "+00:00"
103
+ try:
104
+ parsed = datetime.fromisoformat(text)
105
+ except ValueError:
106
+ return None
107
+ if parsed.tzinfo is None:
108
+ parsed = parsed.replace(tzinfo=timezone.utc)
109
+ return parsed.astimezone(timezone.utc)
110
+
111
+
112
+ def _is_expired_or_missing(expires_at: str | None) -> bool:
113
+ parsed = _parse_iso(expires_at)
114
+ if parsed is None:
115
+ return True
116
+ return parsed <= datetime.now(timezone.utc)
117
+
118
+
119
+ def _save_current_openai_values(target_path: Path) -> None:
120
+ target_path.parent.mkdir(parents=True, exist_ok=True)
121
+ target_path.write_text(OpenAIConfig.render_env_file(), encoding="utf-8")
122
+
123
+
124
+ @app.get("/health")
125
+ def health() -> dict[str, str]:
126
+ return {"status": "ok"}
127
+
128
+
129
+ @app.get("/oauth/status")
130
+ def oauth_status(_: str = Depends(require_service_token)) -> dict[str, str]:
131
+ _load_openai_env()
132
+ return build_openai_oauth_status(env_file=_active_openai_env_file())
133
+
134
+
135
+ @app.post("/oauth/config")
136
+ def oauth_config(
137
+ request: OAuthConfigRequest,
138
+ _: str = Depends(require_service_token),
139
+ ) -> dict[str, str]:
140
+ _load_openai_env()
141
+ if request.access_token is not None:
142
+ OpenAIConfig.OPENAI_ACCESS_TOKEN.value = request.access_token
143
+ if request.refresh_token is not None:
144
+ OpenAIConfig.OPENAI_REFRESH_TOKEN.value = request.refresh_token
145
+ if request.oauth_base_url is not None:
146
+ OpenAIConfig.OPENAI_OAUTH_BASE_URL.value = request.oauth_base_url.strip().rstrip("/")
147
+ elif config.get("oauth_base_url"):
148
+ OpenAIConfig.OPENAI_OAUTH_BASE_URL.value = _configured_oauth_base_url()
149
+ if request.access_token_expires_at is not None:
150
+ OpenAIConfig.OPENAI_ACCESS_TOKEN_EXPIRES_AT.value = request.access_token_expires_at
151
+ target_path = _active_openai_env_file()
152
+ _save_current_openai_values(target_path)
153
+ return build_openai_oauth_status(env_file=target_path)
154
+
155
+
156
+ @app.post("/oauth/refresh")
157
+ def oauth_refresh(
158
+ request: OAuthRefreshRequest | None = None,
159
+ _: str = Depends(require_service_token),
160
+ ) -> dict[str, Any]:
161
+ _load_openai_env()
162
+ request = request or OAuthRefreshRequest()
163
+ refresh_token = (request.refresh_token or OpenAIConfig.OPENAI_REFRESH_TOKEN.value or "").strip()
164
+ if not refresh_token:
165
+ raise HTTPException(status_code=400, detail="OPENAI_REFRESH_TOKEN is required")
166
+ oauth_base_url = _configured_oauth_base_url(request.oauth_base_url)
167
+ timeout_seconds = request.timeout_seconds or float(config.get("timeout_seconds") or 20.0)
168
+ try:
169
+ token_data = refresh_openai_oauth_token(
170
+ refresh_token,
171
+ base_url=oauth_base_url,
172
+ timeout_seconds=timeout_seconds,
173
+ )
174
+ except (RuntimeError, ValueError) as exc:
175
+ raise HTTPException(status_code=502, detail=str(exc)) from exc
176
+ target_path = _active_openai_env_file()
177
+ if request.save:
178
+ save_openai_oauth_token_data(
179
+ token_data=token_data,
180
+ oauth_base_url=oauth_base_url,
181
+ target_path=target_path,
182
+ )
183
+ return build_openai_oauth_refresh_result(
184
+ token_data=token_data,
185
+ oauth_base_url=oauth_base_url,
186
+ saved=request.save,
187
+ env_file=target_path if request.save else None,
188
+ )
189
+
190
+
191
+ @app.get("/oauth/access-token")
192
+ def oauth_access_token(_: str = Depends(require_service_token)) -> dict[str, Any]:
193
+ _load_openai_env()
194
+ access_token = (OpenAIConfig.OPENAI_ACCESS_TOKEN.value or "").strip()
195
+ expires_at = OpenAIConfig.OPENAI_ACCESS_TOKEN_EXPIRES_AT.value
196
+ refreshed = False
197
+ if not access_token or _is_expired_or_missing(expires_at):
198
+ refresh_token = (OpenAIConfig.OPENAI_REFRESH_TOKEN.value or "").strip()
199
+ if not refresh_token:
200
+ raise HTTPException(status_code=400, detail="No valid access token and no refresh token configured")
201
+ oauth_base_url = _configured_oauth_base_url()
202
+ try:
203
+ token_data = refresh_openai_oauth_token(
204
+ refresh_token,
205
+ base_url=oauth_base_url,
206
+ timeout_seconds=float(config.get("timeout_seconds") or 20.0),
207
+ )
208
+ except (RuntimeError, ValueError) as exc:
209
+ raise HTTPException(status_code=502, detail=str(exc)) from exc
210
+ save_openai_oauth_token_data(
211
+ token_data=token_data,
212
+ oauth_base_url=oauth_base_url,
213
+ target_path=_active_openai_env_file(),
214
+ )
215
+ access_token = str(token_data.get("access_token") or "")
216
+ expires_at = str(token_data.get("access_token_expires_at") or "")
217
+ refreshed = True
218
+ return {
219
+ "access_token": access_token,
220
+ "access_token_expires_at": expires_at,
221
+ "refreshed": refreshed,
222
+ }
223
+
224
+
225
+ @click.command(name="oauth")
226
+ @click.option("--host", default="127.0.0.1", show_default=True, help="Bind host.")
227
+ @click.option("--port", default=8787, show_default=True, type=int, help="Bind port.")
228
+ @click.option("--token", default=None, help="Service token required by protected endpoints. Defaults to CHATTOOL_OAUTH_SERVICE_TOKEN.")
229
+ @click.option("--env-dir", default=None, help="ChatArch env directory. Defaults to the active ChatTool env dir.")
230
+ @click.option("--oauth-base-url", default=None, help="Default OAuth auth server base URL.")
231
+ @click.option("--timeout", "timeout_seconds", default=20.0, type=float, show_default=True)
232
+ @click.option("--dry-run", is_flag=True, help="Print resolved server settings without starting uvicorn.")
233
+ def oauth(host: str, port: int, token: str | None, env_dir: str | None, oauth_base_url: str | None, timeout_seconds: float, dry_run: bool) -> None:
234
+ """Run the local OAuth token service."""
235
+ config["token"] = token
236
+ config["env_dir"] = Path(env_dir).expanduser() if env_dir else CHATARCH_ENV_DIR
237
+ config["oauth_base_url"] = oauth_base_url
238
+ config["timeout_seconds"] = timeout_seconds
239
+ click.echo(f"OAuth service: http://{host}:{port}")
240
+ click.echo(f"Env dir: {config['env_dir']}")
241
+ click.echo(f"OAuth base: {_configured_oauth_base_url()}")
242
+ click.echo(f"Service token: {'configured' if _service_token() else 'missing'}")
243
+ if dry_run:
244
+ return
245
+ if not _service_token():
246
+ raise click.ClickException("--token or CHATTOOL_OAUTH_SERVICE_TOKEN is required unless --dry-run is used.")
247
+ import uvicorn
248
+
249
+ uvicorn.run(app, host=host, port=port)
@@ -20,7 +20,6 @@ ALIAS_MAP = {
20
20
  "chatskill": "chattool skill",
21
21
  "chatdns": "chattool dns",
22
22
  "chattp": "chattool tplogin",
23
- "chatgh": "chattool gh",
24
23
  "chatcc": "chattool cc",
25
24
  "chatcli": "chattool client",
26
25
  "chatpypi": "chatpypi",
@@ -176,7 +176,6 @@ def setup_workspace(
176
176
  option_settings["chattool"]["source"],
177
177
  interactive,
178
178
  can_prompt,
179
- option_settings["chattool"].get("github_token"),
180
179
  )
181
180
  )
182
181
  if option_settings["rexblog"]["enabled"]:
@@ -186,7 +185,6 @@ def setup_workspace(
186
185
  option_settings["rexblog"]["source"],
187
186
  interactive,
188
187
  can_prompt,
189
- option_settings["rexblog"].get("github_token"),
190
188
  )
191
189
  )
192
190