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.
- {chattool-7.0.3/src/chattool.egg-info → chattool-7.0.4}/PKG-INFO +7 -6
- {chattool-7.0.3 → chattool-7.0.4}/README.md +3 -2
- {chattool-7.0.3 → chattool-7.0.4}/pyproject.toml +3 -4
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/__init__.py +1 -1
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/client/main.py +0 -1
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/__init__.py +0 -2
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/elements.py +0 -2
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/openai.py +25 -1
- chattool-7.0.4/src/chattool/explore/__init__.py +5 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/__init__.py +4 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/cli.py +8 -0
- chattool-7.0.4/src/chattool/serve/oauth.py +249 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/alias.py +0 -1
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/cli.py +0 -2
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/options.py +0 -98
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/render.py +30 -15
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/templates/default/en/AGENTS.md +7 -5
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/templates/default/en/projects/README.md +1 -1
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/templates/default/zh/AGENTS.md +7 -5
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/templates/default/zh/projects/README.md +1 -1
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/__init__.py +0 -2
- chattool-7.0.4/src/chattool/tools/crs/__init__.py +19 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/crs/cli.py +92 -0
- chattool-7.0.4/src/chattool/tools/crs/openai_oauth.py +138 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/__init__.py +6 -1
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/cli.py +113 -9
- chattool-7.0.4/src/chattool/tools/image/codex.py +393 -0
- chattool-7.0.4/src/chattool/tools/image/helpers.py +96 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/pypi/main.py +1 -0
- {chattool-7.0.3 → chattool-7.0.4/src/chattool.egg-info}/PKG-INFO +7 -6
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool.egg-info/SOURCES.txt +3 -11
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool.egg-info/requires.txt +3 -4
- chattool-7.0.3/src/chattool/config/github.py +0 -27
- chattool-7.0.3/src/chattool/explore/__init__.py +0 -5
- chattool-7.0.3/src/chattool/explore/github/__init__.py +0 -6
- chattool-7.0.3/src/chattool/explore/github/client.py +0 -106
- chattool-7.0.3/src/chattool/explore/github/models.py +0 -42
- chattool-7.0.3/src/chattool/tools/crs/__init__.py +0 -5
- chattool-7.0.3/src/chattool/tools/github/__init__.py +0 -11
- chattool-7.0.3/src/chattool/tools/github/api.py +0 -368
- chattool-7.0.3/src/chattool/tools/github/cli.py +0 -448
- chattool-7.0.3/src/chattool/tools/github/client.py +0 -153
- chattool-7.0.3/src/chattool/tools/github/commands.py +0 -250
- chattool-7.0.3/src/chattool/tools/github/render.py +0 -212
- chattool-7.0.3/src/chattool/tools/github/requests.py +0 -295
- chattool-7.0.3/src/chattool/tools/image/helpers.py +0 -28
- {chattool-7.0.3 → chattool-7.0.4}/LICENSE +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/MANIFEST.in +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/scripts/migrate_chattool_envs_to_chatarch.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/setup.cfg +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/_all.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/client/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/client/cert_client.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/client/svg2gif_client.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/aliyun.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/azure.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/base.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/browser.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/crs.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/feishu.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/huggingface.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/liblib.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/pollinations.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/siliconflow.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/skills.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/tencent.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/tongyi.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/tplink.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/config/zulip.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/const.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/docker/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/docker/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/docker/elements.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/docker/main.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/client.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/daily.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/harvest.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/models.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/presets.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/arxiv/query.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/wordpress/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/wordpress/client.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/explore/wordpress/models.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/interaction/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/interaction/command_schema.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/interaction/policy.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/interaction/warnings.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/llm/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/llm/chattype.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/llm/response.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/mcp/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/mcp/catalog.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/mcp/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/mcp/server.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/capture.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/cert_server.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/lark_serve.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/local.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/serve/svg2gif.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/hermes/install.sh +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/nvm.sh +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/README.md +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/commands/chatloop-help.md +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/commands/chatloop-project.md +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/commands/chatloop-status.md +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/commands/chatloop-stop.md +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/commands/chatloop.md +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/plugins/chatloop/index.ts +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/assets/opencode_chatloop/plugins/chatloop/package.json +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/cc_connect.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/chrome.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/claude.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/codex.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/docker.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/elements.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/frp.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/hermes.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/lark_cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/mode_prompt.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/nodejs.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/opencode.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/opencode_chatloop.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace/core.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/workspace.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/setup/zsh.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/skill/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/skill/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/skill/interaction.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/skill/platforms.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/skill/validation.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/base.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/playwright_impl.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/selenium.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/test_browser.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/test_module.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/test_remote.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/browser/xhs_qrcode.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cc/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cc/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cert/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cert/acme_dns_tiny.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cert/cert_server.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cert/cert_updater.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cert/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/cert/email.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/crs/api.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/aliyun.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/base.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/domain_utils.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/ip_updater.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/mcp.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/tencent.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/dns/utils.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/base.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/huggingface.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/liblib.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/pollinations.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/siliconflow.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/image/tongyi.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/interact.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/bot.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/context.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/docx_blocks.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/elements.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/markdown_blocks.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/lark/session.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/network/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/network/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/network/link_check.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/network/mcp.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/network/scanner.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/network/service_urls.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/nginx/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/nginx/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/nginx/templates.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/pypi/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/pypi/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/tplogin.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/tplogin_cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/zulip/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/zulip/cli.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/zulip/client.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/zulip/legacy.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/tools/zulip/mcp.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/__init__.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/basic.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/custom_logger.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/fastobj.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/httpclient.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/pathing.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/test_helpers.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool/utils/urltool.py +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool.egg-info/dependency_links.txt +0 -0
- {chattool-7.0.3 → chattool-7.0.4}/src/chattool.egg-info/entry_points.txt +0 -0
- {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
|
+
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,
|
|
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`
|
|
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
|
|
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`
|
|
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
|
|
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,
|
|
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",
|
|
@@ -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-
|
|
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):
|
|
@@ -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)
|
|
@@ -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
|
|