chatup 0.2.15__tar.gz → 0.2.16__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 (95) hide show
  1. {chatup-0.2.15/src/chatup.egg-info → chatup-0.2.16}/PKG-INFO +17 -2
  2. {chatup-0.2.15 → chatup-0.2.16}/README.md +16 -1
  3. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/__init__.py +1 -1
  4. chatup-0.2.16/src/chatup/setup/chatgpt.py +132 -0
  5. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/codex.py +1 -1
  6. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/elements.py +53 -6
  7. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/hermes.py +1 -1
  8. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/uv.py +89 -1
  9. {chatup-0.2.15 → chatup-0.2.16/src/chatup.egg-info}/PKG-INFO +17 -2
  10. {chatup-0.2.15 → chatup-0.2.16}/src/chatup.egg-info/SOURCES.txt +4 -0
  11. chatup-0.2.16/tests/test_chatgpt_setup.py +183 -0
  12. {chatup-0.2.15 → chatup-0.2.16}/tests/test_codex_setup.py +85 -3
  13. chatup-0.2.16/tests/test_hermes_model_defaults.py +50 -0
  14. {chatup-0.2.15 → chatup-0.2.16}/tests/test_setup_cli.py +6 -0
  15. chatup-0.2.16/tests/test_uv_activation.py +259 -0
  16. {chatup-0.2.15 → chatup-0.2.16}/tests/test_version.py +1 -1
  17. {chatup-0.2.15 → chatup-0.2.16}/LICENSE +0 -0
  18. {chatup-0.2.15 → chatup-0.2.16}/pyproject.toml +0 -0
  19. {chatup-0.2.15 → chatup-0.2.16}/setup.cfg +0 -0
  20. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/chrome_for_testing.py +0 -0
  21. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/chromedriver.py +0 -0
  22. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/cli.py +0 -0
  23. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/commands/__init__.py +0 -0
  24. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/commands/chrome_for_testing.py +0 -0
  25. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/commands/chromedriver.py +0 -0
  26. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/commands/playwright.py +0 -0
  27. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/config.py +0 -0
  28. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/const.py +0 -0
  29. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/interaction/__init__.py +0 -0
  30. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/interaction/command_schema.py +0 -0
  31. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/interaction/policy.py +0 -0
  32. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/interaction/warnings.py +0 -0
  33. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/playwright.py +0 -0
  34. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/runtime/__init__.py +0 -0
  35. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/runtime/_managed_artifact.py +0 -0
  36. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/runtime/chrome_for_testing.py +0 -0
  37. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/runtime/chromedriver.py +0 -0
  38. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/runtime/playwright.py +0 -0
  39. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/__init__.py +0 -0
  40. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/assets/hermes/install.sh +0 -0
  41. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/assets/nvm.sh +0 -0
  42. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/cc_connect.py +0 -0
  43. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/claude.py +0 -0
  44. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/cli.py +0 -0
  45. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/crs.py +0 -0
  46. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/cursor_agent.py +0 -0
  47. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/discourse.py +0 -0
  48. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/docker.py +0 -0
  49. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/frp.py +0 -0
  50. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/gitea.py +0 -0
  51. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/lark_cli.py +0 -0
  52. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/mode_prompt.py +0 -0
  53. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/mysql.py +0 -0
  54. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/nginx.py +0 -0
  55. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/nodejs.py +0 -0
  56. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/opencode.py +0 -0
  57. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/service_credentials.py +0 -0
  58. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/shell_rc.py +0 -0
  59. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/twikoo.py +0 -0
  60. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/workspace/__init__.py +0 -0
  61. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/workspace/cli.py +0 -0
  62. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/workspace/core.py +0 -0
  63. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/workspace/options.py +0 -0
  64. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/workspace/render.py +0 -0
  65. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/workspace/templates/default/en/AGENTS.md +0 -0
  66. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/workspace/templates/default/en/discussion/README.md +0 -0
  67. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/workspace/templates/default/en/projects/README.md +0 -0
  68. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/workspace/templates/default/zh/AGENTS.md +0 -0
  69. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/workspace/templates/default/zh/discussion/README.md +0 -0
  70. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/workspace/templates/default/zh/projects/README.md +0 -0
  71. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/workspace.py +0 -0
  72. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/zsh.py +0 -0
  73. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/setup/zulip.py +0 -0
  74. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/utils/__init__.py +0 -0
  75. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/utils/custom_logger.py +0 -0
  76. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/utils/pathing.py +0 -0
  77. {chatup-0.2.15 → chatup-0.2.16}/src/chatup/utils/platforming.py +0 -0
  78. {chatup-0.2.15 → chatup-0.2.16}/src/chatup.egg-info/dependency_links.txt +0 -0
  79. {chatup-0.2.15 → chatup-0.2.16}/src/chatup.egg-info/entry_points.txt +0 -0
  80. {chatup-0.2.15 → chatup-0.2.16}/src/chatup.egg-info/requires.txt +0 -0
  81. {chatup-0.2.15 → chatup-0.2.16}/src/chatup.egg-info/top_level.txt +0 -0
  82. {chatup-0.2.15 → chatup-0.2.16}/tests/test_browser_backend_cli.py +0 -0
  83. {chatup-0.2.15 → chatup-0.2.16}/tests/test_chrome_for_testing.py +0 -0
  84. {chatup-0.2.15 → chatup-0.2.16}/tests/test_chromedriver.py +0 -0
  85. {chatup-0.2.15 → chatup-0.2.16}/tests/test_cli_tree_docs.py +0 -0
  86. {chatup-0.2.15 → chatup-0.2.16}/tests/test_crs_setup.py +0 -0
  87. {chatup-0.2.15 → chatup-0.2.16}/tests/test_cursor_agent_setup.py +0 -0
  88. {chatup-0.2.15 → chatup-0.2.16}/tests/test_playwright.py +0 -0
  89. {chatup-0.2.15 → chatup-0.2.16}/tests/test_playwright_cli.py +0 -0
  90. {chatup-0.2.15 → chatup-0.2.16}/tests/test_publish_workflow.py +0 -0
  91. {chatup-0.2.15 → chatup-0.2.16}/tests/test_pyproject_metadata.py +0 -0
  92. {chatup-0.2.15 → chatup-0.2.16}/tests/test_service_installers.py +0 -0
  93. {chatup-0.2.15 → chatup-0.2.16}/tests/test_twikoo_setup.py +0 -0
  94. {chatup-0.2.15 → chatup-0.2.16}/tests/test_windows_compat.py +0 -0
  95. {chatup-0.2.15 → chatup-0.2.16}/tests/test_workspace_setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chatup
3
- Version: 0.2.15
3
+ Version: 0.2.16
4
4
  Summary: ChatArch setup CLI extracted from ChatTool
5
5
  Author-email: rexwzh <1073853456@qq.com>
6
6
  License-Expression: MIT
@@ -62,6 +62,8 @@ chatup playwright install 1.61.1 -I
62
62
  chatup workspace default ~/Playground
63
63
  ```
64
64
 
65
+ `chatup uv` 默认更新已有 `~/.bashrc`、`~/.zshrc` 的自动激活块;使用 `chatup uv --no-activate` 可保持启动配置不变。不会创建缺失的 rc 文件,Windows 跳过此项。
66
+
65
67
  常见安装命令:
66
68
 
67
69
  ```bash
@@ -74,16 +76,29 @@ chatup nginx proxy-pass ./gitea-local.conf --set SERVER_NAME=gitea.local.example
74
76
  chatup crs --install-dir ~/.chatarch/crs/local --port 12392 --redis-port 6379
75
77
  ```
76
78
 
79
+ ## ChatGPT / Codex 桌面应用
80
+
81
+ ```bash
82
+ chatup chatgpt --dry-run
83
+ chatup chatgpt
84
+ chatup chatgpt --yes # Windows 首次安装时接受 Store 协议
85
+ ```
86
+
87
+ 安装包含 Codex 的新版官方 ChatGPT 桌面应用。macOS 需要 Homebrew,Windows 需要 WinGet;Linux preview 暂按[官方说明](https://learn.chatgpt.com/docs/linux/linux-app)手动安装。现有 `chatup codex` 仍安装/配置 Codex CLI。不会自动启动、登录或升级已有应用;[完整约定](https://arch.gh.wzhecnu.cn/ChatUp/commands/#chatgpt-desktop)。
88
+
89
+ `chatup codex`、`chatup hermes` 未配置模型时默认使用 `gpt-5.6-terra`;用户指定的模型和已有配置仍优先。
90
+
77
91
  ## 当前能力
78
92
 
79
93
  | 能力组 | 命令 |
80
94
  | --- | --- |
81
95
  | 基础运行环境 | `doctor`、`uv`、`nodejs`、`docker`、`zsh`、`chrome-for-testing`、`chromedriver`、`playwright`、`frp` |
82
96
  | 工作区脚手架 | `workspace` |
97
+ | 官方桌面应用 | `chatgpt`(含 Codex) |
83
98
  | 本地服务安装 | `gitea`、`discourse`、`zulip`、`mysql`、`nginx`、`crs` |
84
99
  | Agent 工具链 | `cc-connect`、`claude`、`codex`、`cursor-agent`、`opencode`、`hermes`、`lark-cli` |
85
100
 
86
- 所有新增安装项默认目录都收敛到 `~/.chatarch/...`,例如 `~/.chatarch/chrome-for-testing`、`~/.chatarch/chromedriver`、`~/.chatarch/playwright`、`~/.chatarch/chattea`、`~/.chatarch/discourse`、`~/.chatarch/zulip`、`~/.chatarch/chatdata`、`~/.chatarch/nginx` 和 `~/.chatarch/crs/local`。`cursor-agent` 额外注册 ChatEnv `CursorAgent` profile,`discourse`/`zulip` 注册管理员凭据 profile,并支持 `-e/--env` 从 env 文件或 profile 快速配置;显式选择 profile 时不会混入进程环境中的其他账号凭据。CLI 树由 ChatStyle 的注册表渲染。更完整的能力边界见 `docs/capability-map.md`。
101
+ 桌面应用使用系统包管理器的原生目录;ChatArch 自管安装项默认目录收敛到 `~/.chatarch/...`,例如 `~/.chatarch/chrome-for-testing`、`~/.chatarch/chromedriver`、`~/.chatarch/playwright`、`~/.chatarch/chattea`、`~/.chatarch/discourse`、`~/.chatarch/zulip`、`~/.chatarch/chatdata`、`~/.chatarch/nginx` 和 `~/.chatarch/crs/local`。`cursor-agent` 额外注册 ChatEnv `CursorAgent` profile,`discourse`/`zulip` 注册管理员凭据 profile,并支持 `-e/--env` 从 env 文件或 profile 快速配置;显式选择 profile 时不会混入进程环境中的其他账号凭据。CLI 树由 ChatStyle 的注册表渲染。更完整的能力边界见 `docs/capability-map.md`。
87
102
 
88
103
  ## 开发
89
104
 
@@ -31,6 +31,8 @@ chatup playwright install 1.61.1 -I
31
31
  chatup workspace default ~/Playground
32
32
  ```
33
33
 
34
+ `chatup uv` 默认更新已有 `~/.bashrc`、`~/.zshrc` 的自动激活块;使用 `chatup uv --no-activate` 可保持启动配置不变。不会创建缺失的 rc 文件,Windows 跳过此项。
35
+
34
36
  常见安装命令:
35
37
 
36
38
  ```bash
@@ -43,16 +45,29 @@ chatup nginx proxy-pass ./gitea-local.conf --set SERVER_NAME=gitea.local.example
43
45
  chatup crs --install-dir ~/.chatarch/crs/local --port 12392 --redis-port 6379
44
46
  ```
45
47
 
48
+ ## ChatGPT / Codex 桌面应用
49
+
50
+ ```bash
51
+ chatup chatgpt --dry-run
52
+ chatup chatgpt
53
+ chatup chatgpt --yes # Windows 首次安装时接受 Store 协议
54
+ ```
55
+
56
+ 安装包含 Codex 的新版官方 ChatGPT 桌面应用。macOS 需要 Homebrew,Windows 需要 WinGet;Linux preview 暂按[官方说明](https://learn.chatgpt.com/docs/linux/linux-app)手动安装。现有 `chatup codex` 仍安装/配置 Codex CLI。不会自动启动、登录或升级已有应用;[完整约定](https://arch.gh.wzhecnu.cn/ChatUp/commands/#chatgpt-desktop)。
57
+
58
+ `chatup codex`、`chatup hermes` 未配置模型时默认使用 `gpt-5.6-terra`;用户指定的模型和已有配置仍优先。
59
+
46
60
  ## 当前能力
47
61
 
48
62
  | 能力组 | 命令 |
49
63
  | --- | --- |
50
64
  | 基础运行环境 | `doctor`、`uv`、`nodejs`、`docker`、`zsh`、`chrome-for-testing`、`chromedriver`、`playwright`、`frp` |
51
65
  | 工作区脚手架 | `workspace` |
66
+ | 官方桌面应用 | `chatgpt`(含 Codex) |
52
67
  | 本地服务安装 | `gitea`、`discourse`、`zulip`、`mysql`、`nginx`、`crs` |
53
68
  | Agent 工具链 | `cc-connect`、`claude`、`codex`、`cursor-agent`、`opencode`、`hermes`、`lark-cli` |
54
69
 
55
- 所有新增安装项默认目录都收敛到 `~/.chatarch/...`,例如 `~/.chatarch/chrome-for-testing`、`~/.chatarch/chromedriver`、`~/.chatarch/playwright`、`~/.chatarch/chattea`、`~/.chatarch/discourse`、`~/.chatarch/zulip`、`~/.chatarch/chatdata`、`~/.chatarch/nginx` 和 `~/.chatarch/crs/local`。`cursor-agent` 额外注册 ChatEnv `CursorAgent` profile,`discourse`/`zulip` 注册管理员凭据 profile,并支持 `-e/--env` 从 env 文件或 profile 快速配置;显式选择 profile 时不会混入进程环境中的其他账号凭据。CLI 树由 ChatStyle 的注册表渲染。更完整的能力边界见 `docs/capability-map.md`。
70
+ 桌面应用使用系统包管理器的原生目录;ChatArch 自管安装项默认目录收敛到 `~/.chatarch/...`,例如 `~/.chatarch/chrome-for-testing`、`~/.chatarch/chromedriver`、`~/.chatarch/playwright`、`~/.chatarch/chattea`、`~/.chatarch/discourse`、`~/.chatarch/zulip`、`~/.chatarch/chatdata`、`~/.chatarch/nginx` 和 `~/.chatarch/crs/local`。`cursor-agent` 额外注册 ChatEnv `CursorAgent` profile,`discourse`/`zulip` 注册管理员凭据 profile,并支持 `-e/--env` 从 env 文件或 profile 快速配置;显式选择 profile 时不会混入进程环境中的其他账号凭据。CLI 树由 ChatStyle 的注册表渲染。更完整的能力边界见 `docs/capability-map.md`。
56
71
 
57
72
  ## 开发
58
73
 
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.2.15"
5
+ __version__ = "0.2.16"
@@ -0,0 +1,132 @@
1
+ """Install the official ChatGPT desktop app (the successor to Codex App).
2
+
3
+ The OS package manager owns downloads, signatures, application paths and state.
4
+ This module never launches the app or changes Codex CLI configuration/auth.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import platform
9
+ import shutil
10
+ import subprocess
11
+ from typing import Any
12
+
13
+ CHATGPT_CASK = "homebrew/cask/chatgpt"
14
+ CHATGPT_STORE_ID = "9PLM9XGG6VKS"
15
+ DOWNLOAD_URL = "https://chatgpt.com/download/"
16
+ LINUX_GUIDE_URL = "https://learn.chatgpt.com/docs/linux/linux-app"
17
+
18
+
19
+ def plan_chatgpt_install(*, yes: bool = False) -> dict[str, Any]:
20
+ """Return a side-effect-free install plan for the current OS.
21
+
22
+ ``yes`` explicitly accepts Microsoft Store source/package agreements.
23
+ Homebrew/WinGet resolve the current release and enforce OS/CPU requirements.
24
+ Linux preview installation is deliberately not automated here.
25
+ """
26
+ system = platform.system()
27
+ if system == "Darwin":
28
+ manager = "brew"
29
+ package = CHATGPT_CASK
30
+ command = [manager, "install", "--cask", package]
31
+ verify_command = [manager, "list", "--cask", "--versions", package]
32
+ manager_url = "https://brew.sh/"
33
+ elif system == "Windows":
34
+ manager = "winget"
35
+ package = CHATGPT_STORE_ID
36
+ selector = ["--id", package, "--exact", "--source", "msstore", "--disable-interactivity"]
37
+ command = [manager, "install", *selector, "--no-upgrade"]
38
+ verify_command = [manager, "list", *selector]
39
+ if yes:
40
+ command += ["--accept-package-agreements", "--accept-source-agreements"]
41
+ verify_command += ["--accept-source-agreements"]
42
+ manager_url = "https://aka.ms/getwinget"
43
+ else:
44
+ guide = LINUX_GUIDE_URL if system == "Linux" else DOWNLOAD_URL
45
+ raise RuntimeError(
46
+ f"ChatUp automates ChatGPT desktop installation on macOS and Windows only; "
47
+ f"current platform: {system}. Official downloads/instructions: {guide}"
48
+ )
49
+ return {
50
+ "app": "ChatGPT (includes Codex)",
51
+ "platform": system,
52
+ "manager": manager,
53
+ "manager_url": manager_url,
54
+ "package": package,
55
+ "command": command,
56
+ "verify_command": verify_command,
57
+ "download_url": DOWNLOAD_URL,
58
+ }
59
+
60
+
61
+ def _is_installed(plan: dict[str, Any], executable: str) -> bool:
62
+ result = subprocess.run(
63
+ [executable, *plan["verify_command"][1:]],
64
+ capture_output=True,
65
+ text=True,
66
+ encoding="utf-8",
67
+ errors="replace",
68
+ check=False,
69
+ timeout=60,
70
+ )
71
+ if result.returncode != 0 or not result.stdout.strip():
72
+ return False
73
+ if plan["manager"] == "brew":
74
+ for line in result.stdout.splitlines():
75
+ parts = line.split()
76
+ if len(parts) < 2 or parts[0] != "chatgpt":
77
+ continue
78
+ if any(version.startswith("1.") for version in parts[1:]):
79
+ raise RuntimeError(
80
+ "An older ChatGPT Classic cask is registered as chatgpt. "
81
+ "Migrate it manually with Homebrew before installing the new "
82
+ f"ChatGPT/Codex app: {DOWNLOAD_URL}"
83
+ )
84
+ return True
85
+ return False
86
+ # WinGet's exact Store selector exits zero only when a matching package exists.
87
+ return True
88
+
89
+
90
+ def setup_chatgpt(*, dry_run: bool = False, yes: bool = False) -> dict[str, Any]:
91
+ """Install if absent, then verify registration with the package manager.
92
+
93
+ Returns the plan plus ``status`` (planned/already_installed/installed) and
94
+ ``verified``. Dry-run does not require or invoke a package manager. Existing
95
+ installations are not upgraded, overwritten or migrated; authentication and
96
+ launching remain user actions. Raises RuntimeError on failed/unknown results.
97
+ """
98
+ plan = plan_chatgpt_install(yes=yes)
99
+ result = {**plan, "status": "planned", "verified": False}
100
+ if dry_run:
101
+ return result
102
+ executable = shutil.which(plan["manager"])
103
+ if not executable:
104
+ raise RuntimeError(
105
+ f"Required package manager {plan['manager']} is not on PATH. "
106
+ f"Install it first: {plan['manager_url']} or download manually: {DOWNLOAD_URL}"
107
+ )
108
+ try:
109
+ if _is_installed(plan, executable):
110
+ return {**result, "status": "already_installed", "verified": True}
111
+ installed = subprocess.run(
112
+ [executable, *plan["command"][1:]], check=False, timeout=1800,
113
+ )
114
+ if installed.returncode != 0:
115
+ hint = " Use --yes to accept Store agreements." if plan["manager"] == "winget" and not yes else ""
116
+ raise RuntimeError(
117
+ f"ChatGPT installer failed (exit {installed.returncode}). "
118
+ f"Check the package manager output above.{hint}"
119
+ )
120
+ if not _is_installed(plan, executable):
121
+ raise RuntimeError(
122
+ "ChatGPT installation verification failed. Check the package manager "
123
+ "state before retrying; installation success is not confirmed."
124
+ )
125
+ except subprocess.TimeoutExpired as exc:
126
+ raise RuntimeError(
127
+ "ChatGPT package manager timed out; the installation state is unknown. "
128
+ "Check the package manager before retrying."
129
+ ) from exc
130
+ except OSError as exc:
131
+ raise RuntimeError(f"Cannot execute {plan['manager']}: {exc}") from exc
132
+ return {**result, "status": "installed", "verified": True}
@@ -30,7 +30,7 @@ from chatup.setup.nodejs import (
30
30
  from chatup.utils.custom_logger import setup_logger
31
31
  from chatup.utils.platforming import chmod_private
32
32
 
33
- DEFAULT_MODEL = "gpt-5.5"
33
+ DEFAULT_MODEL = "gpt-5.6-terra"
34
34
  DEFAULT_BASE_URL = "https://api.openai.com/v1"
35
35
  DEFAULT_AUTH_METHOD = "apikey"
36
36
  _ENV_VAR_REF_RE = re.compile(r"\$(?:\{[^}]+\}|[A-Za-z_][A-Za-z0-9_]*)")
@@ -6,7 +6,8 @@ import click
6
6
  from chatstyle import INTERACTIVE_OPTION_HELP
7
7
 
8
8
  from chatup.setup.claude import setup_claude
9
- from chatup.setup.codex import setup_codex
9
+ from chatup.setup.codex import DEFAULT_MODEL as DEFAULT_CODEX_MODEL, setup_codex
10
+ from chatup.setup.chatgpt import setup_chatgpt
10
11
  from chatup.setup.cursor_agent import CREDENTIAL_STORE_CHOICES, setup_cursor_agent
11
12
  from chatup.setup.cc_connect import setup_cc_connect
12
13
  import chatup.setup.crs as crs_module
@@ -37,7 +38,7 @@ from chatup.setup.gitea import (
37
38
  DEFAULT_WORK_DIR as DEFAULT_GITEA_WORK_DIR,
38
39
  setup_gitea,
39
40
  )
40
- from chatup.setup.hermes import setup_hermes
41
+ from chatup.setup.hermes import DEFAULT_MODEL as DEFAULT_HERMES_MODEL, setup_hermes
41
42
  from chatup.setup.lark_cli import setup_lark_cli
42
43
  from chatup.setup.mysql import (
43
44
  DEFAULT_MYSQL_BIND_ADDRESS,
@@ -102,8 +103,14 @@ def nodejs_setup(interactive, log_level):
102
103
  setup_nodejs(interactive=interactive, log_level=log_level)
103
104
 
104
105
 
105
- def uv_setup(venv, python_version, force, log_level):
106
- setup_uv(venv=venv, python_version=python_version, force=force, log_level=log_level)
106
+ def uv_setup(venv, python_version, force, log_level, shell_activate):
107
+ setup_uv(
108
+ venv=venv,
109
+ python_version=python_version,
110
+ force=force,
111
+ log_level=log_level,
112
+ shell_activate=shell_activate,
113
+ )
107
114
 
108
115
 
109
116
  def docker_setup(sudo, interactive, log_level):
@@ -120,6 +127,20 @@ def zsh_setup(omz, aliases, login_shell, interactive, log_level):
120
127
  )
121
128
 
122
129
 
130
+ def chatgpt_setup(dry_run, yes):
131
+ try:
132
+ result = setup_chatgpt(dry_run=dry_run, yes=yes)
133
+ except RuntimeError as exc:
134
+ raise click.ClickException(str(exc)) from exc
135
+ click.echo(result["app"])
136
+ if result["status"] == "planned":
137
+ click.echo("预览安装命令(未执行):" + " ".join(result["command"]))
138
+ elif result["status"] == "already_installed":
139
+ click.echo("已安装,并已通过包管理器回读确认;未升级或启动应用。")
140
+ else:
141
+ click.echo("安装完成,已通过包管理器回读确认。请手动打开 ChatGPT 并登录。")
142
+
143
+
123
144
  def codex_setup(api_key, base_url, model, env, interactive, install_only, log_level):
124
145
  setup_codex(
125
146
  api_key=api_key,
@@ -1251,6 +1272,29 @@ SETUP_COMMAND_ELEMENTS = (
1251
1272
  "help": "Clear and recreate the target environment if it already exists.",
1252
1273
  },
1253
1274
  ),
1275
+ SetupOptionElement(
1276
+ param_decls=("--activate/--no-activate", "shell_activate"),
1277
+ kwargs={
1278
+ "default": True,
1279
+ "show_default": True,
1280
+ "help": "Update auto-activation in existing ~/.bashrc and ~/.zshrc (POSIX only).",
1281
+ },
1282
+ ),
1283
+ ),
1284
+ ),
1285
+ SetupCommandElement(
1286
+ name="chatgpt",
1287
+ help="Install the official ChatGPT desktop app (includes Codex), not the Codex CLI.",
1288
+ callback=chatgpt_setup,
1289
+ options=(
1290
+ SetupOptionElement(
1291
+ param_decls=("--dry-run",),
1292
+ kwargs={"is_flag": True, "help": "Show the install plan without executing or downloading anything."},
1293
+ ),
1294
+ SetupOptionElement(
1295
+ param_decls=("--yes", "-y"),
1296
+ kwargs={"is_flag": True, "help": "Accept Microsoft Store source/package agreements on Windows."},
1297
+ ),
1254
1298
  ),
1255
1299
  ),
1256
1300
  SetupCommandElement(
@@ -1282,7 +1326,10 @@ SETUP_COMMAND_ELEMENTS = (
1282
1326
  ),
1283
1327
  SetupOptionElement(
1284
1328
  param_decls=("--model",),
1285
- kwargs={"default": None, "help": "Optional default model name."},
1329
+ kwargs={
1330
+ "default": None,
1331
+ "help": f"Optional model override; fallback: {DEFAULT_CODEX_MODEL}.",
1332
+ },
1286
1333
  ),
1287
1334
  SetupOptionElement(
1288
1335
  param_decls=("-e", "--env"),
@@ -1545,7 +1592,7 @@ SETUP_COMMAND_ELEMENTS = (
1545
1592
  ),
1546
1593
  SetupOptionElement(
1547
1594
  param_decls=("--model",),
1548
- kwargs={"default": None, "help": "Hermes default model."},
1595
+ kwargs={"default": None, "help": f"Hermes model override; fallback: {DEFAULT_HERMES_MODEL}."},
1549
1596
  ),
1550
1597
  SetupOptionElement(
1551
1598
  param_decls=("--with-webui-env",),
@@ -23,7 +23,7 @@ logger = setup_logger("setup_hermes")
23
23
  CHATARCH_HERMES_INSTALLER_URL = (
24
24
  "https://raw.githubusercontent.com/ChatArch/hermes-agent/main/scripts/install.sh"
25
25
  )
26
- DEFAULT_MODEL = "gpt-5.4-mini"
26
+ DEFAULT_MODEL = "gpt-5.6-terra"
27
27
  DEFAULT_BASE_URL = "https://api.openai.com/v1"
28
28
  DEFAULT_WEBUI_HOST = "127.0.0.1"
29
29
  DEFAULT_WEBUI_PORT = 8787
@@ -3,8 +3,11 @@ from __future__ import annotations
3
3
  import os
4
4
  import shlex
5
5
  import shutil
6
+ import stat
6
7
  import subprocess
8
+ import tempfile
7
9
  from pathlib import Path
10
+ from typing import Any
8
11
 
9
12
  import click
10
13
 
@@ -16,6 +19,11 @@ DEFAULT_VENV_PATH = Path("~/.chatarch/venv")
16
19
  UV_INSTALLER_URL = "https://astral.sh/uv/install.sh"
17
20
  UV_WINDOWS_INSTALLER_URL = "https://astral.sh/uv/install.ps1"
18
21
 
22
+ # Share ChatUV's markers so switching bootstrap tools replaces the same block.
23
+ SHELL_ACTIVATE_BEGIN = "# >>> chatuv activate >>>"
24
+ SHELL_ACTIVATE_END = "# <<< chatuv activate <<<"
25
+ SHELL_RC_NAMES = (".zshrc", ".bashrc")
26
+
19
27
  logger = setup_logger("setup_uv")
20
28
 
21
29
 
@@ -140,12 +148,88 @@ def create_chatarch_python_env(
140
148
  raise click.ClickException(f"Failed to create ChatArch Python env at {venv_path}: {message}")
141
149
 
142
150
 
151
+ def _upsert_activation_block(text: str, block: str) -> str:
152
+ lines = text.splitlines(keepends=True)
153
+ starts = [i for i, line in enumerate(lines) if line.rstrip("\r\n") == SHELL_ACTIVATE_BEGIN]
154
+ ends = [i for i, line in enumerate(lines) if line.rstrip("\r\n") == SHELL_ACTIVATE_END]
155
+ if starts or ends:
156
+ if len(starts) != 1 or len(ends) != 1 or starts[0] >= ends[0]:
157
+ raise ValueError("Incomplete or duplicate activation block; repair the marked block first")
158
+ return "".join(lines[:starts[0]]) + block + "".join(lines[ends[0] + 1:])
159
+ separator = "" if not text else ("\n" if text.endswith("\n") else "\n\n")
160
+ return text + separator + block
161
+
162
+
163
+ def _atomic_write_rc(rc_path: Path, text: str) -> None:
164
+ # Replace the resolved target, not a dotfiles symlink itself.
165
+ target = rc_path.resolve(strict=True)
166
+ mode = stat.S_IMODE(target.stat().st_mode)
167
+ fd, name = tempfile.mkstemp(prefix=f".{target.name}.chatup-", dir=target.parent)
168
+ temporary = Path(name)
169
+ try:
170
+ with os.fdopen(fd, "w", encoding="utf-8", newline="") as rc:
171
+ rc.write(text)
172
+ rc.flush()
173
+ temporary.chmod(mode)
174
+ os.fsync(rc.fileno())
175
+ os.replace(temporary, target)
176
+ finally:
177
+ temporary.unlink(missing_ok=True)
178
+
179
+
180
+ def configure_shell_activation(venv_path: Path, *, home: Path | None = None) -> dict[str, Any]:
181
+ """Update existing Bash/Zsh rc files without creating missing ones.
182
+
183
+ Reuse ChatUV's managed markers; preserve all text outside the block and
184
+ leave unchanged files untouched. Paths are absolute and shell-quoted.
185
+ """
186
+ result: dict[str, Any] = {"updated": [], "unchanged": [], "missing": [], "skipped": False}
187
+ if is_windows():
188
+ click.echo("Shell activation update is skipped on Windows.")
189
+ result["skipped"] = True
190
+ return result
191
+
192
+ activate = shlex.quote(str(venv_path.expanduser().absolute() / "bin" / "activate"))
193
+ block = "\n".join((
194
+ SHELL_ACTIVATE_BEGIN,
195
+ "# Managed by ChatUV / ChatUp. Remove this block to stop auto-activation.",
196
+ f"if [ -f {activate} ]; then",
197
+ f" source {activate}",
198
+ "fi",
199
+ SHELL_ACTIVATE_END,
200
+ "",
201
+ ))
202
+ home_path = Path(home).expanduser() if home is not None else Path.home()
203
+ for name in SHELL_RC_NAMES:
204
+ rc_path = home_path / name
205
+ if not rc_path.is_file():
206
+ result["missing"].append(str(rc_path))
207
+ continue
208
+ try:
209
+ with rc_path.open("r", encoding="utf-8", newline="") as rc:
210
+ text = rc.read()
211
+ updated = _upsert_activation_block(text, block)
212
+ if updated == text:
213
+ result["unchanged"].append(str(rc_path))
214
+ continue
215
+ _atomic_write_rc(rc_path, updated)
216
+ except (OSError, ValueError) as exc:
217
+ raise click.ClickException(f"Cannot update shell activation block in {rc_path}: {exc}") from exc
218
+ result["updated"].append(str(rc_path))
219
+ click.echo(f"Updated shell activation: {rc_path}")
220
+ if not result["updated"] and not result["unchanged"]:
221
+ click.echo("No existing shell rc file found; skipped auto-activation update.")
222
+ return result
223
+
224
+
143
225
  def setup_uv(
144
226
  venv: str | None = None,
145
227
  python_version: str = DEFAULT_PYTHON_VERSION,
146
228
  force: bool = False,
147
229
  log_level: str = "INFO",
148
- ) -> dict[str, str]:
230
+ *,
231
+ shell_activate: bool = True,
232
+ ) -> dict[str, Any]:
149
233
  _configure_logger(log_level)
150
234
  target = Path(venv or str(DEFAULT_VENV_PATH)).expanduser()
151
235
  python_version = str(python_version).strip() or DEFAULT_PYTHON_VERSION
@@ -156,12 +240,14 @@ def setup_uv(
156
240
 
157
241
  if is_chatarch_python_ready(target, python_version) and not force:
158
242
  click.echo("ChatArch Python environment already ready.")
243
+ activation = configure_shell_activation(target) if shell_activate else None
159
244
  return {
160
245
  "uv": uv_bin,
161
246
  "venv": str(target),
162
247
  "python": str(_venv_python_path(target)),
163
248
  "python_version": python_version,
164
249
  "status": "ready",
250
+ "activation": activation,
165
251
  }
166
252
 
167
253
  python_bin = _venv_python_path(target)
@@ -191,6 +277,7 @@ def setup_uv(
191
277
  f"ChatArch Python env was created at {target}, but Python {python_version} with pip was not verified."
192
278
  )
193
279
 
280
+ activation = configure_shell_activation(target) if shell_activate else None
194
281
  python_bin = _venv_python_path(target)
195
282
  click.echo(f"ChatArch Python environment ready: {target}")
196
283
  click.echo(f"Activate with: {venv_activate_hint(target)}")
@@ -201,4 +288,5 @@ def setup_uv(
201
288
  "python": str(python_bin),
202
289
  "python_version": python_version,
203
290
  "status": "created",
291
+ "activation": activation,
204
292
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chatup
3
- Version: 0.2.15
3
+ Version: 0.2.16
4
4
  Summary: ChatArch setup CLI extracted from ChatTool
5
5
  Author-email: rexwzh <1073853456@qq.com>
6
6
  License-Expression: MIT
@@ -62,6 +62,8 @@ chatup playwright install 1.61.1 -I
62
62
  chatup workspace default ~/Playground
63
63
  ```
64
64
 
65
+ `chatup uv` 默认更新已有 `~/.bashrc`、`~/.zshrc` 的自动激活块;使用 `chatup uv --no-activate` 可保持启动配置不变。不会创建缺失的 rc 文件,Windows 跳过此项。
66
+
65
67
  常见安装命令:
66
68
 
67
69
  ```bash
@@ -74,16 +76,29 @@ chatup nginx proxy-pass ./gitea-local.conf --set SERVER_NAME=gitea.local.example
74
76
  chatup crs --install-dir ~/.chatarch/crs/local --port 12392 --redis-port 6379
75
77
  ```
76
78
 
79
+ ## ChatGPT / Codex 桌面应用
80
+
81
+ ```bash
82
+ chatup chatgpt --dry-run
83
+ chatup chatgpt
84
+ chatup chatgpt --yes # Windows 首次安装时接受 Store 协议
85
+ ```
86
+
87
+ 安装包含 Codex 的新版官方 ChatGPT 桌面应用。macOS 需要 Homebrew,Windows 需要 WinGet;Linux preview 暂按[官方说明](https://learn.chatgpt.com/docs/linux/linux-app)手动安装。现有 `chatup codex` 仍安装/配置 Codex CLI。不会自动启动、登录或升级已有应用;[完整约定](https://arch.gh.wzhecnu.cn/ChatUp/commands/#chatgpt-desktop)。
88
+
89
+ `chatup codex`、`chatup hermes` 未配置模型时默认使用 `gpt-5.6-terra`;用户指定的模型和已有配置仍优先。
90
+
77
91
  ## 当前能力
78
92
 
79
93
  | 能力组 | 命令 |
80
94
  | --- | --- |
81
95
  | 基础运行环境 | `doctor`、`uv`、`nodejs`、`docker`、`zsh`、`chrome-for-testing`、`chromedriver`、`playwright`、`frp` |
82
96
  | 工作区脚手架 | `workspace` |
97
+ | 官方桌面应用 | `chatgpt`(含 Codex) |
83
98
  | 本地服务安装 | `gitea`、`discourse`、`zulip`、`mysql`、`nginx`、`crs` |
84
99
  | Agent 工具链 | `cc-connect`、`claude`、`codex`、`cursor-agent`、`opencode`、`hermes`、`lark-cli` |
85
100
 
86
- 所有新增安装项默认目录都收敛到 `~/.chatarch/...`,例如 `~/.chatarch/chrome-for-testing`、`~/.chatarch/chromedriver`、`~/.chatarch/playwright`、`~/.chatarch/chattea`、`~/.chatarch/discourse`、`~/.chatarch/zulip`、`~/.chatarch/chatdata`、`~/.chatarch/nginx` 和 `~/.chatarch/crs/local`。`cursor-agent` 额外注册 ChatEnv `CursorAgent` profile,`discourse`/`zulip` 注册管理员凭据 profile,并支持 `-e/--env` 从 env 文件或 profile 快速配置;显式选择 profile 时不会混入进程环境中的其他账号凭据。CLI 树由 ChatStyle 的注册表渲染。更完整的能力边界见 `docs/capability-map.md`。
101
+ 桌面应用使用系统包管理器的原生目录;ChatArch 自管安装项默认目录收敛到 `~/.chatarch/...`,例如 `~/.chatarch/chrome-for-testing`、`~/.chatarch/chromedriver`、`~/.chatarch/playwright`、`~/.chatarch/chattea`、`~/.chatarch/discourse`、`~/.chatarch/zulip`、`~/.chatarch/chatdata`、`~/.chatarch/nginx` 和 `~/.chatarch/crs/local`。`cursor-agent` 额外注册 ChatEnv `CursorAgent` profile,`discourse`/`zulip` 注册管理员凭据 profile,并支持 `-e/--env` 从 env 文件或 profile 快速配置;显式选择 profile 时不会混入进程环境中的其他账号凭据。CLI 树由 ChatStyle 的注册表渲染。更完整的能力边界见 `docs/capability-map.md`。
87
102
 
88
103
  ## 开发
89
104
 
@@ -29,6 +29,7 @@ src/chatup/runtime/chromedriver.py
29
29
  src/chatup/runtime/playwright.py
30
30
  src/chatup/setup/__init__.py
31
31
  src/chatup/setup/cc_connect.py
32
+ src/chatup/setup/chatgpt.py
32
33
  src/chatup/setup/claude.py
33
34
  src/chatup/setup/cli.py
34
35
  src/chatup/setup/codex.py
@@ -71,12 +72,14 @@ src/chatup/utils/custom_logger.py
71
72
  src/chatup/utils/pathing.py
72
73
  src/chatup/utils/platforming.py
73
74
  tests/test_browser_backend_cli.py
75
+ tests/test_chatgpt_setup.py
74
76
  tests/test_chrome_for_testing.py
75
77
  tests/test_chromedriver.py
76
78
  tests/test_cli_tree_docs.py
77
79
  tests/test_codex_setup.py
78
80
  tests/test_crs_setup.py
79
81
  tests/test_cursor_agent_setup.py
82
+ tests/test_hermes_model_defaults.py
80
83
  tests/test_playwright.py
81
84
  tests/test_playwright_cli.py
82
85
  tests/test_publish_workflow.py
@@ -84,6 +87,7 @@ tests/test_pyproject_metadata.py
84
87
  tests/test_service_installers.py
85
88
  tests/test_setup_cli.py
86
89
  tests/test_twikoo_setup.py
90
+ tests/test_uv_activation.py
87
91
  tests/test_version.py
88
92
  tests/test_windows_compat.py
89
93
  tests/test_workspace_setup.py