fix(cli): localize existing-server prompts for Chinese launcher

This commit is contained in:
chenmingxuan committed 2026-10-29 16:48:29 +08:00
1 parent c6d2050ff0
commit 506e3bf1c5
3 files changed
+125 -10

No files matched your search

+44 -9
View File
@@ -15,6 +15,7 @@
"""UI Launch and Web Console CLI Command (artemis ui).""" """UI Launch and Web Console CLI Command (artemis ui)."""
import logging import logging
import os
from pathlib import Path from pathlib import Path
import re import re
import shutil import shutil
@@ -34,6 +35,11 @@ import typer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def _launch_text(english: str, chinese: str) -> str:
"""Honor the language selected by the Windows launch wrapper."""
return chinese if os.environ.get("ARTEMIS_LAUNCH_LANG") == "zh-CN" else english
def running_via_console_script_shim() -> bool: def running_via_console_script_shim() -> bool:
"""Return True when this CLI was launched through a console-script ``.exe`` shim. """Return True when this CLI was launched through a console-script ``.exe`` shim.
@@ -308,7 +314,12 @@ def ui_command(
) )
if restart: if restart:
console.print(f" [yellow]🔄 Restart requested. Recycling port {port}...[/yellow]") console.print(
_launch_text(
f" [yellow]🔄 Restart requested. Recycling port {port}...[/yellow]",
f" [yellow]🔄 已请求重启,正在释放端口 {port}...[/yellow]",
)
)
load_session_reconciler() load_session_reconciler()
stop_server(port=port, timeout=12.0) stop_server(port=port, timeout=12.0)
elif is_port_in_use(port): elif is_port_in_use(port):
@@ -316,19 +327,39 @@ def ui_command(
pid_str = f" (PID: {', '.join(map(str, pids))})" if pids else "" pid_str = f" (PID: {', '.join(map(str, pids))})" if pids else ""
if sys.stdin.isatty(): if sys.stdin.isatty():
console.print( console.print(
f"\n [yellow]⚠ Port {port} is already in use by an active Artemis server{pid_str}.[/yellow]" _launch_text(
f"\n [yellow]⚠ Port {port} is already in use by an active Artemis server{pid_str}.[/yellow]",
f"\n [yellow]⚠ 检测到 ARTEMIS 服务正在运行,占用端口 {port}{pid_str}。[/yellow]",
) )
console.print(" [dim]Choose an action:[/dim]") )
console.print(" [bold]1[/bold] Open browser (default)") console.print(
console.print(" [bold]2[/bold] Restart server (stop existing and start here)") _launch_text(" [dim]Choose an action:[/dim]", " [dim]请选择操作:[/dim]")
console.print(" [bold]3[/bold] Cancel") )
console.print(
_launch_text(
" [bold]1[/bold] Open browser (default)",
" [bold]1[/bold] 打开浏览器(默认)",
)
)
console.print(
_launch_text(
" [bold]2[/bold] Restart server (stop existing and start here)",
" [bold]2[/bold] 重启服务(停止现有服务并在此启动,会中断正在运行的任务)",
)
)
console.print(_launch_text(" [bold]3[/bold] Cancel", " [bold]3[/bold] 取消"))
try: try:
choice = input(" Select [1-3, default 1]: ").strip() choice = input(
_launch_text(" Select [1-3, default 1]: ", " 请输入选项 [1-3,默认 1]:")
).strip()
except (KeyboardInterrupt, EOFError): except (KeyboardInterrupt, EOFError):
choice = "1" choice = "1"
if choice == "2": if choice == "2":
console.print( console.print(
f"\n [yellow]🔄 Terminating previous Artemis instance on port {port}...[/yellow]" _launch_text(
f"\n [yellow]🔄 Terminating previous Artemis instance on port {port}...[/yellow]",
f"\n [yellow]🔄 正在停止端口 {port} 上的现有 ARTEMIS 服务...[/yellow]",
)
) )
load_session_reconciler() load_session_reconciler()
stop_server(port=port, timeout=12.0) stop_server(port=port, timeout=12.0)
@@ -340,8 +371,12 @@ def ui_command(
return return
else: else:
console.print( console.print(
_launch_text(
f"\n [yellow]⚠ Port {port} is already in use by Artemis{pid_str}. " f"\n [yellow]⚠ Port {port} is already in use by Artemis{pid_str}. "
f"Use '[bold]artemis restart[/bold]' to restart.[/yellow]\n" "Use '[bold]artemis restart[/bold]' to restart.[/yellow]\n",
f"\n [yellow]⚠ 检测到 ARTEMIS 服务正在运行,占用端口 {port}{pid_str}。"
"如需重启,请运行 '[bold]artemis restart[/bold]'。[/yellow]\n",
)
) )
if open_browser: if open_browser:
webbrowser.open(local_url) webbrowser.open(local_url)
+5 -1
View File
@@ -8,6 +8,7 @@
- 展开后安全渲染 Markdown 正文、列表和表格,并展示已有结论及已记录依据;长正文限高滚动。 - 展开后安全渲染 Markdown 正文、列表和表格,并展示已有结论及已记录依据;长正文限高滚动。
- 列表摘要优先使用已保存的结论原因,否则使用执行错误首行;展示时移除 FlashRunner 固定技术前缀,原始记录保持不变。 - 列表摘要优先使用已保存的结论原因,否则使用执行错误首行;展示时移除 FlashRunner 固定技术前缀,原始记录保持不变。
- 仅展示已结束任务的已有信息,不新增 AI 复盘或模型请求。展开说明不触发任务切换,列表后台刷新保留展开状态。 - 仅展示已结束任务的已有信息,不新增 AI 复盘或模型请求。展开说明不触发任务切换,列表后台刷新保留展开状态。
- `start-cn.bat` 检测到正在运行的服务时,服务占用提示、打开浏览器/重启/取消菜单、输入提示及重启进度均使用中文;非交互启动提示也使用中文。默认仍只打开浏览器,选择重启才停止现有服务。
本版不改变任务执行、模型重试或批次停止策略,继续保留 2.4.3 的异常输出隔离与参数保护。 本版不改变任务执行、模型重试或批次停止策略,继续保留 2.4.3 的异常输出隔离与参数保护。
@@ -16,10 +17,13 @@
- 前端回归:353 项通过,生产构建通过。 - 前端回归:353 项通过,生产构建通过。
- 内置浏览器使用隔离样例验证默认折叠、Markdown 展开、长内容、无错误完成任务、批次布局、浅色/深色与窄视口。 - 内置浏览器使用隔离样例验证默认折叠、Markdown 展开、长内容、无错误完成任务、批次布局、浅色/深色与窄视口。
- 最终源码 ZIP 的隔离解压、独立空 npm 缓存、官方源与国内镜像安装和生产构建结果随附件 `INSTALL-VERIFICATION.md` 提供。 - 最终源码 ZIP 的隔离解压、独立空 npm 缓存、官方源与国内镜像安装和生产构建结果随附件 `INSTALL-VERIFICATION.md` 提供。
- 未复跑用户业务任务、调用真实模型或进行高并发测试;本次没有 Python 行为变更,未重跑 Python 全量回归。 - 启动语言与 Windows 启动脚本定向回归 31 项通过,Ruff 格式与静态检查、质量基线及核心类型检查通过。Python 默认确定性回归结果随安装验证附件提供。
- 未复跑用户业务任务、调用真实模型或进行高并发测试。
## 升级 ## 升级
已有安装请等待任务结束,运行 `update-cn.bat` 或 `update.bat` 后重启。新安装下载 `artemis-2.4.4-source.zip`,按 `SHA256SUMS.txt` 校验后启动。请勿直接覆盖旧安装目录。 已有安装请等待任务结束,运行 `update-cn.bat` 或 `update.bat` 后重启。新安装下载 `artemis-2.4.4-source.zip`,按 `SHA256SUMS.txt` 校验后启动。请勿直接覆盖旧安装目录。
2026-10-29 撤回首次发布的 2.4.4,补充中文启动提示后重新发布。此前下载过 2.4.4 的用户请重新下载并核对最新校验文件,或使用更新脚本获取最新提交。首次发布包的 SHA256 为 `9a2a678c0cf97b5de140479a3f3946171d20d19efaf3ad249c708ed35a06e67a`,该包已撤回。
人工评审插件 1.1.0 无需更新。本次发布不构建 Docker,不重启用户服务。 人工评审插件 1.1.0 无需更新。本次发布不构建 Docker,不重启用户服务。
+76
View File
@@ -109,6 +109,82 @@ def _recording_console():
return Console(file=io.StringIO(), force_terminal=False, width=120) return Console(file=io.StringIO(), force_terminal=False, width=120)
@pytest.mark.parametrize("language", ["zh-CN", "en"])
@pytest.mark.parametrize("choice", ["", "2", "3"])
def test_existing_server_prompt_respects_launch_language(monkeypatch, language, choice):
from artemis.runtime import server_lifecycle
console = _recording_console()
monkeypatch.setenv("ARTEMIS_LAUNCH_LANG", language)
monkeypatch.setattr(ui, "Console", lambda: console)
monkeypatch.setattr(ui.sys.stdin, "isatty", lambda: True)
monkeypatch.setattr(server_lifecycle, "is_port_in_use", lambda port: True)
monkeypatch.setattr(server_lifecycle, "find_server_pids", lambda port: [1234])
prompts, opened, stopped = [], [], []
monkeypatch.setattr("builtins.input", lambda prompt: prompts.append(prompt) or choice)
monkeypatch.setattr(ui.webbrowser, "open", opened.append)
monkeypatch.setattr(ui, "load_session_reconciler", lambda: None)
class RestartSelected(Exception):
pass
def stop_server(**kwargs):
stopped.append(kwargs)
raise RestartSelected # Never launch or stop a real service.
monkeypatch.setattr(server_lifecycle, "stop_server", stop_server)
if choice == "2":
with pytest.raises(RestartSelected):
ui.ui_command(port=8123)
else:
ui.ui_command(port=8123)
output = console.file.getvalue()
assert "8123" in output and "PID: 1234" in output
if language == "zh-CN":
assert all(
text in output
for text in ("服务正在运行", "请选择操作", "打开浏览器(默认)", "重启服务", "取消")
)
assert "请输入选项" in prompts[0]
assert "Choose an action" not in output and "Restart server" not in output
if choice == "2":
assert "正在停止端口 8123" in output
else:
assert "Choose an action" in output and "Open browser (default)" in output
assert "Select [1-3, default 1]" in prompts[0]
assert opened == (["http://localhost:8123"] if not choice else [])
assert stopped == ([{"port": 8123, "timeout": 12.0}] if choice == "2" else [])
@pytest.mark.parametrize("language", ["zh-CN", None])
@pytest.mark.parametrize("open_browser", [True, False])
def test_existing_server_noninteractive_does_not_restart(monkeypatch, language, open_browser):
from artemis.runtime import server_lifecycle
console = _recording_console()
if language:
monkeypatch.setenv("ARTEMIS_LAUNCH_LANG", language)
else:
monkeypatch.delenv("ARTEMIS_LAUNCH_LANG", raising=False)
monkeypatch.setattr(ui, "Console", lambda: console)
monkeypatch.setattr(ui.sys.stdin, "isatty", lambda: False)
monkeypatch.setattr(server_lifecycle, "is_port_in_use", lambda port: True)
monkeypatch.setattr(server_lifecycle, "find_server_pids", lambda port: [])
monkeypatch.setattr(
server_lifecycle, "stop_server", lambda **kwargs: pytest.fail("Unexpected restart")
)
opened = []
monkeypatch.setattr(ui.webbrowser, "open", opened.append)
ui.ui_command(port=8123, open_browser=open_browser)
output = console.file.getvalue()
assert "artemis restart" in output
assert ("服务正在运行" if language else "already in use") in output
assert opened == (["http://localhost:8123"] if open_browser else [])
def test_ensure_showcase_built_skips_install_when_deps_current(monkeypatch): def test_ensure_showcase_built_skips_install_when_deps_current(monkeypatch):
steps = [] steps = []
monkeypatch.setattr(ui, "_showcase_build_required", lambda showcase_dir: True) monkeypatch.setattr(ui, "_showcase_build_required", lambda showcase_dir: True)